如何在Irrlich引擎中使用ODE物理引擎

如何在Irrlich引擎中使用ODE物理引擎

                                      

(译 注:ODE是个开源的刚性物理引擎,听起来很吓人.其实不复杂,和渲染引擎结合起来并不难.只要几个小步骤,将数据一转换,调用一下ODE过程,渲染引擎 就有了强大的碰撞检测及其他物理特性.这篇文章,通过一个小例子,将Irrlicht引擎与ODE结合起来,可以迅速对ODE有个了解.更进一步,你可以 看ODE的文档及ODE中的多个例子.本文适合对ODE全然没有接触过的人士.(对Irrlicht引擎不熟也没关系,使用的几个参数与Mesh与其他引 擎大同小异)
原文参见http://thomas.webtracker.ch/jahia/Jahia/pid/481
)

  本教程讲述如何整合ODE(Open Dynamics Engine:http://www.ode.org)与Irrlicht.
  这里不细讲ODE过程,而是通过具体的例子,对ODE有个基本的了解.明白了这个例子后,你可以再详细读ODE手册,可以实现更多的功能.

一,ODE在Windows下的编译要点:

   在Makefile.msvc或msvc-dll(在ODE目录/config下)加以下内容:
ifeq ($(BUILD),release)
OPT=2
C_FLAGS+=/Oy /MD
endif

ifeq ($(BUILD),debug)
C_FLAGS+=/MDd
OPT=d
endif
(注意,工程属性要设为/MT,或MTd)
需要下载ODE 0.039或更高版本(带OPCODE支持).Irrlicht为0.6以上版.

二,弹跳的例子

我们下面建立的程序包含起伏的地形,许多方盒子不断下落,在地上弹起.
我们可以在下落的盒子中移动摄影机(W,S,D,A键控制前后左右),鼠标控制视角,Tab键暂停,回车键改变盒子下落距离,ESC键退出.
(程序画面,我不会贴图,是一大堆绿盒子掉到山上)

1,开始:
使用Visual Studio .Net 2003 ,把ODE的include,lib加到系统设置.还有Irrlicht的include,lib.执行目录还需要有Irrlicht的dll.
打开Bounce工程文件.

2.程序入口
非常简单:
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,int nCmdShow)
{
 Bounce::Bounceable::RunApplication(); //执行下列弹跳的类
 return 0;
}
见BounceMain.cpp, line 12-15

3.弹跳主过程(Bounceable类)
类的具体实现不细讲了,请参见程序.重点讲一下关键部分

参数:
dWorldID world; 物体位置句柄
dSpaceID space; 物体空间句柄(用于碰撞检测的)
dBodyID body; 物体的体积数据句柄,这是用于物理交互作用
dGeomID geom; 物体的几何体句柄,用于碰撞交互作用.

方法函数:

static void nearCollisionCallback(void* data, dGeomID o1, dGeomID o2);
 碰撞回调函数:ODE执行过程,两个几何体靠得足够近后,发生碰撞,将调用这个回调函数.

static void QuaternionToEuler(const dQuaternion quaternion,irr::core::vector3df &euler);
       旋转参数转换函数:将Irrlicht的旋转参数转换为ODE的四元数组.

void setGeomData(irr::scene::IMesh* m); Convert the irrlicht mesh structure to an OPCODE collision structure for TriMeshes
        几何体转换:将Irrlicht的Mesh数据转换为ODE的OPCODE的TriMeshes结构.

void setGeomData();
        包围盒转换:将Irrlicht的包围盒转换为OPCODE的碰撞结构,作为物理过程检测的外形.
 
三,运行ODE仿真

基本仿真的循环过程如下:
 1.加一个力,使ODE的物体发生移动位置及旋转.
 2.计算不同物体间的碰撞.
 3.清除结合的信息
       4.得到物体的新的位置及旋转角度,设置并显示物体
 5,循环执行第一步

本例子从简考虑,不加外力.所以跳过第一步,我们的ODE仿真过程如下:
if(simulate){
   //updateEntitiesBeforePhysics();  
   // 把碰撞回调函数加入ODE
   dSpaceCollide(theSpace,0,&nearCollisionCallback);
   // 设定仿真的步长
   dWorldStep(theWorld,0.1f);   
   //可以用下列过程,快一些
   //dWorldStepFast1(theWorld,0.1,1);
   // 清除节点组
   dJointGroupEmpty(theJointGroup);
   // 取得新的位置及旋转
   updateEntitiesAfterPhysics();
}
见bounce.cpp, line 136-151

碰撞回调函数:
void Bounceable::nearCollisionCallback(void* data, dGeomID o1, dGeomID o2){
  int i=0;
  dBodyID b1=dGeomGetBody(o1); //几何体1
  dBodyID b2=dGeomGetBody(o2); //几何体2

  if(b1 && b2 && dAreConnectedExcluding(b1,b2,dJointTypeContact))return; //如果两个物体是连接的,不检测
  dContact contact[MAX_CONTACTS];
  for(i=0;i    contact[i].surface.mode=dContactBounce | dContactSoftCFM;
    contact[i].surface.mu=dInfinity;
    contact[i].surface.mu2=0;
    contact[i].surface.bounce=1e-5f;
    contact[i].surface.bounce_vel=1e-9f;
    contact[i].surface.soft_cfm=1e-6f;
  }
  int numc=dCollide(o1,o2,MAX_CONTACTS,&contact[0].geom,sizeof(dContact));  //计算接触点
//如果有其他的力的作用,我们可以加在这里,进行检测
  if(numc>0){    //有接触
    for(i=0;i
      dJointID c=dJointCreateContact(theWorld,theJointGroup,&contact[i]); //取得接触点
      dJointAttach(c,b1,b2);       //添加影响(本例是反弹),我们可以把碰撞发声加在这里
    }
  }
}
见bounce.cpp, line 326-358

取得物体变化结果:

void Bounceable::updateEntitiesAfterPhysics(){
  irr::core::vector3df pos;
  irr::core::vector3df rot;
  std::list::iterator iter=NULL;
  for(iter=bounceables.begin();iter!=bounceables.end();++iter){
    Bounceable* entity=(*iter);
    dGeomID geom=entity->geom;  
    if(geom!=0){
      dReal* ode_pos=(dReal*)dGeomGetPosition(geom);    //从ODE得到新位置
      pos.set((irr::f32)ode_pos[0],(irr::f32)ode_pos[1],(irr::f32)ode_pos[2]); //设置Irrlicht物体的位置
      entity->node->setPosition(pos);
      dQuaternion result;
      dGeomGetQuaternion(geom, result);  //取得ODE旋转数值
      QuaternionToEuler(result,rot);  //转换一下
      entity->node->setRotation(rot);  //设置Irrlicht的物体旋转 
    }
  }
}
见bounce.cpp, line 174-196

ODE的旋转四元数组转换为Irrlicht的旋转参数:

void Bounceable::QuaternionToEuler(const dQuaternion quaternion, vector3df &euler){
  dReal w,x,y,z;
  w=quaternion[0];
  x=quaternion[1];
  y=quaternion[2];
  z=quaternion[3];
  double sqw = w*w;   
  double sqx = x*x;   
  double sqy = y*y;   
  double sqz = z*z;
 
  euler.Z = (irr::f32) (atan2(2.0 * (x*y + z*w),(sqx - sqy - sqz + sqw)) //不多结实,一目了然
                        *irr::core::GRAD_PI);
 
  euler.X = (irr::f32) (atan2(2.0 * (y*z + x*w),(-sqx - sqy + sqz + sqw))
                        *irr::core::GRAD_PI); 
 
  euler.Y = (irr::f32) (asin(-2.0 * (x*z - y*w))
                        *irr::core::GRAD_PI);
}
见bounce.cpp, line 359-375

把Irrlicht的Meshes几何体数据转换为ODE的TriMesh几何数据:
(不多解释了,无非是不同的顶点和面)

void Bounceable::setGeomData(irr::scene::IMesh* m){
  // do nothing if the mesh or node is NULL
  if(mesh==NULL || node==NULL) return;
    int i,j,ci,cif,cv;
    indexcount=0;
    vertexcount=0;
    // count vertices and indices
    for(i=0;igetMeshBufferCount();i++){
      irr::scene::IMeshBuffer* mb=mesh->getMeshBuffer(i);
      indexcount+=mb->getIndexCount();
      vertexcount+=mb->getVertexCount();
    }
    // build structure for ode trimesh geom
    vertices=new dVector3[vertexcount];
    indices=new int[indexcount];
    // fill trimesh geom
    ci=0; // current index in the indices array
    cif=0; // offset of the irrlicht-vertex-index in the vetices array
    cv=0; // current index in the vertices array
    for(i=0;igetMeshBufferCount();i++){
      irr::scene::IMeshBuffer* mb=mesh->getMeshBuffer(i);
      // fill indices
      irr::u16* mb_indices=mb->getIndices();
      for(j=0;jgetIndexCount();j++){
        // scale the indices from multiple meshbuffers to single index array
        indices[ci]=cif+mb_indices[j];
        ci++;
      }
      // update the offset for the next meshbuffer
      cif=cif+mb->getVertexCount();
      // fill vertices
      if(mb->getVertexType()==irr::video::EVT_STANDARD){
       irr::video::S3DVertex* mb_vertices=
              (irr::video::S3DVertex*)mb->getVertices();
       for(j=0;jgetVertexCount();j++){
         vertices[cv][0]=mb_vertices[j].Pos.X;
         vertices[cv][1]=mb_vertices[j].Pos.Y;
         vertices[cv][2]=mb_vertices[j].Pos.Z;
         cv++;
       }
     }else if(mb->getVertexType()==irr::video::EVT_2TCOORDS){
       irr::video::S3DVertex2TCoords* mb_vertices=
               (irr::video::S3DVertex2TCoords*)mb->getVertices();
       for(j=0;jgetVertexCount();j++){
         vertices[cv][0]=mb_vertices[j].Pos.X;
         vertices[cv][1]=mb_vertices[j].Pos.Y;
         vertices[cv][2]=mb_vertices[j].Pos.Z;
         cv++;
       }   
     }
  }
  irr::core::vector3df pos=node->getPosition();
  // build the trimesh data
  dTriMeshDataID data=dGeomTriMeshDataCreate();
  dGeomTriMeshDataBuildSimple(data,(dReal*)vertices,
                      vertexcount, indices, indexcount);
  // build the trimesh geom
  geom=dCreateTriMesh(space,data,0,0,0);
  // set the geom position
  dGeomSetPosition(geom,pos.X,pos.Y,pos.Z);
  // lets have a pointer to our bounceable
  // we could need this in the collision callback
  dGeomSetData(geom,(void*)this);
  // in our application we don't want geoms
  // converted from meshes to have a body
  dGeomSetBody(geom,0);
}
见bounce.cpp, line 261-319

设置Irrlicht物体的包围盒为ODE检测的物体及几何体:
    方盒子的包围盒与物体一样,圆球就不行了.当然,针对不同的物体,我们应该制作简单的碰撞几何体,要是都把本身的Mesh去检测,那非把ODE累死不可.
(据说已有硬件支持的ODE了,不过什么时候能广泛使用还很难说)
下列过程也不必细解释了
   void Bounceable::setGeomData(){
  // get the boundingbox
  irr::core::aabbox3d box=node->getBoundingBox();
  irr::core::vector3df extend=box.getExtend();
  // get the position of the scenenode
  irr::core::vector3df pos=node->getPosition();
  // build a box shaped geometry for ODE
  geom=dCreateBox(space,(dReal)extend.X,(dReal)extend.Y,(dReal)extend.Z);
  // set the position of the ODE geom
  dGeomSetPosition(geom,pos.X,pos.Y,pos.Z);
  // set a pointer to our Bounceable,
  // this will come in handy when we do more complicated collisions
  dGeomSetData(geom,(void*)this);
  // create a body for this object
  body=dBodyCreate(world);
  // setup the mass
  dMassSetBox(&mass,5.0,(dReal)extend.X,(dReal)extend.Y,(dReal)extend.Z);
  // combine body and mass
  dBodySetMass(body,&mass);
  // add the body to the geom
  dGeomSetBody(geom,body);
  // set the bodys position (same as geom position)
  dBodySetPosition(body,pos.X,pos.Y,pos.Z);
  dBodySetData(body,(void*)this);
}
见bounce.cpp, line 262-277

结束语:
   经过如上简单过程,Irrlicht有了强大的碰撞检测功能.别的渲染引擎也可如法炮制哦 

你可能感兴趣的:(转贴,引擎,vector,structure,build,video,include)