bvTree 碰撞检测

时间:2024-05-22 15:08:54

是一棵二叉树,

包围层次盒BVH是一种场景管理技术,广泛应用于碰撞检测、射线相交测试之类的应用场合中。

bvTree有两种节点(Node)类型:Interior Node 和 Leaf Node。

LeafNode 是最终存放tile的地方

划分策略可以选择比较高端的表面积启发式算法(SurfaceArea Heuristic)

SAH基于的思想:如果某物的表面积越大,那么它被射线击中的可能性也就越大。

 

每次划分,都会得到两个子区域A和B。那么相应也会算出一个cost(A,B)来。比较所有划分方案下所得的cost(A,B),取值最小的方案就是成本最低的划分方案,也作为划分该节点的最终/优方案。

 

bvTree 碰撞检测

我们不用链表来存储树,而是用数组:

offset用来指示第二棵子树的位置

 bvTree 碰撞检测

eg:在bvTree上遍历找到距离给定方框相接触的polygon

void dtNavMeshQuery::queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, const float* qmax,
                                         const dtQueryFilter* filter, dtPolyQuery* query) const
{
    dtAssert(m_nav);
    static const int batchSize = 32;//最多找到多少个poly
    dtPolyRef polyRefs[batchSize];
    dtPoly* polys[batchSize];
    int n = 0;

    if (tile->bvTree)//用数组存的BVH
    {
        const dtBVNode* node = &tile->bvTree[0];
        const dtBVNode* end = &tile->bvTree[tile->header->bvNodeCount];
        const float* tbmin = tile->header->bmin;
        const float* tbmax = tile->header->bmax;
        const float qfac = tile->header->bvQuantFactor; //1.0f / params->cs

        // Calculate quantized box
        unsigned short bmin[3], bmax[3];
        // dtClamp query box to world box.
        float minx = dtClamp(qmin[0], tbmin[0], tbmax[0]) - tbmin[0];
        float miny = dtClamp(qmin[1], tbmin[1], tbmax[1]) - tbmin[1];
        float minz = dtClamp(qmin[2], tbmin[2], tbmax[2]) - tbmin[2];
        float maxx = dtClamp(qmax[0], tbmin[0], tbmax[0]) - tbmin[0];
        float maxy = dtClamp(qmax[1], tbmin[1], tbmax[1]) - tbmin[1];
        float maxz = dtClamp(qmax[2], tbmin[2], tbmax[2]) - tbmin[2];
        // Quantize 可以理解成:bmin和bmax表示各个方向上的第几个cell到第几个cell
        bmin[0] = (unsigned short)(qfac * minx) & 0xfffe;
        bmin[1] = (unsigned short)(qfac * miny) & 0xfffe;
        bmin[2] = (unsigned short)(qfac * minz) & 0xfffe;
        bmax[0] = (unsigned short)(qfac * maxx + 1) | 1;
        bmax[1] = (unsigned short)(qfac * maxy + 1) | 1;
        bmax[2] = (unsigned short)(qfac * maxz + 1) | 1;

        // Traverse tree
        const dtPolyRef base = m_nav->getPolyRefBase(tile);
        while (node < end)
        {
            const bool overlap = dtOverlapQuantBounds(bmin, bmax, node->bmin, node->bmax);//两个包围盒是否overlap
            const bool isLeafNode = node->i >= 0;//i是bvNode的序号,如果是负的说明是escape sequence,那肯定不是叶子,所以也就不会是正的

            if (isLeafNode && overlap)
            {
                dtPolyRef ref = base | (dtPolyRef)node->i;

                if (node->i >= batchSize)
                {
                    return;
                }
                    
                if (filter->passFilter(ref, tile, &tile->polys[node->i]))//测试可通过性
                {
                    polyRefs[n] = ref;
                    polys[n] = &tile->polys[node->i];//如果可通过就记录下来这个叶子

                    if (n == batchSize - 1)
                    {
                        query->process(tile, polys, polyRefs, batchSize);//算出距离点最近的距离和最近的那个ref
                        n = 0;
                    }
                    else
                    {
                        n++;
                    }
                }
            }

            if (overlap || isLeafNode)//重叠了但是不是叶子,当然往下走去判断左子树,是叶子但是不重叠也往下走相当于判断
                                        //根的右子树
                node++;
            else
            {    //既不是叶子也不重叠,说明作为左子树不重叠应该去找跟的右子树,用的就是escape值
                const int escapeIndex = -node->i;
                node += escapeIndex;
            }
        }
    }
    else
    {
        float bmin[3], bmax[3];
        const dtPolyRef base = m_nav->getPolyRefBase(tile);
        for (int i = 0; i < tile->header->polyCount; ++i)
        {
            dtPoly* p = &tile->polys[i];
            // Do not return off-mesh connection polygons.
            if (p->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
                continue;
            // Must pass filter
            const dtPolyRef ref = base | (dtPolyRef)i;
            if (!filter->passFilter(ref, tile, p))
                continue;
            // Calc polygon bounds.
            const float* v = &tile->verts[p->verts[0]*3];
            dtVcopy(bmin, v);
            dtVcopy(bmax, v);
            for (int j = 1; j < p->vertCount; ++j)
            {
                v = &tile->verts[p->verts[j]*3];
                dtVmin(bmin, v);
                dtVmax(bmax, v);
            }
            if (dtOverlapBounds(qmin, qmax, bmin, bmax))
            {
                polyRefs[n] = ref;
                polys[n] = p;

                if (n == batchSize - 1)
                {
                    query->process(tile, polys, polyRefs, batchSize);
                    n = 0;
                }
                else
                {
                    n++;
                }
            }
        }
    }

    // Process the last polygons that didn't make a full batch.
    if (n > 0)
        query->process(tile, polys, polyRefs, n);
}