原始recastnavigation仅支持 圆柱体,box(AABB与OBB)三种形状的障碍物,当然这也是为了效率最大化,那么能否自定义扩展呢?其实也很简单,所以下面说一下具体修改方法,增加一个多边形的支持。
//1 增加一个多边形障碍物类型
enum ObstacleType
{
DT_OBSTACLE_CYLINDER,
DT_OBSTACLE_BOX, // AABB
DT_OBSTACLE_ORIENTED_BOX, // OBB
DT_POLYGON,
};
增加相应存储结构,最大64个顶点,高度我们固定(这个根据具体情况来)
struct dtObstaclePolygon
{
unsigned short nvert;
float verts[64 * 3];
float height;
static void bound(
const unsigned short nvert, const float* verts,
const float height,
float* bmin, float* bmax) {
bmin[0] = bmax[0] = verts[0];
bmin[1] = bmax[1] = verts[1];
bmin[2] = bmax[2] = verts[2];
for (int i = 1; i < nvert; ++i) {
if (bmin[0] > verts[i * 3]) {
bmin[0] = verts[i * 3];
}
if (bmin[1] > verts[i * 3 + 1]) {
bmin[1] = verts[i * 3 + 1];
}
if (bmin[2] > verts[i * 3 + 2]) {
bmin[2] = verts[i * 3 + 2];
}
if (bmax[0] < verts[i * 3]) {
bmax[0] = verts[i * 3];
}
if (bmax[1] < verts[i * 3 + 1]) {
bmax[1] = verts[i * 3 + 1];
}
if (bmax[2] < verts[i * 3 + 2]) {
bmax[2] = verts[i * 3 + 2];
}
}
float avgh = (bmin[1] + bmax[1]) / 2;
bmin[1] = dtMin(bmin[1], avgh - height / 2);
bmax[1] = dtMax(bmax[1], avgh + height / 2);
}
};
联合体增加相应数据(这里截取部分代码)
struct dtTileCacheObstacle
{
union
{
dtObstacleCylinder cylinder;
dtObstacleBox box;
dtObstacleOrientedBox orientedBox;
dtObstaclePolygon polygon;
};
增加bound边框的获取逻辑,修改这个函数 void dtTileCache::getObstacleBounds(const struct dtTileCacheObstacle* ob, float* bmin, float* bmax) const
增加以下代码,获取bbox
else if(ob->type == DT_OBSTACLE_POLYGON){
const dtObstaclePolygon& polygon = ob->polygon;
dtObstaclePolygon::bound(polygon.nvert, polygon.verts, polygon.height, bmin, bmax);
}
增加一个区域标记函数
dtStatus dtMarkPolygonArea(dtTileCacheLayer& layer, const float* orig, const float cs, const float ch,
const unsigned short nvert, const float* verts, const float height, const unsigned char areaId);
把这个函数放到dtStatus dtTileCache::buildNavMeshTile(const dtCompressedTileRef ref, dtNavMesh* navmesh)的区域标记部分
else if (ob->type == DT_OBSTACLE_POLYGON) {
dtMarkPolygonArea(*bc.layer, tile->header->bmin, m_params.cs, m_params.ch,
ob->polygon.nvert, ob->polygon.verts, ob->polygon.height, 0);
} 实现这个函数,将所有处于这个障碍物内的块的标记修改
这边多边形判定不需要自己实现了,源码有现成的dtPointInPolygon
dtStatus dtMarkPolygonArea(dtTileCacheLayer& layer, const float* orig, const float cs, const float ch,
const unsigned short nvert, const float* verts, const float height, const unsigned char areaId) {
float bmin[3], bmax[3];
const int w = (int)layer.header->width;
const int h = (int)layer.header->height;
const float ics = 1.0f / cs;
const float ich = 1.0f / ch;
// 计算当前grid坐标系下的边界
dtObstaclePolygon::bound(nvert, verts, height, bmin, bmax);
int minx = (int)dtMathFloorf((bmin[0] - orig[0])*ics);
int miny = (int)dtMathFloorf((bmin[1] - orig[1])*ich);
int minz = (int)dtMathFloorf((bmin[2] - orig[2])*ics);
int maxx = (int)dtMathFloorf((bmax[0] - orig[0])*ics);
int maxy = (int)dtMathFloorf((bmax[1] - orig[1])*ich);
int maxz = (int)dtMathFloorf((bmax[2] - orig[2])*ics);
if (maxx < 0) return DT_SUCCESS;
if (minx >= w) return DT_SUCCESS;
if (maxz < 0) return DT_SUCCESS;
if (minz >= h) return DT_SUCCESS;
if (minx < 0) minx = 0;
if (maxx >= w) maxx = w - 1;
if (minz < 0) minz = 0;
if (maxz >= h) maxz = h - 1;
for (int z = minz; z <= maxz; ++z)
{
for (int x = minx; x <= maxx; ++x)
{
const int y = layer.heights[x + z*w];
if (y < miny || y > maxy)
continue;
const float pos[] = {
x*cs + orig[0],
y*ch + orig[1],
z*cs + orig[2]
};
if (dtPointInPolygon(pos, verts, nvert))
continue;
layer.areas[x + z*w] = areaId;
}
}
return DT_SUCCESS;
}