الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

جلسة تعارف مع مكتبة Tokamak الفيزيائية

مغلق
بدأه Abdullah.Alshammeri في 20 أكتوبر 2006 · 10 رد · 3,773 مشاهدة · في قسم برمجة الألعاب و الرسوميات العام
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

السلام عليكم .

الموضوع باختصار هو محاولة فهم عمل المكتبة الفيزيائية Tokamak .

أضفت هذا الموضوع بناءً على هذا السؤال .

/index.php?showtopic=106791

الموضوع لن يكون دروس تلقينية كما هي العادة .. سأطرح أمثلة .. وأضيف عليها تعليقات .. وأضيف عليها تساؤلات ..

يعني ستحتاج أن تبذل جهد لكي تستخلض المعلومة ..

أيضا .. أي شخص يستطيع الاستفادة من هذا الموضوع .. سواءً محبي OpenGL او Directx او vb او python ... وأي لغة تحتوي على جرافكس .

----

مقدمة : لماذا Tokamak ؟

هذا أول سؤال يمكن طرحه .. لماذا اخترت هذه المكتبة الفيزيائية من أصل عشرات المكتبات .

الجواب ستعرفه بعد أن تقرأ هذه القصة المملة :

قمت بتحميل عدة مكتبات .. منها :

Novodex : من أروع المكتبات الموجودة .. كنت أنوي بالفعل تعلمها ,, ميزتها كثرة الامثلة وقوة الhelp في الموقع .

لكن بعد ان قامت الشركة ageia بشراء هذه المكتبة .. أصدرت اصدار جديد لا يقل روعة .

لكن المشكلة ان الاصدار الجديد .. كل الامثلة عليه هو من اصدار سابق ,, فحصل عندي fatal error في رأسي .

ومشاكل لاتعد ولاتحصى من صعوبة التثبيت الى تشابك الامثلة .

المهم عرفت في النهاية أن حجم هذه المكتبة أكبر من حجمي .. وتركتها .

ODE - OPAL : هما مكتباتان متشابهتان .. احدها مبنية على الاخرى( OPAL مبنية على ODE ) .

كنت أطمح بالفعل لاتقان هاتين المكتبتين .. قلبت الدنيا فوق تحت لاجد مثال واحد فقط ... لم اجد ؟؟

فتركتهما .

Tokamak : مكتبة ليست بحجم المكتبات الاخرى .. لكنها تعتبر سهلة وعملية بنفس الوقت .

استطعت أن أفهم أول مثال يتكلم عنها ... وبمجرد أن تعلمت أول مثال ...

اكتشفت أني فهمت عمل :

مكتبة ODE + OPAL + Tokamak .

بالتالي سأعرض لكم الطريقة التي من خلالها استطعت أن أفهم طريقة عمل المكتبات الفيزيائية .

لو فهمتوا هذه الطريقة ستجدون أنكم قادرون على التعامل مع أي مكتبة فيزيائية .

أولا لمبرمجي لغة السي ,,,

حمل الSDK للTokamak من خلال هذا الموقع ..

ولايحتاج أن تحمل الامثلة فهي معقدة للغاية .

http://www.tokamakphysics.com/

ثانيا مصادر التعلم :

http://www.adamdawes.com/programming/tokam..._Framework.html

هذه أفضل الدروس على الاطلاق .. وتعلمت مفهوم عمل المكتبة من خلاله .

الدروس على dx ولكنه مفيد للجميع .

وهذا ...

فيه أمثلة للمكتبة للopengl و dx ..

هنا opengl سوف تجد مثالين حملهم .

http://www.codesampler.com/oglsrc/oglsrc_13.htm

وهنا للdx ستجد الامثلة في القائمة ايضا .

http://www.codesampler.com/dx9src/

هنا أمثلة على opengl امثلة جيدة .

http://www.codecolony.de/Tokamak/

http://aaocos.free.fr/flipper/flipper_tokamak_src.rar

أما لمبرمجي الvb : :

فأذكر أني وجدت أمثلة خاصة للفيبي .. أعدت البحث الان في قووقل ولكن لم اجدها .. سأبحث في جهازي لعلي لم أحذفها .

---------------------------

تثبيت المكتبة لمبرمجي السي :

التثبيت سهل .. يوجد ملفات header انسخها لمجلد الinclude او اربطها من خلال المترجم .. وكذلك كلفات الlib .

وملفات الdll انسخها الى system32 .

-----------------------------------

الان لنبدأ بالتعرف على المكتبة ...

المكتبة تستعمل أنواع من المتغيرات خاصة بها .. أهم الانواع هي كالتالي

typedef unsigned int		u32;
typedef float			   f32;
typedef double			  f64;

ايضا يوجد تركيب اسمه neV3

هذا تركيب المتجه ..

نحن في الغالب سنستعمله ليخزن لنا ثلاث قيم

مثلا

x - y - z .

او يمكن استخدامه لتخزين ابعاد مكعب مثلا

width -- height - depth

مثلا تكتب شيء شبيه بالتالي :

neV3 Pos; 

Pos.Set( x , y , z );

الان ستجد القيم مخزنة في تركيب داخل ذلك التركيب

struct neV3
{
public:

	union
	{
		f32 v[4];

		struct nTag
		{
			f32 X,Y,Z,W;
		}n;
	};
.
.
}

بمعنى أن هذا الneV3 تستعمله حسب الرغبة وحسب ما يقتضيه الموقف .

ثانيا : كيفية عمل المكتبة Tokamak .

يوجد كلاس اسمه neSimulator .

هذا هو القلب النابض والمدير العام للمكتبة ..

من خلاله تقوم باعداد قيمة الجاذبية واتجاهها .

من خلاله تحدد عدد المجسمات التي ستدخل تحت تأثير الفيزياء ( من تصادم ونحوه ) .

من خلاله تنشئ الكائنات الاخرى كما سترى بعد قليل .

بالتالي ستجد انك عندما تنشئ كائنات أخرى فانك بطريقة مباشرة او غير مباشرة تربطها بالكائن neSimulator .

الكلاس neRigidBody .

هذا الكلاس هو خاص بالمجسمات المتفاعلة في فضاء اللعبة ..

فمثلا عندك مجسم عبارة عن كرة ... وتريد أن تسقط هذه الكرة على أرضية الملعب ..

الان أين هو neRigidBody ؟؟ هل هو الكرة أو أرضية الملعب . . .

الجواب .. neRigidBody هنا هو الكرة ... لان الكرة تجري عليها الاحداث من ارتداد وطاقة وتسارع الخ ...

الكلاس neAnimatedBody .

نرجع لمثالنا قبل قليل .

عندك مجسم عبارة عن كرة ... وتريد أن تسقط هذه الكرة على أرضية الملعب ..

نحن قلنا أن neRigidBody الان هو الكرة ..

بينما neAnimatedBody هنا هو أرضية الملعب .

بالتالي نستطيع التفريق بين neAnimatedBody و neRigidBody بأن الاول ثابت مجرد مجسم يصطدم به مجسم اخر وهو لايتفاعل ..

بينما neRigidBody كائن يتفاعل مع محيطه .. ويكتسب سرعة وتسارع .. ويتأثر بالجاذبية ومن هذا الكلام ..

أخيرا ,,,

نحن علمنا فائدة neRigidBody و neAnimatedBody لكن .. هذين الكائنين فقط يقومان بتحديد .. حجم المجسم .. موقعه .. ونحو ذلك .

لكنهما لايعلمان شيء عن شكل المجسم نفسه ..

هل هو اسطواني .

هل هو مكعب .

هل هو كرة ..

الخ .

اذا يوجد كائن اخر يحدد شكل المجسم الذي نريد عمله .. سواء أكان هذا المجسم neAnimatedBody او neRigidBody ..

الكلاس اسمه :

neGeometry

الخلاصة ..

neSimulator هو كلاس يقوم بادارة المحرك ككل .. ويقوم بتحديث احداثيات المجسمات ويوجد نوعين من المجسمات يمكن اضافتهما الى neSimulator وهما neRigidBody و neAnimatedBody ولتحديد شكل المجسمات نستخدم الكائن neGeometry وهو يسمح لنا بانشاء ثلاث مجسمات: box - cylinder - sphere .. وبامكاننا انشاء مجسمات أعقد .

بعد كل هذه الدورة الكاملة... تخرج لنا مكتبة tokamak احداثيات الاجسام التي قامت بمعالجتها ..

الاحداثيات عادةً تكون مخزنة في مصفوفة matrix .. وهي نفسها المصفوفة التي تتعامل معها opengl أو directx . .

أيضا يجب أن تعرف أنت كيف ترسم كرة .. مكعب .. متوازي مستطيلات .. وغيرها من الاشكال .. وتوفر opengl و dx طرق جاهزة لعمل ذلك .. أو أن تقوم انت بنفسك برسم هذه المجسمات ,

إلى هنا ينتهي الكلام النظري .. التطبيق القادم سيكون عملي إن شاء الله .

تم تعديل هذه المشاركة بواسطة الشمري في 21 أكتوبر 2006 في 21:26

logo1.png تطبيق طمأنينة ، نسخة بيتا على أندرويد

عبدالله الشمّري - Al-Shammari

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

#2

السلام عليكم

جميل أخي الشمري

ولكن للأسف أمثلة المكتبة ماتشتغل لما أترجم الكود بالمترجم

#3

أي أمثلة تقصد .. اذا كانت الامثلة اللي تجي مع المحرك فلا تهتم لها كثيرا ..

انظر للامثلة الاخرى في المواقع الاخرى فهي أسهل.

logo1.png تطبيق طمأنينة ، نسخة بيتا على أندرويد

عبدالله الشمّري - Al-Shammari

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

#4

السلام عليكم

هكذا نحن على الطريق الصحيح ارجو ان تكون كل الموضوعات على هذا النحو وللاسف لا استطيع المشاركه حتى اشترى جهاز جديد :angry:

163374357.gif

#5

Thank too much bro

حـــــــيـــــــدر

1042922905_iai_killua.jpg
#7

تطبيق :

أفضل مثال هو أبسط مثال .. هذا بمثابة hello wolrd للمكتبة .

نريد أن نرسم كرة( عبارة عن sphere ) + أرضية ( عبارة عن متوازي مستطيلات box ) .

كما في الصورة .

AABB.JPG

حيث ستسقط الكرة للاسفل ( باتجاه الجاذبية) وتصطدم بالارضية وتتوقف .

الكود سيتم وضعه في ملف اسمه tok.h هذا فيه كل التطبيق ..

وبعدها نستدعي دالة وحده للرسم ..

بهذا الشكل

initPhysics()
{
.
.
.
}

initBall()
{
.
.
.
}

initFloor()
{
.
.
}

Draw()
{
.
.
}

KillPhysics()
{


 }

ثم في الملف main.cpp

نستدعي هذه الدوال قبل الدخول في الloop إلا الدالة Draw فنستدعيها أثناء الحلقة التكرارية LOOP

---------------

الملف tok.h

---------------

#include "tokamak.h"
#pragma comment (lib, "tokamak.lib")

ربطنا المكتبة

neSimulator *simTok=NULL;
neRigidBody *rgdBall;
neAnimatedBody *aniFloor;

ثلاث كائنات شرحناها

bool initPhysics(void)
{
	  //   bodies * (bodies-1) / 2


	neSimulatorSizeInfo SizeInfo; 
	neV3 gravity; gravity.Set(0.0f, -9.8f, 0.0f);
	SizeInfo.animatedBodiesCount=1;
	SizeInfo.rigidBodiesCount   =1;
 	int totalBody = SizeInfo.rigidBodiesCount + SizeInfo.animatedBodiesCount;
	SizeInfo.geometriesCount	=totalBody;
	SizeInfo.overlappedPairsCount=totalBody *(totalBody-1) /2;

	simTok = neSimulator::CreateSimulator(SizeInfo,NULL,&gravity);

	return true;
}

قمنا بإعداد الكائن الاول والرئيسي وهو simTok

أولا نقوم بإنشاء تركيب من نوع neSimulatorSizeInfo وسنملأه بالمعلومات التي نريد ثم نمرره للدالة CreateSmulator

المعلومات المطلوبة ..

----- 1 ------

الجاذبية اتجاهها وقيمتها .. ونحن ترحينا الواقعية فجعلنا اتجاهها للاسفل بجعل قيمتها سالبة وللاسفل ..

وأكيد أنك تعرف نظام الاحداثيات .

neV3 gravity; gravity.Set(0.0f, -9.8f, 0.0f);
SizeInfo.animatedBodiesCount=1;

---------------

--------2--------

ثم نعين عدد الكائن اللي من نوع neRigidBody وهو هنا كرة واحدة أي مجسم واحد

SizeInfo.animatedBodiesCount=1;

-----------------

--------3--------

وبالمثل نعين عدد الneAnimatedBody وهو هنا أرضية واحدة أي جسم واحد

SizeInfo.rigidBodiesCount   =1;

-----------------

--------4--------

ثم نمرر عدد المجسمات ككل التي تريد إدخالها تحت المحاكاة الفيزيائية

int totalBody = SizeInfo.rigidBodiesCount + SizeInfo.animatedBodiesCount;
SizeInfo.geometriesCount	=totalBody;

-----------------

--------5--------

ثم تطبق قاعدة أساسية من مبرمج مكتبة Tokamak حيث

SizeInfo.overlappedPairsCount = bodies * (bodies-1) / 2

أي يصبح هكذا

SizeInfo.overlappedPairsCount=totalBody *(totalBody-1) /2;

----------------

--------6 --------

ثم نمرر هذا التركيب + متجه الجاذبية + قيمة البارمتر الثاني دائما NULL

simTok = neSimulator::CreateSimulator(SizeInfo,NULL,&gravity);

وبالتالي نحن الان أنشأنا عالم فيزيائي واقعي .

-----------------

-----------------------------------------======-----------------------------------

======================------------===================

[]الدالة الثانية : إنشاء مجسم من نو ع neRigidBody وهو الكرة .. وتحديد خصائصها[]

مثل ما قلنا في المشاركة السابقة .. تتميز مجسمات الneRigidBody بأنها تتأثر بالجاذبية + التسارع + أمور أخرى بعكس الاجسام التي من نوع neAnimatedBody

bool initBall(void)
{
 	//SetupBall

	rgdBall = simTok->CreateRigidBody();
	neGeometry *geoBall=rgdBall->AddGeometry();
	geoBall->SetSphereDiameter(1.5f);
	rgdBall->UpdateBoundingInfo();
	rgdBall->SetMass(2.0f);
	rgdBall->SetInertiaTensor(neSphereInertiaTensor(1.5f,2.f));

	neV3 ballPos;
	ballPos.Set(0.0f,100.0f,-50.0f);
	rgdBall->SetPos(ballPos);

	return true;

}

----- 1 --------

ننشئ الكائن بهذه الطريقة .. وهي دائما ثابتة

rgdBall = simTok->CreateRigidBody();

==========

----- 2 --------

كما قلنا في السابق .. يجب تحديد شكل هذا الكائن وذلك عن طريق كائن آخر من نوع neGeometry

neGeometry *geoBall=rgdBall->AddGeometry();

الان إذا حددنا شكل geoBall سيستقبل ذلك الكائن rgdBall

==========

----- 3 --------

نحن نريد إنشاء شكل كرة .. نصف قطرها 1.5

geoBall->SetSphereDiameter(1.5f);

==========

----- 4 --------

الان نحدث البيانات ( نخبر الكائن rgdBall بأننا حددنا الشكل المطلوب فيقوم بتخزين أبعاده ..

rgdBall->UpdateBoundingInfo();

==========

----- 5 --------

نضيف كتلة معينة للجسم + قيمة أخرى لا أعرف حتى الان فائدتها

rgdBall->SetMass(2.0f);
rgdBall->SetInertiaTensor(neSphereInertiaTensor(1.5f,2.f));

==========

----- 5 --------

الان نحدد موقع الجسم .. عن طريق تخزينه في تركيب neV3

neV3 ballPos;
ballPos.Set(0.0f,100.0f,-50.0f);
rgdBall->SetPos(ballPos);

رفعنا موقعه وجعلناه للاعلى .. بالتالي فهو سيتأثر بالجاذبية وستجلبه للاسفل ,,

انتهينا من الكرة وإعدادها

==========

ملاحظة هامة :

نحن لم نقم برسم أي مجسم .. كل ما عملناه هو كرة وهمية لاوجود لها .. الهدف فقط أن نحصل على احداثياتها في النهاية ..

ثم نرسم كرة حقيقية باستخدام dx أو GL ونعين الاحداثيات التي حصلنا عليها .

-----------------------------------------======-----------------------------------

======================------------===================

الدالة الثالثة : وهي التي ستقوم بإعداد الارضية .. شكلها + موقعها

افرح لايوجد شيء جديد بنفس الخطوات السابقة

bool initFloor()
{

	neV3 floorPos;
	aniFloor=simTok->CreateAnimatedBody();
	neGeometry *geoFloor=aniFloor->AddGeometry();
	geoFloor->SetBoxSize(30, 1.0f, 30);
	aniFloor->UpdateBoundingInfo();
	floorPos.Set(0.0f,-6.0f,-50.0f);
	aniFloor->SetPos(floorPos);
	return true;
}

يوجد عدة اختلافات بسيطة منها

1- أي كائن من نوع neAnimatedBody ليس له كتلة - mass- لانه ثابت لا يتأثر بالعوامل الفيزيائية

2- اخترنا أن يكون شكل الجسم صندوق - box- وحددنا عرضه + ارتفاعه + عمقه

geoFloor->SetBoxSize(30, 1.0f, 30);

-----------------------------------------======-----------------------------------

======================------------===================

الدالة التالية تقوم برسم المجسمات حقيقة وقد استخدمنا نحن opengl

void Draw()
{
   glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
   glLoadIdentity();



	//BALL
	glPushMatrix();
		  neV3 ballPos=rgdBall->GetPos();
		glTranslatef(ballPos.n.X,ballPos.n.Y,ballPos.n.Z);
 		auxSolidSphere(1.5f);
	glPopMatrix();

رسمنا الكرة عن طريق أحد دوال opengl الجاهزة

وحددنا موقع الكره ... لاحظ أخي كيف حصلنا على احداثيات الكره الوهمية الفيزيائية ووضعنا احداثياتها كاحداثيات الكرة الحقيقية

هنا الخدعة :)

وبالمثل نعمل مع الارضية

	glPushMatrix();

		neV3 floorPos;
		floorPos=aniFloor->GetPos();
		glTranslatef(floorPos.n.X,floorPos.n.Y-1  ,floorPos.n.Z);
   	 	glScalef(30.0f,1.0f,30.0f);
		auxSolidCube(1);

	glPopMatrix();

لكن الفرق أننا قمنا بتحجيم الجسم

ولاحظ أن التحجيم هو بنفس القيم التي أسندناها للارضية الفيزيائية الوهمية

glScalef(30.0f,1.0f,30.0f);
.
.
geoFloor->SetBoxSize(30, 1.0f, 30);

حيث مدّينا الاحداثي السيني والعمق .. وتركنا الارتفاع كما هو لنحاكي الارضية ,,

( تستطيع أن تبني دالة جاهزة لرسم متوازي مستطيلات ,, وهي بنفس طريقة رسم مكعب ,, وعندها لن تحتاج للدالة glScalef)

أخيرا في دالة Draw

   simTok->Advance(0.02);
}

الدالة السابقة موجودة في الكائن الرئيسي .. مهمتها تحديد وقت تحديث بيانات المحاكي neSimulator

لماذا مررنا الرقم 0.02 .؟؟

الافضل أن يكون التحديث كل

1/60 = 0.01

طبعا يصبح بأجزاء الثانية .

لكن انا زدت من القيمة لزيادة الواقعية ..

ولكن توجد طريقة أفضل وهي التي ستعمل بسرعة ثابتة وعلى كل الاجهزة . وهي باستعمال طرق خاصة سنذكرها في تطبيق لاحق ..

أخيرا الدالة التي تمسح آثار المحرك

void KillPhysics()
{
   if (simTok)
  {
	neSimulator::DestroySimulator(simTok);
	simTok = 0;
  }
}

ثم نستدعي تلك الدوال في الملف main.cpp

هذا أطول تطبيق ...

المرفقات ... التطبيق باستخدام glut أو win api مع opengl .. اختر ما تريد .

tokamak_glut_1.zip

tokamak_API_1.zip

تم تعديل هذه المشاركة بواسطة الشمري في 22 أكتوبر 2006 في 21:49

logo1.png تطبيق طمأنينة ، نسخة بيتا على أندرويد

عبدالله الشمّري - Al-Shammari

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

#8

وش رايكم فيني :lol: :P... اخخخ ..هذا واحد من الزملاء رافع ضغطي ..اذا عمل شيء قال وش رايكم فيني :D

المهم ,,

أحس الموضوع فاشل .. لان كثرة القرقرة تفسد الموضوع مثل ما كثرة الطباخين تفسد الطبخة ..

أوعدكم .. التطبيقات الجاية ..ستكون مختصرة ,

لكن انا اكتشفت مميزات أخرى لهذه المكتبة .. منها عملية تطبيق ( السير على التضاريس ) .. تسهلها علينا هذه المكتبة

logo1.png تطبيق طمأنينة ، نسخة بيتا على أندرويد

عبدالله الشمّري - Al-Shammari

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

#9

هذا تطبيق آخر ..

كنا في السابق نحصل على موقع الجسم فقط ..

الان سنحصل على مقدار الازاحة + التدوير الذي حصل للجسم .

  neT3 t; 
   t = aniFloor->GetTransform();
   float matrix[16]={ t.rot[0][0],t.rot[1][0],t.rot[2][0],0.0f,
					  t.rot[0][1],t.rot[1][1],t.rot[2][1],0.0f,
					  t.rot[0][2],t.rot[1][2],t.rot[2][2],0.0f,
					  t.pos[0]  , t.pos[1]-0.5f ,t.pos[2]  , 1.0f};

  glPushMatrix();
		glMultMatrixf(matrix);
   		glScalef(30.0f,1.0f,30.0f);
		auxSolidCube(1);
  glPopMatrix();

نحصل على المصفوفة الخاصة بالشكل الفيزيائي الوهمي .. ثم مضربها بمصفوفة شكلنا

glMultMatrixf(matrix);

-------

ونفس الشيء مع بقية الاشكال ..

================

التغيير الاخر .. هو أننا حسبنا

simTok->Advance(newElapsed);

بشكل أفضل وبمراحل من طريقتنا السابقة ( كنا نختار قيمة 0.01 ) ولا اخفيك أنني ما أعرف بالظبط كيف حصلنا على ذلك

لاحظ :

float GetElapsedTime()
{
  static int oldTime = GetTickCount();
  int newTime = GetTickCount();
  float result = (newTime - oldTime) / 1000.0f;
  oldTime = newTime;
  return result;
}
void UpdateObjects()
{
   //*************	NEW  NEW   **************/
 static float oldElapsed;
  float newElapsed = GetElapsedTime();
  if (oldElapsed != 0.0f)
  {
	 if (newElapsed < 0.8f * oldElapsed)
	  newElapsed = 0.8f * oldElapsed;

	if (newElapsed > 1.2f * oldElapsed)
	  newElapsed = 1.2f * oldElapsed;

	 if (newElapsed > 1.0f/45.0f)
	  newElapsed = 1.0f/45.0f;
  }
  oldElapsed = newElapsed;

  simTok->Advance(newElapsed);
  //*************	NEW  NEW   **************/

}

طبع الفرق الاخر .. أننا اضفنا شكل ( مكعب ) يسقط أرضا .. وستشاهد كيف استطاعت مكتبتنا .. من محاكاته بشكل رائع .

حمل المرفقات . . . وكل التغييرات في الملف tok.h .

post-42837-1161862543_thumb.jpg

tokamak_API_2.zip

tokamak_glut_2.zip

تم تعديل هذه المشاركة بواسطة الشمري في 26 أكتوبر 2006 في 15:12

logo1.png تطبيق طمأنينة ، نسخة بيتا على أندرويد

عبدالله الشمّري - Al-Shammari

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

#11

بسم الله

الموضوع ده قديم جدا

هذا الموضوع مغلق.

مواضيع مشابهة

عدد الزوار حالياً

المتواجدون خلال آخر دقيقتين · يتحدّث كل ٣٠ ثانية

—الإجمالي—أعضاء مسجّلون—زوار بدون تسجيل

جارٍ التحقق من المتواجدين…