bullet入门-DBvt树的碰撞检测

Posted by ysd on August 25, 2017

collide*系列的函数,比如collideTV就是Tree和Volume的

collideTV

深度优先遍历Tree,如果某个node和Volume相交,且是叶子节点,则调用回调函数

collideTT,测试两个树,和TV不同的是:

  • TT每次往栈压入的是一对节点,取出也是一对节点,sStkNN,用这个保存一对节点指针
  • 如果取出的两个节点相交:
    • 如果取出的两个节点都是非叶子节点,则将他们的左右孩子对,共4种节点对组合压入栈中
    • 如果取出的两个节点一个是叶子,一个是非叶子,则压入叶子和非叶子左右孩子的节点对
    • 只有取出的两个节点都是叶子节点时,才调回调
  • 特殊的,如果取出的两个节点是一个且是非叶子,则将它自己的左右孩子对,共3中组合压入

collideKDOP

测试树和k-DOP相交。

// count是k-DOP面的个数,normals和offset确定了这些面

void collideKDOP(const btDbvtNode* root, const btVector3* normals, const btScalar* offsets, int count, DBVT_IPOLICY)
{
DBVT_CHECKTYPE
if(root)
{
// inside的每一位都是1,表示aabb都在k-DOP的count个面的同侧,或者说aabb在k-DOP里面

const int inside=(1<<count)-1;
btAlignedObjectArray<sStkNP> stack;
int signs[sizeof(unsigned)*8];
btAssert(count<int (sizeof(signs)/sizeof(signs[0])));
for(int i=0;i<count;++i)
{
signs[i]= ((normals[i].x()>=0)?1:0)+
((normals[i].y()>=0)?2:0)+
((normals[i].z()>=0)?4:0);
}
stack.reserve(SIMPLE_STACKSIZE);
stack.push_back(sStkNP(root,0));
do {
sStkNP se=stack[stack.size()-1];
bool out=false;
stack.pop_back();
for(int i=0,j=1;(!out)&&(i<count);++i,j<<=1)
{
// mask的第j位是1表明aabb在第j个面的同侧

if(0==(se.mask&j))
{
// 测试aabb和面的关系。其实这个函数我没看懂。。。

const int side=se.node->volume.Classify(normals[i], offsets[i], signs[i]);
switch(side)
{
// 如果aabb在某一个面的异侧,说明aabb完全在k-DOP外

case -1: out=true;break;
// 如果aabb在某一个面的同侧,设置标志位

case +1: se.mask|=j;break;
}
}
}
if(!out)
{
// 如果:

// 1. 当前的aabb已经是叶子,则调回调

// 2. 当前的aabb完全在k-DOP里面,则遍历他的全部叶子节点并调回调

// 否则继续深度优先遍历树

if((se.mask!=inside)&&(se.node->isinternal()))
{
// aabb和k-DOP每个面是否同侧的关系被他的孩子继承

stack.push_back(sStkNP(se.node->childs[0], se.mask));
stack.push_back(sStkNP(se.node->childs[1], se.mask));
}
else
{
if(policy.AllLeaves(se.node)) enumLeaves(se.node, policy);
}
}
} while(stack.size());
}
}