用OpenInventor實現的NeHe OpenGL教程-第十九課

OpenInventor實現的NeHe OpenGL教程-第十九課

 

      

 

這節課我們將討論怎樣使用OpenInventor實現一個簡單的粒子系統。我們在NeHe教程中可以看到,一個簡單的粒子系統實現起來不像想象中那樣的困難。只要能計算好一個粒子的當前狀態(速度,加速度,顏色),就很容易完成一個粒子系統的效果。

 

       NeHe教程中使用三角形帶來顯示每個粒子(GL_TRIANGLE_STRIP),OpenInventor提供了一個相對應的節點類SoTriangleStripSetSoTriangleStripSet節點是OpenInventor中顯示速度最快的節點,特別適合顯示大量粒子效果。

 

       下面的代碼首先定義一些全局變量。

SoMaterial*                 g_pParticleColor = NULL; //所有粒子的顏色

SoCoordinate3*              g_pParticleCoord = NULL; //所有粒子的位置座標

SoTextureCoordinate2*       g_pParticleTexCoord = NULL; //所有粒子的材質座標

SoTriangleStripSet*         g_pParticleStripSet = NULL; //所有粒子的三角形面集

 

       下面定義的變量和NeHe教程定義的變量名稱和含義完全相同,不在贅述

#define  MAX_PARTICLES 1000     // Number Of Particles To Create

 

bool rainbow = true;                  // Rainbow Mode?

float    slowdown = 2.0f;            // Slow Down Particles

float    xspeed = 0;                 // Base X Speed (To Allow Keyboard Direction Of Tail)

float    yspeed = 0;                 // Base Y Speed (To Allow Keyboard Direction Of Tail)

float    zoom = -40.0f;              // Used To Zoom Out

int      col;                        // Current Color Selection

int      delay;                      // Rainbow Effect Delay

 

typedef struct                       // Create A Structure For Particle

{

     bool active;                     // Active (Yes/No)

     float    life;                  // Particle Life

     float    fade;                  // Fade Speed

     float    r;                     // Red Value

     float    g;                     // Green Value

     float    b;                     // Blue Value

     float    x;                     // X Position

     float    y;                     // Y Position

     float    z;                     // Z Position

     float    xi;                    // X Direction

     float    yi;                    // Y Direction

     float    zi;                    // Z Direction

     float    xg;                    // X Gravity

     float    yg;                    // Y Gravity

     float    zg;                    // Z Gravity

} particles;                         // Particles Structure

particles particle[MAX_PARTICLES];   // Particle Array (Room For Particle Info)

 

static float colors[12][3] =         // Rainbow Of Colors

{

     {1.0f,0.5f,0.5f},{1.0f,0.75f,0.5f},{1.0f,1.0f,0.5f},{0.75f,1.0f,0.5f},

     {0.5f,1.0f,0.5f},{0.5f,1.0f,0.75f},{0.5f,1.0f,1.0f},{0.5f,0.75f,1.0f},

     {0.5f,0.5f,1.0f},{0.75f,0.5f,1.0f},{1.0f,0.5f,1.0f},{1.0f,0.5f,0.75f}

};

 

float ParticleColorData[MAX_PARTICLES][3];

float ParticleLifeData[MAX_PARTICLES];

SbVec3f ParticleCoordData[MAX_PARTICLES * 4];

SbVec2f ParticleTexCoordData[MAX_PARTICLES * 4];

int ParticleNumVertices[MAX_PARTICLES];

 

 

       下面的函數用來更新粒子系統中每個粒子的顏色,位置,材質等信息。計算方法和NeHe教程相同。

void UpdateParticle(void)

{

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

     {

         if (particle[i].active)              // If The Particle Is Active

         {

              float x = particle[i].x;         // Grab Our Particle X Position

              float y = particle[i].y;         // Grab Our Particle Y Position

              float z = particle[i].z + zoom;  // Particle Z Pos + Zoom

 

              ParticleCoordData[i * 4 + 0].setValue(x + 0.5f,y + 0.5f,z);

              ParticleCoordData[i * 4 + 1].setValue(x - 0.5f,y + 0.5f,z);

              ParticleCoordData[i * 4 + 2].setValue(x + 0.5f,y - 0.5f,z);

              ParticleCoordData[i * 4 + 3].setValue(x - 0.5f,y - 0.5f,z);

 

              ParticleTexCoordData[i * 4 + 0].setValue(1,1);

              ParticleTexCoordData[i * 4 + 1].setValue(0,1);

              ParticleTexCoordData[i * 4 + 2].setValue(1,0);

              ParticleTexCoordData[i * 4 + 3].setValue(0,0);

 

              ParticleColorData[i][0] = particle[i].r;

              ParticleColorData[i][1] = particle[i].g;

              ParticleColorData[i][2] = particle[i].b;

              ParticleLifeData[i] = 1 - particle[i].life;

 

              particle[i].x += particle[i].xi / (slowdown * 1000);// Move On The X Axis By X Speed

              particle[i].y += particle[i].yi / (slowdown * 1000);// Move On The Y Axis By Y Speed

              particle[i].z += particle[i].zi / (slowdown * 1000);// Move On The Z Axis By Z Speed

 

              particle[i].xi += particle[i].xg;              // Take Pull On X Axis Into Account

              particle[i].yi += particle[i].yg;              // Take Pull On Y Axis Into Account

              particle[i].zi += particle[i].zg;              // Take Pull On Z Axis Into Account

              particle[i].life -= particle[i].fade;          // Reduce Particles Life By 'Fade'

 

              if (particle[i].life < 0.0f)                       // If Particle Is Burned Out

              {

                   particle[i].life = 1.0f;                       // Give It New Life

                   particle[i].fade = float(rand() % 100) / 1000.0f + 0.003f;   // Random Fade Value

                   particle[i].x = 0.0f;                          // Center On X Axis

                   particle[i].y = 0.0f;                          // Center On Y Axis

                   particle[i].z = 0.0f;                          // Center On Z Axis

                   particle[i].xi = xspeed + float((rand() % 60) - 32.0f); // X Axis Speed And Direction

                   particle[i].yi = yspeed + float((rand() % 60) - 30.0f); // Y Axis Speed And Direction

                   particle[i].zi = float((rand() % 60) - 30.0f); // Z Axis Speed And Direction

                   particle[i].r = colors[col][0];           // Select Red From Color Table

                   particle[i].g = colors[col][1];           // Select Green From Color Table

                   particle[i].b = colors[col][2];           // Select Blue From Color Table

              }

         }

     }

 

     g_pParticleColor->diffuseColor.setValuesPointer(MAX_PARTICLES,(float *)ParticleColorData);

     g_pParticleColor->transparency.setValuesPointer(MAX_PARTICLES,ParticleLifeData);

 

     g_pParticleCoord->point.setValuesPointer(MAX_PARTICLES * 4,ParticleCoordData);

     g_pParticleTexCoord->point.setValuesPointer(MAX_PARTICLES * 4,ParticleTexCoordData);

}

 

       定時回調函數,用來不斷更新粒子系統中每個粒子的顏色,位置等狀態

void TimerSensorCB(void * data, SoSensor *)

{

     UpdateParticle();

 

     if(rainbow && (delay > 25))

     {

         delay = 0;

         col++;             // Change The Particle Color

         if (col > 11)

              col = 0; // If Color Is To High Reset It

     }

     delay++;

}

 

創建場景數據

void BuildScene(void)

{

//添加鍵盤迴調事件

     SoEventCallback* pEventCallback = new SoEventCallback;

     pEventCallback->addEventCallback(SoKeyboardEvent::getClassTypeId(),

KeyboardEventCB,g_pOivSceneRoot);

     g_pOivSceneRoot->addChild(pEventCallback);

     //OpenGL回調事件

     SoCallback *pGlCallback = new SoCallback();

     pGlCallback->setCallback(GlRenderCB, NULL);

     g_pOivSceneRoot->addChild(pGlCallback);

 

     SoComplexity *pComplexity = new SoComplexity;

     pComplexity->textureQuality = 0.6;

     g_pOivSceneRoot->addChild(pComplexity);

     //加載材質位圖

     SoTexture2 *Texture = new SoTexture2;

     Texture->filename.setValue("../Data/Particle.png");

     Texture->model = SoTexture2::MODULATE;

     g_pOivSceneRoot->addChild(Texture);

 

     SoMaterialBinding *pMaterialBinding = new SoMaterialBinding;

     pMaterialBinding->value = SoMaterialBindingElement::PER_PART;

     g_pOivSceneRoot->addChild(pMaterialBinding);

 

     g_pParticleColor = new SoMaterial;

     g_pOivSceneRoot->addChild(g_pParticleColor);

 

     g_pParticleCoord = new SoCoordinate3;

     g_pOivSceneRoot->addChild(g_pParticleCoord);

 

     g_pParticleTexCoord = new SoTextureCoordinate2;

     g_pOivSceneRoot->addChild(g_pParticleTexCoord);

 

     g_pParticleStripSet = new SoTriangleStripSet;

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

         ParticleNumVertices[i] = 4;

     g_pParticleStripSet->numVertices.setValues(0,MAX_PARTICLES,ParticleNumVertices);

     g_pOivSceneRoot->addChild(g_pParticleStripSet);

     //初始化粒子狀態數組

     for (int i = 0; i < MAX_PARTICLES; i++)            // Initials All The Textures

     {

         particle[i].active = true;                     // Make All The Particles Active

         particle[i].life = 1.0f;                       // Give All The Particles Full Life

         particle[i].fade = float(rand() % 100) / 1000.0f + 0.003f;                               particle[i].r = colors[int(i * (12.0 / MAX_PARTICLES))][0];                                particle[i].g = colors[int(i * (12.0 / MAX_PARTICLES))][1];                                particle[i].b = colors[int(i * (12.0 / MAX_PARTICLES))][2];                              particle[i].xi = float((rand() % 50) - 26.0f) * 10.0f;                                   particle[i].yi = float((rand() % 50) - 25.0f) * 10.0f;

         particle[i].zi = float((rand() % 50) - 25.0f) * 10.0f;                                   particle[i].xg = 0.0f;                             // Set Horizontal Pull To Zero

         particle[i].yg = -0.8f;                            // Set Vertical Pull Downward

         particle[i].zg = 0.0f;                             // Set Pull On Z Axis To Zero

         particle[i].x =  0;

         particle[i].y =  0;

         particle[i].z =  0;

     }

 

     UpdateParticle();

     //定義粒子更新定時器

     SoTimerSensor *pTimerSensor = new SoTimerSensor(TimerSensorCB, NULL);

     pTimerSensor->setInterval(0.001);

     pTimerSensor->schedule();

}

 

 

 

    下面的代碼是鍵盤響應函數。

void KeyboardEventCB(void *userData, SoEventCallback *pEventCB)

{

     const SoEvent *pEvent = pEventCB->getEvent();

     if(SO_KEY_PRESS_EVENT(pEvent,LEFT_ARROW))

     {

         if(xspeed > -200)

              xspeed -= 1.0f;

     }

     else

     if(SO_KEY_PRESS_EVENT(pEvent,RIGHT_ARROW))

     {

         if(xspeed < 200)

              xspeed += 1.0f;

     }

     else

     if(SO_KEY_PRESS_EVENT(pEvent,UP_ARROW))

     {

         if(yspeed < 200)

              yspeed += 1.0f;

     }

     else

     if(SO_KEY_PRESS_EVENT(pEvent,DOWN_ARROW))

     {

         if(yspeed > -200)

              yspeed -= 1.0f;

     }

     else

     if(SO_KEY_PRESS_EVENT(pEvent,PAGE_UP))

     {       

         zoom += 0.1f;

     }

     else

     if(SO_KEY_PRESS_EVENT(pEvent,PAGE_DOWN))

     {       

         zoom -= 0.1f;

     }

     else

     if(SO_KEY_PRESS_EVENT(pEvent,PAD_ADD))

     {       

         if(slowdown > 1.0f)

              slowdown -= 0.01f;

     }

     else

     if(SO_KEY_PRESS_EVENT(pEvent,PAD_SUBTRACT))

     {       

         if(slowdown < 4.0f)

              slowdown += 0.01f;

     }

     else

     if(SO_KEY_PRESS_EVENT(pEvent,TAB))

     {

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

         {

              if (particle[i].active)                        // If The Particle Is Active

              {

                   particle[i].x = 0.0f;                     // Center On X Axis

                   particle[i].y = 0.0f;                     // Center On Y Axis

                   particle[i].z = 0.0f;                     // Center On Z Axis

                   particle[i].xi = float((rand() % 50) - 26.0f) * 10.0f;                                   particle[i].yi = float((rand() % 50) - 25.0f) * 10.0f;                                 particle[i].zi = float((rand() % 50) - 25.0f) * 10.0f;    

              }

         }

     }

     else

     if(SO_KEY_PRESS_EVENT(pEvent,PAD_8))

     {

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

         {

              if (particle[i].active && particle[i].yg < 1.5f)

                   particle[i].yg += 0.01f;

         }

     }

     else

     if(SO_KEY_PRESS_EVENT(pEvent,PAD_2))

     {

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

         {

              if (particle[i].active && particle[i].yg > -1.5f)

                   particle[i].yg -= 0.01f;

         }

     }

     else

     if(SO_KEY_PRESS_EVENT(pEvent,PAD_6))

     {

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

         {

              if (particle[i].active && particle[i].xg < 1.5f)

                   particle[i].xg += 0.01f;

         }

     }

     else

     if(SO_KEY_PRESS_EVENT(pEvent,PAD_4))

     {

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

         {

              if (particle[i].active && particle[i].xg > -1.5f)

                   particle[i].xg -= 0.01f;

         }

     }

     else

     if(SO_KEY_PRESS_EVENT(pEvent,RETURN))

     {

         rainbow = !rainbow;

     }

     else

     if(SO_KEY_PRESS_EVENT(pEvent,SPACE))

     {

         rainbow = false;

         delay = 0;

         col++;             // Change The Particle Color

         if (col > 11)

              col = 0;      // If Color Is To High Reset It

     }

     pEventCB->setHandled();

}

 

       現在編譯運行我們程序,屏幕上顯示出一個效果非常華麗的噴泉。按下左右方向鍵,噴泉可以左右轉動。按下上下方向鍵,噴泉可以上下轉動。按下PnUp/PnDn鍵,噴泉將放大或縮小。按下TAB鍵,噴泉將初始化一次。按下空格鍵,噴泉將修改顏色。效果和NeHe第十九課是相同的。

 

本課的完整代碼下載。(VC 2003 Coin2.5

 

 

後記

OpenInventor是一種基於OpenGL的面向對象的三維圖形軟件開發包。使用這個開發包,程序員可以快速、簡潔地開發出各種類型的交互式三維圖形軟件。這裏不對OpenInventor做詳細的介紹,讀者如果感興趣,可以閱讀我的blog中的這篇文章《OpenInventor 簡介》。

 

NeHe教程是目前針對初學者來說最好的OpenGL教程,它可以帶領讀者由淺入深,循序漸進地掌握OpenGL編程技巧。到目前爲止(200711月),NeHe教程一共有48節。我的計劃是使用OpenInventor來實現所有48節課程同樣的效果。目的是複習和鞏固OpenGL的知識,同時與各位讀者交流OpenInventor的使用技巧。

 

       因爲篇幅的限制,我不會介紹NeHe教程中OpenGL的實現過程,因爲NeHe的教程已經講解的很清楚了,目前網絡中也有NeHe的中文版本。我將使用VC 2003作爲主要的編譯器。程序框架採用和NeHe一樣的Win32程序框架,不使用MFC。程序也可以在VC ExpressVC 2005/2008中編譯。我採用的OpenInventor開發環境是Coin,這是一個免費開源的OpenInventor開發庫。文章 OpenInventorCoin3D開發環境 介紹瞭如何在VC中使用Coin。我使用的Coin版本是2.5。讀者可以到 www.coin3d.org 中免費下載。

 

       讀者可以在遵循GNU協議的條件下自由使用、修改本文的代碼。水平的原因,代碼可能不是最優化的,我隨時期待讀者的指正和交流。轉載請註明。謝謝。

我的聯繫方式:

E-mail: < [email protected] > < [email protected] >

Blog: < http://blog.csdn.net/RobinHao >

Site: < http://www.openinventor.cn >

 
發佈了55 篇原創文章 · 獲贊 15 · 訪問量 21萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章