Ogre水波演示Demo(注释)

//水波的实现原理并不复杂:建立一个多格Mesh平面,加入多个水波种子,不断扩散,修改Mesm顶点,
//相互作用,形成水波效果。
//但是,Ogre的这个水波程序则搞得无比复杂,很有点Microsoft的风格,
//示例子里包含了Mesh变化,硬件缓存应用,天空,水材料更换,动态贴图,物体及灯光的曲线路径,
//及雨水的粒子特效,GUI图形显示,键盘输入控制多种选择,还有镜象反光,材料的折射等内容......
//完全不象irrlicht引擎那样,简简单单提供了一个水的材料,随便贴到哪里,那里就开始波涛翻滚了。
//不过,Ogre的水的效果也很出色,只好一点点细细读来。

//该演示程序包含三个文件:
//WaterMesh.h :定义了水波平面Mesh的类
//WaterMesh.cpp:水波平面Mesh类程序,如何建立Mesh,如何变形。
//Water.cpp: 主程序,所有效果都在这里实现

//    Water.cpp:

/*
-----------------------------------------------------------------------------
This source file is part of OGRE
      (Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/

Copyright ?2000-2003 The OGRE Team
Also see acknowledgements in Readme.html

You may use this sample code for anything you like, it is not covered by the
LGPL like the rest of the engine.
-----------------------------------------------------------------------------
*/
/* Static water simulation by eru
* Started 29.05.2003, 20:54:37
*/
#include "ExampleApplication.h"
#include "WaterMesh.h"

#include <iostream>

AnimationState* mAnimState;

// Mesh stuff 
#define MESH_NAME "WaterMesh"
#define ENTITY_NAME "WaterEntity"
#define MATERIAL_PREFIX "Examples/Water"
#define MATERIAL_NAME "Examples/Water0"
#define COMPLEXITY 64     // watch out - number of polys is 2*ACCURACY*ACCURACY !
#define PLANE_SIZE 3000.0f
#define CIRCLES_MATERIAL "Examples/Water/Circles"

/* Some global variables */
SceneNode *headNode ;
Overlay *waterOverlay ;
ParticleSystem *particleSystem ;
ParticleEmitter *particleEmitter ;
SceneManager *sceneMgr ;

//绘制一个圆环的图,做雨滴落水的环的贴图。
//这段算法看起来好象很复杂,其实原理很简单,你自己也可设计一种算法
//不过,将来自己的程序,还是用photoshop画一个带羽化的白圈更简单,不合适了重新画过。
//所以,该段只要明白其结果,不必细看算法。
void prepareCircleMaterial() 
{
char *bmap = new char[256 * 256 * 4] ;    //图象缓存大小256x256,每元素4字节
memset(bmap, 127, 256 * 256 * 4);  

      //在数组里画圆环......

for(int b=0;b<16;b++) {   
    int x0 = b % 4 ;
    int y0 = b >> 2 ;
    Real radius = 4.0f + 1.4 * (float) b ;    //环开始的半径为4,结束为4+16*1.4
    for(int x=0;x<64;x++) {     
     for(int y=0;y<64;y++) {
      Real dist = Math::Sqrt((x-32)*(x-32)+(y-32)*(y-32));     //36
      dist = fabs(dist -radius -2) / 2.0f ;    //26
      dist = dist * 255.0f;
      if (dist>255)
       dist=255 ;
      int colour = 255-(int)dist ;    //0
      colour = (int)( ((Real)(15-b))/15.0f * (Real) colour );
    
      bmap[4*(256*(y+64*y0)+x+64*x0)+0]=colour ;
      bmap[4*(256*(y+64*y0)+x+64*x0)+1]=colour ;
      bmap[4*(256*(y+64*y0)+x+64*x0)+2]=colour ;
      bmap[4*(256*(y+64*y0)+x+64*x0)+3]=colour ;
     }
    }
}

SDDataChunk imgchunk(bmap, 256 * 256 * 4);    //数组转换为图象
//~ Image img; 
//~ img.loadRawData( imgchunk, 256, 256, PF_A8R8G8B8 );
//~ TextureManager::getSingleton().loadImage( CIRCLES_MATERIAL , img );
TextureManager::getSingleton().loadRawData(CIRCLES_MATERIAL,
    imgchunk, 256, 256, PF_A8R8G8B8);        // A,R,G,B 格式将缓存数组生成的图象
Material *material = (Material*) 
    MaterialManager::getSingleton().create( CIRCLES_MATERIAL );
TextureUnitState *texLayer = material->getTechnique(0)->getPass(0)->createTextureUnitState( CIRCLES_MATERIAL );
texLayer->setTextureAddressingMode( TextureUnitState::TAM_CLAMP ); 
material->setSceneBlending( SBT_ADD );   
material->setDepthWriteEnabled( false ) ;
      material->load();          //加载该圆圈材料
}


/* =========================================================================*/
/*                 WaterCircle class                                            */
/* =========================================================================*/
#define CIRCLE_SIZE 500.0 
#define CIRCLE_TIME 0.5f

//雨滴落水的水圈处理的类
//这个近300条语句的过程做得异常复杂,费劲读完之后,发现其实只是一个圆圈图闪动几下而已,让人晕到 :-(
//可能是想用这个示例,讲解硬件缓存的使用方法吧

class WaterCircle   
{
private:
String name ;
SceneNode *node ;
Mesh *mesh ;
SubMesh *subMesh ;
Entity *entity ;
Real tm ;
static bool first ;
// some buffers shared by all circles
static HardwareVertexBufferSharedPtr posnormVertexBuffer ; 
static HardwareIndexBufferSharedPtr indexBuffer ; // indices for 2 faces
static HardwareVertexBufferSharedPtr *texcoordsVertexBuffers ;

Real *texBufData;
void _prepareMesh()    //制作一个两个三角形组成的方片Mesh
{
    int i,lvl ;
  
    mesh= (Mesh*) MeshManager::getSingleton().createManual(name) ; //建立一个mesh对象

    subMesh = mesh->createSubMesh();    //建立一个子Mesh
    subMesh->useSharedVertices=false;

    int numFaces = 2 ;     //2个三角形
    int numVertices = 4 ;    //4个顶点

    if (first) { // first Circle, create some static common data     //这里初始化处理
     first = false ;    

     // static buffer for position and normals
     //建立4个点的只写硬件缓存            
     posnormVertexBuffer = 
      HardwareBufferManager::getSingleton().createVertexBuffer( 
       6*sizeof(Real), // size of one vertex data    //位置与法线各3个数据
       4, // number of vertices
       HardwareBuffer::HBU_STATIC_WRITE_ONLY, // usage
       false); // no shadow buffer       //无需内存镜象

     Real *posnormBufData = (Real*) posnormVertexBuffer->
      lock(HardwareBuffer::HBL_DISCARD);      //锁定硬件缓存开始写入
     for(i=0;i<numVertices;i++) {
      //存位置
      posnormBufData[6*i+0]=((Real)(i%2)-0.5f)*CIRCLE_SIZE; // pos X
      posnormBufData[6*i+1]=0; // pos Y
      posnormBufData[6*i+2]=((Real)(i/2)-0.5f)*CIRCLE_SIZE; // pos Z
      //存法线,都向上
      posnormBufData[6*i+3]=0 ; // normal X
      posnormBufData[6*i+4]=1 ; // normal Y
      posnormBufData[6*i+5]=0 ; // normal Z
     }
     posnormVertexBuffer->unlock();      //解锁,格子Mesh在硬件缓存建立完成

     // static buffers for 16 sets of texture coordinates
     //建立16缓存索引段——记录各圈波UV贴图坐标
     texcoordsVertexBuffers = new HardwareVertexBufferSharedPtr[16];
     for(lvl=0;lvl<16;lvl++) {
      texcoordsVertexBuffers[lvl] =     //每段索引一个硬件缓存的贴图模板
       HardwareBufferManager::getSingleton().createVertexBuffer( 
        2*sizeof(Real), // size of one vertex data, //只需x,y坐标两个数据记录UV
        numVertices, // number of vertices
        HardwareBuffer::HBU_STATIC_WRITE_ONLY, // usage
        false); // no shadow buffer

      Real *texcoordsBufData = (Real*) texcoordsVertexBuffers[lvl]->
       lock(HardwareBuffer::HBL_DISCARD);    //给缓存赋值
      //小变大,大变小,多来几次
      float x0 = (Real)(lvl % 4) * 0.25 ;     //变化范围0-1
      float y0 = (Real)(lvl / 4) * 0.25 ;
      y0 = 0.75-y0 ; // upside down
      for(i=0;i<4;i++) {
       texcoordsBufData[i*2 + 0]=      //0
        x0 + 0.25 * (Real)(i%2) ;
       texcoordsBufData[i*2 + 1]=
        y0 + 0.25 * (Real)(i/2) ;
      }
      texcoordsVertexBuffers[lvl]->unlock();
     }

     // Index buffer for 2 faces
     //建立硬件面片缓存,将格子的二个三角写进去,每角对应前面的顶点
     unsigned short faces[6] = {2,1,0,    2,3,1};
     indexBuffer = 
      HardwareBufferManager::getSingleton().createIndexBuffer(
       HardwareIndexBuffer::IT_16BIT, 
       6, 
       HardwareBuffer::HBU_STATIC_WRITE_ONLY);
     indexBuffer->writeData(0, 
      indexBuffer->getSizeInBytes(),
      faces,
      true); // true?
    }
  
    // Initialize vertex data
    subMesh->vertexData = new VertexData();     //建立一个Vertex数据对象

    subMesh->vertexData->vertexStart = 0;
    subMesh->vertexData->vertexCount = 4;
    // first, set vertex buffer bindings
    VertexBufferBinding *vbind = subMesh->vertexData->vertexBufferBinding ; 
    vbind->setBinding(0, posnormVertexBuffer);     //将硬件顶点缓存附加到位置0
    vbind->setBinding(1, texcoordsVertexBuffers[0]); //将硬件贴图缓存[0]附加到位置1

    // now, set vertex buffer declaration
    VertexDeclaration *vdecl = subMesh->vertexData->vertexDeclaration ;//通知数字格式为:
    vdecl->addElement(0, 0, VET_FLOAT3, VES_POSITION);       //位置,3个值
    vdecl->addElement(0, 3*sizeof(Real), VET_FLOAT3, VES_NORMAL);    //法线,3个值
    vdecl->addElement(1, 0, VET_FLOAT2, VES_TEXTURE_COORDINATES);    //UV坐标,3个值
  
    // Initialize index data
    subMesh->indexData->indexBuffer = indexBuffer;        //指定面片索引缓存
    subMesh->indexData->indexStart = 0;
    subMesh->indexData->indexCount = 6;
  
    // set mesh bounds 
    //设置mesh包围盒
    AxisAlignedBox circleBounds(-CIRCLE_SIZE/2.0f, 0, -CIRCLE_SIZE/2.0f,
     CIRCLE_SIZE/2.0f, 0, CIRCLE_SIZE/2.0f);
    mesh->_setBounds(circleBounds);
          mesh->load();         //mesh设置完成
          mesh->touch();         //不知道为什么要摸它一下
}
public:
int lvl ;
void setTextureLevel()
{
    //更换子Mesh的贴图坐标索引,
    subMesh->vertexData->vertexBufferBinding->setBinding(1, texcoordsVertexBuffers[lvl]);
}

//建立水圈
WaterCircle(const String& name, Real x, Real y)
{
    this->name = name ;
    _prepareMesh();        //初始化一个方片Mesm,产生硬件缓存,16个贴图坐标模板
    node = static_cast<SceneNode*> (sceneMgr->getRootSceneNode()->createChild(name));

    //放大到一个WaterMesh的格子大,10高
    node->translate(x*(PLANE_SIZE/COMPLEXITY), 10, y*(PLANE_SIZE/COMPLEXITY));
    entity = sceneMgr->createEntity(name, name);
    entity->setMaterialName(CIRCLES_MATERIAL);
    node->attachObject(entity);      //加入场景
    tm = 0 ;
    lvl = 0 ;          //初始时,lv1=0,强度最大,圈最小
    setTextureLevel();        //选用 0贴图模板
}

~WaterCircle()    //结束圈
{
    MeshManager::getSingleton().unload(mesh);
    delete mesh ; // nice, I think I don't have to delete any buffers here ;)
    sceneMgr->removeEntity(entity->getName());
    static_cast<SceneNode*> (sceneMgr->getRootSceneNode())->removeChild(node->getName());
}

//动画水圈
void animate(Real timeSinceLastFrame)
{
    int lastlvl = lvl ;
    tm += timeSinceLastFrame ;        //计时器增加
    lvl = (int) ( (Real)(tm)/CIRCLE_TIME * 16 );
    if (lvl<16 && lvl!=lastlvl) {       //如按波速递增进位
     setTextureLevel();         //更换贴图模板
    }
}
static void clearStaticBuffers()    //清除16个贴图模板
{
    posnormVertexBuffer = HardwareVertexBufferSharedPtr() ;
    indexBuffer = HardwareIndexBufferSharedPtr() ;
    for(int i=0;i<16;i++) {
     texcoordsVertexBuffers[i] = HardwareVertexBufferSharedPtr() ;
    }
    delete [] texcoordsVertexBuffers;
}
} ;


bool WaterCircle::first = true ;
HardwareVertexBufferSharedPtr WaterCircle::posnormVertexBuffer = 
HardwareVertexBufferSharedPtr() ;
HardwareIndexBufferSharedPtr WaterCircle::indexBuffer = 
HardwareIndexBufferSharedPtr() ;
HardwareVertexBufferSharedPtr* WaterCircle::texcoordsVertexBuffers = 0 ;

//经过上面漫长复杂的处理,圆环终于可以闪动了!不过,还要下面近100条语句来控制........

/* =========================================================================*/
/*                 WaterListener class                                            */
/* =========================================================================*/
// Event handler 
 

//主听筒
class WaterListener: public ExampleFrameListener
{
protected:
WaterMesh *waterMesh ;
Entity *waterEntity ;
int materialNumber ;
bool skyBoxOn ;
Real timeoutDelay ;

#define RAIN_HEIGHT_RANDOM 5
#define RAIN_HEIGHT_CONSTANT 5


typedef std::vector<WaterCircle*> WaterCircles ;
WaterCircles circles ;

void processCircles(Real timeSinceLastFrame)    //处理水圈
{
    for(unsigned int i=0;i<circles.size();i++) { //根据水圈数量,逐个动画处理
     circles[i]->animate(timeSinceLastFrame);
    }
    bool found ;
    do {
     found = false ;
     for(WaterCircles::iterator it = circles.begin() ; 
       it != circles.end(); 
       ++it) 
      if ((*it)->lvl>=16) {      //如果环的模板为16,即周期为16,结束并删除它
       delete (*it);
       circles.erase(it);
       found = true ;
       break ;
      }
    } while (found) ;    //循环直到所有的死环删除
}

//粒子雨水处理,雨滴掉进水中,引发一个圆环,并推动水面
void processParticles()   
{
    static int pindex = 0 ;
    ParticleIterator pit = particleSystem->_getIterator() ;
    while(!pit.end()) {
     Particle *particle = pit.getNext();
     Vector3 ppos = particle->getPosition();    //取得一个雨滴粒子的位置
     if (ppos.y<=0 && particle->mTimeToLive>0) { // 雨滴粒子掉水中了!
      // delete particle
      particle->mTimeToLive = 0.0f;    //结束雨滴

      // push the water
      //在水中生成一个水波
      float x = ppos.x / PLANE_SIZE * COMPLEXITY ;
      float y = ppos.z / PLANE_SIZE * COMPLEXITY ;
      float h = rand() % RAIN_HEIGHT_RANDOM + RAIN_HEIGHT_CONSTANT ;//波高加一定的随机数,避免太规则机械

      //范围检测,道理上应该是掉在池子外的不管它了,这里都收归池子了
      if (x<1) x=1 ;
      if (x>COMPLEXITY-1) x=COMPLEXITY-1;
      if (y<1) y=1 ;
      if (y>COMPLEXITY-1) y=COMPLEXITY-1;

      //按压水的waterMesh,产生变形
      waterMesh->push(x,y,-h) ; //!!!打击到水面,产生一个圆波,这是water.cpp的关键之处

      //加一个闪动的水圈图
      WaterCircle *circle = new WaterCircle(
       "Circle#"+StringConverter::toString(pindex++),
       x, y);
      circles.push_back(circle); //存进队列
     }
    }
}

/** Head animation */
//游动Ogre鬼头,劈开水流向前进
Real headDepth ;
void animateHead(Real timeSinceLastFrame)
{
    if(!headNode) return;

    // sine track? :)
    static double sines[4] = {0,100,200,300};
    static const double adds[4] = {0.3,-1.6,1.1,0.5};
    static Vector3 oldPos = Vector3::UNIT_Z;
    for(int i=0;i<4;i++) {
     sines[i]+=adds[i]*timeSinceLastFrame;
    }
    //计算曲线轨迹,取得当前位置;不必细看,可采用自己的曲线。
    Real tx = ((sin(sines[0]) + sin(sines[1])) / 4 + 0.5 ) * (float)(COMPLEXITY-2) + 1 ;
    Real ty = ((sin(sines[2]) + sin(sines[3])) / 4 + 0.5 ) * (float)(COMPLEXITY-2) + 1 ;

    waterMesh->push(tx,ty, -headDepth);     //!!!!压迫水面,产生波浪
    Real step = PLANE_SIZE / COMPLEXITY ;
    headNode->resetToInitialState();
    headNode->scale(3,3,3);       //为什么要放大鬼头?没明白

    Vector3 newPos = Vector3(step*tx, headDepth, step*ty);    //沿轨迹前进,改变方向
    Vector3 diffPos = newPos - oldPos ;
    Quaternion headRotation = Vector3::UNIT_Z.getRotationTo(diffPos);
    oldPos = newPos ;
    headNode->translate(newPos);
    headNode->rotate(headRotation);
}


//根据面板参数,改变各元素的GUI显示的文字,没多少技术内容,用CTRL+C与CTRL+V而已
// GUI updaters
void updateInfoParamC()
{
    GuiManager::getSingleton().getGuiElement("Example/Water/Param_C") \
     ->setCaption("[1/2]Ripple speed: "+StringConverter::toString(waterMesh->PARAM_C));  
}
void updateInfoParamD()
{
    GuiManager::getSingleton().getGuiElement("Example/Water/Param_D") \
     ->setCaption("[3/4]Distance: "+StringConverter::toString(waterMesh->PARAM_D));  
}
void updateInfoParamU()
{
    GuiManager::getSingleton().getGuiElement("Example/Water/Param_U") \
     ->setCaption("[5/6]Viscosity: "+StringConverter::toString(waterMesh->PARAM_U));  
}
void updateInfoParamT()
{
    GuiManager::getSingleton().getGuiElement("Example/Water/Param_T") \
     ->setCaption("[7/8]Frame time: "+StringConverter::toString(waterMesh->PARAM_T));  
}
void updateInfoNormals()
{
    GuiManager::getSingleton().getGuiElement("Example/Water/Normals") \
     ->setCaption(String("[N]Normals: ")+((waterMesh->useFakeNormals)?"fake":"real"));
}
void switchNormals()
{
    waterMesh->useFakeNormals = !waterMesh->useFakeNormals ;
    updateInfoNormals() ;
}
void updateInfoHeadDepth()
{
    GuiManager::getSingleton().getGuiElement("Example/Water/Depth") \
     ->setCaption(String("[U/J]Head depth: ")+StringConverter::toString(headDepth));
}
void updateInfoSkyBox()
{
    GuiManager::getSingleton().getGuiElement("Example/Water/SkyBox")
     ->setCaption(String("[B]SkyBox: ")+String((skyBoxOn)?"On":"Off") );
}

//水的材料更换,由用户选择
void updateMaterial()
{
    //可更换的名字为"Examples/Water0"、"Examples/Water1"......
    //Example-Water.material脚本定义了该例子的8个材料
    //这涉及到材料脚本的编写,产生环境反射等
    //因我的老电脑不能看全8种效果,在此暂不分析各材料脚本,有感兴趣的可分析
    String materialName = MATERIAL_PREFIX+StringConverter::toString(materialNumber);     //由序号组合材料名字
    Material *material = static_cast<Material*> (MaterialManager::getSingleton().getByName(materialName));
    if (!material){
     if(materialNumber){
      materialNumber = 0 ;
      updateMaterial();    //超过回0
      return ;
     } else {
      Except(Exception::ERR_INTERNAL_ERROR,
       "Material "+materialName+"doesn't exist!",
       "WaterListener::updateMaterial");
     }
    }
    waterEntity->setMaterialName(materialName);    //更换材料

    GuiManager::getSingleton().getGuiElement("Example/Water/Material") \
     ->setCaption(String("[M]Material: ")+materialName);    //改变面板显示
}

//根据用户指示,改变水的材料
void switchMaterial()
{
    materialNumber++;
    updateMaterial();
}

//开关天空的渲染
void switchSkyBox()
{
    skyBoxOn = !skyBoxOn;
    sceneMgr->setSkyBox(skyBoxOn, "Examples/SceneSkyBox2");
    updateInfoSkyBox();
}

public:
//水听筒的处理初始化
      WaterListener(RenderWindow* win, Camera* cam, 
    WaterMesh *waterMesh, Entity *waterEntity)
          : ExampleFrameListener(win, cam)
      {
    this->waterMesh = waterMesh ;    //水面Mesm
    this->waterEntity = waterEntity ;
    materialNumber = 8;       //选择的材料序号
    timeoutDelay = 0.0f;      //延时
    headDepth = 2.0f;       //鬼头吃水深度
    skyBoxOn = false ;       //天空默认关闭
  
    updateMaterial();
    updateInfoParamC();
    updateInfoParamD();
    updateInfoParamU();
    updateInfoParamT();
    updateInfoNormals();
    updateInfoHeadDepth();
    updateInfoSkyBox();
      }

     //结束处理    
virtual ~WaterListener ()
    {
     // If when you finish the application is still raining there
     // are water circles that are still being processed
     unsigned int activeCircles = this->circles.size ();
   
     // Kill the active water circles
     for (unsigned int i = 0; i < activeCircles; i++)
      delete (this->circles[i]);
    }

      //帧处理
      bool frameStarted(const FrameEvent& evt)
      {
    bool retval = ExampleFrameListener::frameStarted(evt); //父功能调用

          mAnimState->addTime(evt.timeSinceLastFrame); //动画器计时,用于移动灯光等外围动画处理
  
    // process keyboard events 
    // 键盘输入处理
    mInputDevice->capture();
    Real changeSpeed = evt.timeSinceLastFrame ;

    //SHIFT加速,CTRL键减速
    // adjust keyboard speed with SHIFT (increase) and CONTROL (decrease)
    if (mInputDevice->isKeyDown(KC_LSHIFT) || mInputDevice->isKeyDown(KC_RSHIFT)) {
     changeSpeed *= 10.0f ;
    }
    if (mInputDevice->isKeyDown(KC_LCONTROL)) { 
     changeSpeed /= 10.0f ;
    }
  
    // rain
    //水圈处理
    processCircles(evt.timeSinceLastFrame);

    //雨水粒子处理,空格键下雨
    if (mInputDevice->isKeyDown(KC_SPACE)) {
     particleEmitter->setEmissionRate(20.0f);
    } else {
     particleEmitter->setEmissionRate(0.0f);
    }
    processParticles();    //水粒子处理

    // adjust values (some macros for faster change  
    //键盘输入处理
#define ADJUST_RANGE(_value,_keyPlus,_keyMinus,_minVal,_maxVal,_change,_macro) {\
if (mInputDevice->isKeyDown(_keyPlus)) \
    { _value+=_change ; if (_value>=_maxVal) _value = _maxVal ; _macro ; } ; \
if (mInputDevice->isKeyDown(_keyMinus)) \
    { _value-=_change; if (_value<=_minVal) _value = _minVal ; _macro ; } ; \
}
    //U,J 键盘改变鬼头吃水深度
    ADJUST_RANGE(headDepth, KC_U, KC_J, 0, 10, 0.5*changeSpeed, updateInfoHeadDepth()) ;

    //1-8改变水波的各钟幅度参数,参见后面的waterMesh
    ADJUST_RANGE(waterMesh->PARAM_C, KC_2, KC_1, 0, 10, 0.1f*changeSpeed, updateInfoParamC()) ;

    ADJUST_RANGE(waterMesh->PARAM_D, KC_4, KC_3, 0.1, 10, 0.1f*changeSpeed, updateInfoParamD()) ;

    ADJUST_RANGE(waterMesh->PARAM_U, KC_6, KC_5, -2, 10, 0.1f*changeSpeed, updateInfoParamU()) ;

    ADJUST_RANGE(waterMesh->PARAM_T, KC_8, KC_7, 0, 10, 0.1f*changeSpeed, updateInfoParamT()) ;

    timeoutDelay-=evt.timeSinceLastFrame ;
    if (timeoutDelay<=0)
     timeoutDelay = 0;

#define SWITCH_VALUE(_key,_timeDelay, _macro) { \
    if (mInputDevice->isKeyDown(_key) && timeoutDelay==0) { \
     timeoutDelay = _timeDelay ; _macro ;} }

    SWITCH_VALUE(KC_N, 0.5f, switchNormals());     //N键切换真伪法线
   
    SWITCH_VALUE(KC_M, 0.5f, switchMaterial());     //M键换水的材料

    SWITCH_VALUE(KC_B, 0.5f, switchSkyBox());     //B键开关天空
   
    animateHead(evt.timeSinceLastFrame);         //移动鬼头
   
    waterMesh->updateMesh(evt.timeSinceLastFrame);    //水波变形
   
    // check if we are exiting, if so, clear static HardwareBuffers to avoid
    // segfault
    if (!retval)
     WaterCircle::clearStaticBuffers();    //如结束清除缓存

          // return result from default
    return retval ;
      }
};

//终于到主程序入口了!
class WaterApplication : public ExampleApplication
{
public:
      WaterApplication() {
    
      }

      ~WaterApplication() {  
          delete waterMesh;
      }

protected:
WaterMesh *waterMesh ;
Entity *waterEntity ;

// Just override the mandatory create scene method
      void createScene(void)    //建立场景
      {
    sceneMgr = mSceneMgr ;
          // Set ambient light
          mSceneMgr->setAmbientLight(ColourValue(0.75, 0.75, 0.75));    //打开均匀的环境光照

          // Create a light
          //建立一个点光源,在水面上按随机路线飞舞运动,加强波光效果(见后面)
    //但实际中看不出灯的效果,不知道哪里出问题了
          Light* l = mSceneMgr->createLight("MainLight");     //缺省的灯光为点光,白色
    // Accept default settings: point light, white diffuse, just set position
          // NB I could attach the light to a SceneNode if I wanted it to move automatically with
          //    other objects, but I don't
          l->setPosition(200,300,100);

    // Create water mesh and entity
    waterMesh = new WaterMesh(MESH_NAME, PLANE_SIZE, COMPLEXITY); //建立一个水的Mesh对象,见WaterMesh.cpp
    waterEntity = mSceneMgr->createEntity(ENTITY_NAME, 
     MESH_NAME);
    //~ waterEntity->setMaterialName(MATERIAL_NAME);
    SceneNode *waterNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
    waterNode->attachObject(waterEntity);     //加入场景

          // Add a head, give it it's own node
          headNode = waterNode->createChildSceneNode();
          Entity *ent = mSceneMgr->createEntity("head", "ogrehead.mesh");
          headNode->attachObject(ent);      //载入Ogre鬼头,加入场景

    // Make sure the camera track this node
          //~ mCamera->setAutoTracking(true, headNode);

    // Create the camera node, set its position & attach camera
    //将摄影机加入场景节点,其实这个例子不加也应该可以用。加上的好处是可以便于程序控制
    //你可以尝试将摄影机节点附加到鬼头脑袋上,跟着鬼头在波浪里滑行,效果一定很震撼!
          SceneNode* camNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
    camNode->translate(0, 500, PLANE_SIZE);
    camNode->yaw(Degree(-45));
          camNode->attachObject(mCamera);
  
    // Create light node
          SceneNode* lightNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
    lightNode->attachObject(l);    //运动灯光加入场景

          // set up spline animation of light node
    //给灯光加随机位置随机曲线
          Animation* anim = mSceneMgr->createAnimation("WaterLight", 20);
    AnimationTrack *track ;
          KeyFrame *key ;
    // create a random spline for light
    track = anim->createTrack(0, lightNode);
    key = track->createKeyFrame(0);
    for(int ff=1;ff<=19;ff++) {
     key = track->createKeyFrame(ff);
     Vector3 lpos (
      rand()%(int)PLANE_SIZE , //- PLANE_SIZE/2,
      rand()%300+100,
      rand()%(int)PLANE_SIZE //- PLANE_SIZE/2
      );
     key->setTranslate(lpos);
    }
    key = track->createKeyFrame(20);    //曲线路径:由上边随机定义的20个关键帧确定
  
          // Create a new animation state to track this
          mAnimState = mSceneMgr->createAnimationState("WaterLight");//建立一个动画器,驱动灯的运动
          mAnimState->setEnabled(true);      //允许动画
    //灯光效果没太看出来

          // Put in a bit of fog for the hell of it
     //     mSceneMgr->setFog(FOG_EXP, ColourValue::White, 0.0002);      //作者本想做个白雾,加强效果。不过,Ogre里雾与雨的粒子系统有冲突,所以在此删掉了。

    // show overlay    //overlay是个什么东西?还没学到。删掉这两句,演示似乎也没有什么变化。
    waterOverlay = (Overlay*)OverlayManager::getSingleton().getByName("Example/WaterOverlay");    
    waterOverlay->show();
  
          // Let there be rain
    //建立粒子雨
          particleSystem = ParticleSystemManager::getSingleton().createSystem("rain", 
              "Examples/Water/Rain");               //建立
    particleEmitter = particleSystem->getEmitter(0);
          SceneNode* rNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
          rNode->translate(PLANE_SIZE/2.0f, 3000, PLANE_SIZE/2.0f);       //Node放大确定降雨范围
          rNode->attachObject(particleSystem);            //将粒子系统附加到节点上
          // Fast-forward the rain so it looks more natural
          particleSystem->fastForward(20);     //雨的方向为快速向下
    // It can't be set in .particle file, and we need it ;)
    particleSystem->setBillboardOrigin(BBO_BOTTOM_CENTER);        //面向屏幕的转片做的雨效,设转动轴心
  
    prepareCircleMaterial();    //制作水圈材料
}

      // Create new frame listener
      void createFrameListener(void)    //新建听筒,便于实时处理过程
      {
          mFrameListener= new WaterListener(mWindow, mCamera, waterMesh, waterEntity);
          mRoot->addFrameListener(mFrameListener);
      }

};


#if OGRE_PLATFORM == PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"

//入口主程序

INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
#else
int main(int argc, char **argv)
#endif
{
      // Create application object
      WaterApplication app;

srand(time(0));     //初始化随机数种子,免得每次老面孔

      try {
          app.go();      //开动场景
      } catch( Exception& e ) {
#if OGRE_PLATFORM == PLATFORM_WIN32
          MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
#else
          fprintf(stderr, "An exception has occured: %s\n",
                  e.getFullDescription().c_str());
#endif
      }

      return 0;     //完了
}

 

//    WaterMesh.cpp//这是水波Mesh的对象,只有一个控制,即push(x,y,depth,absolute),在某个点推一下。
//里面建立了三个顶点缓存,根据前后位相及波形参数,自行推动,自动变换

/*
-----------------------------------------------------------------------------
This source file is part of OGRE
      (Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/

Copyright ?2000-2003 The OGRE Team
Also see acknowledgements in Readme.html

You may use this sample code for anything you like, it is not covered by the
LGPL like the rest of the engine.
-----------------------------------------------------------------------------
*/

//建立vertexBuffers存Mesh点数据,根据波形变换,形成水波

#include "WaterMesh.h"

#define ANIMATIONS_PER_SECOND 100.0f

//水的Mesh,建立参数:名字,尺寸,分格数

WaterMesh::WaterMesh(const String& meshName, Real planeSize, int complexity)
{
//该段应该可以直接载入一个外部的平面多格mesh更简单地实现

int x,y,b; // I prefer to initialize for() variables inside it, but VC doesn't like it ;(

this->meshName = meshName ;
this->complexity =    complexity ;
numFaces = 2 * complexity * complexity;    //一个方格两个三角形面片,计算出总的三角形数
numVertices = (complexity + 1) * (complexity + 1) ; //顶点数
lastTimeStamp = 0 ;
lastAnimationTimeStamp = 0;
lastFrameTime = 0 ;

// initialize algorithm parameters
PARAM_C = 0.3f ; // ripple speed //波速
PARAM_D = 0.4f ; // distance    //波距
PARAM_U = 0.05f ; // viscosity        //粘性
PARAM_T = 0.13f ; // time     //持续时间
useFakeNormals = false ;     //是用伪法线还是真法线

// allocate space for normal calculation
vNormals = new Vector3[numVertices];

// create mesh and submesh
//建立Mesh及子mesh
mesh= (Mesh*) MeshManager::getSingleton().createManual(meshName) ;
subMesh = mesh->createSubMesh();
subMesh->useSharedVertices=false;

// Vertex buffers
subMesh->vertexData = new VertexData();
subMesh->vertexData->vertexStart = 0;
subMesh->vertexData->vertexCount = numVertices;

VertexDeclaration* vdecl = subMesh->vertexData->vertexDeclaration; 
VertexBufferBinding* vbind = subMesh->vertexData->vertexBufferBinding; 

//数据格式申明
vdecl->addElement(0, 0, VET_FLOAT3, VES_POSITION);       //位置
vdecl->addElement(1, 0, VET_FLOAT3, VES_NORMAL);       //法线
vdecl->addElement(2, 0, VET_FLOAT2, VES_TEXTURE_COORDINATES);    //UV坐标

// Prepare buffer for positions - todo: first attempt, slow
//建立顶点的硬件缓存
posVertexBuffer = 
           HardwareBufferManager::getSingleton().createVertexBuffer( 
              3*sizeof(Real), 
     numVertices, 
     HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY_DISCARDABLE);

//      注意上面类型为:HBU_DYNAMIC_WRITE_ONLY_DISCARDABLE,及可被刷新
vbind->setBinding(0, posVertexBuffer);  

// Prepare buffer for normals - write only
normVertexBuffer = 
           HardwareBufferManager::getSingleton().createVertexBuffer( 
              3*sizeof(Real), 
     numVertices, 
     HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY_DISCARDABLE);
vbind->setBinding(1, normVertexBuffer);         //法线附加进去

// Prepare texture coords buffer - static one
// todo: optimize to write directly into buffer       //贴图坐标切分到每各
Real *texcoordsBufData = new Real[numVertices*2];
for(y=0;y<=complexity;y++) {
    for(x=0;x<=complexity;x++) {
     texcoordsBufData[2*(y*(complexity+1)+x)+0] = (float)x / complexity ;
     texcoordsBufData[2*(y*(complexity+1)+x)+1] = 1.0f - ((float)y / (complexity)) ;
    }
}
texcoordsVertexBuffer = 
           HardwareBufferManager::getSingleton().createVertexBuffer( 
              2*sizeof(Real), 
     numVertices, 
     HardwareBuffer::HBU_STATIC_WRITE_ONLY); 
texcoordsVertexBuffer->writeData(0, 
    texcoordsVertexBuffer->getSizeInBytes(),
    texcoordsBufData,
    true); // true?
delete [] texcoordsBufData;
      vbind->setBinding(2, texcoordsVertexBuffer);    //材料贴图坐标附加进去

// Prepare buffer for indices
indexBuffer = 
    HardwareBufferManager::getSingleton().createIndexBuffer(
     HardwareIndexBuffer::IT_16BIT, 
     3*numFaces, 
     HardwareBuffer::HBU_STATIC, true);

//锁定mesh缓存,开始建立数据
unsigned short *faceVertexIndices = (unsigned short*) 
    indexBuffer->lock(0, numFaces*3*2, HardwareBuffer::HBL_DISCARD);

//建立三角形面片,索引到各顶点坐标
for(y=0 ; y    for(int x=0 ; x     unsigned short *twoface = faceVertexIndices + (y*complexity+x)*2*3;
     int p0 = y*(complexity+1) + x ;
     int p1 = y*(complexity+1) + x + 1 ;
     int p2 = (y+1)*(complexity+1) + x ;
     int p3 = (y+1)*(complexity+1) + x + 1 ;
     twoface[0]=p2; //first tri
     twoface[1]=p1;
     twoface[2]=p0;
     twoface[3]=p2; //second tri
     twoface[4]=p3;
     twoface[5]=p1;
    }
}
indexBuffer->unlock();         //完成,解锁缓存

// Set index buffer for this submesh
subMesh->indexData->indexBuffer = indexBuffer;
subMesh->indexData->indexStart = 0;
subMesh->indexData->indexCount = 3*numFaces;

/* prepare vertex positions
    * note - we use 3 vertex buffers, since algorighm uses two last phases 
    * to calculate the next one
    */

//建立顶点位置
for(b=0;b<3;b++) {
    vertexBuffers[b] = new Real[numVertices * 3] ;
    for(y=0;y<=complexity;y++) {
     for(x=0;x<=complexity;x++) {
      int numPoint = y*(complexity+1) + x ;
      Real* vertex = vertexBuffers[b] + 3*numPoint ;
      vertex[0]=(float)(x) / (float)(complexity) * (float) planeSize ;
      vertex[1]= 0 ; // rand() % 30 ;
      vertex[2]=(float)(y) / (float)(complexity) * (float) planeSize ;
     }
    }
}

AxisAlignedBox meshBounds(0,0,0,
    planeSize,0, planeSize);
mesh->_setBounds(meshBounds);    //设置包围盒

currentBuffNumber = 0 ;       //选顶点缓存0作为缓存
posVertexBuffer->writeData(0, 
    posVertexBuffer->getSizeInBytes(), // size 
    vertexBuffers[currentBuffNumber], // source
    true); // discard?      //顶点数据写入硬件缓存

      mesh->load();     //完成后,要载入
      mesh->touch();     //摸一下
}
/* ========================================================================= */
WaterMesh::~WaterMesh ()
{
    delete[] vertexBuffers[0];
    delete[] vertexBuffers[1];
    delete[] vertexBuffers[2];

delete[] vNormals;
}
/* ========================================================================= */

//对mesh的一个位置挤压处理,产生一个波的种子
void WaterMesh::push(Real x, Real y, Real depth, bool absolute)    //absolute确定挤压是直接还是叠加方式
{
Real *buf = vertexBuffers[currentBuffNumber]+1 ; //只修改Y值
// scale pressure according to time passed
depth = depth * lastFrameTime * ANIMATIONS_PER_SECOND ;    //深度根据帧数插值,实现平滑过度
#define _PREP(addx,addy) { \
Real *vertex=buf+3*((int)(y+addy)*(complexity+1)+(int)(x+addx)) ; \
Real diffy = y - floor(y+addy); \
Real diffx = x - floor(x+addx); \
Real dist=sqrt(diffy*diffy + diffx*diffx) ; \
Real power = 1 - dist ; \
if (power<0)    \
    power = 0; \
if (absolute) \
    *vertex = depth*power ;    \
else \
    *vertex += depth*power ;    \
} /* #define */
_PREP(0,0);//分别压格子的四个角点
_PREP(0,1);
_PREP(1,0);
_PREP(1,1);
#undef _PREP 
}

/* ========================================================================= */

//读取任意坐标的高度值,便于雨滴判断落水点
Real WaterMesh::getHeight(Real x, Real y)
{
#define hat(_x,_y) buf[3*((int)_y*(complexity+1)+(int)(_x))]
Real *buf = vertexBuffers[currentBuffNumber] ;
Real xa = floor(x);
Real xb = xa + 1 ;
Real ya = floor(y);
Real yb = ya + 1 ;
Real *vertex = buf + 3*((int)(ya)*(complexity+1)+(int)(xa));
Real yaxavg = hat(xa,ya) * (1.0f-fabs(xa-x)) + hat(xb,ya) * (1.0f-fabs(xb-x));
Real ybxavg = hat(xa,yb) * (1.0f-fabs(xa-x)) + hat(xb,yb) * (1.0f-fabs(xb-x));
Real yavg = yaxavg * (1.0f-fabs(ya-y)) + ybxavg * (1.0f-fabs(yb-y)) ;
return yavg ;
}
/* ========================================================================= */

//计算各点伪法线,简单计算,速度快
void WaterMesh::calculateFakeNormals()
{
int x,y;
Real *buf = vertexBuffers[currentBuffNumber] + 1;
Real *pNormals = (Real*) normVertexBuffer->lock(
    0,normVertexBuffer->getSizeInBytes(), HardwareBuffer::HBL_DISCARD);
for(y=1;y    Real *nrow = pNormals + 3*y*(complexity+1);
    Real *row = buf + 3*y*(complexity+1) ;
    Real *rowup = buf + 3*(y-1)*(complexity+1) ;
    Real *rowdown = buf + 3*(y+1)*(complexity+1) ;
    for(x=1;x     Real xdiff = row[3*x+3] - row[3*x-3] ;
     Real ydiff = rowup[3*x] - rowdown[3*x-3] ;
     Vector3 norm(xdiff,30,ydiff);
     norm.normalise();
     nrow[3*x+0] = norm.x;
     nrow[3*x+1] = norm.y;
     nrow[3*x+2] = norm.z;
    }
}
normVertexBuffer->unlock();
}

/* ========================================================================= */
//根据各面位置,计算真法线,这样更平滑,但需要从硬件缓存读取面的数据
void WaterMesh::calculateNormals()
{
int i,x,y;
Real *buf = vertexBuffers[currentBuffNumber] + 1;
// zero normals
for(i=0;i    vNormals[i] = Vector3::ZERO;
}
// first, calculate normals for faces, add them to proper vertices
buf = vertexBuffers[currentBuffNumber] ;
unsigned short* vinds = (unsigned short*) indexBuffer->lock(
    0, indexBuffer->getSizeInBytes(), HardwareBuffer::HBL_READ_ONLY);
Real *pNormals = (Real*) normVertexBuffer->lock(
    0, normVertexBuffer->getSizeInBytes(), HardwareBuffer::HBL_DISCARD);
for(i=0;i    int p0 = vinds[3*i] ;
    int p1 = vinds[3*i+1] ;
    int p2 = vinds[3*i+2] ;
    Vector3 v0(buf[3*p0], buf[3*p0+1], buf[3*p0+2]);
    Vector3 v1(buf[3*p1], buf[3*p1+1], buf[3*p1+2]);
    Vector3 v2(buf[3*p2], buf[3*p2+1], buf[3*p2+2]);
    Vector3 diff1 = v2 - v1 ;
    Vector3 diff2 = v0 - v1 ;
    Vector3 fn = diff1.crossProduct(diff2);
    vNormals[p0] += fn ;
    vNormals[p1] += fn ;
    vNormals[p2] += fn ;
}
// now normalize vertex normals
for(y=0;y<=complexity;y++) {
    for(x=0;x<=complexity;x++) {
     int numPoint = y*(complexity+1) + x ;
     Vector3 n = vNormals[numPoint] ;
     n.normalise() ;
     Real* normal = pNormals + 3*numPoint ;
     normal[0]=n.x;
     normal[1]=n.y;
     normal[2]=n.z;
    }
}
indexBuffer->unlock();
normVertexBuffer->unlock();
}
/* ========================================================================= */

//Mesh根据定义的参数,进行变化
void WaterMesh::updateMesh(Real timeSinceLastFrame)
{
int x, y ;
return ;
lastFrameTime = timeSinceLastFrame ;
lastTimeStamp += timeSinceLastFrame ;

// do rendering to get ANIMATIONS_PER_SECOND
while(lastAnimationTimeStamp <= lastTimeStamp) {

    // switch buffer numbers
    currentBuffNumber = (currentBuffNumber + 1) % 3 ;     //顶点缓存向前选,总共3个,循环选择
    Real *buf = vertexBuffers[currentBuffNumber] + 1 ; // +1 for Y coordinate
    Real *buf1 = vertexBuffers[(currentBuffNumber+2)%3] + 1 ;     //前一个
    Real *buf2 = vertexBuffers[(currentBuffNumber+1)%3] + 1;    //后一个

    /* we use an algorithm from
     * http://collective.valve-erc.com/index.php?go=water_simulation
     * The params could be dynamically changed every frame ofcourse
     */
    //下列水波的循环推动变化是根据上面网站的文章搞的,原理可参考原文。
    double C = PARAM_C; // ripple speed 
    double D = PARAM_D; // distance
    double U = PARAM_U; // viscosity
    double T = PARAM_T; // time
  
    Real TERM1 = ( 4.0f - 8.0f*C*C*T*T/(D*D) ) / (U*T+2) ;
    Real TERM2 = ( U*T-2.0f ) / (U*T+2.0f) ;
    Real TERM3 = ( 2.0f * C*C*T*T/(D*D) ) / (U*T+2) ;

    //根据参数及前后缓存的位置,相互推动,逐点计算新的变化位置
    ////这个例子里边缘不动,实际应用中,应该可以动
    for(y=1;y     Real *row = buf + 3*y*(complexity+1) ;
     Real *row1 = buf1 + 3*y*(complexity+1) ;
     Real *row1up = buf1 + 3*(y-1)*(complexity+1) ;
     Real *row1down = buf1 + 3*(y+1)*(complexity+1) ;
     Real *row2 = buf2 + 3*y*(complexity+1) ;
     for(x=1;x      row[3*x] = TERM1 * row1[3*x] 
       + TERM2 * row2[3*x]
       + TERM3 * ( row1[3*x-3] + row1[3*x+3] + row1up[3*x]+row1down[3*x] ) ;
     }
    }
  
    lastAnimationTimeStamp += (1.0f / ANIMATIONS_PER_SECOND);
}

//计算变形后的法线或伪法线
if (useFakeNormals) {
    calculateFakeNormals();
} else {
    calculateNormals();
}

// set vertex buffer    把刷新的顶点数据传到硬件顶点缓存
posVertexBuffer->writeData(0, 
    posVertexBuffer->getSizeInBytes(), // size 
    vertexBuffers[currentBuffNumber], // source
    true); // discard?
}

//对Mesh的硬件缓存的直接修改,CPU和GPU来回传数是比较费时间的,更好的办法是写个shader用gpu直接修改顶点数据.但问题GPU不能存数,所以,波形就无法记录推动下面波形.所以,这只是个示范,真正应用水波,还是参考其他的水波吧.

//全部完
 

 
作者:pizi0475 发表于2011-7-29 15:25:06 原文链接
阅读:80 评论:0 查看评论

你可能感兴趣的:(demo)