RecastNavigation中的区域切分方法
-
分水岭分区
- 经典的Recast分区
- 创建最好的细分
- 通常最慢
- 将Heightfield划分为没有孔或重叠的良好区域。
- 在某些极端情况下,此方法创建会产生孔洞和重叠
- 当小的障碍物靠近较大的开放区域时,可能会出现孔(三角剖分可以解决此问题)
- 如果您有狭窄的螺旋形走廊(即楼梯),则可能会发生重叠,这会使三角剖分失败
- 如果是预处理网格,通常是最佳选择,如果您有较大的开放区域,这种方法也适用。
-
单调分区
-
最快的
-
能将高度场划分为无孔和重叠的区域
-
创建长而细的多边形,有时会导致路径走弯
-
如果要快速生成导航网格,请使用此选项
-
-
按层分区
- 较快
- 将heighfield划分为非重叠区域
- 依靠三角剖分来处理孔(因此比单调分区要慢)
- 产生比单调分区更好的三角形
- 没有分水岭分区的特殊情况
- 速度可能很慢,并且会产生一些难看的镶嵌效果(仍然比单调效果更好),如果您的开放区域较大且障碍物较小(如果使用瓷砖则没有问题)
- 用于中小型瓷砖的导航网格的好选择
分水岭划分
过程分为两步:
1. 创建距离场
计算每个span到区域边界的距离(四个方向中最小的一个),距离越远越接近中心。
并用boxblur进行平滑处理(计算九宫格内距离的均值)(为什么?)
2. 按距离场进行划分
2.1 关键函数作用
expandRegions():
作用是扩展对应点集的区域id。扩展过程中会进行反复迭代,通过迭代扩散区域id。
扩散方式:检查四周(紧缩span的connection)是否有满足条件的span,条件为:被标记过 && 非边界 &&可走。
迭代停止条件:点集周围(包括点集)无任何标记 或 达到最大迭代次数(自定义的)
expandRegion运行结束后的结果:
函数运行结束后,点集内会出现两种点,分别有区域标记的点和空白的点。
在本函数大量迭代之后,如果仍有空白的点,则代表这些空白的点是一个新的区域。因为,这些点是无法通过上一层水位的点扩展到的。
floodRegion():
以一个span作为起始点,按4邻域泛洪填充它所能扩展到的区域
遍历搜索的方式采用深度优先(dfs),而dfs的实现则使用了手动压栈的非递归方式
而每次处理新的节点时,会先判断它的8个邻接节点是否已经有了更早的填充标记,如果有,则说明当前节点处于区域相交处,不扩展该节点(该节点会在下一层的expandRegions时被处理)
2.2 具体过程
首先,将边界标记出来
然后,按距离场中的数值进行排序,从大到小排序。
再然后,开始逐渐”加水“,从大的地方慢慢扩散。对于每一层操作如下:
- 将上一层span加入当前层
- 调用expandRegions函数,将当前已有的区域id扩散直到无法扩散。这时候,对于当前点集存在两种状态。一种是有标记的,这种是扩散成功的。另一种则是没有标记的,表示扩散失败。对于扩散失败的span可以认为这些都是一些新的区域
将expandRegion的点集复制一份到另一个点集stack中,并调用floodRegion函数。该函数也会扩散区域id(条件为大于等于指定水深并且当前无标记),并且对于临界的span(即临界点有更早标记)会被强制清除标记。
上一步结束之后,会再次调用expandRegions函数,这时处理的点集是stack的,这次调用把所有未被标记且可走的span也加入stack中在进行之前expandRegions的流程。
Q.E.D.