NeHe Tutorials Народный учебник по OpenGL
Урок X5. OpenGL

В этом уроке будет продемонстрирована возможность создания фигур и их движений из внешнего текстового файла. Все объяснения будут производиться для среды Visual Studio Professional 2010. Для человека, имеющего некоторый опыт работы с Visual Studio, не составит труда разобраться во всем этом независимо от версии от 7-й и выше.

 

Откройте в директории LessonsGLCode_2010 урок X5 LessonX5 проект BoxMan, файл X5_BoxManDrawInit.cpp.

 

Как во всех предыдущих уроках, все начинается с InitGL:

 

int InitGL_X5(GLvoid)                                 // All Setup For OpenGL Goes Here

{

       CheckMyDir();

 

       glShadeModel(GL_SMOOTH);                                           // Enable Smooth Shading

       glClearColor(0.0f, 0.0f, 0.0f, 0.5f);   //Black Background

       glClearDepth(1.0f);                     //Depth Buffer Setup

       glEnable(GL_DEPTH_TEST);                // Enables Depth Testing

       glDepthFunc(GL_LEQUAL);                 // The Type Of Depth Testing To Do

       glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);    // Really Nice Perspective Calculations

 

    quadric=gluNewQuadric();     // Создаем указатель на квадратичный объект // Lesson 18

    gluQuadricNormals(quadric, GLU_SMOOTH); // Создаем плавные нормали // Lesson 18

    gluQuadricTexture(quadric, GL_TRUE);    // Создаем координаты текстуры // Lesson 18

 

//TestMessageBox("InitGL_X5\n1\n");

    m_pCurCamera = new CMyCamera;

       m_pCurCamera->SetCamera(Vector_3D(0, 3,20),  Vector_3D(0, 0, 0),  Vector_3D(0, 1, 0));

 LightPosition[0]= 10;

 LightPosition[1]= 100;

 LightPosition[2]= 50;

 glLightfv(GL_LIGHT1, GL_POSITION, LightPosition);

IitLight1();

OpenGLSceneFmTxtFile("BoxData/Ring.txt");

OpenGLSceneFmTxtFile("BoxData/BoxMan.txt");

 

CMyGLObject * pGl = GetGLObjectOfNameGL("BoxMan");

if(pGl != NULL)

{

m_pBoxMan = pGl;

CMyAction * pA = new CMyAction;

pA->m_Name = "Step";

for(int i = 0; i < 4; i++)

       pA->AppendSceneFmFile("BoxData/Step_" + strFormat("%02d.txt",i+1));

pGl->m_actionList.AddTail(pA);

pGl->m_pCurAction = pA;

 

pA = new CMyAction;

pA->m_Name = "rStraight";

for(int i = 0; i < 2; i++)

       pA->AppendSceneFmFile("BoxData/rStraight_" + strFormat("%02d.txt",i+1));

pGl->m_actionList.AddTail(pA);

 

pA = new CMyAction;

pA->m_Name = "lStraight";

for(int i = 0; i < 2; i++)

       pA->AppendSceneFmFile("BoxData/lStraight_" + strFormat("%02d.txt",i+1));

pGl->m_actionList.AddTail(pA);

 

pA = new CMyAction;

pA->m_Name = "lKick";

for(int i = 0; i < 3; i++)

       pA->AppendSceneFmFile("BoxData/lKick_" + strFormat("%02d.txt",i+1));

pGl->m_actionList.AddTail(pA);

 

pA = new CMyAction;

pA->m_Name = "rKick";

for(int i = 0; i < 3; i++)

       pA->AppendSceneFmFile("BoxData/rKick_" + strFormat("%02d.txt",i+1));

pGl->m_actionList.AddTail(pA);

 

}

scaleValue = 1.0;

return TRUE; // Initialization Went OK

}

 

Новое здесь начинается с открытия текстовых файлов BoxData/Ring.txt и BoxData/BoxMan.txt процедурой OpenGLSceneFmTxtFile.

 

Вы можете открыть и просмотреть структуру этих файлов с помощью любого текстового редактора, начиная с «Блокнота».  Вот фрагмент файла BoxData/BoxMan.txt, в котором описана его левая рука:

 

<GLObject>

      <style>SPHERE_OBJ

      <name>  <Left_Shoulder_Joint> Левый плечевой сустав

         <pos>    0.00   2.30  -1.50

         <ang>    4.00   0.00   4.00

         <angMin>  -40.00   0.00   0.00

         <angMax>  180.00   0.00 180.00

         <cfnt>   1.00   1.00   1.00

         <lbh>    0.50   0.50   0.50

         <Material>

         <ambient>    0.25   0.25   0.25   0.80

         <diffuse>    0.50   0.50   0.00   1.00

         <specular>    0.00   0.00   0.00   0.00

         <emission>    0.20   0.20   0.20   0.00

         <shininess>    1.00

      <numChilds>  1

         <GLObject>

         <style>CYLINDER_OBJ

         <name>  <Forearm> Предплечье

            <pos>    0.00  -1.30   0.00

            <ang>    0.00  90.00   0.00

            <angMin>    0.00   0.00   0.00

            <angMax>    0.00   0.00   0.00

            <cfnt>   1.00   1.00   1.00

            <lbh>    0.40   1.50   1.50

            <Material>

            <ambient>    0.25   0.25   0.25   0.80

            <diffuse>    0.50   0.50   0.00   1.00

            <specular>    0.00   0.00   0.00   0.00

            <emission>    0.20   0.20   0.20   0.00

            <shininess>    1.00

         <numChilds>  1

            <GLObject>

            <style>SPHERE_OBJ

            <name>  <Ulnar_Joint> Локтевой сустав

               <pos>    0.00  -0.70   0.00

               <ang>    0.00   0.00   0.00

               <angMin>  -150.00   0.00 -150.00

               <angMax>    0.00   0.00   0.00

               <cfnt>   1.00   1.00   1.00

               <lbh>    0.50   0.50   0.50

               <Material>

               <ambient>    0.25   0.25   0.25   0.80

               <diffuse>    0.50   0.50   0.00   1.00

               <specular>    0.00   0.00   0.00   0.00

               <emission>    0.20   0.20   0.20   0.00

               <shininess>    1.00

            <numChilds>  1

               <GLObject>

               <style>CYLINDER_OBJ

               <name>  <Arm> Рука

                  <pos>    0.00  -1.30   0.00

                  <ang>    0.00   0.00   0.00

                  <angMin>    0.00   0.00   0.00

                  <angMax>    0.00   0.00   0.00

                  <cfnt>   1.00   1.00   1.00

                  <lbh>    0.40   1.50   1.50

                  <Material>

                  <ambient>    0.25   0.25   0.25   0.80

                  <diffuse>    0.50   0.50   0.00   1.00

                  <specular>    0.00   0.00   0.00   0.00

                  <emission>    0.20   0.20   0.20   0.00

                  <shininess>    1.00

               <numChilds>  1

                  <GLObject>

                  <style>SPHERE_OBJ

                  <name>  <Fist> Кулак

                     <pos>    0.00  -1.00   0.00

                     <ang>    0.00  90.00   0.00

                     <angMin>    0.00   0.00   0.00

                     <angMax>    0.00   0.00   0.00

                     <cfnt>   1.00   1.00   1.00

                     <lbh>    0.70   0.70   0.70

                     <Material>

                     <ambient>    0.25   0.25   0.25   0.80

                     <diffuse>    0.25   0.00   0.00   1.00

                     <specular>    0.00   0.00   0.00   0.00

                     <emission>    0.20   0.20   0.20   0.00

                     <shininess>    1.00

                  <numChilds>  0

                  </GLObject><Fist>Кулак

               </GLObject><Arm>Рука

            </GLObject><Ulnar_Joint>Локтевой сустав

         </GLObject><Forearm>Предплечье

      </GLObject><Left_Shoulder_Joint>Левый плечевой сустав

 

Я думаю, из этого фрагмента структура файла уже понятна.

 

Далее с помощью процедуры AppendSceneFmFile считываются все возможные варианты действий объекта. Например, удар правой ногой записан в 3-х файлах BoxData/rKick_01.._03.

 

   Вот фрагмент файла BoxData/ rKick_02.txt, в котором описанo положение его левой руки на второй стадии удара правой ногой:

 

<name>  <Left_Shoulder_Joint> Левый плечевой сустав

         <pos>    0.00   2.30  -1.50

         <ang>   23.00   0.00  23.00

      <numChilds>  1

         <name>  <Forearm> Предплечье

            <pos>    0.00  -1.30   0.00

            <ang>    0.00  90.00   0.00

         <numChilds>  1

            <name>  <Ulnar_Joint> Локтевой сустав

               <pos>    0.00  -0.70   0.00

               <ang>  -150.00   0.00 0.00

            <numChilds>  1

               <name>  <Arm> Рука

                  <pos>    0.00  -1.30   0.00

                  <ang>    0.00   0.00   0.00

               <numChilds>  1

                  <name>  <Fist> Кулак

                     <pos>    0.00  -1.00   0.00

                     <ang>    0.00  90.00   0.00

                  <numChilds>  0

 

Т.е. из предыдущего фрагмента оставлена информация только о позиции и угле поворота каждого элемента относительно предыдущего элемента.

 

Как во всех предыдущих уроках, изображение осуществляется процедурой DrawGLScene:

 

int DrawGLScene_X5(GLvoid)                     // Here's Where We Do All The Drawing

{

       glDisable(GL_LIGHTING);

       glTranslatef(0, 0, -30);          // Move Into The Screen 30.0

       int nCount = 0;

       for(POSITION pos = m_glObjList.GetHeadPosition(); pos != NULL; nCount++)

       {

       CMyGLObject * pGl = (CMyGLObject*)m_glObjList.GetNext(pos);

          pGl->drawGLObject();

 

       }//for(POSITION pos = m_glObjList.GetHeadPosition(); pos != NULL;)

   if(m_bRot) yrot+=0.2f;                //                  Lesson 06

       return TRUE;                      // Everything Went OK

}

 

Здесь m_glObjList является объектным списком графических элементов, описываемых классом CMyGLObject. Вот фрагмент его описания с основными элементами класса:

 

class CMyGLObject : public CObject      // Графический объект

{

public:

       CMyGLObject();

       virtual ~CMyGLObject();

       Vector_3D pos;             // положение объекта относительно родительского объекта

       Vector_3D vel;             // скорость относительно родительского объекта

       Vector_3D ang;             // углы поворота относительно родительского объекта

       Vector_3D angMin;   // мин. углов поворота относительно родительского объекта

       Vector_3D angMax;   // макс. углов поворота относительно родительского объекта 

 

 

       Vector_3D rot;      // угловые скорости относительно родительского объекта

       Vector_3D lbh;             // размер объекта длина ширина высота

       Vector_3D cfnt;     // коэффициенты сжатия объекта

       GLUquadricObj *m_pQuad; // ссылка на квадратич. объект

       CPlaneBox * m_pBx;  // ссылка на объект, заданный плоскостями            

       int m_style;        // характеристика объекта (сфера, диск, цилиндр…)

       CMaterial material; // материал

       int m_frame;        // здесь не используется

       BOOL m_bHit;        // здесь не используется

       int m_nTick;        // счетчик тиков таймера

 

       CString m_NameGL;   // Название по-английски

       CString m_NameGLrus;       // Название по-русски

 

    Matrix m_glMatrix;            // текущая графическая матрица объекта 

 

CMyGLObject * m_pParentGL; // родительский графический объект

 

CObList m_glObjectList;    // объектный список дочерних объектов

CObList m_actionList;             // объектный список возможных действий

CObList  m_actionStack;    // объектный список для запоминания текущего действия

CMyAction * m_pCurAction;  // ссылка на текущее действие

int m_nScene;              // порядковый номер сцены в текущем дайствии

    ………

 

Главный фокус здесь в отрисовке объекта по принципу «кусая себя за хвост»:

 

void CMyGLObject::drawGLObject( void)

{

 

       glMatrixMode( GL_MODELVIEW );

       glPushMatrix();

 

       BOOL B_DEPTH_TEST = glIsEnabled(GL_DEPTH_TEST);

       BOOL B_LIGHTING = glIsEnabled(GL_LIGHTING);

       BOOL B_AUTO_NORMAL = glIsEnabled(GL_AUTO_NORMAL);

       BOOL B_BLEND = glIsEnabled(GL_BLEND);

       BOOL B_TEXTURE_2D = glIsEnabled(GL_TEXTURE_2D);

 

 

       glEnable(GL_DEPTH_TEST);          

       glEnable(GL_LIGHTING);              

       glEnable(GL_AUTO_NORMAL);              

       glEnable(GL_NORMALIZE);

 

 

       ::glTranslatef( pos.x, pos.y, pos.z);

       ::glRotatef(ang.y ,  0.0f, 1.0f, 0.0f);

       ::glRotatef(ang.x, 1.0f, 0.0f, 0.0f);

       ::glRotatef(ang.z , 0.0f, 0.0f, 1.0f);

        glScalef(cfnt.x, cfnt.y, cfnt.z);

       glColor3f(1,1,1);

    material.SetMaterial(GL_FRONT_AND_BACK);

::glGetFloatv(GL_MODELVIEW_MATRIX, &m_glMatrix.x[0][0]);

       drawObjectOfType();

 

       B_BLEND ? glEnable(GL_BLEND): glDisable(GL_BLEND) ;

       B_DEPTH_TEST ? glEnable(GL_DEPTH_TEST): glDisable(GL_DEPTH_TEST) ;

       B_LIGHTING ? glEnable(GL_LIGHTING): glDisable(GL_LIGHTING) ;

       B_AUTO_NORMAL ? glEnable(GL_AUTO_NORMAL): glDisable(GL_AUTO_NORMAL) ;

       B_TEXTURE_2D ? glEnable(GL_TEXTURE_2D): glDisable(GL_TEXTURE_2D) ;

 

    for(POSITION pos = m_glObjectList.GetHeadPosition(); pos != NULL;)

       {

             CMyGLObject * pGl = (CMyGLObject *)m_glObjectList.GetNext(pos);

             pGl->m_pParentGL = this;

             pGl->drawGLObject();

       }//for(position pos = m_glObjectList.GetHeadPosition(); pos != NULL;)

 

       glMatrixMode( GL_MODELVIEW );

       glPopMatrix();

}

 

Процедуры ::glTranslatef, ::glRotatef, glScalef производят перемещение, вращение и растяжение отображаемого элемента, а затем для каждого из дочерних элементов из объектного списка снова вызывается эта же процедура CMyGLObject::drawGLObject. Способы отображения объекта в OpenGL как нельзя лучше приспособлены для такой процедуры.

 

Движение достигается сменой кадров в процедуре Update_X5:

 

void Update_X5(DWORD milliseconds)

{

 

       if(!m_Cadr)

       for(POSITION pos = m_glObjList.GetHeadPosition(); pos != NULL; )

       {

       CMyGLObject * pGl = (CMyGLObject*)m_glObjList.GetNext(pos);

          pGl->NextGLScene();

       }//for(POSITION pos = m_glObjList.GetHeadPosition(); pos != NULL;)

       m_Cadr++;

       m_Cadr %= MAX_CADR;

 

       for(POSITION pos = m_glObjList.GetHeadPosition(); pos != NULL; )

       {

       CMyGLObject * pGl = (CMyGLObject*)m_glObjList.GetNext(pos);

         pGl->UpdateCadr();

       }//for(POSITION pos = m_glObjList.GetHeadPosition(); pos != NULL;)

 

 

}

Промежуток между двумя сценами делится на 25 кадров (MAX_CADR) , для каждого из которых рассчитывается соответствующее промежуточное положение, оно и выводится на экран.

 

Таким образом, в этом уроке имеется возможность создания фигур и их движений во внешних текстовых файлах, причем пользователю не нужно знать программистских деталей, достаточно только пространственного воображения. Можете попытаться изменить значения параметров в текстовых файлах в директории BoxData, и посмотреть, что из этого получится, вновь запустив программу BoxMan.

 

Кроме того, если вы усвоили предыдущий урок X4, то вы можете создать копию этого проекта и модифицировать все по своему усмотрению.

 

Код к уроку

 

LessonsCode_2010.7z (5.09 Mb)

© Владимир Петров
petrov@msun.ru
Моя страница

PMG  17 апреля 2013