本系列博客是由扭曲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;
再看Query函數中的代碼片段
//是否成功
bool proceed = callback->QueryCallback(nodeId);
if (proceed == false)
{
return;
}
看這句代碼bool proceed = callback->QueryCallback(nodeId); 此處的callback就是剛剛傳進去的this,也就是說我們調用的QueryCallback也就是b2BroadPhase::QueryCallback(int32 proxyId)函數。到此處相信大家已經明白了。
ps:
以上文章僅是一家之言,若有不妥、錯誤之處,請大家多多之出。同時也希望能與大家多多交流,共同進步。