دروس حول مكتبة glut .. دروس على الطاير .. للي يفهم على الطاير

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

السلام عليكم :

هذه دروس مبسطة حول المكتبة GLUT المساندة لOPENGL

ماذا تعني ..وبماذا تفيدنا هذه المكتبة ؟
تستخدم لبناء النافذة وربطها بOPENGL
تستخدم للادخال ... يعني للتعامل مع رسائل الماوس والكيبورد وعصا الالعاب ..
تختصر علينا بعض الامور مثل اضافة النص .. رسم بعض الاشكال الجاهزة .. الخ
تعمل على عدد من الانظمة ..

هذه الدروس مقتبسة من عدد من الدروس المتناثرة .. اقول مقتبسة وليس مترجمة ..
مثلا
http://www.lighthous...om/opengl/glut/


ماذا عن دوال Win Api ?
مميزة ومفيدة ..
لكن قد تكون اصعب ( او اقول ) طويلة نوعا ما ..
في دروس NEHE المستخدم هو دوال WIN API ..
طبعا WIN API تعطيك كل شيء ..
لكن GLUT تسهل حياتك البرمجية .. باكوادها المختصرة .

ايهما اتعلم .. GLUT .. او ..WIN API ?
النتيجة واحدة ... الغرض من تلك المكتبات بالنسبة لنا .. ربط OPENGL بالنافذة ..والتعامل مع الكيبورد والماوس .. وهذا ماتفعله المكتبتان ..
لكن ..انصحط بتعلم GLUT و WIN API لانك ستواجه دروس واكواد بعضها مكتوب بتلك المكتبة وبعضها بالمكتبة الاخرى .. والامر سهل .,

اين اجد هذه المكتبة "GLUT" ؟
حمل من هنا
http://www.xmission....t-3.7.6-bin.zip

انسخ GLUT32.DLL في ملف السيستم اللي عندك وهو في الغالب system32 او system ..
انسخ glut32.lib في ملف المكتبات الخاص بالمصرف اللي عندك


انسخ ملف الهيدر glut.h والصقه اما في مجلد ملفات الهيدر الرئيسي اللي عندك
او في داخل احد المجلدات واللذي يسمى GL الموجود في مجلد الملفات الرأسية

اذا اتبعت الحالة الاولى تستدعيه هكذا

#include"glut.h"




او تستدعيه هكذا في الحالة الثانية
#include"GL/glut.h"

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




الدرس الاول : ربط النافذة بـOPENGL
اولا نريد ان نقسم عملنا الى عدة دوال ذلك للتسهيل .. اعيد واكرر للتسهيل فقط .. وانت حر .

مثلا
سنقوم بعمل ثلاث دوال
الاولى نضع فيها اعدادات OPENGL من اعداد لون الخلفية .. الاكساء.. الاضاءة.. الخ
وسنسميها مثلا
setting

الدالة الثانية نسميها reshape .. وستعرف فائدتها .,

الثالثة . render .. والتي سنضع فيها الرسم ..

اضافة للدالة الرئيسية main ..

لاحظ اننا نعمل على الكونسول .... انتبه .



قم بربط المكتبة glut32.lib و opengl32.lib و glu32.lib
الصق هذا الكود


#include<glut.h>
#include<gl/glu.h>
#include<gl/gl.h>


void reshape(int w, int h) {

        glViewport(0, 0, w, h);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
  gluPerspective(45.0, (float)w/(float)h, 1.0, 300.0);
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();



}


void render(void)
{
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
  glFlush();
       
}

void main(int argc, char **argv) {
        glutInit(&argc, argv);
        glutInitDisplayMode(GLUT_DEPTH | GLUT_SINGLE | GLUT_RGBA);
        glutInitWindowPosition(100,100);
        glutInitWindowSize(320,320);
        glutCreateWindow("arab team - glut  ");
  glutDisplayFunc(render);
        glutReshapeFunc(reshape);
        glutMainLoop();
}
هذا الدرس اطول درس الله يعيننا ..

glutInit(&argc, argv);
الدالة السابقة تعد المكتبة وبارمتراتها هي بارمترات main ...

        glutInitDisplayMode(GLUT_DEPTH | GLUT_SINGLE | GLUT_RGBA);
هنا نعد opengl .. ولها عدة بارمترات
GLUT_RGB
GLUT_RGBA
GLUT_INDEX
GLUT_SINGLE
GLUT_DOUBLE
GLUT_ACCUM
GLUT_ALPHA
GLUT_DEPTH
GLUT_STENCIL
GLUT_MULTISAMPLE
GLUT_STEREO
GLUT_LUMINANCE

من الصعب شرحها كلها لان هذا يتطلب فهم لعمل opengl .. وهذا خارج عن ما نريده الان ..
المهم GLUT_RGB او GLUT_RGBA نظام الالوان الذي تريده
GLUT_SINGLE يعني تريد ان تكتب على سطح واحد فقط .. وبالتالي الحركة لن تكون ناعمة ..
GLUT_DOUBLE تكتب على سطحين .. وهذا مايزيد نعومة الحركة ..

GLUT_DEPTH هذا خاص بالعمق .. يعني هل تريد استخدام المحور z يعني الثري دي .. اذا اردت استخدام الثري دي فمرر هذا البارمتر ..

نحن استخدمنا GLUT_DEPTH | GLUT_SINGLE | GLUT_RGBA
لاحظ GLUT_SINGLE .. هو ليس الخيار المحبب .. لكن لان GLUT_DOUBLE له اعدادات اخرى فسنؤجله للدروس اللاحقة


        glutInitWindowPosition(100,100);

هنا نعين احداثيات ناذتنا بالنسبة للشاشة ..

        glutInitWindowSize(320,320);
الطول والعرض للنافذة

        glutCreateWindow("arab team - glut  ");
صنعنا النافذة الان .. ونمرر اسم النافذة .. الذي نريد ..

  glutDisplayFunc(render);

*** مهم ***
هذه الدالة هي دالة العرض .. يعني اللي رح تعرض الرسوم .. يعني التي يتمر بحلقة دوران loop .. حتى تحفظ الرسم المرسوم وتقوم بتحديثه ..
المطلوب تمرر لها الدالة التي سترسم بها وهي الان render .. لاحظ ان الدالة render ليس لها بارمترات
لننتقل الى الدالة render ونرى ما بها ..

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
مسحنا البتات (جمع بت) الخاصة بالبفر اللي اخترناه .. مثلا نحن اخترنا GLUT_DEPTH اذا نقوم بتنظيفه
GL_DEPTH_BUFFER_BIT وهكذا ...
هذه الدالة من دوال opengl لذا لن نقوم بشرحها لان هذا خارج عن نطاق الدروس ..

  glFlush();
مهم جدا ..
هذه الدالة تقوم بدفع الرسم ليظهر الى الشاشة (بأبسط تعبير) .
يعني اذا رسمت مثلث فلن يظهر الى اذا جاء بعده glFlush ...
لكن احيانا نستغني عن هذه الدالة وستعرف متى .. باذن الله .

نعود الى الدالة main وتحديدا الى
        glutReshapeFunc(reshape);
هذه الدالة مهمة جدا( ومشكلة عند الكثيرين )
تحدث هذه الدالة اذا تغير حجم النافذة ..
وبالتالي تقوم بتظبيط الرؤية ... حسب طول وعرض النافذة ..
تطلب منك دالة .. لها بارمترات الاول للعرض والاخر للطول ..
نحن سميناها reshape وهي كذا

void reshape(int w, int h)
 {

        glViewport(0, 0, w, h);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
  gluPerspective(45.0, (float)w/(float)h, 1.0, 300.0);
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();



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


واخيرا
        glutMainLoop();

التي تدخل برنامجنا (او لعبتنا ) في حلقة من الدوران loop حتى يتم تحديث الرسوم والاشكال المعروضة .. وحتى تأتي رسالة امر بالخروج ..


هذا الدرس طويل وممل وتأسيسي ...
الدروس القادمة ممتعة اكثر .
واسهل .




الدرس الثاني : استخدام double buffer
اذا كنت لاتعرف ما معنى double buffer
سأحاول توضيحه .
اذا رسمنا بالطريقة العادية .. ستلاحظ ان الشكل المرسوم ""يرمش ""اذا تم تحريكه ..
للتغلب على هذه المشكلة تم استحداث مايمسى بالسطحين ...double buffer
لاحظ الصورة

Posted Image



هذه صورة تقريبية ..فقط ..

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


الان سنستخدم double buffer لانه يعطي نعومة للرسم
غير الدالة
        glutInitDisplayMode(GLUT_DEPTH | GLUT_SINGLE | GLUT_RGBA);
الى

        glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
الان نرسم على السطح الاول ثم نظهره على السطح الظاهر للمستخدم

لكن يجب عند استخدام double buffer ان تستخدم الدالة
        glutSwapBuffers();
بدلا من glflush ..
والسبب ان الدالة glutSwapBuffers تقوم بقلب البفر الخلفي للامام (بأبسط تعبير )
وبالتالي يظهر الرسم المستخدم .

** تنبيه : لاحاجة لاستخدام glFlush اذا كنت تستخدم glutSwapBuffers لان الدالة glutSwapBuffers تقوم بـ glflush ايضا ..



الدرس الثالث / رسم بعض الاشكال الجاهزة ..
توفر المكتبة glut عدد من الاشكال الجاهزة .. وهي

void glutSolidSphere(GLdouble radius,GLint slices, GLint stacks);
ترسم كرة مصمتة ...
والبارمترات واضحة فقط قم بتغيير القيم وستفهم ..
اغلب البارمتر ات هي لتنعيم الرسم ..او لتكبير حجمها .. الخ

void glutWireSphere(GLdouble radius,
GLint slices, GLint stacks);
نفس الدالة السابقة الا انها ترسم خطوط ( شبكة)

void glutSolidCube(GLdouble size);
لرسم مكعب

void glutWireCube(GLdouble size);
نفس الدالة السابقة الا انها ترسم خطوط ( شبكة)

void glutSolidCone(GLdouble base, GLdouble height,
GLint slices, GLint stacks);

void glutWireCone(GLdouble base, GLdouble height,
GLint slices, GLint stacks);

void glutSolidTorus(GLdouble innerRadius,
GLdouble outerRadius,
GLint nsides, GLint rings);


void glutWireTorus(GLdouble innerRadius,
GLdouble outerRadius,
GLint nsides, GLint rings);


void glutSolidDodecahedron(void);

void glutWireDodecahedron(void);

void glutSolidOctahedron(void);


void glutWireOctahedron(void);

void glutSolidTetrahedron(void);

void glutSolidIcosahedron(void);

void glutWireIcosahedron(void);

void glutWireTetrahedron(void);

void glutSolidTeapot(GLdouble size);

void glutWireTeapot(GLdouble size);

مثلا لرسم ابريق شاهي الربيع :rolleyes:

void renderScene(void) {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glTranslatef(0,0,-5);

        glutSolidTeapot(1);

        glutSwapBuffers();
}
طبعا اذا وضعت الوان واضاءة الخ .. سيظهر الشكل ثري دي خالص ,, :)
لاحظ الدالة
glTranslatef(0,0,-5);
هذه تحدد موقع الجسم (وهي من دوال opengl لذا لن اشرحها ..)
استخدتها حتى يظهر الشكل المرسوم ..
جرب تغير -5 بارقام اخرى .. ونحو ذلك .

الملف في المرفقات .


الدرس الثالث .
التعامل مع الكيبورد /

يوجد عدد من الدوال وهي
void glutSpecialFunc(void (*func)(int key, int x, int y));
تمرر لها دالة تحتوي على ثلاث بارمترات
هذه الدالة ..مخصصة لازرار معينة .
البارمتر الاول سيحمل احد الازرار التالية

GLUT_KEY_F1
GLUT_KEY_F2
GLUT_KEY_F3
GLUT_KEY_F4
GLUT_KEY_F5
GLUT_KEY_F6
GLUT_KEY_F7
GLUT_KEY_F8
GLUT_KEY_F9
GLUT_KEY_F10
GLUT_KEY_F11
GLUT_KEY_F12

GLUT_KEY_LEFT
GLUT_KEY_UP
GLUT_KEY_RIGHT
GLUT_KEY_DOWN
GLUT_KEY_PAGE_UP
GLUT_KEY_PAGE_DOWN
GLUT_KEY_HOME
GLUT_KEY_END
GLUT_KEY_INSERT

اما البارمتر الثاني فيخبر برنامجنا بموقع الماوس السيني .. والاخير بموقع الماوس الصادي.. لحظة ضغط الزر .

--------------
الدالة
glutKeyboardFunc(void (GLUTCALLBACK *func)(unsigned char key, int x, int y));

هذه الدالة تطلب دالة تحتوي على ثلاث بارمترات الاول رقم الزر المضغوط ..
مثلا زر الهروب Esc رقم 27
زر انتر رقمه 13 وهكذا (( راجع جدول اسكي ) ) او استخدم ثوابت الويندوز api اذا كنت تعرف .
البارمتر الثاي الاحداثي السيني للماوس لحظة ضغط الزر
البارمتر الثالث الاحداثي الصادي للماوس لحظة ضغط الزر .


تطبيق عملي ..

اضف هذه الدالة
void key_1(unsigned char key, int x, int y )
{
        if(key == 27 )
  exit(0);
}

الان ننتقل الى الدالة main
واكتب هذا ..
        glutKeyboardFunc(key_1);


الان اذا ضغطت الزر Esc سينتهي البرنامج

تطبيق على الدالة الاخرى

static double size =1;

void SpecialKeys(int key, int x, int y) 
{
 
        switch(key) {
  case GLUT_KEY_F1 : size++; break;  

  case GLUT_KEY_F2 : size--;if(size==0)size=0;
        }

        glutPostRedisplay();


}
واضحة
اذا ضغط على f1 اضف واحد
والعكس ..مع f2

الان سنرسم
        glutSolidTeapot(size);

الان بالدالة الرئيسية main ضع
        glutSpecialFunc( SpecialKeys);
وذلك لتفعيل عمل الدالة السابقة

جرب التطبيق ..


تنبيه !
لاحظ في الدالة السابقة الدالة
glutPostRedisplay();
هذه الدالة تحدث الرسم ..
بمعنى انها تمسح اي شيء موجود وتقوم بالابقاء فقط على عمل الدالة render يعني
  glutDisplayFunc(render);
وبالتالي سيتم تحديث الرسم ...

بمعنى اخر تقوم باعادة رسم النافذة ... يعني نفس وظيفة الرسالة WM_PAINT الموجودة في دوال API


التطبيق بالمرفقات ..



الدرس الرابع :
التعامل مع الماوس ..


يوجد عدة دوال منها :
void glutMouseFunction(void (*func)(int button, int state, int x, int y));
هذه تطلب منك دالة تحوي على اربع بارمترات
الاول : اي الازرار تم النقر عليها وهي الاتي /
GLUT_LEFT_BUTTON
الزر الايسر
GLUT_MIDDLE_BUTTON
الاوسط
GLUT_RIGHT_BUTTON
الايمن

البارمتر الثاني . حالة الزر المضغوط وهو :
GLUT_DOWN
يعني الزر تم ضغطه ..

GLUT_UP
بعد النقر على الزر يعني رفعت اصبعك منه ... : )

البارمتر الثالث والرابع الاحداثي السيني والصادي للماوس على التوالي ..

مثال تطبيقي ..
void mouse(int button, int state, int x, int y)
{
        if (button == GLUT_RIGHT_BUTTON)
        {
  if (state == GLUT_DOWN)
        cout << "Right button pressed"
        << endl;
  else
        cout << "Right button lifted "
        << "at (" << x << "," << y
        << ")" << endl;
        }
}

اعتقد يشرح نفسه ..

ثم بالدالة الرئيسة MAIN

        glutMouseFunc(mouse);
الان جرب ... التطبيق .


الدالة
glutPassiveMotionFunc(void (GLUTCALLBACK *func)(int x, int y));
هذه الدالة سهلة
تعطيم موقع الفارة فقط

مثال
void motionPassive(int x, int y)
{
        cout << "Mouse moved at "
  << "(" << x << "," << y << ")" << endl;
}

ثم تفعلها بالدالة الرئيسية
        glutPassiveMotionFunc(motionPassive);

--
الدالة
glutMotionFunc(void (GLUTCALLBACK *func)(int x, int y));
هذه الدالة شبيهة بسابقتها
لكن تقع اذا تم النقر على احد الازرار مع التحريك .. يعني مثل .. شل و حط :)
drag and drop
لن نضع عليها مثال ..

اذا فهمت الدالتين السابقتين ستفهم هذه ايضا

اولا انظر اي الازرار تم ضغطها وذلك من خلال الدالة
glutMouseFunc
وضع متغير مثلا
اذا تم الضغط على الزر الايسر اجعل المتغير صحيح
والا اجعله false

ثم عن طريق الدالة
glutMotionFunc
قل ..
اذا كان المتغير صحيحا .. اعمل كذا
والا اعمل كذا

مثال يوضح الفكرة فقط

bool lbuttonDown;

 if (button == GLUT_LEFT_BUTTON)
        {
  if (state == GLUT_DOWN)
        lbuttonDown = true;
  else
        lbuttonDown = false;
        }

ثم

void motion(int x, int y)
{
        if (lbuttonDown)
  cout << "Mouse dragged with left button at "
  << "(" << x << "," << y << ")" << endl;
}




جمعت الدروس في مرفق واحد ..
والدروس مختصرة وغير منظمة والان مستعجل .. لان صاحب المقهى ازعجني يقول خلص الوقت ..



بقي مواضيع ما غطيناها .. ممكن في خمس دروس قادمة نغطيها ..

على فكرة .. تعلم win api اذا كان عندك نية تستمر في برمجة الالعاب تحت الويندوز ..
 
ملف مرفق(ملفات)