您的位置:首页 > 其它

delta3d碰撞检测BUG及修复

2009-07-02 21:49 253 查看
我在上传我做的这个源码之前,我在自己的程序中遇到一个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=0或PI,则法向量结果为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直接覆盖delta3d中ext/bin/ode.dll,再将ode.lib更名为oded.lib,并覆盖delta3d中ext/lib/oded.lib,再也不会出现报错了,是不是觉得很好笑?呵呵,也许目前只能这样吧,治标没治本。



BUG修正经验:

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

l Delta3d物理引擎基于ODE,ODE可以进行刚体动态仿真和碰撞检测,碰撞检测部分有一个通用的监测函数,判断是什么碰撞类型,比如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.9和OPCODE库的兄弟可以向我索要,如果大家都没有,我将这两个库上传到邮件。



本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/fengerfafa/archive/2008/08/25/2828988.aspx
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: