拖更了一年回來填坑??這應該是這個系列正文的最后一篇,之前的內容可以從這里找到
本篇主要分析一下項目中關于動態阻擋的處理方式,順帶對比回顧一下靜態導航網格的生成流程
recastnavigation中提供了3種模式的導航網格:
- SoloMesh
- TileMesh
- TempObstacles
其中SoloMesh模式是靜態的導航網格,即對場景build一次之后,將導航網格緩存起來供尋路使用,后續不再允許場景的地形發生變化
TempObstacles模式可以支持向場景中動態添加或移除預設形狀的阻擋物,導航網格也會隨之更新(不過只支持添加阻擋物,而不支持添加新的可行走區域)
在處理動態阻擋時,由于單個阻擋對地圖的影響區域是有限的,所以會采用將地圖切割成多個固定大小的tile,以tile為單位進行網格的生成。這樣在添加或移除阻擋時,只需要處理與阻擋相交的tile,而不需要處理整個地圖
TileMesh也是靜態的導航網格,只是與SoloMesh相比它按tile來處理地圖,算是介于SoloMesh和TempObstacles之間的一種模式
在分析TempBostacles模式之前,我們先來回顧一下導航網格尋路的主體思路,這在任何模式下都是通用的:
1.將場景模型轉化為可行走面
2.將可行走面分割成凸多邊形集合
3.計算凸多邊形之間的聯通關系
4.在凸多邊形集合上規劃路徑
我們再來回顧一下SoloMesh是怎么具體實現這幾步的:
1.標記導入的場景3角網格的可行走面
2.將三角面體素化,得到高度域
3.將高度域轉化成緊縮高度域
4.通過區域劃分算法合并緊縮高度域,得到連續的區域
5.求出區域的外輪廓
6.由外輪廓得到凸多邊形集合
TempObstacles與SoloMesh的差異主要體現在兩個方面:
1.按tile劃分處理地圖
2.在由緊縮高度域生成連續區域這一步中,加入通過阻擋物體剔除部分可行走空間的步驟
當動態阻擋被添加或移除時,會找到與阻擋相交的tile,并重新計算它的導航網格。
值得注意的是,生成高度域這一步只做一次,中間結果會以壓縮格式存儲在內存中。
這么做的意義,項目原作者Mikko Mononen大神在博客中是這樣解釋的:
將三角面體素化生成高度域這一步,會占據整個生成過程70%的時間,所以采用緩存的方式可以極大的加快動態生成導航網格的速度。而直接存儲高度域結果又會比較消耗內存,所以以壓縮存儲的方式來存儲(項目中使用fastLZ算法進行數據壓縮)。
Triangle voxelization is usually dominating facter when generating a tile.
For example it takes 71.5% of the time to generate the highlighted tile in the test scene.
所以TempObstacles的運作流程大體是這樣的,分成預處理階段和更新處理階段:
Preprocess
for each tile
- rasterize static triangles
- find walkable cells
- mark areas
- store compressed heighfield
At Run Time
for a changed tile
- uncompressed heighfield
- mark obstacles
- build regions
- build contours
- build poly mesh
- build detail mesh
- store navmesh
如果之前已經對SoloMesh有比較好的掌握的話,理解起來TempObstacles應該還是沒有什么障礙的。
代碼中比較難讀懂的可能是這個函數(畢竟寫了快600行)
bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf,
const int borderSize, const int walkableHeight,
rcHeightfieldLayerSet& lset)
它的作用是得到層次區域化的緊縮高度域,實際上它與SoloMesh中區域劃分算法中的Layer partitoining算法幾乎一樣(我抄我自己)。我在這篇中單獨分析一下Layer partitoining算法,這里就不做贅述了。
最后再說一下tile對連通性的處理:
當一個tile發生變化時,會先從網格中移除這個tile,此時對每個與這個tile相鄰的其他tile,遍歷其中的多邊形,刪除與目標tile相關的連接
然后再將新的tile加入到目標位置,對該tile中的每個多邊形,遍歷周圍tile,判斷是否有鄰接的多邊形,若有則建立新的連接