cocos2d-x节点(b2DynamicTree.h)API

本文详细介绍了cocos2d-x中用于提升碰撞检测速度的b2DynamicTree.h API,强调了其在处理轴对齐包围盒(AABBs)操作中的作用。动态树是一个平衡的AABB树,每个节点有2个子节点,主要用于存储用户数据的AABB。建议配合作者的Cocos2d-X权威指南笔记进行深入学习。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文来自http://blog.csdn.net/runaying ,引用必须注明出处!

cocos2d-x节点(b2DynamicTree.h)API

温馨提醒:为了大家能更好学习,强烈推荐大家看看本人的这篇博客 Cocos2d-X权威指南笔记

//用来提高系统的碰撞检测的速度。

//它主要是通过用户数据来操作轴对齐包围盒(axis-alignedbounding boxes,AABBs)来完成树的各种操作。同时动态树继承自AABB树,树上的每一个节点都有两个孩子。叶节点是一个单独的用户AABB。即便是惰性输入,整个数也可以使用旋转保持平衡。

///cocos2d-x-3.0alpha0/external/Box2D/Collision
//用来提高系统的碰撞检测的速度。
//它主要是通过用户数据来操作轴对齐包围盒(axis-alignedbounding boxes,AABBs)来完成树的各种操作。同时动态树继承自AABB树,树上的每一个节点都有两个孩子。叶节点是一个单独的用户AABB。即便是惰性输入,整个数也可以使用旋转保持平衡。

#ifndef B2_DYNAMIC_TREE_H
#define B2_DYNAMIC_TREE_H

#include <Box2D/Collision/b2Collision.h>
#include <Box2D/Common/b2GrowableStack.h>
//定义空节点
#define b2_nullNode (-1)

///一个动态树的子节点,不于外包直接交换
struct b2TreeNode
{
    //是否是叶子节点
    bool IsLeaf() const
    {
        return child1 == b2_nullNode;
    }
    
    /// Enlarged AABB
    //增大aabb变量
    b2AABB aabb;
    //用户数据
    void* userData;
    //父节点指针(索引)或孩子节点指针(索引)
    //因为此动态树是申请一块大的连续的空间,即含有n个元素的动态数组罢了
    //故用动态数组的索引值来模拟指针。为了统一,我们一下均使用指针
    union
    {
        int32 parent;
        int32 next;
    };
    //左右孩子指针
    int32 child1;
    int32 child2;
    //高度 叶子高度为0,空闲节点高度为-1
    // leaf = 0, free node = -1
    int32 height;
};

//动态AABB树broad-phase,灵感来自于Nathanael Presson's btDbvt.
//一个动态树排列一个二叉树的数据来加速查询像体积查询和光线投射。叶子是若干个代理和轴对齐包围盒
//在树上我们用b2_fatAABBFactor扩展了aabb代理,这样aabb代理将比客户端对象大。这样允许客户端少量移动
//而不需更新一个树
//节点是汇集和浮动的,所以我们使用节点索引而不是指针
class b2DynamicTree
{
public:
    ///构造一个树,并初始化节点内存池
    b2DynamicTree();
    
    /// 销毁一个树,并释放节点内存池
    ~b2DynamicTree();
    
    ///在树上创建一个叶子节点代理
    // * 参数说明:aabb    :aabb变量
    //             user    : 数据
    // * 返 回 值:节点的索引来替代指针,来增长我们的节点池 .
    int32 CreateProxy(const b2AABB& aabb, void* userData);
    // 销毁一个代理,如果id无效则断言
    //     * 参数说明:poxyid :代理id
    //     * 返 回 值:节点的索引来替代指针,来增长我们的节点池
    void DestroyProxy(int32 proxyId);
    
    // 移动一个代理并扫描AABB,如果代理移除了使它充实的AABB盒子
    //             代理将会从树上移除并重新插入。否则,函数将立即返回
    // * 参数说明:proxyId     :叶子代理id
    //             aabb        : aabb变量
    //             displacement:移动坐标向量
    // * 返 回 值: true :移动成功
    //              false:移动失败
    bool MoveProxy(int32 proxyId, const b2AABB& aabb1, const b2Vec2& displacement);
    
    //  * 功能描述:获取一个代理的userData
    // * 参数说明:proxyId:叶子代理id
    // * 返 回 值:id有效,则返回代理userData
    //             id无效,则返0
    void* GetUserData(int32 proxyId) const;
    
    // * 功能描述:获取从代理中宽大的AABB
    // * 参数说明:proxyId:叶子代理id
    // * 返 回 值:aabb对象
    const b2AABB& GetFatAABB(int32 proxyId) const;
    
    //  * 参数说明:callback :回调对象
    //             aabb     :要查询的aabb
    // * 返 回 值:aabb对象
    template <typename T>
    void Query(T* callback, const b2AABB& aabb) const;
    
    //  * 功能描述:光线投射到树上的每个代理上。
    //             这依赖于回调去执行一个精确的光线投射到一个包含形状的代理上
    //             这个回调也执行任何碰撞过滤。
    //             性能几乎等于k * log(n),其中k是碰撞的次数,n是树上代理的数量
    // * 参数说明:callback :回调对象类,将会被每一个被光线照到的代理调用
    //             input    :光线投射输入数据,光线从 p1 + maxFraction * (p2 - p1)
    template <typename T>
    void RayCast(T* callback, const b2RayCastInput& input) const;
    
    /// 验证这棵树,用于测试
    void Validate() const;
    
    // 在O(N)时间上计算二叉树的高度,不应该经常调用
    int32 GetHeight() const;
    
    ///在树上获得节点之间最大的平衡。平衡是两个孩子节点最大的高度差
    int32 GetMaxBalance() const;
    
    ///获得节点总数面积之和根面积的比
    float32 GetAreaRatio() const;
    
    ///构建一个最优的树,非常昂贵,用于测试
    // * 参数说明:(void)
    // * 返 回 值:节点总数面积之和根面积的比
    void RebuildBottomUp();
    
private:
    
    int32 AllocateNode();                                       //从内存池中申请一个节点.如果必要增大内存池
    void FreeNode(int32 node);                          //释放节点
    
    void InsertLeaf(int32 node);                        //插入叶子节点
    void RemoveLeaf(int32 node);                        //移除叶子
    
    int32 Balance(int32 index);                 //如果子树a不平衡,则执行一个向左或向右旋转   * 参数说明:iA :子树根节点指针    * 返 回 值: 新的子树根指针
    
    
    int32 ComputeHeight() const;                //计算树的高度
    int32 ComputeHeight(int32 nodeId) const;       // 计算子树的高度      * 参数说明:nodeid:子树的头指针
    
    
    void ValidateStructure(int32 index) const;      //验证子树的结构   * 参数说明:index:子树的头指针
    void ValidateMetrics(int32 index) const;        //验证子树的度量     * 参数说明:index:子树的头指针
    
    int32 m_root;                   //树的根指针(也是索引)
    
    b2TreeNode* m_nodes;             //树的真正的头指针,也是一块连续的内存池的首地址
    int32 m_nodeCount;                //树节点的个数
    int32 m_nodeCapacity;            //内存池中节点的总个数
    
    int32 m_freeList;            //空闲链表指针
    
    /// //  用于增量遍历树的调整
    uint32 m_path;
    
    int32 m_insertionCount;      //记录插入节点总数量
};
//根据代理id获取userData
inline void* b2DynamicTree::GetUserData(int32 proxyId) const
{
    //验证代理id的有效性
    b2Assert(0 <= proxyId && proxyId < m_nodeCapacity);
    return m_nodes[proxyId].userData;
}
//根据代理id获得宽大的AABB
inline const b2AABB& b2DynamicTree::GetFatAABB(int32 proxyId) const
{
    //验证代理id的有效性
    b2Assert(0 <= proxyId && proxyId < m_nodeCapacity);
    return m_nodes[proxyId].aabb;
}
//查询一个aabb重叠代理,每个重叠提供AABB的代理都将回调回调类
template <typename T>
inline void b2DynamicTree::Query(T* callback, const b2AABB& aabb) const
{
    //申请临时栈,根节点进栈
    b2GrowableStack<int32, 256> stack;
    stack.Push(m_root);
    //判断栈的个数
    while (stack.GetCount() > 0)
    {
        //获取节点id
        int32 nodeId = stack.Pop();
        if (nodeId == b2_nullNode)
        {
            //节点内存池中的空闲节点
            continue;
        }
        //获取节点
        const b2TreeNode* node = m_nodes + nodeId;
        //测试重叠
        if (b2TestOverlap(node->aabb, aabb))
        {
            //是否是叶子节点
            if (node->IsLeaf())
            {
                //是否成功
                bool proceed = callback->QueryCallback(nodeId);
                if (proceed == false)
                {
                    return;
                }
            }
            else
            {
                //左右孩子节点进栈
                stack.Push(node->child1);
                stack.Push(node->child2);
            }
        }
    }
}
//光线投射
template <typename T>
inline void b2DynamicTree::RayCast(T* callback, const b2RayCastInput& input) const
{
    b2Vec2 p1 = input.p1;
    b2Vec2 p2 = input.p2;
    b2Vec2 r = p2 - p1;
    b2Assert(r.LengthSquared() > 0.0f);
    r.Normalize();
    
    //v垂直于向量r
    b2Vec2 v = b2Cross(1.0f, r);
    b2Vec2 abs_v = b2Abs(v);
    //分离轴 下面的书籍 p80
    // 《Collision Detection in Interactive 3D Environments》 by Gino van den Bergen
    // 【从 http://download.csdn.net/detail/cg0206/4875309 下载】
    //  计算公式:|dot(v, p1 - c)| > dot(|v|, h)
    
    float32 maxFraction = input.maxFraction;
    
    // 构建一个绑定的盒子用于该线段
    b2AABB segmentAABB;
    {
        b2Vec2 t = p1 + maxFraction * (p2 - p1);
        segmentAABB.lowerBound = b2Min(p1, t);
        segmentAABB.upperBound = b2Max(p1, t);
    }
    //创建一个临时栈,并将根节点进栈
    b2GrowableStack<int32, 256> stack;
    stack.Push(m_root);
    //栈不为空
    while (stack.GetCount() > 0)
    {
        //出栈
        int32 nodeId = stack.Pop();
        if (nodeId == b2_nullNode)
        {
            //节点内存池中的空闲节点
            continue;
        }
        //根据节点索引获取节点
        const b2TreeNode* node = m_nodes + nodeId;
        //判断AABB
        if (b2TestOverlap(node->aabb, segmentAABB) == false)
        {
            continue;
        }
        
        // Separating axis for segment (Gino, p80).             // //分离轴  p80
        // |dot(v, p1 - c)| > dot(|v|, h)
        b2Vec2 c = node->aabb.GetCenter();
        b2Vec2 h = node->aabb.GetExtents();
        float32 separation = b2Abs(b2Dot(v, p1 - c)) - b2Dot(abs_v, h);
        if (separation > 0.0f)
        {
            continue;
        }
        //是否是叶子节点
        if (node->IsLeaf())
        {
            b2RayCastInput subInput;
            subInput.p1 = input.p1;
            subInput.p2 = input.p2;
            subInput.maxFraction = maxFraction;
            
            float32 value = callback->RayCastCallback(subInput, nodeId);
            
            if (value == 0.0f)
            {
                //客户端终止了光线投射
                return;
            }
            
            if (value > 0.0f)
            {
                // 更新线段的盒子边界
                maxFraction = value;
                b2Vec2 t = p1 + maxFraction * (p2 - p1);
                segmentAABB.lowerBound = b2Min(p1, t);
                segmentAABB.upperBound = b2Max(p1, t);
            }
        }
        else
        {
            //将两个孩子进栈  
            stack.Push(node->child1);
            stack.Push(node->child2);
        }
    }
}

#endif


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值