|
|
|
@ -102,82 +102,9 @@ namespace
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void createHeightfield(rcContext& context, rcHeightfield& solid, int width, int height, const float* bmin,
|
|
|
|
|
const float* bmax, const float cs, const float ch)
|
|
|
|
|
{
|
|
|
|
|
const auto result = rcCreateHeightfield(&context, solid, width, height, bmin, bmax, cs, ch);
|
|
|
|
|
|
|
|
|
|
if (!result)
|
|
|
|
|
throw NavigatorException("Failed to create heightfield for navmesh");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void buildCompactHeightfield(rcContext& context, const int walkableHeight, const int walkableClimb,
|
|
|
|
|
rcHeightfield& solid, rcCompactHeightfield& compact)
|
|
|
|
|
{
|
|
|
|
|
const auto result = rcBuildCompactHeightfield(&context, walkableHeight,
|
|
|
|
|
walkableClimb, solid, compact);
|
|
|
|
|
|
|
|
|
|
if (!result)
|
|
|
|
|
throw NavigatorException("Failed to build compact heightfield for navmesh");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void erodeWalkableArea(rcContext& context, int walkableRadius, rcCompactHeightfield& compact)
|
|
|
|
|
{
|
|
|
|
|
const auto result = rcErodeWalkableArea(&context, walkableRadius, compact);
|
|
|
|
|
|
|
|
|
|
if (!result)
|
|
|
|
|
throw NavigatorException("Failed to erode walkable area for navmesh");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void buildDistanceField(rcContext& context, rcCompactHeightfield& compact)
|
|
|
|
|
{
|
|
|
|
|
const auto result = rcBuildDistanceField(&context, compact);
|
|
|
|
|
|
|
|
|
|
if (!result)
|
|
|
|
|
throw NavigatorException("Failed to build distance field for navmesh");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void buildRegions(rcContext& context, rcCompactHeightfield& compact, const int borderSize,
|
|
|
|
|
const int minRegionArea, const int mergeRegionArea)
|
|
|
|
|
{
|
|
|
|
|
const auto result = rcBuildRegions(&context, compact, borderSize, minRegionArea, mergeRegionArea);
|
|
|
|
|
|
|
|
|
|
if (!result)
|
|
|
|
|
throw NavigatorException("Failed to build distance field for navmesh");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void buildContours(rcContext& context, rcCompactHeightfield& compact, const float maxError, const int maxEdgeLen,
|
|
|
|
|
rcContourSet& contourSet, const int buildFlags = RC_CONTOUR_TESS_WALL_EDGES)
|
|
|
|
|
{
|
|
|
|
|
const auto result = rcBuildContours(&context, compact, maxError, maxEdgeLen, contourSet, buildFlags);
|
|
|
|
|
|
|
|
|
|
if (!result)
|
|
|
|
|
throw NavigatorException("Failed to build contours for navmesh");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void buildPolyMesh(rcContext& context, rcContourSet& contourSet, const int maxVertsPerPoly, rcPolyMesh& polyMesh)
|
|
|
|
|
{
|
|
|
|
|
const auto result = rcBuildPolyMesh(&context, contourSet, maxVertsPerPoly, polyMesh);
|
|
|
|
|
|
|
|
|
|
if (!result)
|
|
|
|
|
throw NavigatorException("Failed to build poly mesh for navmesh");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void buildPolyMeshDetail(rcContext& context, const rcPolyMesh& polyMesh, const rcCompactHeightfield& compact,
|
|
|
|
|
const float sampleDist, const float sampleMaxError, rcPolyMeshDetail& polyMeshDetail)
|
|
|
|
|
rcConfig makeConfig(const osg::Vec3f& agentHalfExtents, const osg::Vec3f& boundsMin, const osg::Vec3f& boundsMax,
|
|
|
|
|
const Settings& settings)
|
|
|
|
|
{
|
|
|
|
|
const auto result = rcBuildPolyMeshDetail(&context, polyMesh, compact, sampleDist, sampleMaxError,
|
|
|
|
|
polyMeshDetail);
|
|
|
|
|
|
|
|
|
|
if (!result)
|
|
|
|
|
throw NavigatorException("Failed to build detail poly mesh for navmesh");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NavMeshData makeNavMeshTileData(const osg::Vec3f& agentHalfExtents, const RecastMesh& recastMesh,
|
|
|
|
|
const std::vector<OffMeshConnection>& offMeshConnections, const int tileX, const int tileY,
|
|
|
|
|
const osg::Vec3f& boundsMin, const osg::Vec3f& boundsMax, const Settings& settings)
|
|
|
|
|
{
|
|
|
|
|
rcContext context;
|
|
|
|
|
rcConfig config;
|
|
|
|
|
|
|
|
|
|
config.cs = settings.mCellSize;
|
|
|
|
@ -203,9 +130,20 @@ namespace
|
|
|
|
|
config.bmax[0] += getBorderSize(settings);
|
|
|
|
|
config.bmax[2] += getBorderSize(settings);
|
|
|
|
|
|
|
|
|
|
rcHeightfield solid;
|
|
|
|
|
createHeightfield(context, solid, config.width, config.height, config.bmin, config.bmax, config.cs, config.ch);
|
|
|
|
|
return config;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void createHeightfield(rcContext& context, rcHeightfield& solid, int width, int height, const float* bmin,
|
|
|
|
|
const float* bmax, const float cs, const float ch)
|
|
|
|
|
{
|
|
|
|
|
const auto result = rcCreateHeightfield(&context, solid, width, height, bmin, bmax, cs, ch);
|
|
|
|
|
|
|
|
|
|
if (!result)
|
|
|
|
|
throw NavigatorException("Failed to create heightfield for navmesh");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool rasterizeSolidObjectsTriangles(rcContext& context, const RecastMesh& recastMesh, const rcConfig& config,
|
|
|
|
|
rcHeightfield& solid)
|
|
|
|
|
{
|
|
|
|
|
const auto& chunkyMesh = recastMesh.getChunkyTriMesh();
|
|
|
|
|
std::vector<unsigned char> areas(chunkyMesh.getMaxTrisPerChunk(), AreaType_null);
|
|
|
|
@ -215,7 +153,7 @@ namespace
|
|
|
|
|
chunkyMesh.getChunksOverlappingRect(Rect {tileBoundsMin, tileBoundsMax}, std::back_inserter(cids));
|
|
|
|
|
|
|
|
|
|
if (cids.empty())
|
|
|
|
|
return NavMeshData();
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
for (const auto cid : cids)
|
|
|
|
|
{
|
|
|
|
@ -265,8 +203,12 @@ namespace
|
|
|
|
|
if (!trianglesRasterized)
|
|
|
|
|
throw NavigatorException("Failed to create rasterize triangles from recast mesh for navmesh");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void rasterizeWaterTriangles(rcContext& context, const osg::Vec3f& agentHalfExtents, const RecastMesh& recastMesh,
|
|
|
|
|
const Settings& settings, const rcConfig& config, rcHeightfield& solid)
|
|
|
|
|
{
|
|
|
|
|
const std::array<unsigned char, 2> areas {{AreaType_water, AreaType_water}};
|
|
|
|
|
|
|
|
|
@ -320,14 +262,92 @@ namespace
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rcFilterLowHangingWalkableObstacles(&context, config.walkableClimb, solid);
|
|
|
|
|
rcFilterLedgeSpans(&context, config.walkableHeight, config.walkableClimb, solid);
|
|
|
|
|
rcFilterWalkableLowHeightSpans(&context, config.walkableHeight, solid);
|
|
|
|
|
bool rasterizeTriangles(rcContext& context, const osg::Vec3f& agentHalfExtents, const RecastMesh& recastMesh,
|
|
|
|
|
const rcConfig& config, const Settings& settings, rcHeightfield& solid)
|
|
|
|
|
{
|
|
|
|
|
if (!rasterizeSolidObjectsTriangles(context, recastMesh, config, solid))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
rcPolyMesh polyMesh;
|
|
|
|
|
rcPolyMeshDetail polyMeshDetail;
|
|
|
|
|
initPolyMeshDetail(polyMeshDetail);
|
|
|
|
|
const PolyMeshDetailStackPtr polyMeshDetailPtr(&polyMeshDetail);
|
|
|
|
|
rasterizeWaterTriangles(context, agentHalfExtents, recastMesh, settings, config, solid);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void buildCompactHeightfield(rcContext& context, const int walkableHeight, const int walkableClimb,
|
|
|
|
|
rcHeightfield& solid, rcCompactHeightfield& compact)
|
|
|
|
|
{
|
|
|
|
|
const auto result = rcBuildCompactHeightfield(&context, walkableHeight,
|
|
|
|
|
walkableClimb, solid, compact);
|
|
|
|
|
|
|
|
|
|
if (!result)
|
|
|
|
|
throw NavigatorException("Failed to build compact heightfield for navmesh");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void erodeWalkableArea(rcContext& context, int walkableRadius, rcCompactHeightfield& compact)
|
|
|
|
|
{
|
|
|
|
|
const auto result = rcErodeWalkableArea(&context, walkableRadius, compact);
|
|
|
|
|
|
|
|
|
|
if (!result)
|
|
|
|
|
throw NavigatorException("Failed to erode walkable area for navmesh");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void buildDistanceField(rcContext& context, rcCompactHeightfield& compact)
|
|
|
|
|
{
|
|
|
|
|
const auto result = rcBuildDistanceField(&context, compact);
|
|
|
|
|
|
|
|
|
|
if (!result)
|
|
|
|
|
throw NavigatorException("Failed to build distance field for navmesh");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void buildRegions(rcContext& context, rcCompactHeightfield& compact, const int borderSize,
|
|
|
|
|
const int minRegionArea, const int mergeRegionArea)
|
|
|
|
|
{
|
|
|
|
|
const auto result = rcBuildRegions(&context, compact, borderSize, minRegionArea, mergeRegionArea);
|
|
|
|
|
|
|
|
|
|
if (!result)
|
|
|
|
|
throw NavigatorException("Failed to build distance field for navmesh");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void buildContours(rcContext& context, rcCompactHeightfield& compact, const float maxError, const int maxEdgeLen,
|
|
|
|
|
rcContourSet& contourSet, const int buildFlags = RC_CONTOUR_TESS_WALL_EDGES)
|
|
|
|
|
{
|
|
|
|
|
const auto result = rcBuildContours(&context, compact, maxError, maxEdgeLen, contourSet, buildFlags);
|
|
|
|
|
|
|
|
|
|
if (!result)
|
|
|
|
|
throw NavigatorException("Failed to build contours for navmesh");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void buildPolyMesh(rcContext& context, rcContourSet& contourSet, const int maxVertsPerPoly, rcPolyMesh& polyMesh)
|
|
|
|
|
{
|
|
|
|
|
const auto result = rcBuildPolyMesh(&context, contourSet, maxVertsPerPoly, polyMesh);
|
|
|
|
|
|
|
|
|
|
if (!result)
|
|
|
|
|
throw NavigatorException("Failed to build poly mesh for navmesh");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void buildPolyMeshDetail(rcContext& context, const rcPolyMesh& polyMesh, const rcCompactHeightfield& compact,
|
|
|
|
|
const float sampleDist, const float sampleMaxError, rcPolyMeshDetail& polyMeshDetail)
|
|
|
|
|
{
|
|
|
|
|
const auto result = rcBuildPolyMeshDetail(&context, polyMesh, compact, sampleDist, sampleMaxError,
|
|
|
|
|
polyMeshDetail);
|
|
|
|
|
|
|
|
|
|
if (!result)
|
|
|
|
|
throw NavigatorException("Failed to build detail poly mesh for navmesh");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void setPolyMeshFlags(rcPolyMesh& polyMesh)
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < polyMesh.npolys; ++i)
|
|
|
|
|
{
|
|
|
|
|
if (polyMesh.areas[i] == AreaType_ground)
|
|
|
|
|
polyMesh.flags[i] = Flag_walk;
|
|
|
|
|
else if (polyMesh.areas[i] == AreaType_water)
|
|
|
|
|
polyMesh.flags[i] = Flag_swim;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool fillPolyMesh(rcContext& context, const rcConfig& config, rcHeightfield& solid, rcPolyMesh& polyMesh,
|
|
|
|
|
rcPolyMeshDetail& polyMeshDetail)
|
|
|
|
|
{
|
|
|
|
|
rcCompactHeightfield compact;
|
|
|
|
|
buildCompactHeightfield(context, config.walkableHeight, config.walkableClimb, solid, compact);
|
|
|
|
@ -340,21 +360,41 @@ namespace
|
|
|
|
|
buildContours(context, compact, config.maxSimplificationError, config.maxEdgeLen, contourSet);
|
|
|
|
|
|
|
|
|
|
if (contourSet.nconts == 0)
|
|
|
|
|
return NavMeshData();
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
buildPolyMesh(context, contourSet, config.maxVertsPerPoly, polyMesh);
|
|
|
|
|
|
|
|
|
|
buildPolyMeshDetail(context, polyMesh, compact, config.detailSampleDist, config.detailSampleMaxError,
|
|
|
|
|
polyMeshDetail);
|
|
|
|
|
|
|
|
|
|
setPolyMeshFlags(polyMesh);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < polyMesh.npolys; ++i)
|
|
|
|
|
NavMeshData makeNavMeshTileData(const osg::Vec3f& agentHalfExtents, const RecastMesh& recastMesh,
|
|
|
|
|
const std::vector<OffMeshConnection>& offMeshConnections, const TilePosition& tile,
|
|
|
|
|
const osg::Vec3f& boundsMin, const osg::Vec3f& boundsMax, const Settings& settings)
|
|
|
|
|
{
|
|
|
|
|
if (polyMesh.areas[i] == AreaType_ground)
|
|
|
|
|
polyMesh.flags[i] = Flag_walk;
|
|
|
|
|
else if (polyMesh.areas[i] == AreaType_water)
|
|
|
|
|
polyMesh.flags[i] = Flag_swim;
|
|
|
|
|
}
|
|
|
|
|
rcContext context;
|
|
|
|
|
const auto config = makeConfig(agentHalfExtents, boundsMin, boundsMax, settings);
|
|
|
|
|
|
|
|
|
|
rcHeightfield solid;
|
|
|
|
|
createHeightfield(context, solid, config.width, config.height, config.bmin, config.bmax, config.cs, config.ch);
|
|
|
|
|
|
|
|
|
|
if (!rasterizeTriangles(context, agentHalfExtents, recastMesh, config, settings, solid))
|
|
|
|
|
return NavMeshData();
|
|
|
|
|
|
|
|
|
|
rcFilterLowHangingWalkableObstacles(&context, config.walkableClimb, solid);
|
|
|
|
|
rcFilterLedgeSpans(&context, config.walkableHeight, config.walkableClimb, solid);
|
|
|
|
|
rcFilterWalkableLowHeightSpans(&context, config.walkableHeight, solid);
|
|
|
|
|
|
|
|
|
|
rcPolyMesh polyMesh;
|
|
|
|
|
rcPolyMeshDetail polyMeshDetail;
|
|
|
|
|
initPolyMeshDetail(polyMeshDetail);
|
|
|
|
|
const PolyMeshDetailStackPtr polyMeshDetailPtr(&polyMeshDetail);
|
|
|
|
|
if (!fillPolyMesh(context, config, solid, polyMesh, polyMeshDetail))
|
|
|
|
|
return NavMeshData();
|
|
|
|
|
|
|
|
|
|
const auto offMeshConVerts = getOffMeshVerts(offMeshConnections);
|
|
|
|
|
const std::vector<float> offMeshConRad(offMeshConnections.size(), getRadius(settings, agentHalfExtents));
|
|
|
|
@ -391,8 +431,8 @@ namespace
|
|
|
|
|
params.ch = config.ch;
|
|
|
|
|
params.buildBvTree = true;
|
|
|
|
|
params.userId = 0;
|
|
|
|
|
params.tileX = tileX;
|
|
|
|
|
params.tileY = tileY;
|
|
|
|
|
params.tileX = tile.x();
|
|
|
|
|
params.tileY = tile.y();
|
|
|
|
|
params.tileLayer = 0;
|
|
|
|
|
|
|
|
|
|
unsigned char* navMeshData;
|
|
|
|
@ -483,17 +523,16 @@ namespace DetourNavigator
|
|
|
|
|
return removeTile();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto boundsMin = recastMesh->getBoundsMin();
|
|
|
|
|
auto boundsMax = recastMesh->getBoundsMax();
|
|
|
|
|
auto recastMeshBounds = recastMesh->getBounds();
|
|
|
|
|
|
|
|
|
|
for (const auto& water : recastMesh->getWater())
|
|
|
|
|
{
|
|
|
|
|
const auto bounds = getWaterBounds(water, settings, agentHalfExtents);
|
|
|
|
|
boundsMin.y() = std::min(boundsMin.y(), bounds.mMin.y());
|
|
|
|
|
boundsMax.y() = std::max(boundsMax.y(), bounds.mMax.y());
|
|
|
|
|
const auto waterBounds = getWaterBounds(water, settings, agentHalfExtents);
|
|
|
|
|
recastMeshBounds.mMin.y() = std::min(recastMeshBounds.mMin.y(), waterBounds.mMin.y());
|
|
|
|
|
recastMeshBounds.mMax.y() = std::max(recastMeshBounds.mMax.y(), waterBounds.mMax.y());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (boundsMin == boundsMax)
|
|
|
|
|
if (isEmpty(recastMeshBounds))
|
|
|
|
|
{
|
|
|
|
|
log("ignore add tile: recastMesh is empty");
|
|
|
|
|
return removeTile();
|
|
|
|
@ -510,10 +549,10 @@ namespace DetourNavigator
|
|
|
|
|
if (!cachedNavMeshData)
|
|
|
|
|
{
|
|
|
|
|
const auto tileBounds = makeTileBounds(settings, changedTile);
|
|
|
|
|
const osg::Vec3f tileBorderMin(tileBounds.mMin.x(), boundsMin.y() - 1, tileBounds.mMin.y());
|
|
|
|
|
const osg::Vec3f tileBorderMax(tileBounds.mMax.x(), boundsMax.y() + 1, tileBounds.mMax.y());
|
|
|
|
|
const osg::Vec3f tileBorderMin(tileBounds.mMin.x(), recastMeshBounds.mMin.y() - 1, tileBounds.mMin.y());
|
|
|
|
|
const osg::Vec3f tileBorderMax(tileBounds.mMax.x(), recastMeshBounds.mMax.y() + 1, tileBounds.mMax.y());
|
|
|
|
|
|
|
|
|
|
auto navMeshData = makeNavMeshTileData(agentHalfExtents, *recastMesh, offMeshConnections, x, y,
|
|
|
|
|
auto navMeshData = makeNavMeshTileData(agentHalfExtents, *recastMesh, offMeshConnections, changedTile,
|
|
|
|
|
tileBorderMin, tileBorderMax, settings);
|
|
|
|
|
|
|
|
|
|
if (!navMeshData.mValue)
|
|
|
|
|