用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];

SbVec 3f ParticleCoordData[MAX_PARTICLES * 4];

SbVec 2f 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: < openinventor@gmail.com > < openinventor@126.com >

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

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

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值