OpenGL .. الدرس رقم صفر

الناقل : elmasry | الكاتب الأصلى : AlDeaj | المصدر : www.arabteam2000-forum.com

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


مقدمة مملة ..

OpenGL .. لا أعلم لما أعشق تلك المكتبة وافضلها على Directx . أولا وكما الحال مع اي شاب طموح ( او حالم) كنت ارغب في تعلم برمجة الألعاب فكنت ابحث عن لغة برمجة (سهلة) وتلبي رغباتي في بناء حلم طفولتي بإنتاج لعبة ذات مستوى عالي ( فاينل فانتسي مثلا )  .. اتجهت إلى الفيجوال بيسيك visual basic
ولكن رأيتها لغة هشة .. بسيطة .. لاتصلح للمشاريع الكبيرة فاتجهت الى c++ .. بحثت عن دروسها كثيرا ولكن دائما ماكنت اصاب باحباط عند البرنامج الأزلي لكل لغة برمجة Hello world .. لا أعلم لماذا يراودني احساس بان هذا البرنامج بالذات هو مصدر كل احباط لكل مبرمج برغب في تعلم لغة معينة :)

رأيت بأن لغة كال C++ اذا احتجت لاتقانها لصنع لعبة ما قد يستغرق سنوات عديدة يشيب بها رأسي ويذهب قبلها حماسي فتركتها و انا اجر اذيال الخيبة .. في تلك الفترة سمعت كثيرا عن Directx وكيف ان الكثير من الألعاب وبرامج المالتي ميديا قد صنعت بها او بمساعدتها فتوجهت الى DirectX .. قمت بتنزيل الكثير من الكتب لتلك المكتبة كما قمت بشراء كتاب باللغة العربية يشرح DirectX .. لا أعرف لماذا لم اهضم تلك المكتبة ( ربما لان شرح الكتاب لم يكن جيدا) فلم أفهم منها شيئا .. بعد ان رأيت انني لا انفع للعلم :) فكرت جديا بترك البرمجة والاتجاه لاي مجال آخر .. تركت البحث لمدة سنة تقريبا وفي الفترة الاخيرة انتقلت الى نظام التشغيل لينكس linux ومن هناك تعرفت على OpenGL وعند البحث والتقصي وجدت لها عدة تطبيقات مذهلة ( ومنها ماسأقوم بشرحه بالدروس القادمة ) ولكن ما أحزنني هو عدم وجود دروس عربية لتلك المكتبة أو ربما يوجد ولكني لم أستطع الوصول اليها ، فقلت لنفسي لم لا أقوم بوضع دروس وترجمة دروس إلى اللغة العربية بما انها تفتقر الى تلك الدروس .. فتلك المكتبة سهلة الفهم وبسيطة في اوامرها عظيمة في عملها كما انني وللمرة الاولى استطعت فهم شيئا عن البرمجة .. نخيلوا ؟ :)

عند رؤيتي للدروس العربية بأي مجال كان ، تواجهني مشكلة في بعض المفردات المعربة .. فغالبا كنت لا افهم مايدور بسبب الترجمة الحرفية لمفردات معينة والتي تشتت الذهن وتضيع المعنى .. وكنت ارجع مضطرا الى البحث عن المعنى الانجليزي لتلك الكلمة لفهم الفكرة او الجملة .. لذا أثناء الدروس التي سأقوم بكتابتها او ترجمتها سوف اقوم بكتابة المفردات او الكلمات باللغة الانجليزية مع كتابة شرحها او عملها باللغة العربية .. إلى هنا تنتهي تلك المقدمة المملة . :)

درس طويل ..

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

أولا يجب عليك إنشاء مشروع فارغ project بواسطة Visual C++ 6.0 ويجب اختيار Win32 Application )وليس ) console application .

ثانيا يجب ربط المكتبات OpenGL libraries
بداخل Visual C++ 6.0 قم بالذهاب إلى Project ثم Settings ثم اضغط على Link الموجودة بالقائمة
تحت "Object/Library Modules" سوف تجد مجموعة من المكتبات قمت باضافة المكتبات الثلاثة التالية لهم سواء في البداية او النهاية وتأكد بوجود مسافة بين كل مكتبة واخرى
OpenGL32.lib GLu32.lib GLaux.lib
مع العلم بان تلك المكتبات تأتي مع نظام التشغيل وندوز تلقائيا

السطور الأربع الاولى من البرنامج تحتوي على الملفات الرأسية Header Files للمكتبات التي قمنا باضافتها

 #include <windows.h> // الملف الرأسي لويندوز   Header File For Windows
#include <gl\gl.h> // الملف الرأسي للمكتبة   OpenGL32
#include <gl\glu.h> // الملف الرأسي للمكتبة   GLu32
#include <gl\glaux.h> //  الملف الرأسي للمكتبة   GLaux




الآن نريد وضع جميع المتغيرات variables التي سوف نستخدمها في البرنامج .. بما اننا بصدد انشاء نافذة فارغة فقط ( لا اريد احباط ) :) فلن نستخدم الكثير من المتغيرات الآن
بالسطر الاول نقوم باعداد مايسمى بال Rendering Context ( لا اعرف ترجمته )  .. كل برنامج تقوم ببرمجته عن طريق OpenGL يحتاج Rendering Context .وهو يقوم بربط OpenGL ب Device Context الذي يقوم بعرض النافذة .. دون ان ندخل بالتفاصيل هذا مانحتاج معرفته حتى الآن .. نحتاج الى اعداد Device Context الذي يجب ربطه مع OpenGL عن طريق Rendering Context .
ال Rendering Context يعرف اختصارا hRC اي اننا اثناء البرمجة نستخدم هذا الاختصار فقط وقد قمنا بتعريفه بالسطر الأول
السطر الثاني نقوم باعداد وتهيئة Device Context وهو مايسمى اختصارا hDC

HGLRC           hRC=NULL;
HDC             hDC=NULL;  
HWND            hWnd=NULL;
HINSTANCE       hInstance


السطر الأول من الكود التالي يقوم باعداد لوحة المفاتيح Keyboard والاستجابة لاي رد فعل منه
في السطر الثاني المتغير active يستخدم لاخبار برنامجنا ان كانت النافذة قد وضعت في الخلفية بعد ان كانت بحجم كامل الشاشة او حتى بنص الشاشة .. اي اننا قمنا باخفاء النافذة minimized او انزالها حتى نقوم بتشغيل او رؤية برنامج آخر .
في السطر الثالث المتغير Full Screen واضح ولا يحتاج الى شرح ولكن سوف اقوم بشرحه :) في حال كان برنامجنا يستخدم كامل الشاشة (full screen) فان المتغير سووف يكن true واذا كان غير ذلك اي ليس بكامل الشاشة سوف يكون false
يجب وضع هذا المتغير حتى يعلم البرنامج اذا ماكنا في وضع الشاشة الكاملة ام لاوحتى يعرف كل اجراء ان كان سيطبق في وضع الشاشة الكامل ام لا
bool    keys[256];    // لتسجيل حركة لوحة المفاتيح
bool    active=TRUE; // هذا المتغير يجب ان يكون   true
fullscreen=TRUE; // متغير كامل الشاشة


يجب علينا الآن تعريف WndProc() السبب في ذلك لان CreateGLWindow() يرتبط في WndProc() ولكنه يأتي بعد CreateGLWindow() .
في لغة C اذا اردنا كتابة اجراء معين او قسم معين من الكود يكون بعد الكود الذي نعمل عليه حاليا يجب علينا ان نعلن عن الاجراء اولا قبل كتابة الكود ( اقرأ هذه النقطة مرة اخرى ) 
لذا في السطر القادم سوف نقوم بتعريف WndProc() لنستطيع استخدام CreateGLWindow() .. اتفقنا ؟ سواء اتفقنا ام لا يجب علينا كتابة هذا السطر :)

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);


يقوم الكود التالي باعادة حجم النافذة بعد تصغيرها ( بافتراض انك لاتعمل على كامل الشاشة full screen )

GLvoid ReSizeGLScene(GLsizei width, GLsizei height)
{
        if (height==0)
        {
  height=1;
        }

        glViewport(0, 0, width, height);


في السطور التالية يتم اضافة القليل من الواقعية لمشهدنا ( على افتراض اننا نضع كائنات معينة في الشاشة)
فرؤية المشهد تتم من زاوية مقدارها 45 درجة كما هو مبين بالكود مستندة على الارتفاع والعرض للنافذة
النقاط 0.1f, 100.0f تعبر عن نقطه بداية ونقطه نهاية لمدى العمق الذي نضع به الكائنات ( سوف اشرح هذا الموضوع بتفصيل اكبر في دروس قادمة ) :)

glMatrixMode(GL_PROJECTION) يشير الى ان السطرين التاليين من الكود سوف يؤثران على مايسمى بمصفوفة التقدير
glLoadIdentity() يقوم بنفس عمل الامر reset تقريبا فهو يعيد المصفوفة المختارة الى حالتها الاولى

كل مانريد معرفته الآن ان اضافة هذا الكود تقوم بتحسين عملنا :) ( وسوف نفصل في هذا الموضوع في دروس قادمة)

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);
glMatrixMode(GL_MODELVIEW);    
glLoadIdentity();
}


هنا نقوم بتنصيب OpenGL فيجب كتابة هذا السطر حتى نقوم باختيار الالوان ونمكن التظليل الناعم smooth shading .. الخ .

int InitGL(GLvoid)
{


بالسطر التالي نقوم بتمكين التظليل الناعم smooth shading

glShadeModel(GL_SMOOTH );


الآن نقوم بوضع الألوان .. سوف اقوم بشرح الألوان بشكل مبسط على ان نتعمق فيها لاحقا .. مدى الألوان يكون بين 0.0f و1.0f حيث ان 0.0f يكون لون داكن و 1.0f يكون لون فاتح و 0.5f يكون لون متوسط بين الداكن والفاتح .
البارامتر الأول في الكود بعد glClearColor يعبر عن اللون الأحمر والبارامتر الثاني يعبر عن اللون الأخضر والبارامتر الثالث يعبر عن اللون الأزرق والبارامتر الرابع لقيمة الفا Alpha .. لانحتاج الآن الى معرفة عمل البارامتر الفا ونكتفي بوضع قيمته صفر في الوقت الحالي .
قد يتسائل البعض لدينا 3 ألوان فقط .. الأحمر ، الأخضر والأزرق أين بقية الألوان ؟ الاجابة هي : عن طريق مزج تلك الألوان نقوم بإظهار الوان جديدة وسوف نفصل في هذا الأمر لاحقا ( يوجد الكثير من الدروس قادمة فلاتخافوا سوف نشرح كل شيء :) )
الان اذا كان لدينا glClearColor(0.0f,0.0f,1.0f,0.0f) سوف نرى ان قيمة كل من اللون الأحمر والاخضر تساوي صفر بينما قيمة اللون الأزرق تساوي واحد .. اذا سوف نحصل على اللون الازرق على الشاشة .. واذا كان لدينا glClearColor(0.5f,0.0f,0.0f,0.0f) فاننا سوف نحصل على على لون أحمر متوسط وليس احمر فاتح او احمر داكن .. وهكذا .
في الكود التالي سوف نقوم بوضع جميع القيم صفر اي اننا سوف نحصل على اللون الأسود للخلفية

glClearColor(0.0f, 0.0f, 0.0f, 0.0f);


الأسطر الثلاثة التالية لتحديد العمق في الشاشة .. مثلا لدينا كائنين على الشاشة ونريد ان نظهرهما بمظهر ثلاثي الأبعاد او ان نظهر احداهما خلف الاخر فاذا افترضنا بانه لدينا مربع ودائرة نريد عرضهما على الشاشة بحيث يكونا في بعدين مختلفين اي ان المربع بعيد والدائرة قريبه من الشاشة فمن االطبيعي ان الدائرة سوف تغطي على المربع او اجزاء منه اذا تقاطعا على خط واحد .
ومهمة الكود التالي هي توزيع الكائنات في فضاء الشاشة .. بما اننا الان نقوم بعمل شاشة فارغة فقط فاننا لانحتاج الو وضع قيم لاي كائنات ونكتفي الان بتعريفه فقط
glClearDepth(1.0f);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);


في السطر التالي نخبر OpenGL بتصحيح المنظور ليخرج لنا اداء افضل للمشهد ولو قليلا
 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);     
return TRUE;
}


القسم القادم من الكود يستدعى عندما تريد الخروج من البرنامج .. KillGLWindow() يقوم بتحرير release
كل من Rendering Context و Device Context

GLvoid KillGLWindow(GLvoid)
{


من خلال الكود التالي يجب علينا رؤية ما اذا كنا في طور الشاشة الكاملة full screen ام لا .. لماذا علينا فعل ذلك ؟ .. هل صادفت مرة اثناء خروجك من احد البرامج اختفاء بعض الايقونات من سطح المكتب وتحتاج الى عمل تحديث refresh لسطح المكتب لاستعادتها ؟ حسنا هذا السطر يقوم برؤية ما اذا كان هذا البرنامج في طور الشاشة الكاملة ام لا فاذا كان كذلك يقوم بالخروج من الشاشة الكاملة قبل اغلاق البرنامج وان كان غير ذلك ف(خير وبركة) :)
if (fullscreen)
        {



نقوم الآن باستخدامChangeDisplaySettings(NULL,0) حتى نقوم بالرجوع الى الوضع الطبيعي لسطح المكتب desktop .. اي نقوم باسترجاع القيم المخزنة في ملفات الرجستري لنظام الوندوز مثل دقة الشاشة وعمق الالوان وغيرها .. كما يقوم
ShowCursor بإظهار مؤشر الفأرةMouse Pointer

ChangeDisplaySettings(NULL,0);  
ShowCursor(TRUE);
        }


السطر التالي يقوم برؤية ان كان لدينا Rendering Context .. اذا كان غير موجودا فالبرنامج سف يقفز الى الكود الذي يقوم بالبحث عن Device Context

if (hRC)
        {


اذا كان ال Rendering Context موجودا فسوف نرى امكانية ان نقوم بتحريره release it اي نقوم بفصله عن
Device Context

if (!wglMakeCurrent(NULL,NULL))
  {


اذا لم يستطلع البرنامج تحرير hRC سوف يقوم باظهار رسالة تخبرنا بذلك
MessageBox(NULL,"Release Of DC And RC Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
  }


نقوم الآن بمحاولة حذف delete hRC

if (!wglDeleteContext(hRC))


اذا فشلت العملية سوف تظهر رسالة تخبرنا بذلك

MessageBox(NULL,"Release Rendering Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
  }
hRC=NULL;
 
        }


الآن نقوم بفحص مااذا كان لدينا Device Context ام لا وامكانية تحريره وفي حالة لم نستطع تحريره سوف تظهرر سالة تخبرنا بذلك

if (hDC && !ReleaseDC(hWnd,hDC))
{
  MessageBox(NULL,"Release Device Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
hDC=NULL;
}

ونفس الامر السابق نستخدمه مع Window Handle ونرى امكانية عمل تحطيم destroy لها كما هو واضح من
DestroyWindow(hWnd) الموجود في الكود واذا فشلنا سوف تظهر رسالة تخبرنا بذلك

if (hWnd && !DestroyWindow(hWnd))       
{
MessageBox(NULL,"Could Not Release hWnd.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
hWnd=NULL;
}


الشئ الاخير الذي نقوم به هو عمل unregister لصنف النوافذ Windows Class حتى لاتردنا اي رسالة خطأ اثناء تشغيل البرنامج مرة اخرى واذا فشلنا تظهر لنا رسالة تخبرنا بذلك
if (!UnregisterClass("OpenGL",hInstance))
{
MessageBox(NULL,"Could Not Unregister Class.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
hInstance=NULL;
}
}


الآن نقوم بإنشاء نافذة OpenGL كما هو ظاهر من CreateGLWindow .. في الجزء التالي من الكود لدينا 5 بارامترات title العنوان width العرض height الطول bits البت و fullscreenflag اذا كان true يعني الشاشة كاملة وfalse يعني العكس
BOOL CreateGLWindow(char* title, int width, int height, int bits, bool fullscreenflag)
 {

عندما نريد ايجاد صيغة البكسل pixel الذي يتوافق مع عملنا سوف تقوم النافذة بتحديدها وجعلها في المتغير PixelFormat

GLuint PixelFormat;


الآن يجب علينا عمل رجستر register لصنف النافذة window class .. يقوم صنف النافذة بتخزين معلومات النافذة التي سوف نقوم بعملها

WNDCLASS        wc;
DWORD   dwExStyle;
DWORD   dwStyle;


الاسطر الخمس التالية نقوم بتحديد موقع النافذة وسوف نفصل فيه لاحقا

RECT WindowRect;
WindowRect.left=(long)0;
WindowRect.right=(long)width;
WindowRect.top=(long)0;
WindowRect.bottom=(long)height;

الآن يجب علينا جعل المتغيران fullscreen=fullscreenflag لم افهم هذه النقطه جيدا ولماذا يجب علينا فعل ذلك ولكن يبدو اننا ان لم نفعل ذلك سوف تحدث بعض المشاكل ويكفي معرفة ذلك حتى الآن على ان نقوم بالبحث في الموضوع لاحقا

fullscreen=fullscreenflag;


الآن نقوم بتعريف define لل window class
CS_HREDRAW و CS_VREDRAW يقوم باعادة عرض النافذة عند تكبيرها او تصغيرها
CS_OWNDC يقوم بانشاء DC لنافذ تنا
WndProc هو الاجراء الذي يقوم بمراقبه الرسائل التي تظهر في البرنامج
LoadIcon يقوم بتحميل الايقونة icon
LoadCursor يقوم بتحميل مؤشر الفأرة
لم نقم باستخدام خلفية لذلك نضع wc.hbrBackground تساوي NULL
وكذلك لانريد قائمة menu فنقوم بوضع wc.lpszMenuName تساوي NULL
السطر الاخير يعبر عن اسم الصنف class name وقمنا بوضعه هنا OpenGL
hInstance  = GetModuleHandle(NULL);   wc.style                       = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wc.lpfnWndProc  = (WNDPROC) WndProc;
wc.cbClsExtra  = 0;
wc.cbWndExtra  = 0;
wc.hInstance  = hInstance;
wc.hIcon  = LoadIcon(NULL, IDI_WINLOGO);        
wc.hCursor  = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground        = NULL;
wc.lpszMenuName  = NULL;
wc.lpszClassName        = "OpenGL";


الآن نقوم بعمل رجستر register للصنف واذا حدث اي خطأ سوف تظهر رسالة

if (!RegisterClass(&wc))
{
  MessageBox(NULL,"Failed To Register The Window Class.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE;
}


الآن نسأل البرنامج ما أذا كنا بكامل الشاشة ام لا

 if (fullscreen)        
{



هذا القسم هو الخاص بالتغيير الى وضح الشاشة الكاملة .. يجب ان يكون الطول والعرض في حالة الشاشة الكاملة مثل الطول والعرض التي تنوي استدخدامها لبرنامجك ويجب ايضا ان تقوم بوضع متغيرات الشاشة الكاملة قبل انشاء نافذة البرنامج .. الآن ليس علينا الاهتمام بالطول والعرض وقيمتهما فهي سوف تحدد تلقائيا من خلال هذا الكود

DEVMODE dmScreenSettings;
memset(&dmScreenSettings,0,sizeof(dmScreenSettings));
dmScreenSettings.dmSize=sizeof(dmScreenSettings);
dmScreenSettings.dmPelsWidth    = width;
dmScreenSettings.dmPelsHeight   = height;
dmScreenSettings.dmBitsPerPel   = bits;
dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;


في الكود التالي سوف نقوم بوضع العرض والارتفاع التي نريد الانتقال اليها والتي نقوم باستيرادها من
dmScreenSettings
ChangeDisplaySettings تقوم بتغيير حجم الشاشة الى المعطيات ( الطول والعرض ) الموجودة في dmScreenSettings

if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
{


اذا لم تتم العمليه ولم تظهر الشاشاة كاملة full screen فسوف تظهر لنا رسالة بخيارين .. اغلاق الشاشة والخروج من البرنامج او الاستمرار بدون شاشة كاملة

if (MessageBox(NULL,"The Requested Fullscreen Mode Is Not Supported By\nYour Video Card. Use Windowed Mode Instead?","NeHe GL",MB_YESNO|MB_ICONEXCLAMATION)==IDYES)


اذا اخترنا الاستمرار سوف تصبح قيمة المتغير fullscreen = false
ويستمر البرنامج في عمله

fullscreen=FALSE;
}
        else
        {


واذا اخترنا الخروج فسوف تظهر رسالة تخبرنا بان البرنامج سوف يغلق
القيمة false تخبر البرنامج بانه يوجد خلل في العمليه ويجب علينا الخروج

MessageBox(NULL,"Program Will Now Close.","ERROR",MB_OK|MB_ICONSTOP);
return FALSE;
        }
  }
        }


نقوم الآن بالتأكد مره اخرى ما اذا كان وضع الشاشة الكاملة يعمل ام لا

if (fullscreen)
        {


اخفاء مؤشر الفأرة في وضعية fullscreen وان اردت اظهارها اجعل قيمتها true

dwExStyle=WS_EX_APPWINDOW;
dwStyle=WS_POPUP;
ShowCursor(FALSE);
        }
        else
        {

dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;  
  dwStyle=WS_OVERLAPPEDWINDOW;
        }


الآن نقوم بتعديل نافذة البرنامج حتى تناسب ابعاد النافذة المختارة سواء fullscreen او window type

AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);


نقوم بانشاء نافذتنا الآن حيث نقوم بالتمرير الى CreateWindowEx() جميع البارامترات التي يحتاجها

 if (!(hWnd=CreateWindowEx(     dwExStyle,
"OpenGL",
title,
WS_CLIPSIBLINGS |
WS_CLIPCHILDREN |
dwStyle,
0, 0,
WindowRect.right-WindowRect.left,
WindowRect.bottom-WindowRect.top,
NULL,  
NULL,
hInstance,
NULL)))


في الكود التالي سوف نقوم باختيار الالوان والبت (16 – 24 – 32 ) وغيرها من الامور التي تحتاج دروس اخرى لتفصيلها فلا تستجعلوا معرفتها الآن :)

static  PIXELFORMATDESCRIPTOR pfd=
{
sizeof(PIXELFORMATDESCRIPTOR),  
1,
PFD_DRAW_TO_WINDOW |
PFD_SUPPORT_OPENGL |
PFD_DOUBLEBUFFER,
PFD_TYPE_RGBA,  
bits,
0, 0, 0, 0, 0, 0,
0,
0,
0,
0, 0, 0, 0,
16,
0,
0,
PFD_MAIN_PLANE,
0,
0, 0, 0
        };


والآن مجموعة من رسائل الاخطاء -لاتحتاج الى تفصيل- والتي ستظهر لنا في حالة فشل اي عملية مع بيان سبب الخطأ

if (!(hDC=GetDC(hWnd)))
{
KillGLWindow();
MessageBox(NULL,"Can't Create A GL Device Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE;
}
if (!(PixelFormat=ChoosePixelFormat(hDC,&pfd)))
{
  KillGLWindow();
MessageBox(NULL,"Can't Find A Suitable PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
  return FALSE;
}
if(!SetPixelFormat(hDC,PixelFormat,&pfd))
{
  KillGLWindow();
MessageBox(NULL,"Can't Set The PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
  return FALSE;
        }      
if (!(hRC=wglCreateContext(hDC)))  
{
  KillGLWindow();
MessageBox(NULL,"Can't Activate The GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
  return FALSE;
}


اذا لم يحدث اي اخطاء سوف يت الانتقال الى الكود التالي والذي يعطي اولوية لظهور النافذة على الشاشة اي انه عند بدء التشغيل تكون في المقدمة حتى تراها

ShowWindow(hWnd,SW_SHOW);
SetForegroundWindow(hWnd);
SetFocus(hWnd);
ReSizeGLScene(width, height);


InitGL() يقوم بمساعدتنا لوضع الاضاءة ، اضافة الأنسجة وغيرها

if (!InitGL())
{
KillGLWindow();
MessageBox(NULL,"Initialization Failed.","ERROR",MB_OK|MB_ICONEXCLAMATION);
  return FALSE;
        }
return TRUE;
  }


هنا يتم التعامل مع جميع الرسائل التي تظهر فعندما يتم عمل register لصنف النافذة window class تقفز مباشرة الى هذا الكود اي كيف سيتعامل البرنامج عند حدوث امور معينة

 LRESULT CALLBACK WndProc(      HWND    hWnd,   
UINT    uMsg,  
WPARAM  wParam,
LPARAM  lParam)
{


الآن اذا كانت قيمة uMsg هي WM_ACTIVE سوف نقوم بفحص ما اذا كانت نافذتنا في الواجهة .. ان لم تكن كذلك فالمتغير active سوف يكون false وان كانت في الواجهة فسوف يكون المتغير true

switch (uMsg)
        {

  case WM_ACTIVATE:
{
if (!HIWORD(wParam))
{
         active=TRUE
}
        else
{
active=FALSE;
}
return 0;
  }


واذا كانت WM_SYSCOMMAND فيوجد احتمالين .. اذا كانت wParam قيمتها SC_SCREENSAVE او SC_MONITORPOWER فان حافظة الشاشة screen saver تحاول البدء او ان الشاشة سوف تدخل في طور المحافظة على الطاقة اي اعندما تغلق الشاشة بعد فترة من عدم لمس الكمبيوتر

case WM_SYSCOMMAND:
{
switch (wParam)
{
case SC_SCREENSAVE:    
case SC_MONITORPOWER:
return 0;      
}
break;
}


اما اذا كانت WM_CLOSE فسوف يغلق البرنامج

case WM_CLOSE:
{
PostQuitMessage(0);
return 0;      
}
case WM_KEYDOWN:
{
keys[wParam] = TRUE;    
return 0;
}
case WM_KEYUP:
{
keys[wParam] = FALSE;
return 0;      
}
case WM_SIZE:
{
ReSizeGLScene(LOWORD(lParam),HIWORD(lParam));
return 0;
}
}
return DefWindowProc(hWnd,uMsg,wParam,lParam);
int WINAPI WinMain(     HINSTANCE       hInstance,
HINSTANCE       hPrevInstance,
LPSTR  lpCmdLine,
int  nCmdShow)
{
MSG     msg;
BOOL    done=FALSE



القسم التالي من الكود يخبرك عند تشغيل البرنامج اذا ماكنت تريد تشغيله بكامل الشاشة ام لا

if (MessageBox(NULL,"Would You Like To Run In Fullscreen Mode?", "Start FullScreen?",MB_YESNO|MB_ICONQUESTION)==IDNO)
        {
fullscreen=FALSE;      
}


الكود التالي لانشاء نافذة OpenGL مع الكتابة في أعلى البرنامج title وهنا مكتوب NeHe's OpenGL Framework وتستطيع كتابة اي جملة اخرى مكانها

if (!CreateGLWindow("NeHe's OpenGL Framework",640,480,16,fullscreen))
{
return 0;      
        }
while(!done)

        {


والان نقوم بالتأكد اذا ماكانت هناك رسائل ترد الى البرنامج لتطبيقها وجعل البرنامج يتعامل معها
if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
if (msg.message==WM_QUIT)
{
done=TRUE;
}
        else
{
TranslateMessage(&msg);
       
DispatchMessage(&msg);
}
  }
  else
  {


وان لم تكن هناك اي رسائل خطأ سوف تعرض شاشتنا
في السطر الاول نرى ان كانت نافذتنا في الواجهة ام لا
وفي السطر الثاني نخبر البرنامج انه عند الضغط على الزر Esc يجب عليه الخروج من البرنامج

if (active)

{
        if (keys[VK_ESCAPE])
{
  done=TRUE;
}
    else
{

DrawGLScene();  
SwapBuffers(hDC);
}
        }

السطر التالي يجعلنا قادرين على الخروج من طور الشاشة الكاملة والعودة الى الوضع التالي عن طريق الضغط على الزر f1

if (keys[VK_F1])
{
keys[VK_F1]=FALSE;
KillGLWindow();
fullscreen=!fullscreen;
if (!CreateGLWindow("NeHe's OpenGL Framework",640,480,16,fullscreen))
{
return 0;
}
        }
  }
        }



نهاية سعيدة ..

انتهى درسنا الأول ولله الحمد .. وهو طويل جدا كما ترون .. الدروس التالية سوف تكون قصيرة بإذن الله وجميلة وممتعة في نفس الوقت
الملف المصدري لهذا الدرس على هذا العنوان
http://nehe.gamedev....vc/lesson01.zip

الدرس الأصلي موجود في هذا الموقع لمن أراد الاطلاع عليه
http://nehe.gamedev....n.asp?lesson=01

اخيرا انتظر الاستفسارات او انتظر احد الاخوة الذي يقول لي بانه مستعد للدرس الثاني ومستعد لبدء الدرس الثاني في اي وقت ولو من اجل شخص واحد

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

اسألكم الدعاء ..

لكم تحياتي وتقديري
علي الدعيج

رابط الدرس التالي :
الدرس رقم 1