版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/birdy_/article/details/80589097
关于BVH的介绍可以参考
https://blog.csdn.net/StevenZhai0504/article/details/28420195
在这里参考代码
https://github.com/sglab/OpenCCD.git
这里实现我实现的是一个模仿这个的碰撞检测加速的BVH。因为布料的性质,往往碰撞的情况会出现在相邻的位置(这里可以想象自己身上的衣服?),因此在划分的时候可以简单的采用距离的划分,然后我这里建立的是一块方形的布所以做的是一个‘田’的划分。
而在原文中,进行划分的依据是AABB包围盒哪个轴比较长。对于长轴进行划分(好像是,没有看的非常仔细)
类的定义
class BVH_Node {
public:
bool isleaf; //判断是不是叶节点
Vec3d minBox, maxBox; //确定包围盒
int node[3]; //如果是叶节点,记录对应的三个点的index
BVH_Node *_leftChild; //对于非叶节点记录左右子节点
BVH_Node *_rightChild;
public:
//非叶节点的初始化
BVH_Node(int startX, int startY, int endX, int endY);
//叶节点的初始化
BVH_Node(int index1, int index2, int index3);
~BVH_Node();
//重新计算包围盒
void refit();
/* CCD */
//非叶节点的碰撞检测
void collide(BVH_Node *target);
//叶节点的碰撞检测
bool leafCollide(BVH_Node *target);
//内部的碰撞检测
void selfCollide();
bool isLeaf() { return isleaf; };
//判断包围盒是否相交
bool overlaps(BVH_Node *box);
};
BVH建立
BVH_Node::BVH_Node(int startX, int startY, int endX, int endY) :isleaf(false)
{
node[0] = -1;
node[1] = -1;
node[2] = -1;
if (endX - startX == 1 && endY - startY == 1)
{
//将1*1的正方形块划分成两个三角形
_leftChild = new BVH_Node(XYtoIndex(startX, startY), XYtoIndex(startX + 1, startY), XYtoIndex(startX, startY + 1));
_rightChild = new BVH_Node(XYtoIndex(startX + 1, startY), XYtoIndex(startX, startY + 1),
XYtoIndex(startX + 1, startY + 1));
return;
}
// 对于X、Y按长轴剖分
if (endX - startX > endY - startY)
{
int middleX = (startX + endX) / 2;
_leftChild = new BVH_Node(startX, startY, middleX, endY);
_rightChild = new BVH_Node(middleX, startY, endX, endY);
return;
}
else
{
int middleY = (startY + endY) / 2;
_leftChild = new BVH_Node(startX, startY, endX, middleY);
_rightChild = new BVH_Node(startX, middleY, endX, endY);
return;
}
assert(!"should not get there");
return;
}
BVH_Node::BVH_Node(int index1, int index2, int index3) :isleaf(true)
{
_leftChild = NULL;
_rightChild = NULL;
node[0] = index1;
node[1] = index2;
node[2] = index3;
}
BVH_Node::~BVH_Node()
{
if (!isleaf) {
delete _leftChild;
delete _rightChild;
}
}
包围盒重新计算
每次物体运动后,需要重新计算包围盒。
void BVH_Node::refit()
{
if (isLeaf()) {
minmax(glmToVec3d(node[0]), glmToVec3d(node[1]), glmToVec3d(node[2]),
newVec3d(node[0]), newVec3d(node[1]), newVec3d(node[2]),
minBox, maxBox);
return;
}
_leftChild->refit();
_rightChild->refit();
minmax(_leftChild->minBox, _leftChild->maxBox, _rightChild->minBox, _rightChild->maxBox, minBox, maxBox);
};
碰撞检测
这里需要重新计算碰撞
/* CCD */
void BVH_Node::selfCollide()
{
if (isLeaf())
return;
_leftChild->collide(_rightChild);
_leftChild->selfCollide();
_rightChild->selfCollide();
};
void BVH_Node::collide(BVH_Node *target)
{
if (overlaps(target)) {
if (isLeaf()) {
leafCollide(target);
return;
}
else {
_leftChild->collide(target);
_rightChild->collide(target);
}
}
};
bool BVH_Node::leafCollide(BVH_Node *target)
{
bool ret = false;
if (target->isLeaf()) {
//这里省略对于两个叶结点碰撞检测的代码
//如果是三角形需要3+3次点的碰撞和3*3次边和边的碰撞
}
else {
if (overlaps(target->_leftChild))
ret |= leafCollide(target->_leftChild);
if (overlaps(target->_rightChild))
ret |= leafCollide(target->_rightChild);
}
return ret;
};
bool BVH_Node::overlaps(BVH_Node *box)
{
if (minBox[0] - box->maxBox[0] >= ERROR_BOUND) return false; // x
if (minBox[1] - box->maxBox[1] >= ERROR_BOUND) return false; // y
if (minBox[2] - box->maxBox[2] >= ERROR_BOUND) return false; // z
if (ERROR_BOUND <= box->minBox[0] - maxBox[0]) return false;
if (ERROR_BOUND <= box->minBox[1] - maxBox[1]) return false;
if (ERROR_BOUND <= box->minBox[2] - maxBox[2]) return false;
return true;
}