BUG00001 BUG症状: 我在上传我做的这个源码之前,我在自己的程序中遇 ...
BUG00001
BUG
我在上传我做的这个源码之前,我在自己的程序中遇到一个BUG,但我发现delta3d也出现同样的错误,因此先解决delta3d本身的BUG: , 在delta3d中将例子testPhysics中空中下落物块,比如按下’b’键掉下
盒子,默认将碰撞模型设为setCollisionBox,程序运行正常,如果我们采取
三角形片检测,设为testPysics.cpp line230中修改为 box->SetCollisionMesh();//bNormalizationResult有错!,默认的SetCollisionBox正确,程序总是不定时地弹出错误,并终止程序,如下图。
, 同样的问
出现在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);
/* if (IsOk) {
if ( Collider.GetContactStatus() ) // Get collision status => if true, objects overlap
{
。。。。。。。。。。。。代码很长,忽略 }
}*/
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过程中学到的一点东西: , Delta3d物理引擎基于ODE,ODE可以进行刚体动态仿真和碰撞检测,碰撞检测部分有
一个通用的监测函数,判断是什么碰撞类型,比如box vs box,cylinder vs box,mesh vs
mesh等。
, 每个碰撞类型都有一个类,ODE将根据碰撞检测类型分别选择不同的碰撞检测函数,
比如若是两个mesh间的碰撞检测,则调用dCollideTTL(),两个“T”代
“TriMesh”
一目了然。
, Delta3d碰撞检测执行
(我没有函数调用查询及执行流程查询的工具,哪位有提供
下谢谢):
OnMessage->NearCallBack(dtcore/scene.cpp)->dCollide(ext/include/ode/collision)->dCollideTT
L(ode/collision_trimesh-trimesh.cpp)。
, 而对于ODE,本身并没有mesh vs mesh的碰撞检测功能,需用库Opcode,大家可以上
网了解下OPCODE。因此函数dCollideTTL()函数最终将会调用OPCODE库中的函数,
在DELTA3D中看不到OPCODE 的链接库是因为ODE已经将其打包封装起来一起编
译了。没有ODE0.9和OPCODE库的兄弟可以向我索要,如果大家都没有,我将这两
个库上传到邮件。