本系列博客是由扭曲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中的定义部分。
- //pair定义
- struct b2Pair
- {
- int32 proxyIdA; //代理a
- int32 proxyIdB; //代理b
- int32 next; //下一个pair
- };
- // broad-phase用于计算pairs,执行体积查询和光线投射
- // broad-phase不会持续pairs.相反,它会汇报新的pairs。这取决于客户端是否用掉新的pairs和是否跟踪后续重叠。
- class b2BroadPhase
- {
- public:
- //空节点代理
- enum
- {
- e_nullProxy = -1
- };
- b2BroadPhase();
- ~b2BroadPhase();
- /**************************************************************************
- * 功能描述:创建一个代理,并用aabb初始化。pairs不会汇报直到UpdatePairs被调用
- * 参数说明: allocator :soa分配器对象指针
- userData :用户数据
- * 返 回 值: (void)
- ***************************************************************************/
- int32 CreateProxy(const b2AABB& aabb, void* userData);
- /**************************************************************************
- * 功能描述:销毁一个代理,任何pairs的删除都取决于客户端
- * 参数说明: proxyId :代理id
- * 返 回 值: (void)
- ***************************************************************************/
- void DestroyProxy(int32 proxyId);
- /**************************************************************************
- * 功能描述:移动一个代理。只要你喜欢可以多次调用MoveProxy,
- 当你完成后调用UpdatePairs用于完成代理pairs(在你的时间步内)
- * 参数说明: proxyId :代理id
- aabb :aabb变量
- displacement :移动坐标向量
- * 返 回 值: (void)
- ***************************************************************************/
- void MoveProxy(int32 proxyId, const b2AABB& aabb, const b2Vec2& displacement);
- /**************************************************************************
- * 功能描述: 在下次调用UpdatePairs时,调用一个触发器触发它的pairs
- * 参数说明: proxyId :代理id
- * 返 回 值: (void)
- ***************************************************************************/
- void TouchProxy(int32 proxyId);
- /**************************************************************************
- * 功能描述: 获取宽大的aabb
- * 参数说明: proxyId :代理id
- * 返 回 值: (void)
- ***************************************************************************/
- const b2AABB& GetFatAABB(int32 proxyId) const;
- /**************************************************************************
- * 功能描述: 通过一个代理获取userData,如果id无效,返回NULL
- * 参数说明: proxyId :代理id
- * 返 回 值: 用户数据
- ***************************************************************************/
- void* GetUserData(int32 proxyId) const;
- /**************************************************************************
- * 功能描述: 测试宽大aabb的重复部分
- * 参数说明: proxyIdA :A代理id
- proxyIdB :B代理id
- * 返 回 值: true :不重叠
- false:重 叠
- ***************************************************************************/
- bool TestOverlap(int32 proxyIdA, int32 proxyIdB) const;
- /**************************************************************************
- * 功能描述: 获取代理数量
- * 参数说明: (void)
- * 返 回 值: 代理数量
- ***************************************************************************/
- int32 GetProxyCount() const;
- /**************************************************************************
- * 功能描述: 更新pairs.这会对pair进行回调。只能添加pairs
- * 参数说明: callback :回调对象
- * 返 回 值: (void)
- ***************************************************************************/
- template <typename T>
- void UpdatePairs(T* callback);
- /**************************************************************************
- * 功能描述: 在重叠代理中查询一个aabb.每个提供aabb重叠的代理将会被回调类调用
- * 参数说明: callback :回调对象类
- aabb :aabb变量
- * 返 回 值: (void)
- ***************************************************************************/
- template <typename T>
- void Query(T* callback, const b2AABB& aabb) const;
- /**************************************************************************
- * 功能描述: 光线投射在树上的代理。
- 这依赖于回调被执行一个精确的光线投射在一个代理包含一个形状
- * 参数说明: callback : 一个回调对象类,当被调用时,光线将会撒到每个代理中。
- input :光线投射输入数据。这个光线从p1扩展到p1+maxFraction *(p2 - p1)
- * 返 回 值: (void)
- ***************************************************************************/
- template <typename T>
- void RayCast(T* callback, const b2RayCastInput& input) const;
- /**************************************************************************
- * 功能描述: 获取嵌入树的高度
- * 参数说明: (void)
- * 返 回 值: (void)
- ***************************************************************************/
- int32 GetTreeHeight() const;
- /**************************************************************************
- * 功能描述: 获取嵌入树的平衡值
- * 参数说明: (void)
- * 返 回 值: (void)
- ***************************************************************************/
- int32 GetTreeBalance() const;
- /**************************************************************************
- * 功能描述: 获取嵌入树的质量,即是树的总aabbs周长与根节点aabb周长的比
- * 参数说明: (void)
- * 返 回 值: 树的质量
- ***************************************************************************/
- float32 GetTreeQuality() const;
- private:
- //友元类
- friend class b2DynamicTree;
- /**************************************************************************
- * 功能描述: 根据代理id添加代理到移动缓冲区中
- * 参数说明: proxyId :代理id
- * 返 回 值: (void)
- ***************************************************************************/
- void BufferMove(int32 proxyId);
- /**************************************************************************
- * 功能描述: 将代理移出移动缓存区
- * 参数说明: proxyId :代理id
- * 返 回 值: (void)
- ***************************************************************************/
- void UnBufferMove(int32 proxyId);
- /**************************************************************************
- * 功能描述: 查询回调函数
- * 参数说明: proxyId :代理id
- * 返 回 值: true :表示正常回调
- ***************************************************************************/
- bool QueryCallback(int32 proxyId);
- //动态树声明
- b2DynamicTree m_tree;
- //代理数量
- int32 m_proxyCount;
- //移动的缓冲区
- int32* m_moveBuffer;
- //移动缓冲区的总容量
- int32 m_moveCapacity;
- //需要移动的代理数量
- int32 m_moveCount;
- //pair缓冲区
- b2Pair* m_pairBuffer;
- //pair缓冲区中的总容量
- int32 m_pairCapacity;
- //pair数量
- int32 m_pairCount;
- //查询代理id
- int32 m_queryProxyId;
- };
在这类中,可以看到b2BroadPhase将b2DynamicTree定义为友元类,也就是说b2DynamicTree每一个对象均可访问b2BroadPhase的任何成员,不管是否是私有的。同时我们又可以看到在b2BrodPhase类中,我们定义了个动态树对象m_tree,这样我们形成的好像是形成了一个环,但m_tree并不能访问b2DynamicTree中的私有变量。其他部分,不多说了,看注释。再来看内联函数的实现。
- /**************************************************************************
- * 功能描述: 用于pairs的排序
- * 参数说明: pair1:Pari对象引用
- pair2: Pari对象引用
- * 返 回 值: true : pair1较小
- fasle:pair2较小
- ***************************************************************************/
- inline bool b2PairLessThan(const b2Pair& pair1, const b2Pair& pair2)
- {
- //比对pair的代理idA
- if (pair1.proxyIdA < pair2.proxyIdA)
- {
- return true;
- }
- //再比对代理idB
- if (pair1.proxyIdA == pair2.proxyIdA)
- {
- return pair1.proxyIdB < pair2.proxyIdB;
- }
- return false;
- }
- //根据代理id获取userData
- inline void* b2BroadPhase::GetUserData(int32 proxyId) const
- {
- return m_tree.GetUserData(proxyId);
- }
- //测试重叠
- inline bool b2BroadPhase::TestOverlap(int32 proxyIdA, int32 proxyIdB) const
- {
- const b2AABB& aabbA = m_tree.GetFatAABB(proxyIdA);
- const b2AABB& aabbB = m_tree.GetFatAABB(proxyIdB);
- return b2TestOverlap(aabbA, aabbB);
- }
- //获取aabb
- inline const b2AABB& b2BroadPhase::GetFatAABB(int32 proxyId) const
- {
- return m_tree.GetFatAABB(proxyId);
- }
- //获取代理数量
- inline int32 b2BroadPhase::GetProxyCount() const
- {
- return m_proxyCount;
- }
- //获取树的高度
- inline int32 b2BroadPhase::GetTreeHeight() const
- {
- return m_tree.GetHeight();
- }
- //获取树的平衡值
- inline int32 b2BroadPhase::GetTreeBalance() const
- {
- return m_tree.GetMaxBalance();
- }
- //获取树的质量
- inline float32 b2BroadPhase::GetTreeQuality() const
- {
- return m_tree.GetAreaRatio();
- }
- //更新pairs
- template <typename T>
- void b2BroadPhase::UpdatePairs(T* callback)
- {
- //重置pair缓存区
- m_pairCount = 0;
- //执行查询树上所有需要移动代理
- for (int32 i = 0; i < m_moveCount; ++i)
- {
- m_queryProxyId = m_moveBuffer[i];
- if (m_queryProxyId == e_nullProxy)
- {
- continue;
- }
- // 我们需要查询树的宽大的AABB,以便当我们创建pair失败时,可以再次创建
- const b2AABB& fatAABB = m_tree.GetFatAABB(m_queryProxyId);
- // 查询树,创建多个pair并将他们添加到pair缓冲区中
- m_tree.Query(this, fatAABB);
- }
- //重置移动缓冲区
- m_moveCount = 0;
- // 排序pair缓冲区
- std::sort(m_pairBuffer, m_pairBuffer + m_pairCount, b2PairLessThan);
- // 发送pair到客户端
- int32 i = 0;
- while (i < m_pairCount)
- {
- //在pair缓冲区中获取当前的pair
- b2Pair* primaryPair = m_pairBuffer + i;
- //根据相交记录
- void* userDataA = m_tree.GetUserData(primaryPair->proxyIdA);
- void* userDataB = m_tree.GetUserData(primaryPair->proxyIdB);
- callback->AddPair(userDataA, userDataB);
- ++i;
- //跳过重复的pair
- while (i < m_pairCount)
- {
- b2Pair* pair = m_pairBuffer + i;
- if (pair->proxyIdA != primaryPair->proxyIdA || pair->proxyIdB != primaryPair->proxyIdB)
- {
- break;
- }
- ++i;
- }
- }
- // Try to keep the tree balanced.
- //m_tree.Rebalance(4);
- }
- //区域查询
- template <typename T>
- inline void b2BroadPhase::Query(T* callback, const b2AABB& aabb) const
- {
- m_tree.Query(callback, aabb);
- }
- //光线投射
- template <typename T>
- inline void b2BroadPhase::RayCast(T* callback, const b2RayCastInput& input) const
- {
- m_tree.RayCast(callback, input);
- }
关于这部分,就是对动态树中相关方法的封装,如果还有童鞋有疑问的话,不妨看看我的上一篇文章《Box2d源码学习<六>动态树的实现》,接下来来看看b2BroadPhase部分。
- //构造函数,初始化数据
- b2BroadPhase::b2BroadPhase()
- {
- m_proxyCount = 0;
- m_pairCapacity = 16;
- m_pairCount = 0;
- m_pairBuffer = (b2Pair*)b2Alloc(m_pairCapacity * sizeof(b2Pair));
- m_moveCapacity = 16;
- m_moveCount = 0;
- m_moveBuffer = (int32*)b2Alloc(m_moveCapacity * sizeof(int32));
- }
- //析构函数
- b2BroadPhase::~b2BroadPhase()
- {
- b2Free(m_moveBuffer);
- b2Free(m_pairBuffer);
- }
- //创建一个代理
- int32 b2BroadPhase::CreateProxy(const b2AABB& aabb, void* userData)
- {
- //获取代理id
- int32 proxyId = m_tree.CreateProxy(aabb, userData);
- //代理数量自增
- ++m_proxyCount;
- //添加代理到移动缓冲区中
- BufferMove(proxyId);
- return proxyId;
- }
- //销毁一个代理
- void b2BroadPhase::DestroyProxy(int32 proxyId)
- {
- UnBufferMove(proxyId);
- --m_proxyCount;
- m_tree.DestroyProxy(proxyId);
- }
- //移动一个代理
- void b2BroadPhase::MoveProxy(int32 proxyId, const b2AABB& aabb, const b2Vec2& displacement)
- {
- bool buffer = m_tree.MoveProxy(proxyId, aabb, displacement);
- if (buffer)
- {
- BufferMove(proxyId);
- }
- }
- //在下次调用UpdatePairs时,调用一个触发器触发它的pairs
- void b2BroadPhase::TouchProxy(int32 proxyId)
- {
- BufferMove(proxyId);
- }
- //根据代理id添加代理到移动缓冲区中
- void b2BroadPhase::BufferMove(int32 proxyId)
- {
- //移动缓冲区过小,增容
- if (m_moveCount == m_moveCapacity)
- {
- //获取移动缓冲区
- int32* oldBuffer = m_moveBuffer;
- //将容量扩增为原来的2倍
- m_moveCapacity *= 2;
- //重新申请移动缓冲区
- m_moveBuffer = (int32*)b2Alloc(m_moveCapacity * sizeof(int32));
- //拷贝旧的移动缓冲区内容到新的里面去,并释放旧的移动缓冲区
- memcpy(m_moveBuffer, oldBuffer, m_moveCount * sizeof(int32));
- b2Free(oldBuffer);
- }
- //添加代理id到移动缓冲区中
- m_moveBuffer[m_moveCount] = proxyId;
- //自增
- ++m_moveCount;
- }
- //移除移动缓存区
- void b2BroadPhase::UnBufferMove(int32 proxyId)
- {
- //查找相应的代理
- for (int32 i = 0; i < m_moveCount; ++i)
- {
- //找到代理,并置空
- if (m_moveBuffer[i] == proxyId)
- {
- m_moveBuffer[i] = e_nullProxy;
- return;
- }
- }
- }
- //当我们聚集pairs时这个函数将会被b2DynamicTree:Query调用
- bool b2BroadPhase::QueryCallback(int32 proxyId)
- {
- // 一个代理不需要自己pair更新自己的pair
- if (proxyId == m_queryProxyId)
- {
- return true;
- }
- // 如果需要增加pair缓冲区
- if (m_pairCount == m_pairCapacity)
- {
- //获取旧的pair缓冲区,并增加容量
- b2Pair* oldBuffer = m_pairBuffer;
- m_pairCapacity *= 2;
- //重新申请pair缓冲区,并拷贝旧缓冲区中的内容
- m_pairBuffer = (b2Pair*)b2Alloc(m_pairCapacity * sizeof(b2Pair));
- memcpy(m_pairBuffer, oldBuffer, m_pairCount * sizeof(b2Pair));
- //释放旧的pair缓冲区
- b2Free(oldBuffer);
- }
- //设置最新的pair
- //并自增pair数量
- m_pairBuffer[m_pairCount].proxyIdA = b2Min(proxyId, m_queryProxyId);
- m_pairBuffer[m_pairCount].proxyIdB = b2Max(proxyId, m_queryProxyId);
- ++m_pairCount;
- return true;
- }
通过源代码我们可以看到,它的实现主要靠移动缓冲区(m_moveBuffer)和pair缓冲区(m_pariBuffer)。构造函数b2BroadPhase()主要是申请这两个缓冲区,析构函数~b2BroadPhase()释放这两个缓冲区,创建一个代理函数CreateProxy()主要添加代理到移动缓冲区,销毁代理函数DestroyProxy主要是销毁一个在移动缓冲区的代理,MoveProxy()、TouchProxy()、BufferMove()均是在移动缓冲区中添加代理,UnBufferMove()是在移动缓冲区中移除代理,QueryCallback()是对pair缓冲区的操作。
突然我们心中有一个疑问,这两个缓冲区各自操作各自的,通过这段代码我们看不到任何的联系,它们到底是如何通信的呢?请先大家思考下。。。
好了,大家知道了吗?有人猜到是通过UpdatePairs函数实现的,这是正确的,但具体的还是通过动态树中的Query函数来实现的,我们不妨回顾一下updatepairs中的代码段。
- //执行查询树上所有需要移动代理
- for (int32 i = 0; i < m_moveCount; ++i)
- {
- m_queryProxyId = m_moveBuffer[i];
- if (m_queryProxyId == e_nullProxy)
- {
- continue;
- }
- // 我们需要查询树的宽大的AABB,以便当我们创建pair失败时,可以再次创建
- const b2AABB& fatAABB = m_tree.GetFatAABB(m_queryProxyId);
- // 查询树,创建多个pair并将他们添加到pair缓冲区中
- m_tree.Query(this, fatAABB);
- }
有人也许会说,这段代码我们只看到移动缓冲区(m_moveBuffer),看不出来与 pair缓冲区(m_pariBuffer)有任何关系,它们怎么产生联系的呢?注意m_tree.Query(this,fatAABB)这句代码和它传递的参数,this表示当前类的对象,fatAABB表示移动缓冲区中一个代理的AABB,存储了代理的信息。我们再回顾一下query函数:
- /**************************************************************************
- * 功能描述:查询一个aabb重叠代理,每个重叠提供AABB的代理都将回调回调类
- * 参数说明:callback :回调对象
- aabb :要查询的aabb
- * 返 回 值:aabb对象
- ***************************************************************************/
- template <typename T>
- void Query(T* callback, const b2AABB& aabb) const;
- //是否成功
- bool proceed = callback->QueryCallback(nodeId);
- if (proceed == false)
- {
- return;
- }
ps:
以上文章仅是一家之言,若有不妥、错误之处,请大家多多之出。同时也希望能与大家多多交流,共同进步