delta3d碰撞检测BUG及修复

来源:互联网 发布:现代网络小说家排行榜 编辑:程序博客网 时间:2024/05/20 23:59
 
我在上传我做的这个源码之前,我在自己的程序中遇到一个BUG,但我发现delta3d也出现同样
的错误,因此先解决delta3d本身的BUG
l        delta3d中将例子testPhysics中空中下落物块,比如按下’b’键掉下盒子,默认将碰
撞模型设为setCollisionBox,程序运行正常,如果我们采取三角形片检测,设为
testPysics.cpp line230中修改为

box->SetCollisionMesh();//bNormalizationResult有错!,默认的SetCollisionBox正确,程序总是不定时地弹出错误,

并终止程序

 

l         同样的问题出现在ODE中,我们下载ODE0.9的源码,编译运行“demoMovingTrimesh

例子,当我们按下键盘“m”,天上掉下兔子,如果我们一直按下m键,则程序也会弹出消息框:

 

两者错误出处函数来自odemath.h 302

static __inline void _dNormalize3(dVector3 a)

{

     int bNormalizationResult = dSafeNormalize3(a);//返回矢量是否为

     dIASSERT(bNormalizationResult);//容易出错的地方

     dVARIABLEUSED(bNormalizationResult);

}

BUG产生的可能原因:

由此可见,问题出现在ODE中,并且这是ODE的一个BUG,网上有不同的观点:有的认为是矢量出现为0,当然,这个错误就是这么提示的;有的认为是因为两个mesh刚刚接触,互相穿透深度为0;有的认为是ODE中全局约束力混合值CFM(见ODE中文指南)设得过小,我调到最大1还是不行;有的认为是出现“退化三角形”的缘故,这里给大家介绍下什么是“退化三角形”:

退化三角形degenerate triangle)是一个面积为零的三角形,或者换句话说,是一个三点位于一线上的三角形。如果我们传入一个退化三角形到渲染管线,则该三角形显示为空。

而对退化三角形求其法向量,我们知道法向量用叉乘u|vec1||vec2|sin(theta),theta是两者夹角,如果为退化三角形theta=0PI,则法向量结果为0,这可能是assertion "bNormalizationResult"报错的直接原因。

解决办法:

我尝试过修改碰撞检测函数dCollideTTL(),将互相嵌入深度为0的情况剔除掉,也尝试过手动判断接触点矢量是否为0,如果为0,将其置为单位矢量,,甚至还尝试过只要dCollidTTL检测是否碰撞,而不需要求得碰撞点个数及属性,均没有解决掉这个BUG,如下:

//dCollideTTL等同于PerformTest(见碰撞检测/opcode/CDTestFramework/CDTestFramework/ OBBMeshQuery.cpp(112)

int

dCollideTTL(dxGeom* g1, dxGeom* g2, int Flags, dContactGeom* Contacts, int Stride)

{

     dIASSERT (Stride >= (int)sizeof(dContactGeom));

     dIASSERT (g1->type == dTriMeshClass);

     dIASSERT (g2->type == dTriMeshClass);

     dIASSERT ((Flags & NUMC_MASK) >= 1);

 

    dxTriMesh* TriMesh1 = (dxTriMesh*) g1;

    dxTriMesh* TriMesh2 = (dxTriMesh*) g2;

    dReal * TriNormals1 = (dReal *) TriMesh1->Data->Normals;

    dReal * TriNormals2 = (dReal *) TriMesh2->Data->Normals;

    const dVector3& TLPosition1 = *(const dVector3*) dGeomGetPosition(TriMesh1);

    // TLRotation1 = column-major order

    const dMatrix3& TLRotation1 = *(const dMatrix3*) dGeomGetRotation(TriMesh1);

    const dVector3& TLPosition2 = *(const dVector3*) dGeomGetPosition(TriMesh2);

    // TLRotation2 = column-major order

    const dMatrix3& TLRotation2 = *(const dMatrix3*) dGeomGetRotation(TriMesh2);

    AABBTreeCollider& Collider = TriMesh1->_AABBTreeCollider;

 

    static BVTCache ColCache;

    ColCache.Model0 = &TriMesh1->Data->BVTree;

    ColCache.Model1 = &TriMesh2->Data->BVTree;

 

     // 碰撞检测第四步:Perform a collision query

     // 见碰撞检测/opcode/CDTestFramework/CDTestFramework/OBBMeshQuery.cpp(112)

     // Collision query: Collide(ColCache, World0, World1);

     //Returned bool just says everything was ok. It's not the collision status

    Matrix4x4 amatrix, bmatrix;

    BOOL IsOk = Collider.Collide(ColCache,

                                 &MakeMatrix(TLPosition1, TLRotation1, amatrix),

                                 &MakeMatrix(TLPosition2, TLRotation2, bmatrix) );

   

    // Make "double" versions of these matrices, if appropriate

    dMatrix4 A, B;

    dMakeMatrix4(TLPosition1, TLRotation1, A);

    dMakeMatrix4(TLPosition2, TLRotation2, B);

 

    // 工业仿真添加-----------------------------Start------------------------------//

    if (IsOk) { //如果碰撞检测准备工作正常

        if ( Collider.GetContactStatus() ) // Get collision status => if true, objects overlap

            return 1;

        else

            return 0;

    }

    // 工业仿真添加-----------------------------End------------------------------//

//工业仿真删除-----------------------------Start------------------------------//

/* if (IsOk) {       

        if ( Collider.GetContactStatus() ) // Get collision status => if true, objects overlap

         {

               。。。。。。。。。。。。代码很长,忽略

}

}*/

// 工业仿真删除-----------------------------End------------------------------//

 

于是我将报错函数出处dNormalize3中两句屏蔽掉,如下

static __inline void _dNormalize3(dVector3 a)

{

     int bNormalizationResult = dSafeNormalize3(a);//返回矢量是否为

     /* common.h中有定义:

     #define dIASSERT(a) if (!(a)) dDebug (d_ERR_IASSERT, /

     "assertion /"" #a "/" failed in %s:%d",__FILE__,__LINE__);   */

//   dIASSERT(bNormalizationResult);//报错的地方

//   dVARIABLEUSED(bNormalizationResult);

}

重新编译ODE0.9,生成ode.dll,ode.lib,ode.dll直接覆盖delta3dext/bin/ode.dll,再将ode.lib更名为oded.lib,并覆盖delta3dext/lib/oded.lib,再也不会出现报错了,是不是觉得很好笑?呵呵,也许目前只能这样吧,治标没治本。

 

BUG修正经验:

给大家讲几点我修正BUG过程中学到的一点东西:

l         Delta3d物理引擎基于ODEODE可以进行刚体动态仿真和碰撞检测,碰撞检测部分有一个通用的监测函数,判断是什么碰撞类型,比如box vs box,cylinder vs box,mesh vs mesh等。

l         每个碰撞类型都有一个类,ODE将根据碰撞检测类型分别选择不同的碰撞检测函数,比如若是两个mesh间的碰撞检测,则调用dCollideTTL(),两个“T”代表“TriMesh”一目了然。

l         Delta3d碰撞检测执行流程(我没有函数调用查询及执行流程查询的工具,哪位有提供下谢谢):

OnMessage->NearCallBack(dtcore/scene.cpp)->dCollide(ext/include/ode/collision)->dCollideTTL(ode/collision_trimesh-trimesh.cpp)

l         而对于ODE,本身并没有mesh vs mesh的碰撞检测功能,需用库Opcode,大家可以上网了解下OPCODE。因此函数dCollideTTL()函数最终将会调用OPCODE库中的函数,在DELTA3D中看不到OPCODE 的链接库是因为ODE已经将其打包封装起来一起编译了。没有ODE0.9OPCODE库的兄弟可以向我索要,如果大家都没有,我将这两个库上传到邮件。

 

原创粉丝点击