Box2d源码学习<七>Broad-phase的实现

来源:互联网 发布:中小企业进销存软件 编辑:程序博客网 时间:2024/04/28 13:37

本系列博客是由扭曲45原创,欢迎转载,转载时注明出处,http://blog.csdn.net/cg0206/article/details/8300658

在一个物理步长内,碰撞处理可以被划分成narrow-phase和broad-phase两个阶段。在narrow-phase阶段计算一对形状的接触。假设有N个形状,直接使用蛮力进行计算,我们需要调用N*N/2次narrow-phase算法。

 

b2BroadPhase类通过使用动态树降低了管理数据方面的开销。这极大的降低了调用narrow-phase算法的次数。

 

一般情况下,你不需要直接和broad-phase打交道。Box2D来内部来创建和管理broad-phase。另外,b2BroadPhase是使用Box2D的模拟循环的思路来设计的,所以它可能不适合用于其他用途。

                                                                                           ---摘自oh!coder的博客

Box2d中broad-phase用于计算pairs【相交记录】,执行容量查询和光线投射。主要还是调用上一节我们说的动态树进行数据方面的管理。首先,我们还是看看头文件b2BroadPhase.h中的定义部分。

[cpp] view plain copy
  1. //pair定义  
  2. struct b2Pair  
  3. {  
  4.     int32 proxyIdA;  //代理a  
  5.     int32 proxyIdB;  //代理b  
  6.     int32 next;      //下一个pair  
  7. };  
  8.   
  9. // broad-phase用于计算pairs,执行体积查询和光线投射  
  10. // broad-phase不会持续pairs.相反,它会汇报新的pairs。这取决于客户端是否用掉新的pairs和是否跟踪后续重叠。  
  11. class b2BroadPhase  
  12. {  
  13. public:  
  14.     //空节点代理  
  15.     enum  
  16.     {  
  17.         e_nullProxy = -1  
  18.     };  
  19.   
  20.     b2BroadPhase();  
  21.     ~b2BroadPhase();  
  22.     /************************************************************************** 
  23.     * 功能描述:创建一个代理,并用aabb初始化。pairs不会汇报直到UpdatePairs被调用 
  24.     * 参数说明: allocator :soa分配器对象指针 
  25.                  userData  :用户数据 
  26.     * 返 回 值: (void) 
  27.     ***************************************************************************/  
  28.     int32 CreateProxy(const b2AABB& aabb, void* userData);  
  29.     /************************************************************************** 
  30.     * 功能描述:销毁一个代理,任何pairs的删除都取决于客户端 
  31.     * 参数说明: proxyId :代理id 
  32.     * 返 回 值: (void) 
  33.     ***************************************************************************/  
  34.     void DestroyProxy(int32 proxyId);  
  35.   
  36.     /************************************************************************** 
  37.     * 功能描述:移动一个代理。只要你喜欢可以多次调用MoveProxy, 
  38.                 当你完成后调用UpdatePairs用于完成代理pairs(在你的时间步内) 
  39.     * 参数说明: proxyId      :代理id 
  40.                  aabb         :aabb变量 
  41.                  displacement :移动坐标向量 
  42.     * 返 回 值: (void) 
  43.     ***************************************************************************/  
  44.     void MoveProxy(int32 proxyId, const b2AABB& aabb, const b2Vec2& displacement);  
  45.   
  46.     /************************************************************************** 
  47.     * 功能描述: 在下次调用UpdatePairs时,调用一个触发器触发它的pairs 
  48.     * 参数说明: proxyId      :代理id 
  49.     * 返 回 值: (void) 
  50.     ***************************************************************************/  
  51.     void TouchProxy(int32 proxyId);  
  52.     /************************************************************************** 
  53.     * 功能描述: 获取宽大的aabb 
  54.     * 参数说明: proxyId      :代理id 
  55.     * 返 回 值: (void) 
  56.     ***************************************************************************/  
  57.     const b2AABB& GetFatAABB(int32 proxyId) const;  
  58.     /************************************************************************** 
  59.     * 功能描述: 通过一个代理获取userData,如果id无效,返回NULL 
  60.     * 参数说明: proxyId      :代理id 
  61.     * 返 回 值: 用户数据 
  62.     ***************************************************************************/  
  63.     void* GetUserData(int32 proxyId) const;  
  64.     /************************************************************************** 
  65.     * 功能描述: 测试宽大aabb的重复部分 
  66.     * 参数说明: proxyIdA    :A代理id 
  67.                  proxyIdB    :B代理id 
  68.     * 返 回 值: true :不重叠 
  69.                  false:重  叠 
  70.     ***************************************************************************/  
  71.     bool TestOverlap(int32 proxyIdA, int32 proxyIdB) const;  
  72.     /************************************************************************** 
  73.     * 功能描述: 获取代理数量 
  74.     * 参数说明: (void) 
  75.     * 返 回 值: 代理数量 
  76.     ***************************************************************************/  
  77.     int32 GetProxyCount() const;  
  78.     /************************************************************************** 
  79.     * 功能描述: 更新pairs.这会对pair进行回调。只能添加pairs 
  80.     * 参数说明: callback :回调对象 
  81.     * 返 回 值: (void) 
  82.     ***************************************************************************/  
  83.     template <typename T>  
  84.     void UpdatePairs(T* callback);  
  85.     /************************************************************************** 
  86.     * 功能描述: 在重叠代理中查询一个aabb.每个提供aabb重叠的代理将会被回调类调用 
  87.     * 参数说明: callback :回调对象类 
  88.                  aabb     :aabb变量 
  89.     * 返 回 值: (void) 
  90.     ***************************************************************************/  
  91.     template <typename T>  
  92.     void Query(T* callback, const b2AABB& aabb) const;  
  93.     /************************************************************************** 
  94.     * 功能描述: 光线投射在树上的代理。 
  95.                  这依赖于回调被执行一个精确的光线投射在一个代理包含一个形状 
  96.     * 参数说明: callback : 一个回调对象类,当被调用时,光线将会撒到每个代理中。 
  97.                  input    :光线投射输入数据。这个光线从p1扩展到p1+maxFraction *(p2 - p1) 
  98.     * 返 回 值: (void) 
  99.     ***************************************************************************/  
  100.     template <typename T>  
  101.     void RayCast(T* callback, const b2RayCastInput& input) const;  
  102.     /************************************************************************** 
  103.     * 功能描述: 获取嵌入树的高度 
  104.     * 参数说明: (void) 
  105.     * 返 回 值: (void) 
  106.     ***************************************************************************/  
  107.     int32 GetTreeHeight() const;  
  108.     /************************************************************************** 
  109.     * 功能描述: 获取嵌入树的平衡值 
  110.     * 参数说明: (void) 
  111.     * 返 回 值: (void) 
  112.     ***************************************************************************/  
  113.     int32 GetTreeBalance() const;  
  114.     /************************************************************************** 
  115.     * 功能描述: 获取嵌入树的质量,即是树的总aabbs周长与根节点aabb周长的比 
  116.     * 参数说明: (void) 
  117.     * 返 回 值: 树的质量 
  118.     ***************************************************************************/  
  119.     float32 GetTreeQuality() const;  
  120.   
  121. private:  
  122.     //友元类  
  123.     friend class b2DynamicTree;  
  124.     /************************************************************************** 
  125.     * 功能描述: 根据代理id添加代理到移动缓冲区中 
  126.     * 参数说明: proxyId :代理id 
  127.     * 返 回 值: (void) 
  128.     ***************************************************************************/  
  129.     void BufferMove(int32 proxyId);  
  130.     /************************************************************************** 
  131.     * 功能描述: 将代理移出移动缓存区 
  132.     * 参数说明: proxyId :代理id 
  133.     * 返 回 值: (void) 
  134.     ***************************************************************************/  
  135.     void UnBufferMove(int32 proxyId);  
  136.     /************************************************************************** 
  137.     * 功能描述: 查询回调函数 
  138.     * 参数说明: proxyId :代理id 
  139.     * 返 回 值: true :表示正常回调 
  140.     ***************************************************************************/  
  141.     bool QueryCallback(int32 proxyId);  
  142.     //动态树声明  
  143.     b2DynamicTree m_tree;  
  144.     //代理数量  
  145.     int32 m_proxyCount;  
  146.     //移动的缓冲区  
  147.     int32* m_moveBuffer;  
  148.     //移动缓冲区的总容量  
  149.     int32 m_moveCapacity;  
  150.     //需要移动的代理数量  
  151.     int32 m_moveCount;  
  152.     //pair缓冲区  
  153.     b2Pair* m_pairBuffer;  
  154.     //pair缓冲区中的总容量  
  155.     int32 m_pairCapacity;  
  156.     //pair数量  
  157.     int32 m_pairCount;  
  158.     //查询代理id  
  159.     int32 m_queryProxyId;  
  160. };  

在这类中,可以看到b2BroadPhase将b2DynamicTree定义为友元类,也就是说b2DynamicTree每一个对象均可访问b2BroadPhase的任何成员,不管是否是私有的。同时我们又可以看到在b2BrodPhase类中,我们定义了个动态树对象m_tree,这样我们形成的好像是形成了一个环,但m_tree并不能访问b2DynamicTree中的私有变量。其他部分,不多说了,看注释。再来看内联函数的实现。

[cpp] view plain copy
  1. /************************************************************************** 
  2. * 功能描述: 用于pairs的排序 
  3. * 参数说明: pair1:Pari对象引用 
  4.              pair2: Pari对象引用 
  5. * 返 回 值: true : pair1较小 
  6.              fasle:pair2较小 
  7. ***************************************************************************/  
  8. inline bool b2PairLessThan(const b2Pair& pair1, const b2Pair& pair2)  
  9. {  
  10.     //比对pair的代理idA  
  11.     if (pair1.proxyIdA < pair2.proxyIdA)  
  12.     {  
  13.         return true;  
  14.     }  
  15.     //再比对代理idB  
  16.     if (pair1.proxyIdA == pair2.proxyIdA)  
  17.     {  
  18.         return pair1.proxyIdB < pair2.proxyIdB;  
  19.     }  
  20.   
  21.     return false;  
  22. }  
  23.   
  24.   
  25. //根据代理id获取userData  
  26. inline void* b2BroadPhase::GetUserData(int32 proxyId) const  
  27. {  
  28.     return m_tree.GetUserData(proxyId);  
  29. }  
  30. //测试重叠  
  31. inline bool b2BroadPhase::TestOverlap(int32 proxyIdA, int32 proxyIdB) const  
  32. {  
  33.     const b2AABB& aabbA = m_tree.GetFatAABB(proxyIdA);  
  34.     const b2AABB& aabbB = m_tree.GetFatAABB(proxyIdB);  
  35.     return b2TestOverlap(aabbA, aabbB);  
  36. }  
  37. //获取aabb  
  38. inline const b2AABB& b2BroadPhase::GetFatAABB(int32 proxyId) const  
  39. {  
  40.     return m_tree.GetFatAABB(proxyId);  
  41. }  
  42. //获取代理数量  
  43. inline int32 b2BroadPhase::GetProxyCount() const  
  44. {  
  45.     return m_proxyCount;  
  46. }  
  47. //获取树的高度  
  48. inline int32 b2BroadPhase::GetTreeHeight() const  
  49. {  
  50.     return m_tree.GetHeight();  
  51. }  
  52. //获取树的平衡值  
  53. inline int32 b2BroadPhase::GetTreeBalance() const  
  54. {  
  55.     return m_tree.GetMaxBalance();  
  56. }  
  57. //获取树的质量  
  58. inline float32 b2BroadPhase::GetTreeQuality() const  
  59. {  
  60.     return m_tree.GetAreaRatio();  
  61. }  
  62. //更新pairs  
  63. template <typename T>  
  64. void b2BroadPhase::UpdatePairs(T* callback)  
  65. {  
  66.     //重置pair缓存区  
  67.     m_pairCount = 0;  
  68.     //执行查询树上所有需要移动代理  
  69.     for (int32 i = 0; i < m_moveCount; ++i)  
  70.     {  
  71.         m_queryProxyId = m_moveBuffer[i];  
  72.         if (m_queryProxyId == e_nullProxy)  
  73.         {  
  74.             continue;  
  75.         }  
  76.         // 我们需要查询树的宽大的AABB,以便当我们创建pair失败时,可以再次创建  
  77.         const b2AABB& fatAABB = m_tree.GetFatAABB(m_queryProxyId);  
  78.         // 查询树,创建多个pair并将他们添加到pair缓冲区中  
  79.         m_tree.Query(this, fatAABB);  
  80.     }  
  81.     //重置移动缓冲区  
  82.     m_moveCount = 0;  
  83.     // 排序pair缓冲区  
  84.     std::sort(m_pairBuffer, m_pairBuffer + m_pairCount, b2PairLessThan);  
  85.     // 发送pair到客户端  
  86.     int32 i = 0;  
  87.     while (i < m_pairCount)  
  88.     {  
  89.         //在pair缓冲区中获取当前的pair  
  90.         b2Pair* primaryPair = m_pairBuffer + i;  
  91.         //根据相交记录  
  92.         void* userDataA = m_tree.GetUserData(primaryPair->proxyIdA);  
  93.         void* userDataB = m_tree.GetUserData(primaryPair->proxyIdB);  
  94.   
  95.         callback->AddPair(userDataA, userDataB);  
  96.         ++i;  
  97.   
  98.         //跳过重复的pair  
  99.         while (i < m_pairCount)  
  100.         {  
  101.             b2Pair* pair = m_pairBuffer + i;  
  102.             if (pair->proxyIdA != primaryPair->proxyIdA || pair->proxyIdB != primaryPair->proxyIdB)  
  103.             {  
  104.                 break;  
  105.             }  
  106.             ++i;  
  107.         }  
  108.     }  
  109.   
  110.     // Try to keep the tree balanced.  
  111.     //m_tree.Rebalance(4);  
  112. }  
  113. //区域查询  
  114. template <typename T>  
  115. inline void b2BroadPhase::Query(T* callback, const b2AABB& aabb) const  
  116. {  
  117.     m_tree.Query(callback, aabb);  
  118. }  
  119. //光线投射  
  120. template <typename T>  
  121. inline void b2BroadPhase::RayCast(T* callback, const b2RayCastInput& input) const  
  122. {  
  123.     m_tree.RayCast(callback, input);  
  124. }  

关于这部分,就是对动态树中相关方法的封装,如果还有童鞋有疑问的话,不妨看看我的上一篇文章《Box2d源码学习<>动态树的实现》,接下来来看看b2BroadPhase部分。

[cpp] view plain copy
  1. //构造函数,初始化数据  
  2. b2BroadPhase::b2BroadPhase()  
  3. {  
  4.     m_proxyCount = 0;  
  5.   
  6.     m_pairCapacity = 16;  
  7.     m_pairCount = 0;  
  8.     m_pairBuffer = (b2Pair*)b2Alloc(m_pairCapacity * sizeof(b2Pair));  
  9.   
  10.     m_moveCapacity = 16;  
  11.     m_moveCount = 0;  
  12.     m_moveBuffer = (int32*)b2Alloc(m_moveCapacity * sizeof(int32));  
  13. }  
  14. //析构函数  
  15. b2BroadPhase::~b2BroadPhase()  
  16. {  
  17.     b2Free(m_moveBuffer);  
  18.     b2Free(m_pairBuffer);  
  19. }  
  20. //创建一个代理  
  21. int32 b2BroadPhase::CreateProxy(const b2AABB& aabb, void* userData)  
  22. {  
  23.     //获取代理id  
  24.     int32 proxyId = m_tree.CreateProxy(aabb, userData);  
  25.     //代理数量自增  
  26.     ++m_proxyCount;  
  27.     //添加代理到移动缓冲区中  
  28.     BufferMove(proxyId);  
  29.     return proxyId;  
  30. }  
  31. //销毁一个代理  
  32. void b2BroadPhase::DestroyProxy(int32 proxyId)  
  33. {  
  34.     UnBufferMove(proxyId);  
  35.     --m_proxyCount;  
  36.     m_tree.DestroyProxy(proxyId);  
  37. }  
  38. //移动一个代理  
  39. void b2BroadPhase::MoveProxy(int32 proxyId, const b2AABB& aabb, const b2Vec2& displacement)  
  40. {  
  41.     bool buffer = m_tree.MoveProxy(proxyId, aabb, displacement);  
  42.     if (buffer)  
  43.     {  
  44.         BufferMove(proxyId);  
  45.     }  
  46. }  
  47. //在下次调用UpdatePairs时,调用一个触发器触发它的pairs  
  48. void b2BroadPhase::TouchProxy(int32 proxyId)  
  49. {  
  50.     BufferMove(proxyId);  
  51. }  
  52. //根据代理id添加代理到移动缓冲区中  
  53. void b2BroadPhase::BufferMove(int32 proxyId)  
  54. {  
  55.     //移动缓冲区过小,增容  
  56.     if (m_moveCount == m_moveCapacity)  
  57.     {  
  58.         //获取移动缓冲区  
  59.         int32* oldBuffer = m_moveBuffer;  
  60.         //将容量扩增为原来的2倍  
  61.         m_moveCapacity *= 2;  
  62.         //重新申请移动缓冲区  
  63.         m_moveBuffer = (int32*)b2Alloc(m_moveCapacity * sizeof(int32));  
  64.         //拷贝旧的移动缓冲区内容到新的里面去,并释放旧的移动缓冲区  
  65.         memcpy(m_moveBuffer, oldBuffer, m_moveCount * sizeof(int32));  
  66.         b2Free(oldBuffer);  
  67.     }  
  68.     //添加代理id到移动缓冲区中  
  69.     m_moveBuffer[m_moveCount] = proxyId;  
  70.     //自增  
  71.     ++m_moveCount;  
  72. }  
  73. //移除移动缓存区  
  74. void b2BroadPhase::UnBufferMove(int32 proxyId)  
  75. {  
  76.     //查找相应的代理  
  77.     for (int32 i = 0; i < m_moveCount; ++i)  
  78.     {  
  79.         //找到代理,并置空  
  80.         if (m_moveBuffer[i] == proxyId)  
  81.         {  
  82.             m_moveBuffer[i] = e_nullProxy;  
  83.             return;  
  84.         }  
  85.     }  
  86. }  
  87. //当我们聚集pairs时这个函数将会被b2DynamicTree:Query调用  
  88. bool b2BroadPhase::QueryCallback(int32 proxyId)  
  89. {  
  90.     // 一个代理不需要自己pair更新自己的pair  
  91.     if (proxyId == m_queryProxyId)  
  92.     {  
  93.         return true;  
  94.     }  
  95.     // 如果需要增加pair缓冲区  
  96.     if (m_pairCount == m_pairCapacity)  
  97.     {  
  98.         //获取旧的pair缓冲区,并增加容量  
  99.         b2Pair* oldBuffer = m_pairBuffer;  
  100.         m_pairCapacity *= 2;  
  101.         //重新申请pair缓冲区,并拷贝旧缓冲区中的内容  
  102.         m_pairBuffer = (b2Pair*)b2Alloc(m_pairCapacity * sizeof(b2Pair));  
  103.         memcpy(m_pairBuffer, oldBuffer, m_pairCount * sizeof(b2Pair));  
  104.         //释放旧的pair缓冲区  
  105.         b2Free(oldBuffer);  
  106.     }  
  107.     //设置最新的pair  
  108.     //并自增pair数量  
  109.     m_pairBuffer[m_pairCount].proxyIdA = b2Min(proxyId, m_queryProxyId);  
  110.     m_pairBuffer[m_pairCount].proxyIdB = b2Max(proxyId, m_queryProxyId);  
  111.     ++m_pairCount;  
  112.   
  113.     return true;  
  114. }  

通过源代码我们可以看到,它的实现主要靠移动缓冲区(m_moveBuffer)和pair缓冲区(m_pariBuffer)。构造函数b2BroadPhase()主要是申请这两个缓冲区,析构函数~b2BroadPhase()释放这两个缓冲区,创建一个代理函数CreateProxy()主要添加代理到移动缓冲区,销毁代理函数DestroyProxy主要是销毁一个在移动缓冲区的代理,MoveProxy()、TouchProxy()、BufferMove()均是在移动缓冲区中添加代理,UnBufferMove()是在移动缓冲区中移除代理,QueryCallback()是对pair缓冲区的操作。

突然我们心中有一个疑问,这两个缓冲区各自操作各自的,通过这段代码我们看不到任何的联系,它们到底是如何通信的呢?请先大家思考下。。。

好了,大家知道了吗?有人猜到是通过UpdatePairs函数实现的,这是正确的,但具体的还是通过动态树中的Query函数来实现的,我们不妨回顾一下updatepairs中的代码段。

[cpp] view plain copy
  1. //执行查询树上所有需要移动代理  
  2.     for (int32 i = 0; i < m_moveCount; ++i)  
  3.     {  
  4.         m_queryProxyId = m_moveBuffer[i];  
  5.         if (m_queryProxyId == e_nullProxy)  
  6.         {  
  7.             continue;  
  8.         }  
  9.         // 我们需要查询树的宽大的AABB,以便当我们创建pair失败时,可以再次创建  
  10.         const b2AABB& fatAABB = m_tree.GetFatAABB(m_queryProxyId);  
  11.         // 查询树,创建多个pair并将他们添加到pair缓冲区中  
  12.         m_tree.Query(this, fatAABB);  
  13.     }  

有人也许会说,这段代码我们只看到移动缓冲区(m_moveBuffer),看不出来与pair缓冲区(m_pariBuffer)有任何关系,它们怎么产生联系的呢?注意m_tree.Query(this,fatAABB)这句代码和它传递的参数,this表示当前类的对象,fatAABB表示移动缓冲区中一个代理的AABB,存储了代理的信息。我们再回顾一下query函数:

[cpp] view plain copy
  1.        /************************************************************************** 
  2. * 功能描述:查询一个aabb重叠代理,每个重叠提供AABB的代理都将回调回调类 
  3. * 参数说明:callback :回调对象 
  4.             aabb     :要查询的aabb 
  5. * 返 回 值:aabb对象 
  6.        ***************************************************************************/  
  7. template <typename T>  
  8. void Query(T* callback, const b2AABB& aabb) const;  
再看Query函数中的代码片段

[cpp] view plain copy
  1.     //是否成功  
  2.     bool proceed = callback->QueryCallback(nodeId);  
  3.     if (proceed == false)  
  4.     {  
  5. return;  
  6.     }  
看这句代码bool proceed = callback->QueryCallback(nodeId); 此处的callback就是刚刚传进去的this,也就是说我们调用的QueryCallback也就是b2BroadPhase::QueryCallback(int32 proxyId)函数。到此处相信大家已经明白了。


ps:

以上文章仅是一家之言,若有不妥、错误之处,请大家多多之出。同时也希望能与大家多多交流,共同进步。

0 0
原创粉丝点击