الشبكة العربية لمطوري الألعاب

خبير مدير همام البهنسي مشاركة 1

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

توضيحاً لفكرة مشروع تعلم الـ scripting في برامج الرسوميات أود في هذه المشاركة توضيح آلية العمل المقترحة من خلال أخذ أحد الأدوات الموجودة ضمن مكتبة الـ http://netview.inframez.com والتوضيح من خلالها.

الأداة التي سنتحدث عنها: Bones2Chains.
الرابط للتفاصيل:
http://netview.inframez.com/xsitools/bones2chains.htm

كما هو واضح في الرابط، تقوم هذه الأداة بإضافة ميزة لطيفة لبرنامج الـ XSI بحيث يمكن للمستخدم إنشاء سلسلة معدنية وربط عناصرها بهيكل حركة بحيث تكون قابلة للتحريك أو المحاكاة الفيزيائية كسلسة معدنية حقيقية.

مايهمنا هنا، هو التعرف على آلية عمل الأداة ووظيفتها حيث سنقوم في الخطوة الأولى بقراءة الكود المكتوب وتحليله وفهمه بشكل جيد ومحاولة تحسينه.

في حال أداة Bones2Chains سنلاحظ أننا سنتعرف على الكثير من المفاهيم الهامة للتطوير في برامج الرسوميات مثل الـ XSI.
بعض المفاهيم الموجودة في هذه الأداة:
- التعامل مع العناصر البرمجية الأساسية في الـ XSI مثل XSICollection, X3DObject, …
- التعرف على كيفية الحصول على مدخلات من المستخدم بشكل تفاعلي.
- إنشاء واجهات ونوافذ لتحديد المدخلات.
- إضافة لعدد من المفاهيم البرمجية العامة.

بعد الانتهاء من هذه المرحلة سننتقل للمرحلة الثانية وهي إعادة كتابة نفس هذه الأداة ولكن هذه المرة في المايا بحيث نقوم بتطبيق جميع المفاهيم البرمجية التي تعلمناها في الـ XSI ولكن هذه المرة بيئة مختلفة وهي المايا.

بهذه الطريقة يفترض أن تتشكل لدينا معرفة لابأس بها بمفاهيم البرمجة العامة ضمن برامج الـ 3D إضافة لخبرة عملية في الـ SDK الخاصة بكل من الـ XSI والمايا.

في المشاركة التالية سأقوم بإذن الله بكتابة شرح مختصر لكيفية استخدام الأداة لنتمكن من التعرف على وظيفتها بشكل عملي أفضل.
إلى اللقاء إذن...

خبير مدير همام البهنسي مشاركة 2

استكمالاً للعرض السابق حول أداة Bones2Chains، في هذه المشاركة سنأخذ جولة سريعة في كيفية استخدام هذه الأداة ليتم للجميع اختبارها بشكل عملي والتعرف على خصائصها بشيء من التفصيل.

بداية سنبدأ بالتعرف على المدخلات التي يُطلب من المستخدم تحديدها والتي يلخصها الرسم التوضيحي التالي:



بالطبع بالإضافة للبارمترات السابقة، هذه الأداة تطلب من المستخدم تحديد سلسلة العناصر التي ستسخدم كهيكل للتحريك (Animation Rig).

لاختبار الأداة إذن نحن بحاجة بالبداية لانشاء هيكل الحركة المطلوب. بالنسبة لهذه الخطوة لدينا احتمالين:
الأول إنشاء هيكل حركة ديناميكي يعتمد على المحاكاة الفيزيائية أو الاحتمال الثاني هو انشاء هيكل حركة بسيط.

لتبسيط الاختبار سنقوم ببناء هيكل حركة بسيط بواسطة الأمر:
Skeleton -> 2D Chain



ثم تحديد نقط المفاصل للهيكل ضمن أحد منافذ العرض (على سبيل المثال Front Viewport)



يمكننا إضافة أي عدد من المفاصل بواسطة ضغط زر الماوس الأيسر في المكان المطلوب. لإنهاء العملية يمكن الضغط على زر escape أو الزر الأيمن للماوس.

الآن جاء دور الأداة الجديدة التي نود اختبارها...

بعد تحميل الأداة من مكتبة الـ http://netview.inframez.com أو مباشرة من الرابط:
http://www.inframez.com/netview/xsitools/bones2chains.vbs

كل ما علينا هو فقط تنفيذها من الـ Script Editor الخاص ببرنامج الـ XSI.
- للوصول للـ Script Editor علينا طلبه إما من القائمة View->Scripting->Script Editor أو من الاختصار Alt+4.
- فتح ملف الـ script الذي قمنا بتحميله في الـ Script Editor من القائمة File->Open.
- تنفيذ الـ script إما من القائمة: Edit->Run أو بالاختصار F5.

عند تنفيذ الـ script سيُطلب منا اختيار أي عنصر من عناصر هيكل الحركة الذي قمنا بإنشاءه مسبقاً. استجابة لهذا الأمر يمكننا اختيار أحد العناصر بالزر الماوس الأيسر.
في حال كان اختيارنا صحيح، ستظهر نافذة صغيرة لتحديد البرامترات المطلوبة من المستخدم والمطابقة لتلك الموضحة سابقاً في الرسم.



بعد هذه المرحلة سنلاحظ كيف يقوم الـ script بالمعالجة وإنشاء الحلقات المعدنية للسلسة وربطها بشكل صحيح مع عناصر هيكل الحركة المناسبة.



الآن وبعد كل هذا، سنحصل على نتيجة لا بأس بها لسلسة معدنية جاهزة للتحريك ببساطة. يمكننا قضاء بعض الوقت باللهو قليلاً بتحريك هذه السلسلة من خلال مفصل النهاية (end-effector).

طبعاً قد تكون الخطوات السابقة لشرح كيفية تنفيذ script في الـ XSI بديهية بالنسبة لمستخدمين البرنامج ولكن يجب أن لا ننسى ضيوفنا الجدد القادمون من الفريق البرمجي والذين قد لا يملكون خبرة عملية بمثل هذه البرامج.

على كل حال... إلى هنا نكون قد أنجزنا مرحلة التعرف على وظيفة الأداة وآلية عملها، في المشاركة التالية إن شاء الله سنبدأ باستعراض الكتل البرمجية الأساسية للـ script والتعليق عليها بشكل مختصر. هذا يعني أنه على أصدقائنا المبرمجين الاستعداد للمساعدة لأننا على وشك الاقتراب من منطقتهم المفضلة (الكود). ☺

إلى لقاء قريب بإذن الله...

خبير مدير همام البهنسي مشاركة 3

في هذا الجزء من النقاش سنبدأ مباشرة بإلقاء نظرة سريعة على الكود المكتوب في هذا الـ script وتحليله للتعرف على المفاهيم الهامة التي علينا التركيز بها في الـ XSI SDK:

الجزء الأول من الكود يشمل مفهوم هام، عادة يكون هو نقطة البداية المنطقية لأي أداة في برامج الـ 3D. المفهوم هو تخزين الأجسام التي تم اختيارها مسبقاً من قبل المستخدم في المشهد ضمن متغير برمجي للتعامل معها لاحقاً.
 

set oSelection = CreateObject("XSI.Collection") ‘ تعريف متغير خاص لتخزين مجموعة عناصر
set oSelection = Application.Selection ‘ الحصول على العناصر المنتقاة وتخزينها في المتغير
 
الجزء التالي مباشرة من الكود يقوم بالتحقق من محتوى المتغير oSelection ونداء عدد من الاجراءات التي سنتحدث عنها لاحقاً.
سننتقل مباشرة بالتعرف على الإجراء : PickChain قبل غيره لتبسيط عملية التحليل والمتابعة.
هذا الإجراء يقوم بعمل مشابه لما تحدثنا عنه في الجزء الأول من الكود، الهدف منه أيضاً تخزين مجسم ما ضمن متغير، ولكن هذه المرة بفارق هام وهو أن هذا الإجراء يعطي للمستخدم حرية انتقاء المجسم بعد تشغيل الـ script، حيث ستظهر للمستخدم رسالة تطلب منه انتقاء مجسم من المشهد.
 

function PickChain(ChainElement)
  PickChain = true
  logmessage "Please Select Chain Element..." ‘ طباعة الأمر للمستخدم
  PickElement "chain_element", "Select Chain Element",
  _"Select Chain Element", ChainElement, Button ‘ تفعيل وضع الانتقاء للمستخدم
  if Button = 0 then ‘ التحقق من تنفيذ عملية الانتقاء
    logmessage "Cancelled By User"
    PickChain = false
  end if
end function
 
الإجراء التالي التي سنتعرف عليه بشيء من الاختصار هو الإجراء GetParams، هذا أيضاً من الإجراءات الهامة التي لايكاد يخلو script منها، يقوم هذا الإجراء بإنشاء نافذة التي يقوم المستخدم من خلالها بتحديد المعطيات المطلوبة بطريقة سهلة وواضحة.
 

function GetParams(Radius, SecRadius, Length, Res)
  GetParams = false
  set oRoot = ActiveProject.ActiveScene.Root
  Set oDial = AddProp("Custom_Parameter_List", oRoot, , "General_Options").Value("Value")
  OptionSliders = oRoot & "." & oDial
  SIAddCustomParameter OptionSliders, "Radius", siDouble, 0.4, 0.001, 999.000,
  _, siPersistable+siSilent  , 0.01, 10,, "Ring Radius"
  SIAddCustomParameter OptionSliders, "SecRadius", siDouble, 0.175, 0.001, 999.000,
  _, siPersistable+siSilent  , 0.01, 10.000,, "Section Radius"
  SIAddCustomParameter OptionSliders, "Length", siDouble, 0.8, 0.001, 999.000,
  _, siPersistable+siSilent, 0.01, 10.000,, "Ring Length"
  SIAddCustomParameter OptionSliders, "Resolution", siUByte, 0.000, 0.000, 2.000,
  _, siPersistable+siSilent, 0.000, 2.000,, "Ring Resolution"
 
  on error resume next
  InspectObj OptionSliders ,,,SIModal
  if Err.Number <> 0 then
    DeleteObj OptionSliders
    exit function
  end if
 
  Radius = GetValue(OptionSliders & ".Radius")
  SecRadius = GetValue(OptionSliders & ".SecRadius")
  Length = GetValue(OptionSliders & ".Length")
  Res = GetValue(OptionSliders & ".Resolution")
 
  DeleteObj OptionSliders
  GetParams = true
end function
 
سنقوم بالعمل على هذا الجزء لأهميته بشكل منفصل ضمن المشروع ان شاء الله.
الإجراء التالي لدينا هو CreateChainElement، هذا الإجراء يقوم بإنشاء الوحدة الأساسية للسلسلة والتي تتمثل بحلقة بالأبعاد التي حددها المستخدم من خلال المدخلات في الإجراء السابق.
يشمل هذا الإجراء عدد من المفاهيم البرمجية الهامة، مثل إنشاء مجسمات جديدة في المشهد، تعديل الباراميترات الخاصة بها، الوصول للمكونات الجزئية للمجسمات (النقط، الإضلاع،...) والتحكم بها.
 

function CreateChainElement(rad,secrad,length,res)
  set CreateChainElement = CreatePrim ("Torus", "MeshSurface","Chain0") ‘إنشاء مجسم جديد وتخزينه في متغير
 ‘الوصول لخصائص المجسم الذي تم انشاءه وتعديلها 
  CreateChainElement.Parameters("radius").value = rad
  CreateChainElement.Parameters("sectionradius").value = secrad
  select case res
  case 0 '-- Low Resolution...
    CreateChainElement.Parameters("subdivu").value = 6
    CreateChainElement.Parameters("subdivv").value = 4
    Translate CreateChainElement & ".pnt[8-19]", length-rad,
    _0, 0, siRelative, siGlobal, siObj, siX ‘ إزاحة مجموعة من محددة من نقاط الجسم لتغير شكله
  case 1 '-- Medieum Resolution...
    CreateChainElement.Parameters("subdivu").value = 10
    CreateChainElement.Parameters("subdivv").value = 8
    Translate CreateChainElement & ".pnt[24-63]", length-rad, 0, 0, siRelative, siGlobal, siObj, siX
  case 2 '-- High Resolution...
    CreateChainElement.Parameters("subdivu").value = 18
    CreateChainElement.Parameters("subdivv").value = 16
    Translate CreateChainElement & ".pnt[80-223]", length-rad, 0, 0, siRelative, siGlobal, siObj, siX
  end select
end function
 
الإجراء ماقبل الأخير في هذا الـ script هو CloneChain، يقوم هذا الإجراء باستنساخ الوحدة الأساسية للسلسلة التي إنشاؤها في الإجراء CreateChainElement على طول السلسلة المطلوب. إذا قمنا بتتبع التفاصيل قليلاً في هذا الإجراء، سنلاحظ أنه يقوم بعملية النسخ وربط جميع العناصر الناتجة مع هيكل الحركة مع مراعاة التوضع الصحيح لكل عنصر.
 

'-- Clone Base Element & Position it...
function CloneChain(Bone,BaseElement,SecRad,Length)
  set CloneChain = CreateObject("XSI.Collection")
  ParentObj Bone, BaseElement
  ResetTransform BaseElement, siObj, siSRT, siXYZ
  BaseElement.Kinematics.Global.Parameters("sclx").value =
  _1/Bone.Kinematics.Global.Parameters("sclx").value
  BoneLength = Bone.Parameters("length").value * Bone.Kinematics.Global.Parameters("sclx").value
  ChainCount = Round(BoneLength / length,0)

  for ChainIndex = 0 to ChainCount-1
    Switch = round(abs(sin(ChainIndex*1.5707963267948966192313216916398)))
    set ChainItemsCol = Clone(BaseElement, 1, siSharedParent,_
    siNoGrouping, siDuplicateProperties, siDuplicateAnimation,_
    siDuplicateConstraints, siNoSelection,siGlobalXForm,_
    1,1,1, 0,0,0, 0,0,0, True)
    set ChianItem = ChainItemsCol(0)
    ChianItem.Kinematics.Local.Parameters("posx").value =
    _(1/Bone.Kinematics.Global.Parameters("sclx").value)*length*ChainIndex
    ChianItem.Kinematics.Local.Parameters("rotx").value = Switch*90
    ChianItem.Kinematics.Local.Parameters("sclx").value = 1/Bone.Kinematics.Global.Parameters("sclx").value
    CloneChain.Add ChianItem
  next

end function
 
الإجراء الأخير في هذا الـ script هو ChainMaker. يعتبر هذا الأجراء هو الرابط الأساسي الذي يستدعي معظم الإجراءات السابقة. يعرض هذا الإجراء أيضاً كيفية توظيف بعض المزايا الأنيقة في الـ softimage مثل progress bar لجعل الـ script أكثر وضوح أثناء المعالجة.
 

function ChainMaker(pChainElement,inRad,inSecRad,inLength,inRes)
  '-- Checking Input...
  if pChainElement.Type <> "bone"_
  and pChainElement.Type <> "eff"_
  and pChainElement.Type <> "root" then
    PickChainReturn = PickChain (pChainElement)
    if PickChainReturn = false then exit function
  end if
  '-- Creating return collection...
  set ChainMaker = CreateObject("XSI.Collection")
  '-- Creating Base Element...
  set baseChainElement = CreateChainElement(inRad,inSecRad,inLength,inRes)
  ChainMaker.Add baseChainElement
  '-- Get Chain Root
  set oChainRoot = pChainElement.Root
  set oChainBones = oChainRoot.Bones
  set oProgressBar = XSIUIToolkit.ProgressBar
  oProgressBar.maximum = oChainBones.Count
  oProgressBar.step = 1
  oProgressBar.visible = true
  oProgressBar.caption = "Processing Bones..."
  for each oBone in oChainBones
    Logmessage "Duplicating..."
    set ClonedChains = CloneChain (oBone, baseChainElement, inSecRad, inLength)
    oProgressBar.caption = "Processing Bone:: " & oBone.Name
    oProgressBar.Increment
    for each ClonedChain in ClonedChains
      ChainMaker.Add ClonedChain
    next
  next
end function
 
بهذا الشكل نكون قد أنهينا جولتنا السريعة الأولى على هذا الـ script. في المشاركات التالية بإذن الله سنبدأ بالعمل على تفصيل الإجراءات لتغطية أدق المفاهيم ضمنها.
إلى لقاء قريب...

محترف مشرف عبد اللطيف حاجي علي مشاركة 4

اسمح لي أن أساعدك في شرح الجزء الأول من الـ script. و هو الجزء الأساسي والذي يقوم بنداء جميع الإجراءات التي ذكرتها.
(ملاحطة كلمة مؤشر فيما يلي هي ترجمتي الساذجة لكلمة reference ريثما أجد ترجمة أفضل)
set oSelection = CreateObject("XSI.Collection")
set oSelection = Application.Selection

السطر الأول أعتقد أنه سهوة من مطور الـ script 😲 . في هذا السطر يقوم الـ script بإنشاء object من نوع XSI.Collection.  لكن المؤشر لهذا الـ object يضيع بتنفيذ السطر الثاني حيث يصبح المتغير يحوي نفس فيمة المؤشر المدعو Application.Selection والذي يشير بدوره إلى object من نوع XSI.Collection. هذا الأخير يحوي قائمة بالعناصر المحددة حالياً.

if oSelection.count <> 0 then
	valid = GetParams(inRad, inSecRad, inLength, inRes)
	if valid = true then ChainMaker oSelection(0), inRad, inSecRad, inLength, inRes

هذا الشرط يفحص ما إذا كان هناك عناصر محددة من قبل المستخدم قبل تنفيذ الـ script وفي هذه الحالة يقوم بطلب المحددات (باستخدام الإجراء GetParams)  ومن ثم (في حال كون المحددات صالحة، مثلاً المستخدم لم يلغي العملية) يقوم بنداء الإجراء ChainMaker على أول عنصر تم تحديده و باستخدام المحددات التي حصل عليها من GetParams

else
	PickChainReturn = PickChain (pChainElement)
	if PickChainReturn = true then
		valid = GetParams(inRad, inSecRad, inLength, inRes)
		if valid = true then ChainMaker pChainElement, inRad, inSecRad, inLength, inRes
	end if
end if

في الحالة الثانية (ليس هناك عناصر محددة) فإن هذا الـ code يقوم باختيار عنصر باستخدام الأجراء PickChain. و في حال كون العنصر قد تم تحديده بنجاح يقوم بإعادة نفس العملية السابقة بالضبط.

لاحظ أن قلت "نفس العملية السابقة بالضبط"، هل يدلك هذا على شيء؟ أحسنت، العملية هي نفسها بالضبط 😄 . الفكرة هنا أن هذا يدل على تكرار في الـ code وهو يعتبر من النقاط السلبية والتي يستحسن الابتعاد عنها. إذاً فإني أقترح أن يتم تعديل هذا الـ code كالتالي:

set validSelection = true
set oSelection = Nothing

if Application.Selection.count <> 0 then
	set oSelection = Application.Selection(0)
else
	validSelection = PickChain (oSelection)	
end if

if validSelection = true then
	validParams = GetParams(inRad, inSecRad, inLength, inRes)
	if validParams = true then
		ChainMaker oSelection, inRad, inSecRad, inLength, inRes
	end if
end if

هنا تم إزالة الـ code المكرر. أتمنى أن يكون الـ code والفرق واضحين.

ملاحظة: هناك خطأ إملائي بسيط في صفحة التفاصيل (وبعض أجزاء الـ script) حيث أن كلمة Chain مكتوبة أغلب الأحيان كـ Chian
ملاحظة 2: أعتقد أن الرابط لتحميل الـ code يجب أن يكون موجوداً في صفحة التفاصيل

عبد اللطيف حاجي علي
مبرمج
In|Framez

خبير مدير وسام البهنسي مشاركة 5

في 19 مارس 2009 01:53 ص، غمغم عبد اللطيف حاجي علي باستغراب قائلاً:

(ملاحطة كلمة مؤشر فيما يلي هي ترجمتي الساذجة لكلمة reference ريثما أجد ترجمة أفضل)

مرجع ☺ . دع مؤشر لـ pointer.

وسام البهنسي
مبرمج في إنفيديا وإنفريمز

خبير مدير همام البهنسي مشاركة 6

بتاريخ 19 مارس 2009 01:53 ص، قطب عبد اللطيف حاجي علي حاجبيه بشدة وهو يقول:

اسمح لي أن أساعدك في شرح الجزء الأول من الـ script.

شكراً لك على المساعدة! وعلى الرحب والسعة في شرح ونقد ونقاش أي جزء من هذا الـ script أو غيرها التي سنبدأ بالتعامل معها إن شاء الله.




وفي 19 مارس 2009 01:53 ص، أعرب عبد اللطيف حاجي علي عن رأيه بالموقف كالآتي:

السطر الأول أعتقد أنه سهوة من مطور الـ script 😲

أعتقد أنك ستجد الكثير من هذه الهفوات والسهوات للكثير من الأسباب التي يمكنني التحجج بها، مثل عدم وجود نظام Debugging لمتابعة حالة المتغيرات... ولكني في النهاية أعترف أن السبب الرئيسي هو عدم التركيز المميز الذي أتمتع به. 😖
 



في 19 مارس 2009 01:53 ص، قال عبد اللطيف حاجي علي بهدوء وتؤدة:

الفكرة هنا أن هذا يدل على تكرار في الـ code وهو يعتبر من النقاط السلبية والتي يستحسن الابتعاد عنها
 
يالله! كانت هذه أحد الحزازير التي أفضل طرحها على طريقة الأخ همام شقرا لتنشيط المشاركات في هذا المشروع. يبدو أنك حرقتها بس لامشكلة فهناك الكثير من هذه العيوب التي يمكننا المناورة بها لاحقاً.


في 19 مارس 2009 01:53 ص، عقد عبد اللطيف حاجي علي حاجبيه بتفكير وقال:

ملاحظة: هناك خطأ إملائي بسيط في صفحة التفاصيل (وبعض أجزاء الـ script) حيث أن كلمة Chain مكتوبة أغلب الأحيان كـ Chian

هذه المرة لا أملك أي حجة، إنها مشكلة عدم التركيز إياها 😳  
 

وفي 19 مارس 2009 01:53 ص، قال عبد اللطيف حاجي علي متحمساً:

ملاحظة 2: أعتقد أن الرابط لتحميل الـ code يجب أن يكون موجوداً في صفحة التفاصيل
 
اقتراح بمحله، سأحاول إضافة الروابط لكل الأدوات في الـ netview.