void Sample::handleCommonSettings() { if (m_geom) { const float* bmin = m_geom->getMeshBoundsMin(); const float* bmax = m_geom->getMeshBoundsMax(); int gw = 0, gh = 0; rcCalcGridSize(bmin, bmax, m_cellSize, &gw, &gh); char text[64]; snprintf(text, 64, "Voxels %d x %d", gw, gh); } }
void Sample::handleCommonSettings() { imguiLabel("Rasterization"); imguiSlider("Cell Size", &m_cellSize, 0.1f, 1.0f, 0.01f); imguiSlider("Cell Height", &m_cellHeight, 0.1f, 1.0f, 0.01f); if (m_geom) { const float* bmin = m_geom->getMeshBoundsMin(); const float* bmax = m_geom->getMeshBoundsMax(); int gw = 0, gh = 0; rcCalcGridSize(bmin, bmax, m_cellSize, &gw, &gh); char text[64]; snprintf(text, 64, "Voxels %d x %d", gw, gh); imguiValue(text); } imguiSeparator(); imguiLabel("Agent"); imguiSlider("Height", &m_agentHeight, 0.1f, 5.0f, 0.1f); imguiSlider("Radius", &m_agentRadius, 0.0f, 5.0f, 0.1f); imguiSlider("Max Climb", &m_agentMaxClimb, 0.1f, 5.0f, 0.1f); imguiSlider("Max Slope", &m_agentMaxSlope, 0.0f, 90.0f, 1.0f); imguiSeparator(); imguiLabel("Region"); imguiSlider("Min Region Size", &m_regionMinSize, 0.0f, 150.0f, 1.0f); imguiSlider("Merged Region Size", &m_regionMergeSize, 0.0f, 150.0f, 1.0f); imguiSeparator(); imguiLabel("Partitioning"); if (imguiCheck("Watershed", m_partitionType == SAMPLE_PARTITION_WATERSHED)) m_partitionType = SAMPLE_PARTITION_WATERSHED; if (imguiCheck("Monotone", m_partitionType == SAMPLE_PARTITION_MONOTONE)) m_partitionType = SAMPLE_PARTITION_MONOTONE; if (imguiCheck("Layers", m_partitionType == SAMPLE_PARTITION_LAYERS)) m_partitionType = SAMPLE_PARTITION_LAYERS; imguiSeparator(); imguiLabel("Polygonization"); imguiSlider("Max Edge Length", &m_edgeMaxLen, 0.0f, 50.0f, 1.0f); imguiSlider("Max Edge Error", &m_edgeMaxError, 0.1f, 3.0f, 0.1f); imguiSlider("Verts Per Poly", &m_vertsPerPoly, 3.0f, 12.0f, 1.0f); imguiSeparator(); imguiLabel("Detail Mesh"); imguiSlider("Sample Distance", &m_detailSampleDist, 0.0f, 16.0f, 1.0f); imguiSlider("Max Sample Error", &m_detailSampleMaxError, 0.0f, 16.0f, 1.0f); imguiSeparator(); }
void Sample_TileMesh::removeAllTiles() { const float* bmin = m_geom->getMeshBoundsMin(); const float* bmax = m_geom->getMeshBoundsMax(); int gw = 0, gh = 0; rcCalcGridSize(bmin, bmax, m_cellSize, &gw, &gh); const int ts = (int)m_tileSize; const int tw = (gw + ts-1) / ts; const int th = (gh + ts-1) / ts; for (int y = 0; y < th; ++y) for (int x = 0; x < tw; ++x) m_navMesh->removeTile(m_navMesh->getTileRefAt(x,y),0,0); }
void Sample_TileMesh::buildAllTiles() { if (!m_geom) return; if (!m_navMesh) return; const float* bmin = m_geom->getNavMeshBoundsMin(); const float* bmax = m_geom->getNavMeshBoundsMax(); int gw = 0, gh = 0; rcCalcGridSize(bmin, bmax, m_cellSize, &gw, &gh); const int ts = (int)m_tileSize; const int tw = (gw + ts-1) / ts; const int th = (gh + ts-1) / ts; const float tcs = m_tileSize*m_cellSize; // Start the build process. m_ctx->startTimer(RC_TIMER_TEMP); for (int y = 0; y < th; ++y) { for (int x = 0; x < tw; ++x) { m_lastBuiltTileBmin[0] = bmin[0] + x*tcs; m_lastBuiltTileBmin[1] = bmin[1]; m_lastBuiltTileBmin[2] = bmin[2] + y*tcs; m_lastBuiltTileBmax[0] = bmin[0] + (x+1)*tcs; m_lastBuiltTileBmax[1] = bmax[1]; m_lastBuiltTileBmax[2] = bmin[2] + (y+1)*tcs; int dataSize = 0; unsigned char* data = buildTileMesh(x, y, m_lastBuiltTileBmin, m_lastBuiltTileBmax, dataSize); if (data) { // Remove any previous data (navmesh owns and deletes the data). m_navMesh->removeTile(m_navMesh->getTileRefAt(x,y,0),0,0); // Let the navmesh own the data. dtStatus status = m_navMesh->addTile(data,dataSize,DT_TILE_FREE_DATA,0,0); if (dtStatusFailed(status)) dtFree(data); } } } // Start the build process. m_ctx->stopTimer(RC_TIMER_TEMP); m_totalBuildTimeMs = m_ctx->getAccumulatedTime(RC_TIMER_TEMP)/1000.0f; }
void RecastMapRenderer::Render() { if (!m_RecastMap->GetGeometry() || !m_RecastMap->GetGeometry()->getMesh()) return; // ========================================== setup glDepthFunc(GL_LESS); glDepthMask(GL_TRUE); DebugDrawGL dd; const float texScale = 1.0f / (RECAST_CELL_SIZE * 10.0f); duDebugDrawTriMeshSlope(&dd, m_RecastMap->GetGeometry()->getMesh()->getVerts(), m_RecastMap->GetGeometry()->getMesh()->getVertCount(), m_RecastMap->GetGeometry()->getMesh()->getTris(), m_RecastMap->GetGeometry()->getMesh()->getNormals(), m_RecastMap->GetGeometry()->getMesh()->getTriCount(), RECAST_AGENT_MAX_SLOPE, texScale); DrawAgents(&dd); glDepthMask(GL_FALSE); // Draw bounds const float* bmin = m_RecastMap->GetGeometry()->getMeshBoundsMin(); const float* bmax = m_RecastMap->GetGeometry()->getMeshBoundsMax(); duDebugDrawBoxWire(&dd, bmin[0],bmin[1],bmin[2], bmax[0],bmax[1],bmax[2], duRGBA(255,255,255,128), 1.0f); // Tiling grid. int gw = 0, gh = 0; rcCalcGridSize(bmin, bmax, RECAST_CELL_SIZE, &gw, &gh); const int tw = (gw + RECAST_TILE_SIZE - 1) / RECAST_TILE_SIZE; const int th = (gh + RECAST_TILE_SIZE - 1) / RECAST_TILE_SIZE; const float s = RECAST_TILE_SIZE*RECAST_CELL_SIZE; duDebugDrawGridXZ(&dd, bmin[0],bmin[1],bmin[2], tw,th, s, duRGBA(0,0,0,64), 1.0f); if(m_RecastMap->GetMesh() && m_RecastMap->GetQuery()) { duDebugDrawNavMesh(&dd, *(m_RecastMap->GetMesh()), DU_DRAWNAVMESH_COLOR_TILES|DU_DRAWNAVMESH_CLOSEDLIST|DU_DRAWNAVMESH_OFFMESHCONS); duDebugDrawNavMeshPolysWithFlags(&dd, *(m_RecastMap->GetMesh()), POLY_ABILITY_DISABLED, duRGBA(0,0,0,128)); } if (m_RecastMap->GetTileCache()) //DrawTiles(&dd, m_RecastMap->GetTileCache()); if (m_RecastMap->GetTileCache()) DrawObstacles(&dd, m_RecastMap->GetTileCache()); if (m_RecastMap->GetGeometry()) DrawConvexVolumes(&dd, m_RecastMap->GetGeometry()); }
void Sample_TileMesh::buildAllTiles() { if (!m_geom) return; if (!m_navMesh) return; const float* bmin = m_geom->getMeshBoundsMin(); const float* bmax = m_geom->getMeshBoundsMax(); int gw = 0, gh = 0; rcCalcGridSize(bmin, bmax, m_cellSize, &gw, &gh); const int ts = (int)m_tileSize; const int tw = (gw + ts-1) / ts; const int th = (gh + ts-1) / ts; const float tcs = m_tileSize*m_cellSize; // Start the build process. rcTimeVal totStartTime = m_ctx->getTime(); for (int y = 0; y < th; ++y) { for (int x = 0; x < tw; ++x) { m_tileBmin[0] = bmin[0] + x*tcs; m_tileBmin[1] = bmin[1]; m_tileBmin[2] = bmin[2] + y*tcs; m_tileBmax[0] = bmin[0] + (x+1)*tcs; m_tileBmax[1] = bmax[1]; m_tileBmax[2] = bmin[2] + (y+1)*tcs; int dataSize = 0; unsigned char* data = buildTileMesh(x, y, m_tileBmin, m_tileBmax, dataSize); if (data) { // Remove any previous data (navmesh owns and deletes the data). m_navMesh->removeTile(m_navMesh->getTileRefAt(x,y),0,0); // Let the navmesh own the data. if (!m_navMesh->addTile(data,dataSize,true)) dtFree(data); } } } // Start the build process. rcTimeVal totEndTime = m_ctx->getTime(); m_totalBuildTimeMs = m_ctx->getDeltaTimeUsec(totStartTime, totEndTime)/1000.0f; }
void Sample::handleCommonSettings() { imguiLabel("Rasterization"); imguiSlider("Cell Size", &m_fCellSize, 0.1f, 1.0f, 0.01f); imguiSlider("Cell Height", &m_fCellHeight, 0.1f, 1.0f, 0.01f); if (m_pInputGeom) { const float* bmin = m_pInputGeom->getMeshBoundsMin(); const float* bmax = m_pInputGeom->getMeshBoundsMax(); int gw = 0, gh = 0; rcCalcGridSize(bmin, bmax, m_fCellSize, &gw, &gh); char text[64]; snprintf(text, 64, "Voxels %d x %d", gw, gh); imguiValue(text); } imguiSeparator(); imguiLabel("Agent"); imguiSlider("Height", &m_fAgentHeight, 0.1f, 5.0f, 0.1f); imguiSlider("Radius", &m_fAgentRadius, 0.0f, 5.0f, 0.1f); imguiSlider("Max Climb", &m_fAgentMaxClimb, 0.1f, 5.0f, 0.1f); imguiSlider("Max Slope", &m_fAgentMaxSlope, 0.0f, 90.0f, 1.0f); imguiSeparator(); imguiLabel("Region"); imguiSlider("Min Region Size", &m_fRegionMinSize, 0.0f, 150.0f, 1.0f); imguiSlider("Merged Region Size", &m_fRegionMergeSize, 0.0f, 150.0f, 1.0f); if (imguiCheck("Monotore Partitioning", m_bMonotonePartitioning)) m_bMonotonePartitioning = !m_bMonotonePartitioning; imguiSeparator(); imguiLabel("Polygonization"); imguiSlider("Max Edge Length", &m_fEdgeMaxLen, 0.0f, 50.0f, 1.0f); imguiSlider("Max Edge Error", &m_fEdgeMaxError, 0.1f, 3.0f, 0.1f); imguiSlider("Verts Per Poly", &m_fVertsPerPoly, 3.0f, 12.0f, 1.0f); imguiSeparator(); imguiLabel("Detail Mesh"); imguiSlider("Sample Distance", &m_fDetailSampleDist, 0.0f, 16.0f, 1.0f); imguiSlider("Max Sample Error", &m_fDetailSampleMaxError, 0.0f, 16.0f, 1.0f); imguiSeparator(); }
void Sample::handleCommonSettings() { imguiLabel("Rasterization"); imguiSlider("Cell Size", &m_cellSize, 0.01f, 1.0f, 0.01f); imguiSlider("Cell Height", &m_cellHeight, 0.01f, 1.0f, 0.01f); if (m_geom) { const dtCoordinates bmin( m_geom->getMeshBoundsMin() ); const dtCoordinates bmax( m_geom->getMeshBoundsMax() ); int gw = 0, gh = 0; rcCalcGridSize(bmin, bmax, m_cellSize, &gw, &gh); char text[64]; snprintf(text, 64, "Voxels %d x %d", gw, gh); imguiValue(text); } imguiSeparator(); imguiLabel("Agent"); imguiSlider("Height", &m_agentHeight, 0.0f, 2.0f, 0.01f); imguiSlider("Radius", &m_agentRadius, 0.0f, 2.0f, 0.01f); imguiSlider("Max Climb", &m_agentMaxClimb, 0.f, 1.0f, 0.01f); imguiSlider("Max Slope", &m_agentMaxSlope, 0.0f, 180.0f, 1.0f); imguiSeparator(); imguiLabel("Region"); imguiSlider("Min Region Size", &m_regionMinSize, 0.0f, 400.0f, 2.0f); imguiSlider("Merged Region Size", &m_regionMergeSize, 0.0f, 400.0f, 2.0f); if (imguiCheck("Monotone Partitioning", m_monotonePartitioning)) m_monotonePartitioning = !m_monotonePartitioning; imguiSeparator(); imguiLabel("Polygonization"); imguiSlider("Max Edge Length", &m_edgeMaxLen, 0.0f, 100.0f, 1.0f); imguiSlider("Max Edge Error", &m_edgeMaxError, 0.0f, 10.0f, 0.1f); imguiSlider("Verts Per Poly", &m_vertsPerPoly, 3.0f, 6.0f, 1.0f); imguiSeparator(); imguiLabel("Detail Mesh"); imguiSlider("Sample Distance", &m_detailSampleDist, 0.0f, 30.0f, 1.0f); imguiSlider("Max Sample Error", &m_detailSampleMaxError, 0.0f, 30.0f, 1.0f); imguiSeparator(); }
unsigned char* RecastTileBuilder::build(float x, float y, const AABB& lastTileBounds, int& dataSize) { int gw = 0, gh = 0; float bmin[3]; float bmax[3]; bmin[0] = bounds.getXMin(); bmin[1] = bounds.getYMin(); bmin[2] = bounds.getZMin(); bmax[0] = bounds.getXMax(); bmax[1] = bounds.getYMax(); bmax[2] = bounds.getZMax(); rcCalcGridSize(bmin, bmax, settings.m_cellSize, &gw, &gh); const int ts = (int) settings.m_tileSize; const int tw = (gw + ts - 1) / ts; const int th = (gh + ts - 1) / ts; // Max tiles and max polys affect how the tile IDs are caculated. // There are 22 bits available for identifying a tile and a polygon. int tileBits = rcMin((int) ilog2(nextPow2(tw * th)), 14); int polyBits = 22 - tileBits; m_maxTiles = 1<<tileBits; m_maxPolysPerTile = 1<<polyBits; dtNavMeshParams params; params.orig[0] = bounds.getXMin(); params.orig[1] = bounds.getYMin(); params.orig[2] = bounds.getZMin(); //rcVcopy(params.orig, m_geom->getNavMeshBoundsMin()); params.tileWidth = settings.m_tileSize * settings.m_cellSize; params.tileHeight = settings.m_tileSize * settings.m_cellSize; params.maxTiles = m_maxTiles; params.maxPolys = m_maxPolysPerTile; dtStatus status; this->lastTileBounds = lastTileBounds; return buildTileMesh(x, y, dataSize); }
void Sample_TileMesh::handleSettings() { Sample::handleCommonSettings(); if (imguiCheck("Keep Itermediate Results", m_keepInterResults)) m_keepInterResults = !m_keepInterResults; if (imguiCheck("Build All Tiles", m_buildAll)) m_buildAll = !m_buildAll; imguiLabel("Tiling"); imguiSlider("TileSize", &m_tileSize, 16.0f, 1024.0f, 16.0f); if (m_geom) { char text[64]; int gw = 0, gh = 0; const float* bmin = m_geom->getNavMeshBoundsMin(); const float* bmax = m_geom->getNavMeshBoundsMax(); rcCalcGridSize(bmin, bmax, m_cellSize, &gw, &gh); const int ts = (int)m_tileSize; const int tw = (gw + ts-1) / ts; const int th = (gh + ts-1) / ts; snprintf(text, 64, "Tiles %d x %d", tw, th); imguiValue(text); // Max tiles and max polys affect how the tile IDs are caculated. // There are 22 bits available for identifying a tile and a polygon. int tileBits = rcMin((int)ilog2(nextPow2(tw*th)), 14); if (tileBits > 14) tileBits = 14; int polyBits = 22 - tileBits; m_maxTiles = 1 << tileBits; m_maxPolysPerTile = 1 << polyBits; snprintf(text, 64, "Max Tiles %d", m_maxTiles); imguiValue(text); snprintf(text, 64, "Max Polys %d", m_maxPolysPerTile); imguiValue(text); } else { m_maxTiles = 0; m_maxPolysPerTile = 0; } imguiSeparator(); imguiIndent(); imguiIndent(); if (imguiButton("Save")) { Sample::saveAll("all_tiles_navmesh.bin", m_navMesh); } if (imguiButton("Load")) { dtFreeNavMesh(m_navMesh); m_navMesh = Sample::loadAll("all_tiles_navmesh.bin"); m_navQuery->init(m_navMesh, 2048); } imguiUnindent(); imguiUnindent(); char msg[64]; snprintf(msg, 64, "Build Time: %.1fms", m_totalBuildTimeMs); imguiLabel(msg); imguiSeparator(); imguiSeparator(); }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CRecastMesh::Build( CMapMesh *pMapMesh ) { double fStartTime = Plat_FloatTime(); Reset(); // Clean any existing data BuildContext ctx; ctx.enableLog( true ); dtStatus status; V_memset(&m_cfg, 0, sizeof(m_cfg)); // Init cache rcCalcBounds( pMapMesh->GetVerts(), pMapMesh->GetNumVerts(), m_cfg.bmin, m_cfg.bmax ); rcCalcGridSize(m_cfg.bmin, m_cfg.bmax, m_cfg.cs, &m_cfg.width, &m_cfg.height); int gw = 0, gh = 0; rcCalcGridSize(m_cfg.bmin, m_cfg.bmax, m_cellSize, &gw, &gh); const int ts = (int)m_tileSize; const int tw = (gw + ts-1) / ts; const int th = (gh + ts-1) / ts; // Max tiles and max polys affect how the tile IDs are caculated. // There are 22 bits available for identifying a tile and a polygon. int tileBits = rcMin((int)dtIlog2(dtNextPow2(tw*th*EXPECTED_LAYERS_PER_TILE)), 14); if (tileBits > 14) tileBits = 14; int polyBits = 22 - tileBits; m_maxTiles = 1 << tileBits; m_maxPolysPerTile = 1 << polyBits; // Generation params. m_cfg.cs = m_cellSize; m_cfg.ch = m_cellHeight; m_cfg.walkableSlopeAngle = m_agentMaxSlope; m_cfg.walkableHeight = (int)ceilf(m_agentHeight / m_cfg.ch); m_cfg.walkableClimb = (int)floorf(m_agentMaxClimb / m_cfg.ch); m_cfg.walkableRadius = (int)ceilf(m_agentRadius / m_cfg.cs); m_cfg.maxEdgeLen = (int)(m_edgeMaxLen / m_cellSize); m_cfg.maxSimplificationError = m_edgeMaxError; m_cfg.minRegionArea = (int)rcSqr(m_regionMinSize); // Note: area = size*size m_cfg.mergeRegionArea = (int)rcSqr(m_regionMergeSize); // Note: area = size*size m_cfg.maxVertsPerPoly = (int)m_vertsPerPoly; m_cfg.tileSize = (int)m_tileSize; m_cfg.borderSize = m_cfg.walkableRadius + 3; // Reserve enough padding. m_cfg.width = m_cfg.tileSize + m_cfg.borderSize*2; m_cfg.height = m_cfg.tileSize + m_cfg.borderSize*2; m_cfg.detailSampleDist = m_detailSampleDist < 0.9f ? 0 : m_cellSize * m_detailSampleDist; m_cfg.detailSampleMaxError = m_cellHeight * m_detailSampleMaxError; // Tile cache params. dtTileCacheParams tcparams; memset(&tcparams, 0, sizeof(tcparams)); rcVcopy(tcparams.orig, m_cfg.bmin); tcparams.cs = m_cellSize; tcparams.ch = m_cellHeight; tcparams.width = (int)m_tileSize; tcparams.height = (int)m_tileSize; tcparams.walkableHeight = m_agentHeight; tcparams.walkableRadius = m_agentRadius; tcparams.walkableClimb = m_agentMaxClimb; tcparams.maxSimplificationError = m_edgeMaxError; tcparams.maxTiles = tw*th*EXPECTED_LAYERS_PER_TILE; tcparams.maxObstacles = 2048; dtFreeTileCache(m_tileCache); m_tileCache = dtAllocTileCache(); if (!m_tileCache) { ctx.log(RC_LOG_ERROR, "buildTiledNavigation: Could not allocate tile cache."); return false; } status = m_tileCache->init(&tcparams, m_talloc, m_tcomp, m_tmproc); if (dtStatusFailed(status)) { ctx.log(RC_LOG_ERROR, "buildTiledNavigation: Could not init tile cache."); return false; } dtFreeNavMesh(m_navMesh); m_navMesh = dtAllocNavMesh(); if (!m_navMesh) { ctx.log(RC_LOG_ERROR, "buildTiledNavigation: Could not allocate navmesh."); return false; } dtNavMeshParams params; memset(¶ms, 0, sizeof(params)); rcVcopy(params.orig, m_cfg.bmin); params.tileWidth = m_tileSize*m_cellSize; params.tileHeight = m_tileSize*m_cellSize; params.maxTiles = m_maxTiles; params.maxPolys = m_maxPolysPerTile; status = m_navMesh->init(¶ms); if (dtStatusFailed(status)) { ctx.log(RC_LOG_ERROR, "buildTiledNavigation: Could not init navmesh."); return false; } status = m_navQuery->init( m_navMesh, RECAST_NAVQUERY_MAXNODES ); if (dtStatusFailed(status)) { ctx.log(RC_LOG_ERROR, "buildTiledNavigation: Could not init Detour navmesh query"); return false; } // Preprocess tiles. ctx.resetTimers(); m_cacheLayerCount = 0; m_cacheCompressedSize = 0; m_cacheRawSize = 0; for (int y = 0; y < th; ++y) { for (int x = 0; x < tw; ++x) { TileCacheData tiles[MAX_LAYERS]; memset(tiles, 0, sizeof(tiles)); int ntiles = rasterizeTileLayers(&ctx, pMapMesh, x, y, m_cfg, tiles, MAX_LAYERS); for (int i = 0; i < ntiles; ++i) { TileCacheData* tile = &tiles[i]; status = m_tileCache->addTile(tile->data, tile->dataSize, DT_COMPRESSEDTILE_FREE_DATA, 0); if (dtStatusFailed(status)) { dtFree(tile->data); tile->data = 0; continue; } m_cacheLayerCount++; m_cacheCompressedSize += tile->dataSize; m_cacheRawSize += calcLayerBufferSize(tcparams.width, tcparams.height); } } } // Build initial meshes ctx.startTimer(RC_TIMER_TOTAL); for (int y = 0; y < th; ++y) for (int x = 0; x < tw; ++x) m_tileCache->buildNavMeshTilesAt(x,y, m_navMesh); ctx.stopTimer(RC_TIMER_TOTAL); m_cacheBuildTimeMs = ctx.getAccumulatedTime(RC_TIMER_TOTAL)/1000.0f; m_cacheBuildMemUsage = ((LinearAllocator *)m_talloc)->high; const dtNavMesh* nav = m_navMesh; int navmeshMemUsage = 0; for (int i = 0; i < nav->getMaxTiles(); ++i) { const dtMeshTile* tile = nav->getTile(i); if (tile->header) navmeshMemUsage += tile->dataSize; } DevMsg( "CRecastMesh: Generated navigation mesh %s in %f seconds\n", m_Name.Get(), Plat_FloatTime() - fStartTime ); return true; }
void Sample_TempObstacles::handleSettings() { Sample::handleCommonSettings(); if (imguiCheck("Keep Itermediate Results", m_keepInterResults)) m_keepInterResults = !m_keepInterResults; imguiLabel("Tiling"); imguiSlider("TileSize", &m_tileSize, 16.0f, 128.0f, 8.0f); int gridSize = 1; if (m_geom) { const float* bmin = m_geom->getNavMeshBoundsMin(); const float* bmax = m_geom->getNavMeshBoundsMax(); char text[64]; int gw = 0, gh = 0; rcCalcGridSize(bmin, bmax, m_cellSize, &gw, &gh); const int ts = (int)m_tileSize; const int tw = (gw + ts-1) / ts; const int th = (gh + ts-1) / ts; snprintf(text, 64, "Tiles %d x %d", tw, th); imguiValue(text); // Max tiles and max polys affect how the tile IDs are caculated. // There are 22 bits available for identifying a tile and a polygon. int tileBits = rcMin((int)dtIlog2(dtNextPow2(tw*th*EXPECTED_LAYERS_PER_TILE)), 14); if (tileBits > 14) tileBits = 14; int polyBits = 22 - tileBits; m_maxTiles = 1 << tileBits; m_maxPolysPerTile = 1 << polyBits; snprintf(text, 64, "Max Tiles %d", m_maxTiles); imguiValue(text); snprintf(text, 64, "Max Polys %d", m_maxPolysPerTile); imguiValue(text); gridSize = tw*th; } else { m_maxTiles = 0; m_maxPolysPerTile = 0; } imguiSeparator(); imguiLabel("Tile Cache"); char msg[64]; const float compressionRatio = (float)m_cacheCompressedSize / (float)(m_cacheRawSize+1); snprintf(msg, 64, "Layers %d", m_cacheLayerCount); imguiValue(msg); snprintf(msg, 64, "Layers (per tile) %.1f", (float)m_cacheLayerCount/(float)gridSize); imguiValue(msg); snprintf(msg, 64, "Memory %.1f kB / %.1f kB (%.1f%%)", m_cacheCompressedSize/1024.0f, m_cacheRawSize/1024.0f, compressionRatio*100.0f); imguiValue(msg); snprintf(msg, 64, "Navmesh Build Time %.1f ms", m_cacheBuildTimeMs); imguiValue(msg); snprintf(msg, 64, "Build Peak Mem Usage %.1f kB", m_cacheBuildMemUsage/1024.0f); imguiValue(msg); imguiSeparator(); imguiIndent(); imguiIndent(); if (imguiButton("Save")) { saveAll("all_tiles_tilecache.bin"); } if (imguiButton("Load")) { dtFreeNavMesh(m_navMesh); dtFreeTileCache(m_tileCache); loadAll("all_tiles_tilecache.bin"); m_navQuery->init(m_navMesh, 2048); } imguiUnindent(); imguiUnindent(); imguiSeparator(); }
bool Sample_TempObstacles::handleBuild() { dtStatus status; if (!m_geom || !m_geom->getMesh()) { m_ctx->log(RC_LOG_ERROR, "buildTiledNavigation: No vertices and triangles."); return false; } m_tmproc->init(m_geom); // Init cache const float* bmin = m_geom->getNavMeshBoundsMin(); const float* bmax = m_geom->getNavMeshBoundsMax(); int gw = 0, gh = 0; rcCalcGridSize(bmin, bmax, m_cellSize, &gw, &gh); const int ts = (int)m_tileSize; const int tw = (gw + ts-1) / ts; const int th = (gh + ts-1) / ts; // Generation params. rcConfig cfg; memset(&cfg, 0, sizeof(cfg)); cfg.cs = m_cellSize; cfg.ch = m_cellHeight; cfg.walkableSlopeAngle = m_agentMaxSlope; cfg.walkableHeight = (int)ceilf(m_agentHeight / cfg.ch); cfg.walkableClimb = (int)floorf(m_agentMaxClimb / cfg.ch); cfg.walkableRadius = (int)ceilf(m_agentRadius / cfg.cs); cfg.maxEdgeLen = (int)(m_edgeMaxLen / m_cellSize); cfg.maxSimplificationError = m_edgeMaxError; cfg.minRegionArea = (int)rcSqr(m_regionMinSize); // Note: area = size*size cfg.mergeRegionArea = (int)rcSqr(m_regionMergeSize); // Note: area = size*size cfg.maxVertsPerPoly = (int)m_vertsPerPoly; cfg.tileSize = (int)m_tileSize; cfg.borderSize = cfg.walkableRadius + 3; // Reserve enough padding. cfg.width = cfg.tileSize + cfg.borderSize*2; cfg.height = cfg.tileSize + cfg.borderSize*2; cfg.detailSampleDist = m_detailSampleDist < 0.9f ? 0 : m_cellSize * m_detailSampleDist; cfg.detailSampleMaxError = m_cellHeight * m_detailSampleMaxError; rcVcopy(cfg.bmin, bmin); rcVcopy(cfg.bmax, bmax); // Tile cache params. dtTileCacheParams tcparams; memset(&tcparams, 0, sizeof(tcparams)); rcVcopy(tcparams.orig, bmin); tcparams.cs = m_cellSize; tcparams.ch = m_cellHeight; tcparams.width = (int)m_tileSize; tcparams.height = (int)m_tileSize; tcparams.walkableHeight = m_agentHeight; tcparams.walkableRadius = m_agentRadius; tcparams.walkableClimb = m_agentMaxClimb; tcparams.maxSimplificationError = m_edgeMaxError; tcparams.maxTiles = tw*th*EXPECTED_LAYERS_PER_TILE; tcparams.maxObstacles = 128; dtFreeTileCache(m_tileCache); m_tileCache = dtAllocTileCache(); if (!m_tileCache) { m_ctx->log(RC_LOG_ERROR, "buildTiledNavigation: Could not allocate tile cache."); return false; } status = m_tileCache->init(&tcparams, m_talloc, m_tcomp, m_tmproc); if (dtStatusFailed(status)) { m_ctx->log(RC_LOG_ERROR, "buildTiledNavigation: Could not init tile cache."); return false; } dtFreeNavMesh(m_navMesh); m_navMesh = dtAllocNavMesh(); if (!m_navMesh) { m_ctx->log(RC_LOG_ERROR, "buildTiledNavigation: Could not allocate navmesh."); return false; } dtNavMeshParams params; memset(¶ms, 0, sizeof(params)); rcVcopy(params.orig, bmin); params.tileWidth = m_tileSize*m_cellSize; params.tileHeight = m_tileSize*m_cellSize; params.maxTiles = m_maxTiles; params.maxPolys = m_maxPolysPerTile; status = m_navMesh->init(¶ms); if (dtStatusFailed(status)) { m_ctx->log(RC_LOG_ERROR, "buildTiledNavigation: Could not init navmesh."); return false; } status = m_navQuery->init(m_navMesh, 2048); if (dtStatusFailed(status)) { m_ctx->log(RC_LOG_ERROR, "buildTiledNavigation: Could not init Detour navmesh query"); return false; } // Preprocess tiles. m_ctx->resetTimers(); m_cacheLayerCount = 0; m_cacheCompressedSize = 0; m_cacheRawSize = 0; for (int y = 0; y < th; ++y) { for (int x = 0; x < tw; ++x) { TileCacheData tiles[MAX_LAYERS]; memset(tiles, 0, sizeof(tiles)); int ntiles = rasterizeTileLayers(m_ctx, m_geom, x, y, cfg, tiles, MAX_LAYERS); for (int i = 0; i < ntiles; ++i) { TileCacheData* tile = &tiles[i]; status = m_tileCache->addTile(tile->data, tile->dataSize, DT_COMPRESSEDTILE_FREE_DATA, 0); if (dtStatusFailed(status)) { dtFree(tile->data); tile->data = 0; continue; } m_cacheLayerCount++; m_cacheCompressedSize += tile->dataSize; m_cacheRawSize += calcLayerBufferSize(tcparams.width, tcparams.height); } } } // Build initial meshes m_ctx->startTimer(RC_TIMER_TOTAL); for (int y = 0; y < th; ++y) for (int x = 0; x < tw; ++x) m_tileCache->buildNavMeshTilesAt(x,y, m_navMesh); m_ctx->stopTimer(RC_TIMER_TOTAL); m_cacheBuildTimeMs = m_ctx->getAccumulatedTime(RC_TIMER_TOTAL)/1000.0f; m_cacheBuildMemUsage = m_talloc->high; const dtNavMesh* nav = m_navMesh; int navmeshMemUsage = 0; for (int i = 0; i < nav->getMaxTiles(); ++i) { const dtMeshTile* tile = nav->getTile(i); if (tile->header) navmeshMemUsage += tile->dataSize; } printf("navmeshMemUsage = %.1f kB", navmeshMemUsage/1024.0f); if (m_tool) m_tool->init(this); initToolStates(this); return true; }
void Sample_TempObstacles::handleRender() { if (!m_geom || !m_geom->getMesh()) return; DebugDrawGL dd; const float texScale = 1.0f / (m_cellSize * 10.0f); // Draw mesh if (m_drawMode != DRAWMODE_NAVMESH_TRANS) { // Draw mesh duDebugDrawTriMeshSlope(&dd, m_geom->getMesh()->getVerts(), m_geom->getMesh()->getVertCount(), m_geom->getMesh()->getTris(), m_geom->getMesh()->getNormals(), m_geom->getMesh()->getTriCount(), m_agentMaxSlope, texScale); m_geom->drawOffMeshConnections(&dd); } if (m_tileCache && m_drawMode == DRAWMODE_CACHE_BOUNDS) drawTiles(&dd, m_tileCache); if (m_tileCache) drawObstacles(&dd, m_tileCache); glDepthMask(GL_FALSE); // Draw bounds const float* bmin = m_geom->getNavMeshBoundsMin(); const float* bmax = m_geom->getNavMeshBoundsMax(); duDebugDrawBoxWire(&dd, bmin[0],bmin[1],bmin[2], bmax[0],bmax[1],bmax[2], duRGBA(255,255,255,128), 1.0f); // Tiling grid. int gw = 0, gh = 0; rcCalcGridSize(bmin, bmax, m_cellSize, &gw, &gh); const int tw = (gw + (int)m_tileSize-1) / (int)m_tileSize; const int th = (gh + (int)m_tileSize-1) / (int)m_tileSize; const float s = m_tileSize*m_cellSize; duDebugDrawGridXZ(&dd, bmin[0],bmin[1],bmin[2], tw,th, s, duRGBA(0,0,0,64), 1.0f); if (m_navMesh && m_navQuery && (m_drawMode == DRAWMODE_NAVMESH || m_drawMode == DRAWMODE_NAVMESH_TRANS || m_drawMode == DRAWMODE_NAVMESH_BVTREE || m_drawMode == DRAWMODE_NAVMESH_NODES || m_drawMode == DRAWMODE_NAVMESH_PORTALS || m_drawMode == DRAWMODE_NAVMESH_INVIS)) { if (m_drawMode != DRAWMODE_NAVMESH_INVIS) duDebugDrawNavMeshWithClosedList(&dd, *m_navMesh, *m_navQuery, m_navMeshDrawFlags/*|DU_DRAWNAVMESH_COLOR_TILES*/); if (m_drawMode == DRAWMODE_NAVMESH_BVTREE) duDebugDrawNavMeshBVTree(&dd, *m_navMesh); if (m_drawMode == DRAWMODE_NAVMESH_PORTALS) duDebugDrawNavMeshPortals(&dd, *m_navMesh); if (m_drawMode == DRAWMODE_NAVMESH_NODES) duDebugDrawNavMeshNodes(&dd, *m_navQuery); duDebugDrawNavMeshPolysWithFlags(&dd, *m_navMesh, SAMPLE_POLYFLAGS_DISABLED, duRGBA(0,0,0,128)); } glDepthMask(GL_TRUE); m_geom->drawConvexVolumes(&dd); if (m_tool) m_tool->handleRender(); renderToolStates(); glDepthMask(GL_TRUE); }
dtNavMesh* buildMesh(InputGeom* geom, WCellBuildContext* wcellContext, int numCores) { dtNavMesh* mesh = 0; if (!geom || !geom->getMesh()) { CleanupAfterBuild(); wcellContext->log(RC_LOG_ERROR, "buildTiledNavigation: No vertices and triangles."); return 0; } mesh = dtAllocNavMesh(); if (!mesh) { CleanupAfterBuild(); wcellContext->log(RC_LOG_ERROR, "buildTiledNavigation: Could not allocate navmesh."); return 0; } // setup some default parameters rcConfig cfg; memset(&cfg, 0, sizeof(rcConfig)); const float agentHeight = 2.1f; // most character toons are about this tall const float agentRadius = 0.6f; // most character toons are about this big around const float agentClimb = 1.0f; // character toons can step up this far. Seems ridiculously high ... const float tileSize = 1600.0f/3.0f/16.0f; // The size of one chunk cfg.cs = 0.1f; // cell size is a sort of resolution -> the bigger the faster cfg.ch = 0.05f; // cell height -> distance from mesh to ground, if too low, recast will not build essential parts of the mesh for some reason cfg.walkableSlopeAngle = 50.0f; // max climbable slope, bigger values won't make much of a change cfg.walkableHeight = (int)ceilf(agentHeight/cfg.ch);// minimum space to ceiling cfg.walkableClimb = (int)floorf(agentClimb/cfg.ch); // how high the agent can climb in one step cfg.walkableRadius = (int)ceilf(agentRadius/cfg.cs);// minimum distance to objects cfg.tileSize = (int)(tileSize/cfg.cs + 0.5f); cfg.maxEdgeLen = cfg.tileSize/2;; cfg.borderSize = cfg.walkableRadius + 3; cfg.width = cfg.tileSize + cfg.borderSize*2; cfg.height = cfg.tileSize + cfg.borderSize*2; cfg.maxSimplificationError = 1.3f; cfg.minRegionArea = (int)rcSqr(8); // Note: area = size*size cfg.mergeRegionArea = (int)rcSqr(20); // Note: area = size*size cfg.maxVertsPerPoly = 3; cfg.detailSampleDist = cfg.cs * 9; cfg.detailSampleMaxError = cfg.ch * 1.0f; // default calculations - for some reason not included in basic recast const float* bmin = geom->getMeshBoundsMin(); const float* bmax = geom->getMeshBoundsMax(); int gw = 0, gh = 0; rcCalcGridSize(bmin, bmax, cfg.cs, &gw, &gh); const int ts = cfg.tileSize; const int tw = (gw + ts-1) / ts; const int th = (gh + ts-1) / ts; // Max tiles and max polys affect how the tile IDs are caculated. // There are 22 bits available for identifying a tile and a polygon. int tileBits = rcMin((int)ilog2(nextPow2(tw*th)), 14); if (tileBits > 14) tileBits = 14; int polyBits = 22 - tileBits; int maxTiles = 1 << tileBits; int maxPolysPerTile = 1 << polyBits; dtNavMeshParams params; rcVcopy(params.orig, geom->getMeshBoundsMin()); params.tileWidth = cfg.tileSize * cfg.cs; params.tileHeight = cfg.tileSize * cfg.cs; params.maxTiles = maxTiles; params.maxPolys = maxPolysPerTile; dtStatus status; status = mesh->init(¶ms); if (dtStatusFailed(status)) { CleanupAfterBuild(); wcellContext->log(RC_LOG_ERROR, "buildTiledNavigation: Could not init navmesh."); return 0; } // start building const float tcs = cfg.tileSize*cfg.cs; wcellContext->startTimer(RC_TIMER_TEMP); TileAdder Adder; dispatcher.Reset(); dispatcher.maxHeight = th; dispatcher.maxWidth = tw; int numThreads = 0; numThreads = std::min(2*numCores, 8); boost::thread *threads[8]; for(int i = 0; i < numThreads; ++i) { QuadrantTiler newTiler; newTiler.geom = geom; newTiler.cfg = cfg; newTiler.ctx = *wcellContext; boost::thread newThread(boost::ref(newTiler)); threads[i] = &newThread; } Adder.mesh = mesh; Adder.numThreads = numThreads; boost::thread AdderThread(boost::ref(Adder)); AdderThread.join(); // Start the build process. wcellContext->stopTimer(RC_TIMER_TEMP); return mesh; }
/*! Build a NAVIGATION mesh from an OBJ mesh index. Usually this OBJMESH is either a collision map or a mesh that have been built especially for navigation. \param[in,out] navigation A valid NAVIGATION structure pointer. \param[in] obj A valid OBJ structure pointer. \param[in] mesh_index The mesh index of the OBJMESH to use to create the NAVIGATION mesh. \return Return 1 if the NAVIGATION mesh have been generated successfully, else this function will return 0. */ unsigned char NAVIGATION_build( NAVIGATION *navigation, OBJ *obj, unsigned int mesh_index ) { unsigned int i = 0, j = 0, k = 0, triangle_count = 0; int *indices = NULL; OBJMESH *objmesh = &obj->objmesh[ mesh_index ]; vec3 *vertex_array = ( vec3 * ) malloc( objmesh->n_objvertexdata * sizeof( vec3 ) ), *vertex_start = vertex_array; rcHeightfield *rcheightfield; rcCompactHeightfield *rccompactheightfield; rcContourSet *rccontourset; rcPolyMesh *rcpolymesh; rcPolyMeshDetail *rcpolymeshdetail; while( i != objmesh->n_objvertexdata ) { memcpy( vertex_array, &obj->indexed_vertex[ objmesh->objvertexdata[ i ].vertex_index ], sizeof( vec3 ) ); vec3_to_recast( vertex_array ); ++vertex_array; ++i; } i = 0; while( i != objmesh->n_objtrianglelist ) { triangle_count += objmesh->objtrianglelist[ i ].n_indice_array; indices = ( int * ) realloc( indices, triangle_count * sizeof( int ) ); j = 0; while( j != objmesh->objtrianglelist[ i ].n_indice_array ) { indices[ k ] = objmesh->objtrianglelist[ i ].indice_array[ j ]; ++k; ++j; } ++i; } triangle_count /= 3; rcConfig rcconfig; memset( &rcconfig, 0, sizeof( rcConfig ) ); rcconfig.cs = navigation->navigationconfiguration.cell_size; rcconfig.ch = navigation->navigationconfiguration.cell_height; rcconfig.walkableHeight = ( int )ceilf ( navigation->navigationconfiguration.agent_height / rcconfig.ch ); rcconfig.walkableRadius = ( int )ceilf ( navigation->navigationconfiguration.agent_radius / rcconfig.cs ); rcconfig.walkableClimb = ( int )floorf( navigation->navigationconfiguration.agent_max_climb / rcconfig.ch ); rcconfig.walkableSlopeAngle = navigation->navigationconfiguration.agent_max_slope; rcconfig.minRegionSize = ( int )rcSqr( navigation->navigationconfiguration.region_min_size ); rcconfig.mergeRegionSize = ( int )rcSqr( navigation->navigationconfiguration.region_merge_size ); rcconfig.maxEdgeLen = ( int )( navigation->navigationconfiguration.edge_max_len / rcconfig.cs ); rcconfig.maxSimplificationError = navigation->navigationconfiguration.edge_max_error; rcconfig.maxVertsPerPoly = ( int )navigation->navigationconfiguration.vert_per_poly; rcconfig.detailSampleDist = rcconfig.cs * navigation->navigationconfiguration.detail_sample_dst; rcconfig.detailSampleMaxError = rcconfig.ch * navigation->navigationconfiguration.detail_sample_max_error; rcCalcBounds( ( float * )vertex_start, objmesh->n_objvertexdata, rcconfig.bmin, rcconfig.bmax ); rcCalcGridSize( rcconfig.bmin, rcconfig.bmax, rcconfig.cs, &rcconfig.width, &rcconfig.height ); rcheightfield = rcAllocHeightfield(); rcCreateHeightfield( *rcheightfield, rcconfig.width, rcconfig.height, rcconfig.bmin, rcconfig.bmax, rcconfig.cs, rcconfig.ch ); navigation->triangle_flags = new unsigned char[ triangle_count ]; memset( navigation->triangle_flags, 0, triangle_count * sizeof( unsigned char ) ); rcMarkWalkableTriangles( rcconfig.walkableSlopeAngle, ( float * )vertex_start, objmesh->n_objvertexdata, indices, triangle_count, navigation->triangle_flags ); rcRasterizeTriangles( ( float * )vertex_start, objmesh->n_objvertexdata, indices, navigation->triangle_flags, triangle_count, *rcheightfield, rcconfig.walkableClimb ); delete []navigation->triangle_flags; navigation->triangle_flags = NULL; free( vertex_start ); free( indices ); rcFilterLowHangingWalkableObstacles( rcconfig.walkableClimb, *rcheightfield ); rcFilterLedgeSpans( rcconfig.walkableHeight, rcconfig.walkableClimb, *rcheightfield ); rcFilterWalkableLowHeightSpans( rcconfig.walkableHeight, *rcheightfield ); rccompactheightfield = rcAllocCompactHeightfield(); rcBuildCompactHeightfield( rcconfig.walkableHeight, rcconfig.walkableClimb, RC_WALKABLE, *rcheightfield, *rccompactheightfield ); rcFreeHeightField( rcheightfield ); rcheightfield = NULL; rcErodeArea( RC_WALKABLE_AREA, rcconfig.walkableRadius, *rccompactheightfield ); rcBuildDistanceField( *rccompactheightfield ); rcBuildRegions( *rccompactheightfield, rcconfig.borderSize, rcconfig.minRegionSize, rcconfig.mergeRegionSize ); rccontourset = rcAllocContourSet(); rcBuildContours( *rccompactheightfield, rcconfig.maxSimplificationError, rcconfig.maxEdgeLen, *rccontourset ); rcpolymesh = rcAllocPolyMesh(); rcBuildPolyMesh( *rccontourset, rcconfig.maxVertsPerPoly, *rcpolymesh ); rcpolymeshdetail = rcAllocPolyMeshDetail(); rcBuildPolyMeshDetail( *rcpolymesh, *rccompactheightfield, rcconfig.detailSampleDist, rcconfig.detailSampleMaxError, *rcpolymeshdetail ); rcFreeCompactHeightfield( rccompactheightfield ); rccompactheightfield = NULL; rcFreeContourSet( rccontourset ); rccontourset = NULL; if( rcconfig.maxVertsPerPoly <= DT_VERTS_PER_POLYGON ) { dtNavMeshCreateParams dtnavmeshcreateparams; unsigned char *nav_data = NULL; int nav_data_size = 0; i = 0; while( i != rcpolymesh->npolys ) { if( rcpolymesh->areas[ i ] == RC_WALKABLE_AREA ) { rcpolymesh->areas[ i ] = 0; rcpolymesh->flags[ i ] = 0x01; } ++i; } memset( &dtnavmeshcreateparams, 0, sizeof( dtNavMeshCreateParams ) ); dtnavmeshcreateparams.verts = rcpolymesh->verts; dtnavmeshcreateparams.vertCount = rcpolymesh->nverts; dtnavmeshcreateparams.polys = rcpolymesh->polys; dtnavmeshcreateparams.polyAreas = rcpolymesh->areas; dtnavmeshcreateparams.polyFlags = rcpolymesh->flags; dtnavmeshcreateparams.polyCount = rcpolymesh->npolys; dtnavmeshcreateparams.nvp = rcpolymesh->nvp; dtnavmeshcreateparams.detailMeshes = rcpolymeshdetail->meshes; dtnavmeshcreateparams.detailVerts = rcpolymeshdetail->verts; dtnavmeshcreateparams.detailVertsCount = rcpolymeshdetail->nverts; dtnavmeshcreateparams.detailTris = rcpolymeshdetail->tris; dtnavmeshcreateparams.detailTriCount = rcpolymeshdetail->ntris; dtnavmeshcreateparams.walkableHeight = navigation->navigationconfiguration.agent_height; dtnavmeshcreateparams.walkableRadius = navigation->navigationconfiguration.agent_radius; dtnavmeshcreateparams.walkableClimb = navigation->navigationconfiguration.agent_max_climb; rcVcopy( dtnavmeshcreateparams.bmin, rcpolymesh->bmin ); rcVcopy( dtnavmeshcreateparams.bmax, rcpolymesh->bmax ); dtnavmeshcreateparams.cs = rcconfig.cs; dtnavmeshcreateparams.ch = rcconfig.ch; dtCreateNavMeshData( &dtnavmeshcreateparams, &nav_data, &nav_data_size ); if( !nav_data ) return 0; navigation->dtnavmesh = dtAllocNavMesh(); navigation->dtnavmesh->init( nav_data, nav_data_size, DT_TILE_FREE_DATA, NAVIGATION_MAX_NODE ); rcFreePolyMesh( rcpolymesh ); rcpolymesh = NULL; rcFreePolyMeshDetail( rcpolymeshdetail ); rcpolymeshdetail = NULL; return 1; } return 0; }
PDT_NAV_MESH gkRecast::createNavMesh(PMESHDATA meshData, const Config& config) { if (!meshData.get()) return PDT_NAV_MESH(0); rcConfig cfg; cfg.cs = config.CELL_SIZE; cfg.ch = config.CELL_HEIGHT; GK_ASSERT(cfg.ch && "cfg.ch cannot be zero"); GK_ASSERT(cfg.ch && "cfg.ch cannot be zero"); cfg.walkableSlopeAngle = config.AGENT_MAX_SLOPE; cfg.walkableHeight = (int)ceilf(config.AGENT_HEIGHT / cfg.ch); cfg.walkableClimb = (int)ceilf(config.AGENT_MAX_CLIMB / cfg.ch); cfg.walkableRadius = (int)ceilf(config.AGENT_RADIUS / cfg.cs); cfg.maxEdgeLen = (int)(config.EDGE_MAX_LEN / cfg.cs); cfg.maxSimplificationError = config.EDGE_MAX_ERROR; cfg.minRegionSize = (int)rcSqr(config.REGION_MIN_SIZE); cfg.mergeRegionSize = (int)rcSqr(config.REGION_MERGE_SIZE); cfg.maxVertsPerPoly = gkMin(config.VERTS_PER_POLY, DT_VERTS_PER_POLYGON); cfg.tileSize = config.TILE_SIZE; cfg.borderSize = cfg.walkableRadius + 4; // Reserve enough padding. cfg.detailSampleDist = config.DETAIL_SAMPLE_DIST < 0.9f ? 0 : cfg.cs * config.DETAIL_SAMPLE_DIST; cfg.detailSampleMaxError = cfg.ch * config.DETAIL_SAMPLE_ERROR; if (!meshData->getVertCount()) return PDT_NAV_MESH(0); gkScalar bmin[3], bmax[3]; const gkScalar* verts = meshData->getVerts(); int nverts = meshData->getVertCount(); const int* tris = meshData->getTris(); const gkScalar* trinorms = meshData->getNormals(); int ntris = meshData->getTriCount(); rcCalcBounds(verts, nverts, bmin, bmax); // // Step 1. Initialize build config. // // Set the area where the navigation will be build. // Here the bounds of the input mesh are used, but the // area could be specified by an user defined box, etc. rcVcopy(cfg.bmin, bmin); rcVcopy(cfg.bmax, bmax); rcCalcGridSize(cfg.bmin, cfg.bmax, cfg.cs, &cfg.width, &cfg.height); rcBuildTimes m_buildTimes; // Reset build times gathering. memset(&m_buildTimes, 0, sizeof(m_buildTimes)); rcSetBuildTimes(&m_buildTimes); // Start the build process. rcTimeVal totStartTime = rcGetPerformanceTimer(); //gkPrintf("Building navigation:"); //gkPrintf(" - %d x %d cells", cfg.width, cfg.height); //gkPrintf(" - %.1fK verts, %.1fK tris", nverts/1000.0f, ntris/1000.0f); // // Step 2. Rasterize input polygon soup. // // Allocate voxel heighfield where we rasterize our input data to. rcHeightfield heightField; if (!rcCreateHeightfield(heightField, cfg.width, cfg.height, cfg.bmin, cfg.bmax, cfg.cs, cfg.ch)) { gkPrintf("buildNavigation: Could not create solid heightfield."); return PDT_NAV_MESH(0); } { // Allocate array that can hold triangle flags. // If you have multiple meshes you need to process, allocate // and array which can hold the max number of triangles you need to process. utArray<unsigned char> triflags; triflags.resize(ntris); // Find triangles which are walkable based on their slope and rasterize them. // If your input data is multiple meshes, you can transform them here, calculate // the flags for each of the meshes and rasterize them. memset(triflags.ptr(), 0, ntris * sizeof(unsigned char)); rcMarkWalkableTriangles(cfg.walkableSlopeAngle, verts, nverts, tris, ntris, triflags.ptr()); rcRasterizeTriangles(verts, nverts, tris, triflags.ptr(), ntris, heightField); } // // Step 3. Filter walkables surfaces. // // Once all geoemtry is rasterized, we do initial pass of filtering to // remove unwanted overhangs caused by the conservative rasterization // as well as filter spans where the character cannot possibly stand. rcFilterLedgeSpans(cfg.walkableHeight, cfg.walkableClimb, heightField); rcFilterWalkableLowHeightSpans(cfg.walkableHeight, heightField); // // Step 4. Partition walkable surface to simple regions. // // Compact the heightfield so that it is faster to handle from now on. // This will result more cache coherent data as well as the neighbours // between walkable cells will be calculated. rcCompactHeightfield chf; if (!rcBuildCompactHeightfield(cfg.walkableHeight, cfg.walkableClimb, RC_WALKABLE, heightField, chf)) { gkPrintf("buildNavigation: Could not build compact data."); return PDT_NAV_MESH(0); } // Erode the walkable area by agent radius. if (!rcErodeArea(RC_WALKABLE_AREA, cfg.walkableRadius, chf)) { gkPrintf("buildNavigation: Could not erode."); return PDT_NAV_MESH(0); } // // Mark areas from objects // gkScene* scene = gkEngine::getSingleton().getActiveScene(); gkGameObjectSet& objects = scene->getInstancedObjects(); gkGameObjectSet::Iterator it = objects.iterator(); while (it.hasMoreElements()) { gkGameObject* obj = it.getNext(); if (!obj->getNavData().isEmpty()) { size_t tBaseIndex = obj->getNavData().triangleBaseIndex; size_t vBaseIndex = tBaseIndex / 2; const float* v = verts + vBaseIndex; const int nVerts = obj->getNavData().nIndex / 3; const gkGameObjectProperties& prop = obj->getProperties(); rcMarkConvexPolyArea(v, nVerts, obj->getNavData().hmin, obj->getNavData().hmax, prop.m_findPathFlag, chf); } } // Prepare for region partitioning, by calculating distance field along the walkable surface. if (!rcBuildDistanceField(chf)) { gkPrintf("buildNavigation: Could not build distance field."); return PDT_NAV_MESH(0); } // Partition the walkable surface into simple regions without holes. if (!rcBuildRegions(chf, cfg.borderSize, cfg.minRegionSize, cfg.mergeRegionSize)) { gkPrintf("buildNavigation: Could not build regions."); return PDT_NAV_MESH(0); } // // Step 5. Trace and simplify region contours. // // Create contours. rcContourSet cset; if (!rcBuildContours(chf, cfg.maxSimplificationError, cfg.maxEdgeLen, cset)) { gkPrintf("buildNavigation: Could not create contours."); return PDT_NAV_MESH(0); } // // Step 6. Build polygons mesh from contours. // // Build polygon navmesh from the contours. rcPolyMesh pmesh; if (!rcBuildPolyMesh(cset, cfg.maxVertsPerPoly, pmesh)) { gkPrintf("buildNavigation: Could not triangulate contours."); return PDT_NAV_MESH(0); } // // Step 7. Create detail mesh which allows to access approximate height on each polygon. // rcPolyMeshDetail dmesh; if (!rcBuildPolyMeshDetail(pmesh, chf, cfg.detailSampleDist, cfg.detailSampleMaxError, dmesh)) { gkPrintf("buildNavigation: Could not build detail mesh."); return PDT_NAV_MESH(0); } // At this point the navigation mesh data is ready, you can access it from pmesh. // See rcDebugDrawPolyMesh or dtCreateNavMeshData as examples how to access the data. // // Step 8. Create Detour data from Recast poly mesh. // PDT_NAV_MESH navMesh; // Update poly flags from areas. for (int i = 0; i < pmesh.npolys; ++i) pmesh.flags[i] = 0xFFFF & pmesh.areas[i]; dtNavMeshCreateParams params; memset(¶ms, 0, sizeof(params)); params.verts = pmesh.verts; params.vertCount = pmesh.nverts; params.polys = pmesh.polys; params.polyAreas = pmesh.areas; params.polyFlags = pmesh.flags; params.polyCount = pmesh.npolys; params.nvp = pmesh.nvp; params.detailMeshes = dmesh.meshes; params.detailVerts = dmesh.verts; params.detailVertsCount = dmesh.nverts; params.detailTris = dmesh.tris; params.detailTriCount = dmesh.ntris; /* params.offMeshConVerts = m_geom->getOffMeshConnectionVerts(); params.offMeshConRad = m_geom->getOffMeshConnectionRads(); params.offMeshConDir = m_geom->getOffMeshConnectionDirs(); params.offMeshConAreas = m_geom->getOffMeshConnectionAreas(); params.offMeshConFlags = m_geom->getOffMeshConnectionFlags(); params.offMeshConCount = m_geom->getOffMeshConnectionCount(); */ params.walkableHeight = cfg.walkableHeight * cfg.ch; params.walkableRadius = cfg.walkableRadius * cfg.cs;; params.walkableClimb = cfg.walkableClimb * cfg.ch; rcVcopy(params.bmin, pmesh.bmin); rcVcopy(params.bmax, pmesh.bmax); params.cs = cfg.cs; params.ch = cfg.ch; unsigned char* navData = 0; int navDataSize = 0; if (!dtCreateNavMeshData(¶ms, &navData, &navDataSize)) { gkPrintf("Could not build Detour navmesh."); return PDT_NAV_MESH(0); } navMesh = PDT_NAV_MESH(new gkDetourNavMesh(new dtNavMesh)); if (!navMesh->m_p->init(navData, navDataSize, DT_TILE_FREE_DATA, 2048)) { delete [] navData; gkPrintf("Could not init Detour navmesh"); return PDT_NAV_MESH(0); } rcTimeVal totEndTime = rcGetPerformanceTimer(); gkPrintf("Navigation mesh created: %.1fms", rcGetDeltaTimeUsec(totStartTime, totEndTime) / 1000.0f); return navMesh; }
void MapBuilder::buildMoveMapTile(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData, float bmin[3], float bmax[3], dtNavMesh* navMesh) { // console output std::string tileString = Trinity::StringFormat("[Map %04u] [%02i,%02i]: ", mapID, tileX, tileY); printf("%s Building movemap tiles...\n", tileString.c_str()); IntermediateValues iv; float* tVerts = meshData.solidVerts.getCArray(); int tVertCount = meshData.solidVerts.size() / 3; int* tTris = meshData.solidTris.getCArray(); int tTriCount = meshData.solidTris.size() / 3; float* lVerts = meshData.liquidVerts.getCArray(); int lVertCount = meshData.liquidVerts.size() / 3; int* lTris = meshData.liquidTris.getCArray(); int lTriCount = meshData.liquidTris.size() / 3; uint8* lTriFlags = meshData.liquidType.getCArray(); // these are WORLD UNIT based metrics // this are basic unit dimentions // value have to divide GRID_SIZE(533.3333f) ( aka: 0.5333, 0.2666, 0.3333, 0.1333, etc ) const static float BASE_UNIT_DIM = m_bigBaseUnit ? 0.5333333f : 0.2666666f; // All are in UNIT metrics! const static int VERTEX_PER_MAP = int(GRID_SIZE/BASE_UNIT_DIM + 0.5f); const static int VERTEX_PER_TILE = m_bigBaseUnit ? 40 : 80; // must divide VERTEX_PER_MAP const static int TILES_PER_MAP = VERTEX_PER_MAP/VERTEX_PER_TILE; rcConfig config; memset(&config, 0, sizeof(rcConfig)); rcVcopy(config.bmin, bmin); rcVcopy(config.bmax, bmax); config.maxVertsPerPoly = DT_VERTS_PER_POLYGON; config.cs = BASE_UNIT_DIM; config.ch = BASE_UNIT_DIM; config.walkableSlopeAngle = m_maxWalkableAngle; config.tileSize = VERTEX_PER_TILE; config.walkableRadius = m_bigBaseUnit ? 1 : 2; config.borderSize = config.walkableRadius + 3; config.maxEdgeLen = VERTEX_PER_TILE + 1; // anything bigger than tileSize config.walkableHeight = m_bigBaseUnit ? 3 : 6; // a value >= 3|6 allows npcs to walk over some fences // a value >= 4|8 allows npcs to walk over all fences config.walkableClimb = m_bigBaseUnit ? 4 : 8; config.minRegionArea = rcSqr(60); config.mergeRegionArea = rcSqr(50); config.maxSimplificationError = 1.8f; // eliminates most jagged edges (tiny polygons) config.detailSampleDist = config.cs * 64; config.detailSampleMaxError = config.ch * 2; // this sets the dimensions of the heightfield - should maybe happen before border padding rcCalcGridSize(config.bmin, config.bmax, config.cs, &config.width, &config.height); // allocate subregions : tiles Tile* tiles = new Tile[TILES_PER_MAP * TILES_PER_MAP]; // Initialize per tile config. rcConfig tileCfg = config; tileCfg.width = config.tileSize + config.borderSize*2; tileCfg.height = config.tileSize + config.borderSize*2; // merge per tile poly and detail meshes rcPolyMesh** pmmerge = new rcPolyMesh*[TILES_PER_MAP * TILES_PER_MAP]; rcPolyMeshDetail** dmmerge = new rcPolyMeshDetail*[TILES_PER_MAP * TILES_PER_MAP]; int nmerge = 0; // build all tiles for (int y = 0; y < TILES_PER_MAP; ++y) { for (int x = 0; x < TILES_PER_MAP; ++x) { Tile& tile = tiles[x + y * TILES_PER_MAP]; // Calculate the per tile bounding box. tileCfg.bmin[0] = config.bmin[0] + float(x*config.tileSize - config.borderSize)*config.cs; tileCfg.bmin[2] = config.bmin[2] + float(y*config.tileSize - config.borderSize)*config.cs; tileCfg.bmax[0] = config.bmin[0] + float((x+1)*config.tileSize + config.borderSize)*config.cs; tileCfg.bmax[2] = config.bmin[2] + float((y+1)*config.tileSize + config.borderSize)*config.cs; // build heightfield tile.solid = rcAllocHeightfield(); if (!tile.solid || !rcCreateHeightfield(m_rcContext, *tile.solid, tileCfg.width, tileCfg.height, tileCfg.bmin, tileCfg.bmax, tileCfg.cs, tileCfg.ch)) { printf("%s Failed building heightfield! \n", tileString.c_str()); continue; } // mark all walkable tiles, both liquids and solids unsigned char* triFlags = new unsigned char[tTriCount]; memset(triFlags, NAV_GROUND, tTriCount*sizeof(unsigned char)); rcClearUnwalkableTriangles(m_rcContext, tileCfg.walkableSlopeAngle, tVerts, tVertCount, tTris, tTriCount, triFlags); rcRasterizeTriangles(m_rcContext, tVerts, tVertCount, tTris, triFlags, tTriCount, *tile.solid, config.walkableClimb); delete[] triFlags; rcFilterLowHangingWalkableObstacles(m_rcContext, config.walkableClimb, *tile.solid); rcFilterLedgeSpans(m_rcContext, tileCfg.walkableHeight, tileCfg.walkableClimb, *tile.solid); rcFilterWalkableLowHeightSpans(m_rcContext, tileCfg.walkableHeight, *tile.solid); rcRasterizeTriangles(m_rcContext, lVerts, lVertCount, lTris, lTriFlags, lTriCount, *tile.solid, config.walkableClimb); // compact heightfield spans tile.chf = rcAllocCompactHeightfield(); if (!tile.chf || !rcBuildCompactHeightfield(m_rcContext, tileCfg.walkableHeight, tileCfg.walkableClimb, *tile.solid, *tile.chf)) { printf("%s Failed compacting heightfield! \n", tileString.c_str()); continue; } // build polymesh intermediates if (!rcErodeWalkableArea(m_rcContext, config.walkableRadius, *tile.chf)) { printf("%s Failed eroding area! \n", tileString.c_str()); continue; } if (!rcBuildDistanceField(m_rcContext, *tile.chf)) { printf("%s Failed building distance field! \n", tileString.c_str()); continue; } if (!rcBuildRegions(m_rcContext, *tile.chf, tileCfg.borderSize, tileCfg.minRegionArea, tileCfg.mergeRegionArea)) { printf("%s Failed building regions! \n", tileString.c_str()); continue; } tile.cset = rcAllocContourSet(); if (!tile.cset || !rcBuildContours(m_rcContext, *tile.chf, tileCfg.maxSimplificationError, tileCfg.maxEdgeLen, *tile.cset)) { printf("%s Failed building contours! \n", tileString.c_str()); continue; } // build polymesh tile.pmesh = rcAllocPolyMesh(); if (!tile.pmesh || !rcBuildPolyMesh(m_rcContext, *tile.cset, tileCfg.maxVertsPerPoly, *tile.pmesh)) { printf("%s Failed building polymesh! \n", tileString.c_str()); continue; } tile.dmesh = rcAllocPolyMeshDetail(); if (!tile.dmesh || !rcBuildPolyMeshDetail(m_rcContext, *tile.pmesh, *tile.chf, tileCfg.detailSampleDist, tileCfg.detailSampleMaxError, *tile.dmesh)) { printf("%s Failed building polymesh detail! \n", tileString.c_str()); continue; } // free those up // we may want to keep them in the future for debug // but right now, we don't have the code to merge them rcFreeHeightField(tile.solid); tile.solid = NULL; rcFreeCompactHeightfield(tile.chf); tile.chf = NULL; rcFreeContourSet(tile.cset); tile.cset = NULL; pmmerge[nmerge] = tile.pmesh; dmmerge[nmerge] = tile.dmesh; nmerge++; } } iv.polyMesh = rcAllocPolyMesh(); if (!iv.polyMesh) { printf("%s alloc iv.polyMesh FAILED!\n", tileString.c_str()); delete[] pmmerge; delete[] dmmerge; delete[] tiles; return; } rcMergePolyMeshes(m_rcContext, pmmerge, nmerge, *iv.polyMesh); iv.polyMeshDetail = rcAllocPolyMeshDetail(); if (!iv.polyMeshDetail) { printf("%s alloc m_dmesh FAILED!\n", tileString.c_str()); delete[] pmmerge; delete[] dmmerge; delete[] tiles; return; } rcMergePolyMeshDetails(m_rcContext, dmmerge, nmerge, *iv.polyMeshDetail); // free things up delete[] pmmerge; delete[] dmmerge; delete[] tiles; // set polygons as walkable // TODO: special flags for DYNAMIC polygons, ie surfaces that can be turned on and off for (int i = 0; i < iv.polyMesh->npolys; ++i) if (iv.polyMesh->areas[i] & RC_WALKABLE_AREA) iv.polyMesh->flags[i] = iv.polyMesh->areas[i]; // setup mesh parameters dtNavMeshCreateParams params; memset(¶ms, 0, sizeof(params)); params.verts = iv.polyMesh->verts; params.vertCount = iv.polyMesh->nverts; params.polys = iv.polyMesh->polys; params.polyAreas = iv.polyMesh->areas; params.polyFlags = iv.polyMesh->flags; params.polyCount = iv.polyMesh->npolys; params.nvp = iv.polyMesh->nvp; params.detailMeshes = iv.polyMeshDetail->meshes; params.detailVerts = iv.polyMeshDetail->verts; params.detailVertsCount = iv.polyMeshDetail->nverts; params.detailTris = iv.polyMeshDetail->tris; params.detailTriCount = iv.polyMeshDetail->ntris; params.offMeshConVerts = meshData.offMeshConnections.getCArray(); params.offMeshConCount = meshData.offMeshConnections.size()/6; params.offMeshConRad = meshData.offMeshConnectionRads.getCArray(); params.offMeshConDir = meshData.offMeshConnectionDirs.getCArray(); params.offMeshConAreas = meshData.offMeshConnectionsAreas.getCArray(); params.offMeshConFlags = meshData.offMeshConnectionsFlags.getCArray(); params.walkableHeight = BASE_UNIT_DIM*config.walkableHeight; // agent height params.walkableRadius = BASE_UNIT_DIM*config.walkableRadius; // agent radius params.walkableClimb = BASE_UNIT_DIM*config.walkableClimb; // keep less that walkableHeight (aka agent height)! params.tileX = (((bmin[0] + bmax[0]) / 2) - navMesh->getParams()->orig[0]) / GRID_SIZE; params.tileY = (((bmin[2] + bmax[2]) / 2) - navMesh->getParams()->orig[2]) / GRID_SIZE; rcVcopy(params.bmin, bmin); rcVcopy(params.bmax, bmax); params.cs = config.cs; params.ch = config.ch; params.tileLayer = 0; params.buildBvTree = true; // will hold final navmesh unsigned char* navData = NULL; int navDataSize = 0; do { // these values are checked within dtCreateNavMeshData - handle them here // so we have a clear error message if (params.nvp > DT_VERTS_PER_POLYGON) { printf("%s Invalid verts-per-polygon value! \n", tileString.c_str()); break; } if (params.vertCount >= 0xffff) { printf("%s Too many vertices! \n", tileString.c_str()); break; } if (!params.vertCount || !params.verts) { // occurs mostly when adjacent tiles have models // loaded but those models don't span into this tile // message is an annoyance //printf("%sNo vertices to build tile! \n", tileString.c_str()); break; } if (!params.polyCount || !params.polys || TILES_PER_MAP*TILES_PER_MAP == params.polyCount) { // we have flat tiles with no actual geometry - don't build those, its useless // keep in mind that we do output those into debug info // drop tiles with only exact count - some tiles may have geometry while having less tiles printf("%s No polygons to build on tile! \n", tileString.c_str()); break; } if (!params.detailMeshes || !params.detailVerts || !params.detailTris) { printf("%s No detail mesh to build tile! \n", tileString.c_str()); break; } printf("%s Building navmesh tile...\n", tileString.c_str()); if (!dtCreateNavMeshData(¶ms, &navData, &navDataSize)) { printf("%s Failed building navmesh tile! \n", tileString.c_str()); break; } dtTileRef tileRef = 0; printf("%s Adding tile to navmesh...\n", tileString.c_str()); // DT_TILE_FREE_DATA tells detour to unallocate memory when the tile // is removed via removeTile() dtStatus dtResult = navMesh->addTile(navData, navDataSize, DT_TILE_FREE_DATA, 0, &tileRef); if (!tileRef || dtResult != DT_SUCCESS) { printf("%s Failed adding tile to navmesh! \n", tileString.c_str()); break; } // file output char fileName[255]; sprintf(fileName, "mmaps/%04u%02i%02i.mmtile", mapID, tileY, tileX); FILE* file = fopen(fileName, "wb"); if (!file) { char message[1024]; sprintf(message, "[Map %04u] Failed to open %s for writing!\n", mapID, fileName); perror(message); navMesh->removeTile(tileRef, NULL, NULL); break; } printf("%s Writing to file...\n", tileString.c_str()); // write header MmapTileHeader header; header.usesLiquids = m_terrainBuilder->usesLiquids(); header.size = uint32(navDataSize); fwrite(&header, sizeof(MmapTileHeader), 1, file); // write data fwrite(navData, sizeof(unsigned char), navDataSize, file); fclose(file); // now that tile is written to disk, we can unload it navMesh->removeTile(tileRef, NULL, NULL); } while (0); if (m_debugOutput) { // restore padding so that the debug visualization is correct for (int i = 0; i < iv.polyMesh->nverts; ++i) { unsigned short* v = &iv.polyMesh->verts[i*3]; v[0] += (unsigned short)config.borderSize; v[2] += (unsigned short)config.borderSize; } iv.generateObjFile(mapID, tileX, tileY, meshData); iv.writeIV(mapID, tileX, tileY); } }
bool OgreDetourTileCache::configure(InputGeom *inputGeom) { m_geom = inputGeom; // Reuse OgreRecast context for tiled navmesh building m_ctx = m_recast->m_ctx; if (!m_geom || m_geom->isEmpty()) { m_recast->m_pLog->logMessage("ERROR: OgreDetourTileCache::configure: No vertices and triangles."); return false; } if (!m_geom->getChunkyMesh()) { m_recast->m_pLog->logMessage("ERROR: OgreDetourTileCache::configure: Input mesh has no chunkyTriMesh built."); return false; } m_tmproc->init(m_geom); // Init cache bounding box const float* bmin = m_geom->getMeshBoundsMin(); const float* bmax = m_geom->getMeshBoundsMax(); // Navmesh generation params. // Use config from recast module m_cfg = m_recast->m_cfg; // Most params are taken from OgreRecast::configure, except for these: m_cfg.tileSize = m_tileSize; m_cfg.borderSize = (int) (m_cfg.walkableRadius + BORDER_PADDING); // Reserve enough padding. m_cfg.width = m_cfg.tileSize + m_cfg.borderSize*2; m_cfg.height = m_cfg.tileSize + m_cfg.borderSize*2; // Set mesh bounds rcVcopy(m_cfg.bmin, bmin); rcVcopy(m_cfg.bmax, bmax); // Also define navmesh bounds in recast component rcVcopy(m_recast->m_cfg.bmin, bmin); rcVcopy(m_recast->m_cfg.bmax, bmax); // Cell size navmesh generation property is copied from OgreRecast config m_cellSize = m_cfg.cs; // Determine grid size (number of tiles) based on bounding box and grid cell size int gw = 0, gh = 0; rcCalcGridSize(bmin, bmax, m_cellSize, &gw, &gh); // Calculates total size of voxel grid const int ts = m_tileSize; const int tw = (gw + ts-1) / ts; // Tile width const int th = (gh + ts-1) / ts; // Tile height m_tw = tw; m_th = th; Ogre::LogManager::getSingletonPtr()->logMessage("Total Voxels: "+Ogre::StringConverter::toString(gw) + " x " + Ogre::StringConverter::toString(gh)); Ogre::LogManager::getSingletonPtr()->logMessage("Tilesize: "+Ogre::StringConverter::toString(m_tileSize)+" Cellsize: "+Ogre::StringConverter::toString(m_cellSize)); Ogre::LogManager::getSingletonPtr()->logMessage("Tiles: "+Ogre::StringConverter::toString(m_tw)+" x "+Ogre::StringConverter::toString(m_th)); // Max tiles and max polys affect how the tile IDs are caculated. // There are 22 bits available for identifying a tile and a polygon. int tileBits = rcMin((int)dtIlog2(dtNextPow2(tw*th*EXPECTED_LAYERS_PER_TILE)), 14); if (tileBits > 14) tileBits = 14; int polyBits = 22 - tileBits; m_maxTiles = 1 << tileBits; m_maxPolysPerTile = 1 << polyBits; Ogre::LogManager::getSingletonPtr()->logMessage("Max Tiles: " + Ogre::StringConverter::toString(m_maxTiles)); Ogre::LogManager::getSingletonPtr()->logMessage("Max Polys: " + Ogre::StringConverter::toString(m_maxPolysPerTile)); // Tile cache params. memset(&m_tcparams, 0, sizeof(m_tcparams)); rcVcopy(m_tcparams.orig, bmin); m_tcparams.width = m_tileSize; m_tcparams.height = m_tileSize; m_tcparams.maxTiles = tw*th*EXPECTED_LAYERS_PER_TILE; m_tcparams.maxObstacles = MAX_OBSTACLES; // Max number of temp obstacles that can be added to or removed from navmesh // Copy the rest of the parameters from OgreRecast config m_tcparams.cs = m_cfg.cs; m_tcparams.ch = m_cfg.ch; m_tcparams.walkableHeight = (float) m_cfg.walkableHeight; m_tcparams.walkableRadius = (float) m_cfg.walkableRadius; m_tcparams.walkableClimb = (float) m_cfg.walkableClimb; m_tcparams.maxSimplificationError = m_cfg.maxSimplificationError; return initTileCache(); }
uint8* TileBuilder::Build(bool dbg, dtNavMeshParams& navMeshParams) { _Geometry = new Geometry(); _Geometry->Transform = true; ADT* adt = new ADT(Utils::GetAdtPath(World, X, Y)); adt->Read(); _Geometry->AddAdt(adt); delete adt; if (_Geometry->Vertices.empty() && _Geometry->Triangles.empty()) return NULL; // again, we load everything - wasteful but who cares for (int ty = Y - 2; ty <= Y + 2; ty++) { for (int tx = X - 2; tx <= X + 2; tx++) { // don't load main tile again if (tx == X && ty == Y) continue; ADT* _adt = new ADT(Utils::GetAdtPath(World, tx, ty)); // If this condition is met, it means that this wdt does not contain the ADT if (!_adt->Data->Stream) { delete _adt; continue; } _adt->Read(); _Geometry->AddAdt(_adt); delete _adt; } } if (dbg) { char buff[100]; sprintf(buff, "mmaps/%s_%02u%02u.obj", World.c_str(), Y, X); FILE* debug = fopen(buff, "wb"); for (uint32 i = 0; i < _Geometry->Vertices.size(); ++i) fprintf(debug, "v %f %f %f\n", _Geometry->Vertices[i].x, _Geometry->Vertices[i].y, _Geometry->Vertices[i].z); for (uint32 i = 0; i < _Geometry->Triangles.size(); ++i) fprintf(debug, "f %i %i %i\n", _Geometry->Triangles[i].V0 + 1, _Geometry->Triangles[i].V1 + 1, _Geometry->Triangles[i].V2 + 1); fclose(debug); } uint32 numVerts = _Geometry->Vertices.size(); uint32 numTris = _Geometry->Triangles.size(); float* vertices; int* triangles; uint8* areas; _Geometry->GetRawData(vertices, triangles, areas); _Geometry->Vertices.clear(); _Geometry->Triangles.clear(); rcVcopy(Config.bmin, cBuilder->bmin); rcVcopy(Config.bmax, cBuilder->bmax); // this sets the dimensions of the heightfield - should maybe happen before border padding rcCalcGridSize(Config.bmin, Config.bmax, Config.cs, &Config.width, &Config.height); // Initialize per tile config. rcConfig tileCfg = Config; tileCfg.width = Config.tileSize + Config.borderSize * 2; tileCfg.height = Config.tileSize + Config.borderSize * 2; // merge per tile poly and detail meshes rcPolyMesh** pmmerge = new rcPolyMesh*[Constants::TilesPerMap * Constants::TilesPerMap]; rcPolyMeshDetail** dmmerge = new rcPolyMeshDetail*[Constants::TilesPerMap * Constants::TilesPerMap]; int nmerge = 0; for (int y = 0; y < Constants::TilesPerMap; ++y) { for (int x = 0; x < Constants::TilesPerMap; ++x) { // Calculate the per tile bounding box. tileCfg.bmin[0] = Config.bmin[0] + float(x * Config.tileSize - Config.borderSize) * Config.cs; tileCfg.bmin[2] = Config.bmin[2] + float(y * Config.tileSize - Config.borderSize) * Config.cs; tileCfg.bmax[0] = Config.bmin[0] + float((x + 1) * Config.tileSize + Config.borderSize) * Config.cs; tileCfg.bmax[2] = Config.bmin[2] + float((y + 1) * Config.tileSize + Config.borderSize) * Config.cs; rcHeightfield* hf = rcAllocHeightfield(); rcCreateHeightfield(Context, *hf, tileCfg.width, tileCfg.height, tileCfg.bmin, tileCfg.bmax, tileCfg.cs, tileCfg.ch); rcClearUnwalkableTriangles(Context, tileCfg.walkableSlopeAngle, vertices, numVerts, triangles, numTris, areas); rcRasterizeTriangles(Context, vertices, numVerts, triangles, areas, numTris, *hf, Config.walkableClimb); // Once all geometry is rasterized, we do initial pass of filtering to // remove unwanted overhangs caused by the conservative rasterization // as well as filter spans where the character cannot possibly stand. rcFilterLowHangingWalkableObstacles(Context, Config.walkableClimb, *hf); rcFilterLedgeSpans(Context, tileCfg.walkableHeight, tileCfg.walkableClimb, *hf); rcFilterWalkableLowHeightSpans(Context, tileCfg.walkableHeight, *hf); // Compact the heightfield so that it is faster to handle from now on. // This will result in more cache coherent data as well as the neighbours // between walkable cells will be calculated. rcCompactHeightfield* chf = rcAllocCompactHeightfield(); rcBuildCompactHeightfield(Context, tileCfg.walkableHeight, tileCfg.walkableClimb, *hf, *chf); rcFreeHeightField(hf); // Erode the walkable area by agent radius. rcErodeWalkableArea(Context, Config.walkableRadius, *chf); // Prepare for region partitioning, by calculating distance field along the walkable surface. rcBuildDistanceField(Context, *chf); // Partition the walkable surface into simple regions without holes. rcBuildRegions(Context, *chf, tileCfg.borderSize, tileCfg.minRegionArea, tileCfg.mergeRegionArea); // Create contours. rcContourSet* cset = rcAllocContourSet(); rcBuildContours(Context, *chf, tileCfg.maxSimplificationError, tileCfg.maxEdgeLen, *cset); // Build polygon navmesh from the contours. rcPolyMesh* pmesh = rcAllocPolyMesh(); rcBuildPolyMesh(Context, *cset, tileCfg.maxVertsPerPoly, *pmesh); // Build detail mesh. rcPolyMeshDetail* dmesh = rcAllocPolyMeshDetail(); rcBuildPolyMeshDetail(Context, *pmesh, *chf, tileCfg.detailSampleDist, tileCfg.detailSampleMaxError, *dmesh); // Free memory rcFreeCompactHeightfield(chf); rcFreeContourSet(cset); pmmerge[nmerge] = pmesh; dmmerge[nmerge] = dmesh; ++nmerge; } } rcPolyMesh* pmesh = rcAllocPolyMesh(); rcMergePolyMeshes(Context, pmmerge, nmerge, *pmesh); rcPolyMeshDetail* dmesh = rcAllocPolyMeshDetail(); rcMergePolyMeshDetails(Context, dmmerge, nmerge, *dmesh); delete[] pmmerge; delete[] dmmerge; printf("[%02i,%02i] Meshes merged!\n", X, Y); // Remove padding from the polymesh data. (Remove this odditity) for (int i = 0; i < pmesh->nverts; ++i) { unsigned short* v = &pmesh->verts[i * 3]; v[0] -= (unsigned short)Config.borderSize; v[2] -= (unsigned short)Config.borderSize; } // Set flags according to area types (e.g. Swim for Water) for (int i = 0; i < pmesh->npolys; i++) { if (pmesh->areas[i] == Constants::POLY_AREA_ROAD || pmesh->areas[i] == Constants::POLY_AREA_TERRAIN) pmesh->flags[i] = Constants::POLY_FLAG_WALK; else if (pmesh->areas[i] == Constants::POLY_AREA_WATER) pmesh->flags[i] = Constants::POLY_FLAG_SWIM; } dtNavMeshCreateParams params; memset(¶ms, 0, sizeof(params)); // PolyMesh data params.verts = pmesh->verts; params.vertCount = pmesh->nverts; params.polys = pmesh->polys; params.polyAreas = pmesh->areas; params.polyFlags = pmesh->flags; params.polyCount = pmesh->npolys; params.nvp = pmesh->nvp; // PolyMeshDetail data params.detailMeshes = dmesh->meshes; params.detailVerts = dmesh->verts; params.detailVertsCount = dmesh->nverts; params.detailTris = dmesh->tris; params.detailTriCount = dmesh->ntris; rcVcopy(params.bmin, pmesh->bmin); rcVcopy(params.bmax, pmesh->bmax); // General settings params.ch = Config.ch; params.cs = Config.cs; params.walkableClimb = Constants::BaseUnitDim * Config.walkableClimb; params.walkableHeight = Constants::BaseUnitDim * Config.walkableHeight; params.walkableRadius = Constants::BaseUnitDim * Config.walkableRadius; params.tileX = (((cBuilder->bmin[0] + cBuilder->bmax[0]) / 2) - navMeshParams.orig[0]) / Constants::TileSize; params.tileY = (((cBuilder->bmin[2] + cBuilder->bmax[2]) / 2) - navMeshParams.orig[2]) / Constants::TileSize; rcVcopy(params.bmin, cBuilder->bmin); rcVcopy(params.bmax, cBuilder->bmax); // Offmesh-connection settings params.offMeshConCount = 0; // none for now params.tileSize = Constants::VertexPerMap; if (!params.polyCount || !params.polys || Constants::TilesPerMap * Constants::TilesPerMap == params.polyCount) { // we have flat tiles with no actual geometry - don't build those, its useless // keep in mind that we do output those into debug info // drop tiles with only exact count - some tiles may have geometry while having less tiles printf("[%02i,%02i] No polygons to build on tile, skipping.\n", X, Y); rcFreePolyMesh(pmesh); rcFreePolyMeshDetail(dmesh); delete areas; delete triangles; delete vertices; return NULL; } int navDataSize; uint8* navData; printf("[%02i,%02i] Creating the navmesh with %i vertices, %i polys, %i triangles!\n", X, Y, pmesh->nverts, pmesh->npolys, dmesh->ntris); bool result = dtCreateNavMeshData(¶ms, &navData, &navDataSize); // Free some memory rcFreePolyMesh(pmesh); rcFreePolyMeshDetail(dmesh); delete areas; delete triangles; delete vertices; if (result) { printf("[%02i,%02i] NavMesh created, size %i!\n", X, Y, navDataSize); DataSize = navDataSize; return navData; } return NULL; }
bool RecastInterface::buildNavMesh(InputGeometry* inputGeom) { //Step 1 : Initialize build configuration //Start the timers. #ifdef _DEBUG std::cout << "NavMesh build started." << std::endl; unsigned long start = Ogre::Root::getSingleton().getTimer()->getMilliseconds(); unsigned long end = 0; #endif //Step 2 : Rasterize input polygon soup //InputGeometry* input = inputGeom; WTF? Is this necessary? rcVcopy(_config.bmin, inputGeom->getMeshBoundsMin()); rcVcopy(_config.bmax, inputGeom->getMeshBoundsMax()); rcCalcGridSize(_config.bmin,_config.bmax,_config.cs,&_config.width,&_config.height); int numVerts = inputGeom->getVertexCount(); int numTris = inputGeom->getTriangleCount(); #ifdef _DEBUG Ogre::Vector3 min,max; Utility::floatPtr_toVector3(inputGeom->getMeshBoundsMin(),min); Utility::floatPtr_toVector3(inputGeom->getMeshBoundsMax(),max); std::cout << "Bounds: min=" << min << " max=" << max << std::endl; std::cout << "Building navmesh" << std::endl; std::cout << " - " << _config.width << " x " << _config.height << std::endl; std::cout << " - " << numVerts / 1000.0f << "K vertices, "; std::cout << numTris / 1000.0f << "K triangles" << std::endl; //_printConfig(); #endif _solid = rcAllocHeightfield(); if(!_solid) { std::cout << "Error! Out of memory needed for '_solid'." << std::endl; return false; } if(!rcCreateHeightfield(_context,*_solid, _config.width,_config.height, _config.bmin,_config.bmax, _config.cs,_config.ch)) { std::cout << "Error! Couldn't create heightfield, try higher cellSize and cellHeight values." << std::endl; return false; } //holds triangle area types _triangleAreas = new unsigned char[numTris]; if(!_triangleAreas) { std::cout << "Error! Out of memory '_triangleAreas'([" << numTris << "])" << std::endl; return false; } //find triangles that are walkable in slope and rasterize them memset(_triangleAreas,0,numTris * sizeof(unsigned char)); rcMarkWalkableTriangles(_context,_config.walkableSlopeAngle, inputGeom->getVertices(),inputGeom->getVertexCount(), inputGeom->getTriangles(),inputGeom->getTriangleCount(), _triangleAreas); rcRasterizeTriangles(_context,inputGeom->getVertices(),inputGeom->getVertexCount(), inputGeom->getTriangles(),_triangleAreas, inputGeom->getTriangleCount(),*_solid,_config.walkableClimb); //I know I put this option in the params, but... if(!_recastParams.getKeepIntermediateResults()) { delete[] _triangleAreas; _triangleAreas = nullptr; } //Step 3 : Filter walkables surfaces //Initial pass of filtering to remove unwanted overhangs caused //by the conservative rasterization. //Also filters spans where the character can't stand. rcFilterLowHangingWalkableObstacles(_context,_config.walkableClimb,*_solid); rcFilterLedgeSpans(_context,_config.walkableHeight,_config.walkableClimb,*_solid); rcFilterWalkableLowHeightSpans(_context,_config.walkableHeight,*_solid); //Step 4 : Partition walkable surface to simple regions //Compact the heightfield so that it is faster to handle from now on. _compactHeightfield = rcAllocCompactHeightfield(); if(!_compactHeightfield) { std::cout << "Error! Out of memory '_compactHeightfield'" << std::endl; return false; } if(!rcBuildCompactHeightfield(_context, _config.walkableHeight,_config.walkableClimb, *_solid,*_compactHeightfield)) { std::cout << "Error! BuildNav - Could not build compact data." << std::endl; return false; } if(!_recastParams.getKeepIntermediateResults()) { rcFreeHeightField(_solid); _solid = nullptr; } //Erode walkable area by agent radius if(!rcErodeWalkableArea(_context,_config.walkableRadius,*_compactHeightfield)) { std::cout << "Error! BuildNav - Could not erode walkable areas." << std::endl; return false; } //Prepare for region partitioning, generate distance field if(!rcBuildDistanceField(_context,*_compactHeightfield)) { std::cout << "Error! BuildNav - Could not build distance field." << std::endl; return false; } //Partition the walkable surface into simple regions w/o holes if(!rcBuildRegions(_context,*_compactHeightfield, _config.borderSize, _config.minRegionArea,_config.mergeRegionArea)) { std::cout << "Error! BuildNav - Could not build regions." << std::endl; return false; } //Step 5 : Trace and simplify region contours. //create contours _contourSet = rcAllocContourSet(); if(!_contourSet) { std::cout << "Error! BuildNav - Out of memory '_contourSet'" << std::endl; return false; } if(!rcBuildContours(_context,*_compactHeightfield,_config.maxSimplificationError,_config.maxEdgeLen,*_contourSet)) { std::cout << "Error! BuildNav - Could not create contours." << std::endl; return false; } if(_contourSet->nconts == 0) { //Check this thread for details on solving these: //http://groups.google.com/group/recastnavigation/browse_thread/thread/a6fbd509859a12c8 std::cout << "Error! BuildNav - RecastNav created no contours!" << std::endl; std::cout << _compactHeightfield->spanCount << std::endl; } //Step 6 : Build polygons mesh from contours //Build polygon navmesh from the contours _polyMesh = rcAllocPolyMesh(); if(!_polyMesh) { std::cout << "Error! Out of memory '_polyMesh'." << std::endl; return false; } if(!rcBuildPolyMesh(_context,*_contourSet,_config.maxVertsPerPoly,*_polyMesh)) { std::cout << "Error! BuildNav - Could not triangulate contours." << std::endl; } //Step 7 : Create detail mesh which allows access to approximate height on each polygon. _detailMesh = rcAllocPolyMeshDetail(); if(!_detailMesh) { std::cout << "Error! Out of memory '_detailMesh'." << std::endl; return false; } if(!rcBuildPolyMeshDetail(_context, *_polyMesh,*_compactHeightfield, _config.detailSampleDist,_config.detailSampleMaxError, *_detailMesh)) { std::cout << "Error! BuildNav - Could not build detail mesh." << std::endl; return false; } if(!_recastParams.getKeepIntermediateResults()) { rcFreeCompactHeightfield(_compactHeightfield); _compactHeightfield = nullptr; rcFreeContourSet(_contourSet); _contourSet = nullptr; } //Recast navmesh is finished! #ifdef _DEBUG end = Ogre::Root::getSingletonPtr()->getTimer()->getMilliseconds(); std::cout << "Navmesh build finished." << std::endl; std::cout << " - Time elapsed:" << end - start << "ms" << std::endl; #endif return true; }
bool NavMesher::Build() { // ******* Only for OBJ Loading **** cleanup(); const char * filepath = "../../media/models/"; if (!m_geom || !m_geom->loadMesh(filepath)) { delete m_geom; m_geom = 0; m_ctx->log(RC_LOG_ERROR, "Geom load log %s:"); } assert(m_geom); if (!m_geom || !m_geom->getMesh()) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Input mesh is not specified."); return false; } if(m_geom->getMesh()->getTriCount() <= 0 || m_geom->getMesh()->getVertCount()<=0) Ogre::Exception(0,Ogre::String("Bad verts or Triangle count. Verts: "+ StringConverter::toString( m_geom->getMesh()->getVertCount()) + "/n" + "Triangles :" +StringConverter::toString(m_geom->getMesh()->getTriCount())),"NavMesher::Build"); //reset timer Ogre::Timer tm; tm.reset(); unsigned long stime = tm.getMicroseconds(); //clear existing Clear(); // ******* Only for OBJ Loading **** const float* bmin = m_geom->getMeshBoundsMin(); const float* bmax = m_geom->getMeshBoundsMax(); const float* verts = m_geom->getMesh()->getVerts(); const int nverts = m_geom->getMesh()->getVertCount(); const int *tris = m_geom->getMesh()->getTris(); const int ntris = m_geom->getMesh()->getTriCount(); if(sizeof(tris) <= 0 || ntris <= 0) { return false; } // // Step 1. Initialize build config. // // Init build configuration from GUI memset(&m_cfg, 0, sizeof(m_cfg)); m_cfg.cs = m_cellSize; m_cfg.ch = m_cellHeight; m_cfg.walkableSlopeAngle = m_agentMaxSlope; m_cfg.walkableHeight = (int)ceilf(m_agentHeight / m_cfg.ch); m_cfg.walkableClimb = (int)floorf(m_agentMaxClimb / m_cfg.ch); m_cfg.walkableRadius = (int)ceilf(m_agentRadius / m_cfg.cs); m_cfg.maxEdgeLen = (int)(m_edgeMaxLen / m_cellSize); m_cfg.maxSimplificationError = m_edgeMaxError; m_cfg.minRegionArea = (int)rcSqr(m_regionMinSize); // Note: area = size*size m_cfg.mergeRegionArea = (int)rcSqr(m_regionMergeSize); // Note: area = size*size m_cfg.maxVertsPerPoly = (int)m_vertsPerPoly; m_cfg.detailSampleDist = m_detailSampleDist < 0.9f ? 0 : m_cellSize * m_detailSampleDist; m_cfg.detailSampleMaxError = m_cellHeight * m_detailSampleMaxError; // Set the area where the navigation will be build. // Here the bounds of the input mesh are used, but the // area could be specified by an user defined box, etc. rcVcopy(m_cfg.bmin, bmin); rcVcopy(m_cfg.bmax, bmax); rcCalcGridSize(m_cfg.bmin, m_cfg.bmax, m_cfg.cs, &m_cfg.width, &m_cfg.height); // Reset build times gathering. m_ctx->resetTimers(); // Start the build process. m_ctx->startTimer(RC_TIMER_TOTAL); m_ctx->log(RC_LOG_PROGRESS, "Building navigation:"); m_ctx->log(RC_LOG_PROGRESS, " - %d x %d cells", m_cfg.width, m_cfg.height); m_ctx->log(RC_LOG_PROGRESS, " - %.1fK verts, %.1fK tris", nverts/1000.0f, ntris/1000.0f); // // Step 2. Rasterize input polygon soup. // // Allocate voxel heightfield where we rasterize our input data to. m_solid = rcAllocHeightfield(); if (!m_solid) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'solid'."); return false; } if (!rcCreateHeightfield(m_ctx, *m_solid, m_cfg.width, m_cfg.height, m_cfg.bmin, m_cfg.bmax, m_cfg.cs, m_cfg.ch)) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not create solid heightfield."); return false; } // Allocate array that can hold triangle area types. // If you have multiple meshes you need to process, allocate // and array which can hold the max number of triangles you need to process. m_triareas = new unsigned char[ntris]; if (!m_triareas) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'm_triareas' (%d).", ntris); return false; } // Find triangles which are walkable based on their slope and rasterize them. // If your input data is multiple meshes, you can transform them here, calculate // the are type for each of the meshes and rasterize them. memset(m_triareas, 0, ntris*sizeof(unsigned char)); rcMarkWalkableTriangles(m_ctx, m_cfg.walkableSlopeAngle, verts, nverts, tris, ntris, m_triareas); rcRasterizeTriangles(m_ctx, verts, nverts, tris, m_triareas, ntris, *m_solid, m_cfg.walkableClimb); if (!m_keepInterResults) { delete [] m_triareas; m_triareas = 0; } // // Step 3. Filter walkables surfaces. // // Once all geoemtry is rasterized, we do initial pass of filtering to // remove unwanted overhangs caused by the conservative rasterization // as well as filter spans where the character cannot possibly stand. rcFilterLowHangingWalkableObstacles(m_ctx, m_cfg.walkableClimb, *m_solid); rcFilterLedgeSpans(m_ctx, m_cfg.walkableHeight, m_cfg.walkableClimb, *m_solid); rcFilterWalkableLowHeightSpans(m_ctx, m_cfg.walkableHeight, *m_solid); // // Step 4. Partition walkable surface to simple regions. // // Compact the heightfield so that it is faster to handle from now on. // This will result more cache coherent data as well as the neighbours // between walkable cells will be calculated. m_chf = rcAllocCompactHeightfield(); if (!m_chf) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'chf'."); return false; } if (!rcBuildCompactHeightfield(m_ctx, m_cfg.walkableHeight, m_cfg.walkableClimb, *m_solid, *m_chf)) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build compact data."); return false; } if (!m_keepInterResults) { rcFreeHeightField(m_solid); m_solid = 0; } // Erode the walkable area by agent radius. if (!rcErodeWalkableArea(m_ctx, m_cfg.walkableRadius, *m_chf)) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not erode."); return false; } // (Optional) Mark areas. const ConvexVolume* vols = m_geom->getConvexVolumes(); for (int i = 0; i < m_geom->getConvexVolumeCount(); ++i) rcMarkConvexPolyArea(m_ctx, vols[i].verts, vols[i].nverts, vols[i].hmin, vols[i].hmax, (unsigned char)vols[i].area, *m_chf); if (m_monotonePartitioning) { // Partition the walkable surface into simple regions without holes. // Monotone partitioning does not need distancefield. if (!rcBuildRegionsMonotone(m_ctx, *m_chf, 0, m_cfg.minRegionArea, m_cfg.mergeRegionArea)) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build regions."); return false; } } else { // Prepare for region partitioning, by calculating distance field along the walkable surface. if (!rcBuildDistanceField(m_ctx, *m_chf)) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build distance field."); return false; } // Partition the walkable surface into simple regions without holes. if (!rcBuildRegions(m_ctx, *m_chf, 0, m_cfg.minRegionArea, m_cfg.mergeRegionArea)) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build regions."); return false; } } // // Step 5. Trace and simplify region contours. // // Create contours. m_cset = rcAllocContourSet(); if (!m_cset) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'cset'."); return false; } if (!rcBuildContours(m_ctx, *m_chf, m_cfg.maxSimplificationError, m_cfg.maxEdgeLen, *m_cset)) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not create contours."); return false; } // // Step 6. Build polygons mesh from contours. // // Build polygon navmesh from the contours. m_pmesh = rcAllocPolyMesh(); if (!m_pmesh) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'pmesh'."); return false; } if (!rcBuildPolyMesh(m_ctx, *m_cset, m_cfg.maxVertsPerPoly, *m_pmesh)) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not triangulate contours."); return false; } // // Step 7. Create detail mesh which allows to access approximate height on each polygon. // m_dmesh = rcAllocPolyMeshDetail(); if (!m_dmesh) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'pmdtl'."); return false; } if (!rcBuildPolyMeshDetail(m_ctx, *m_pmesh, *m_chf, m_cfg.detailSampleDist, m_cfg.detailSampleMaxError, *m_dmesh)) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build detail mesh."); return false; } if (!m_keepInterResults) { rcFreeCompactHeightfield(m_chf); m_chf = 0; rcFreeContourSet(m_cset); m_cset = 0; } // At this point the navigation mesh data is ready, you can access it from m_pmesh. // See rcDebugDrawPolyMesh or dtCreateNavMeshData as examples how to access the data. // // (Optional) Step 8. Create Detour data from Recast poly mesh. // // The GUI may allow more max points per polygon than Detour can handle. // Only build the detour navmesh if we do not exceed the limit. unsigned char* navData = 0; int navDataSize = 0; // Update poly flags from areas. for (int i = 0; i < m_pmesh->npolys; ++i) { if (m_pmesh->areas[i] == RC_WALKABLE_AREA) m_pmesh->areas[i] = SAMPLE_POLYAREA_GROUND; if (m_pmesh->areas[i] == SAMPLE_POLYAREA_GROUND || m_pmesh->areas[i] == SAMPLE_POLYAREA_GRASS || m_pmesh->areas[i] == SAMPLE_POLYAREA_ROAD) { m_pmesh->flags[i] = SAMPLE_POLYFLAGS_WALK; } else if (m_pmesh->areas[i] == SAMPLE_POLYAREA_WATER) { m_pmesh->flags[i] = SAMPLE_POLYFLAGS_SWIM; } else if (m_pmesh->areas[i] == SAMPLE_POLYAREA_DOOR) { m_pmesh->flags[i] = SAMPLE_POLYFLAGS_WALK | SAMPLE_POLYFLAGS_DOOR; } } memset(&m_params, 0, sizeof(m_params)); m_params.verts = m_pmesh->verts; m_params.vertCount = m_pmesh->nverts; m_params.polys = m_pmesh->polys; m_params.polyAreas = m_pmesh->areas; m_params.polyFlags = m_pmesh->flags; m_params.polyCount = m_pmesh->npolys; m_params.nvp = m_pmesh->nvp; m_params.detailMeshes = m_dmesh->meshes; m_params.detailVerts = m_dmesh->verts; m_params.detailVertsCount = m_dmesh->nverts; m_params.detailTris = m_dmesh->tris; m_params.detailTriCount = m_dmesh->ntris; m_params.walkableHeight = m_agentHeight; m_params.walkableRadius = m_agentRadius; m_params.walkableClimb = m_agentMaxClimb; rcVcopy(m_params.bmin, m_pmesh->bmin); rcVcopy(m_params.bmax, m_pmesh->bmax); m_params.cs = m_cfg.cs; m_params.ch = m_cfg.ch; m_params.buildBvTree = true; if (!dtCreateNavMeshData(&m_params, &navData, &navDataSize)) { m_ctx->log(RC_LOG_ERROR, "Could not build Detour navmesh."); return false; } m_navMesh = dtAllocNavMesh(); if (!m_navMesh) { delete [] navData; m_ctx->log(RC_LOG_ERROR, "Could not create Detour navmesh"); return false; } m_navQuery = dtAllocNavMeshQuery(); dtStatus status = m_navQuery->init(m_navMesh, 2048); if (dtStatusFailed(status)) { m_ctx->log(RC_LOG_ERROR, "Could not init Detour navmesh query"); return false; } if (!m_navMesh->init(navData, navDataSize, true)) { delete [] navData; m_ctx->log(RC_LOG_ERROR, "Could not init Detour navmesh"); return false; } //take time stime = tm.getMicroseconds() - stime; DrawDebug(); return true; }
void Sample_TileMesh::handleRender() { if (!m_geom || !m_geom->getMesh()) return; const float texScale = 1.0f / (m_cellSize * 10.0f); // Draw mesh if (m_drawMode != DRAWMODE_NAVMESH_TRANS) { // Draw mesh duDebugDrawTriMeshSlope(&m_dd, m_geom->getMesh()->getVerts(), m_geom->getMesh()->getVertCount(), m_geom->getMesh()->getTris(), m_geom->getMesh()->getNormals(), m_geom->getMesh()->getTriCount(), m_agentMaxSlope, texScale); m_geom->drawOffMeshConnections(&m_dd); } glDepthMask(GL_FALSE); // Draw bounds const float* bmin = m_geom->getNavMeshBoundsMin(); const float* bmax = m_geom->getNavMeshBoundsMax(); duDebugDrawBoxWire(&m_dd, bmin[0],bmin[1],bmin[2], bmax[0],bmax[1],bmax[2], duRGBA(255,255,255,128), 1.0f); // Tiling grid. int gw = 0, gh = 0; rcCalcGridSize(bmin, bmax, m_cellSize, &gw, &gh); const int tw = (gw + (int)m_tileSize-1) / (int)m_tileSize; const int th = (gh + (int)m_tileSize-1) / (int)m_tileSize; const float s = m_tileSize*m_cellSize; duDebugDrawGridXZ(&m_dd, bmin[0],bmin[1],bmin[2], tw,th, s, duRGBA(0,0,0,64), 1.0f); // Draw active tile duDebugDrawBoxWire(&m_dd, m_lastBuiltTileBmin[0],m_lastBuiltTileBmin[1],m_lastBuiltTileBmin[2], m_lastBuiltTileBmax[0],m_lastBuiltTileBmax[1],m_lastBuiltTileBmax[2], m_tileCol, 1.0f); if (m_navMesh && m_navQuery && (m_drawMode == DRAWMODE_NAVMESH || m_drawMode == DRAWMODE_NAVMESH_TRANS || m_drawMode == DRAWMODE_NAVMESH_BVTREE || m_drawMode == DRAWMODE_NAVMESH_NODES || m_drawMode == DRAWMODE_NAVMESH_PORTALS || m_drawMode == DRAWMODE_NAVMESH_INVIS)) { if (m_drawMode != DRAWMODE_NAVMESH_INVIS) duDebugDrawNavMeshWithClosedList(&m_dd, *m_navMesh, *m_navQuery, m_navMeshDrawFlags); if (m_drawMode == DRAWMODE_NAVMESH_BVTREE) duDebugDrawNavMeshBVTree(&m_dd, *m_navMesh); if (m_drawMode == DRAWMODE_NAVMESH_PORTALS) duDebugDrawNavMeshPortals(&m_dd, *m_navMesh); if (m_drawMode == DRAWMODE_NAVMESH_NODES) duDebugDrawNavMeshNodes(&m_dd, *m_navQuery); duDebugDrawNavMeshPolysWithFlags(&m_dd, *m_navMesh, SAMPLE_POLYFLAGS_DISABLED, duRGBA(0,0,0,128)); } glDepthMask(GL_TRUE); if (m_chf && m_drawMode == DRAWMODE_COMPACT) duDebugDrawCompactHeightfieldSolid(&m_dd, *m_chf); if (m_chf && m_drawMode == DRAWMODE_COMPACT_DISTANCE) duDebugDrawCompactHeightfieldDistance(&m_dd, *m_chf); if (m_chf && m_drawMode == DRAWMODE_COMPACT_REGIONS) duDebugDrawCompactHeightfieldRegions(&m_dd, *m_chf); if (m_solid && m_drawMode == DRAWMODE_VOXELS) { glEnable(GL_FOG); duDebugDrawHeightfieldSolid(&m_dd, *m_solid); glDisable(GL_FOG); } if (m_solid && m_drawMode == DRAWMODE_VOXELS_WALKABLE) { glEnable(GL_FOG); duDebugDrawHeightfieldWalkable(&m_dd, *m_solid); glDisable(GL_FOG); } if (m_cset && m_drawMode == DRAWMODE_RAW_CONTOURS) { glDepthMask(GL_FALSE); duDebugDrawRawContours(&m_dd, *m_cset); glDepthMask(GL_TRUE); } if (m_cset && m_drawMode == DRAWMODE_BOTH_CONTOURS) { glDepthMask(GL_FALSE); duDebugDrawRawContours(&m_dd, *m_cset, 0.5f); duDebugDrawContours(&m_dd, *m_cset); glDepthMask(GL_TRUE); } if (m_cset && m_drawMode == DRAWMODE_CONTOURS) { glDepthMask(GL_FALSE); duDebugDrawContours(&m_dd, *m_cset); glDepthMask(GL_TRUE); } if (m_chf && m_cset && m_drawMode == DRAWMODE_REGION_CONNECTIONS) { duDebugDrawCompactHeightfieldRegions(&m_dd, *m_chf); glDepthMask(GL_FALSE); duDebugDrawRegionConnections(&m_dd, *m_cset); glDepthMask(GL_TRUE); } if (m_pmesh && m_drawMode == DRAWMODE_POLYMESH) { glDepthMask(GL_FALSE); duDebugDrawPolyMesh(&m_dd, *m_pmesh); glDepthMask(GL_TRUE); } if (m_dmesh && m_drawMode == DRAWMODE_POLYMESH_DETAIL) { glDepthMask(GL_FALSE); duDebugDrawPolyMeshDetail(&m_dd, *m_dmesh); glDepthMask(GL_TRUE); } m_geom->drawConvexVolumes(&m_dd); if (m_tool) m_tool->handleRender(); renderToolStates(); glDepthMask(GL_TRUE); }
void recast_calcGridSize(const float *bmin, const float *bmax, float cs, int *w, int *h) { rcCalcGridSize(bmin, bmax, cs, w, h); }
void CMaNGOS_Map::handleSettings() { if (m_MapInfos->IsEmpty()) return; if (m_SelectedTile) { bool tileFound = false; imguiLabel("Tile commands"); std::string bText; if (m_MapInfos->GetTileRef(m_SelectedTile->tx, m_SelectedTile->ty)) { tileFound = true; bText = "Clear selected tile navmesh"; if (imguiButton(bText.c_str())) { setTool(NULL); m_MapInfos->ClearNavMeshOfTile(m_SelectedTile->tx, m_SelectedTile->ty); } } else { bText = "Load selected tile navmesh"; if (imguiButton(bText.c_str())) { if (m_MapInfos->LoadNavMeshOfTile(m_SelectedTile->tx, m_SelectedTile->ty)) setTool(new NavMeshTesterTool); } bText = "Build navmesh for selected tile"; if (imguiButton(bText.c_str())) { rcConfig cfg; cfg.cs = m_cellSize; cfg.ch = m_cellHeight; cfg.walkableSlopeAngle = m_agentMaxSlope; cfg.walkableHeight = (int)ceilf(m_agentHeight);// (int)ceilf(m_agentHeight / m_cfg.ch); cfg.walkableClimb = (int)floorf(m_agentMaxClimb);// (int)floorf(m_agentMaxClimb / m_cfg.ch); cfg.walkableRadius = (int)ceilf(m_agentRadius);// (int)ceilf(m_agentRadius / m_cfg.cs); cfg.maxEdgeLen = (int)m_edgeMaxLen;// (int)(m_edgeMaxLen / m_cellSize); cfg.maxSimplificationError = m_edgeMaxError; cfg.minRegionArea = (int)rcSqr(m_regionMinSize); // Note: area = size*size cfg.mergeRegionArea = (int)rcSqr(m_regionMergeSize); // Note: area = size*size cfg.maxVertsPerPoly = (int)m_vertsPerPoly; cfg.tileSize = (int)m_tileSize; cfg.borderSize = cfg.walkableRadius + 3; // Reserve enough padding. cfg.detailSampleDist = m_cellSize * m_detailSampleDist; cfg.detailSampleMaxError = m_cellHeight * m_detailSampleMaxError; m_MapInfos->BuildNavMeshOfTile(m_SelectedTile->tx, m_SelectedTile->ty, &cfg, m_partitionType); setTool(new NavMeshTesterTool); } } if (tileFound) return; char tmpStr[50]; imguiLabel("Rasterization"); snprintf(tmpStr, sizeof(tmpStr), "Cell Size = %4.3f", m_cellSize); imguiValue(tmpStr); snprintf(tmpStr, sizeof(tmpStr), "Cell Height = %4.3f", m_cellHeight); imguiValue(tmpStr); if (!m_MapInfos->GetGeomsMap()->empty()) { int gw = 0, gh = 0; rcCalcGridSize(m_MapInfos->BMin(), m_MapInfos->BMax(), m_cellSize, &gw, &gh); char text[64]; snprintf(text, 64, "Voxels %d x %d", gw, gh); imguiValue(text); } imguiSeparator(); imguiLabel("Agent"); imguiSlider("Height", &m_agentHeight, 0.1f, 5.0f, 0.1f); imguiSlider("Radius", &m_agentRadius, 0.0f, 5.0f, 0.1f); imguiSlider("Max Climb", &m_agentMaxClimb, 0.1f, 5.0f, 0.1f); imguiSlider("Max Slope", &m_agentMaxSlope, 0.0f, 90.0f, 1.0f); imguiSeparator(); imguiLabel("Region"); imguiSlider("Min Region Size", &m_regionMinSize, 0.0f, 150.0f, 1.0f); imguiSlider("Merged Region Size", &m_regionMergeSize, 0.0f, 150.0f, 1.0f); imguiSeparator(); imguiLabel("Partitioning"); if (imguiCheck("Watershed", m_partitionType == SAMPLE_PARTITION_WATERSHED)) m_partitionType = SAMPLE_PARTITION_WATERSHED; if (imguiCheck("Monotone", m_partitionType == SAMPLE_PARTITION_MONOTONE)) m_partitionType = SAMPLE_PARTITION_MONOTONE; if (imguiCheck("Layers", m_partitionType == SAMPLE_PARTITION_LAYERS)) m_partitionType = SAMPLE_PARTITION_LAYERS; imguiSeparator(); imguiLabel("Polygonization"); imguiSlider("Max Edge Length", &m_edgeMaxLen, 0.0f, 100.0f, 1.0f); imguiSlider("Max Edge Error", &m_edgeMaxError, 0.1f, 3.0f, 0.1f); imguiSlider("Verts Per Poly", &m_vertsPerPoly, 3.0f, 12.0f, 1.0f); imguiSeparator(); imguiLabel("Detail Mesh"); imguiSlider("Sample Distance", &m_detailSampleDist, 0.0f, 100.0f, 1.0f); imguiSlider("Max Sample Error", &m_detailSampleMaxError, 0.0f, 10.0f, 1.0f); imguiSeparator(); char text[64]; int gw = 0, gh = 0; rcCalcGridSize(m_MapInfos->BMin(), m_MapInfos->BMax(), m_cellSize, &gw, &gh); const int ts = (int)m_tileSize; const int tw = (gw + ts - 1) / ts; const int th = (gh + ts - 1) / ts; snprintf(text, 64, "Tiles %d x %d", tw, th); imguiValue(text); imguiSeparator(); } }
bool OgreRecast::NavMeshBuild(InputGeom* input) { // TODO: clean up unused variables m_pLog->logMessage("NavMeshBuild Start"); // // Step 1. Initialize build config. // // Reset build times gathering. m_ctx->resetTimers(); // Start the build process. m_ctx->startTimer(RC_TIMER_TOTAL); // // Step 2. Rasterize input polygon soup. // InputGeom *inputGeom = input; rcVcopy(m_cfg.bmin, inputGeom->getMeshBoundsMin()); rcVcopy(m_cfg.bmax, inputGeom->getMeshBoundsMax()); rcCalcGridSize(m_cfg.bmin, m_cfg.bmax, m_cfg.cs, &m_cfg.width, &m_cfg.height); int nverts = inputGeom->getVertCount(); int ntris = inputGeom->getTriCount(); Ogre::Vector3 min; FloatAToOgreVect3(inputGeom->getMeshBoundsMin(), min); Ogre::Vector3 max; FloatAToOgreVect3(inputGeom->getMeshBoundsMax(), max); //Ogre::LogManager::getSingletonPtr()->logMessage("Bounds: "+Ogre::StringConverter::toString(min) + " "+ Ogre::StringConverter::toString(max)); m_pLog->logMessage("Building navigation:"); m_pLog->logMessage(" - " + Ogre::StringConverter::toString(m_cfg.width) + " x " + Ogre::StringConverter::toString(m_cfg.height) + " cells"); m_pLog->logMessage(" - " + Ogre::StringConverter::toString(nverts/1000.0f) + " K verts, " + Ogre::StringConverter::toString(ntris/1000.0f) + " K tris"); // Allocate voxel heightfield where we rasterize our input data to. m_solid = rcAllocHeightfield(); if (!m_solid) { m_pLog->logMessage("ERROR: buildNavigation: Out of memory 'solid'."); return false; } if (!rcCreateHeightfield(m_ctx, *m_solid, m_cfg.width, m_cfg.height, m_cfg.bmin, m_cfg.bmax, m_cfg.cs, m_cfg.ch)) { m_pLog->logMessage("ERROR: buildNavigation: Could not create solid heightfield. Possibly it requires too much memory, try setting a higher cellSize and cellHeight value."); return false; } // Allocate array that can hold triangle area types. // If you have multiple meshes you need to process, allocate // an array which can hold the max number of triangles you need to process. m_triareas = new unsigned char[ntris]; if (!m_triareas) { m_pLog->logMessage("ERROR: buildNavigation: Out of memory 'm_triareas' ("+Ogre::StringConverter::toString(ntris)+")."); return false; } // Find triangles which are walkable based on their slope and rasterize them. // If your input data is multiple meshes, you can transform them here, calculate // the are type for each of the meshes and rasterize them. memset(m_triareas, 0, ntris*sizeof(unsigned char)); rcMarkWalkableTriangles(m_ctx, m_cfg.walkableSlopeAngle, inputGeom->getVerts(), inputGeom->getVertCount(), inputGeom->getTris(), inputGeom->getTriCount(), m_triareas); rcRasterizeTriangles(m_ctx, inputGeom->getVerts(), inputGeom->getVertCount(), inputGeom->getTris(), m_triareas, inputGeom->getTriCount(), *m_solid, m_cfg.walkableClimb); if (!m_keepInterResults) { delete [] m_triareas; m_triareas = 0; } // // Step 3. Filter walkables surfaces. // // Once all geoemtry is rasterized, we do initial pass of filtering to // remove unwanted overhangs caused by the conservative rasterization // as well as filter spans where the character cannot possibly stand. rcFilterLowHangingWalkableObstacles(m_ctx, m_cfg.walkableClimb, *m_solid); rcFilterLedgeSpans(m_ctx, m_cfg.walkableHeight, m_cfg.walkableClimb, *m_solid); rcFilterWalkableLowHeightSpans(m_ctx, m_cfg.walkableHeight, *m_solid); // // Step 4. Partition walkable surface to simple regions. // // Compact the heightfield so that it is faster to handle from now on. // This will result more cache coherent data as well as the neighbours // between walkable cells will be calculated. m_chf = rcAllocCompactHeightfield(); if (!m_chf) { m_pLog->logMessage("ERROR: buildNavigation: Out of memory 'chf'."); return false; } if (!rcBuildCompactHeightfield(m_ctx, m_cfg.walkableHeight, m_cfg.walkableClimb, *m_solid, *m_chf)) { m_pLog->logMessage("ERROR: buildNavigation: Could not build compact data."); return false; } if (!m_keepInterResults) { rcFreeHeightField(m_solid); m_solid = 0; } // Erode the walkable area by agent radius. if (!rcErodeWalkableArea(m_ctx, m_cfg.walkableRadius, *m_chf)) { m_pLog->logMessage("ERROR: buildNavigation: Could not erode walkable areas."); return false; } // TODO implement // (Optional) Mark areas. //const ConvexVolume* vols = m_geom->getConvexVolumes(); //for (int i = 0; i < m_geom->getConvexVolumeCount(); ++i) // rcMarkConvexPolyArea(m_ctx, vols[i].verts, vols[i].nverts, vols[i].hmin, vols[i].hmax, (unsigned char)vols[i].area, *m_chf); // Prepare for region partitioning, by calculating distance field along the walkable surface. if (!rcBuildDistanceField(m_ctx, *m_chf)) { m_pLog->logMessage("ERROR: buildNavigation: Could not build distance field."); return false; } // Partition the walkable surface into simple regions without holes. if (!rcBuildRegions(m_ctx, *m_chf, m_cfg.borderSize, m_cfg.minRegionArea, m_cfg.mergeRegionArea)) { m_pLog->logMessage("ERROR: buildNavigation: Could not build regions."); return false; } // // Step 5. Trace and simplify region contours. // // Create contours. m_cset = rcAllocContourSet(); if (!m_cset) { m_pLog->logMessage("ERROR: buildNavigation: Out of memory 'cset'."); return false; } if (!rcBuildContours(m_ctx, *m_chf, m_cfg.maxSimplificationError, m_cfg.maxEdgeLen, *m_cset)) { m_pLog->logMessage("ERROR: buildNavigation: Could not create contours."); return false; } if (m_cset->nconts == 0) { // In case of errors see: http://groups.google.com/group/recastnavigation/browse_thread/thread/a6fbd509859a12c8 // You should probably tweak the parameters m_pLog->logMessage("ERROR: No contours created (Recast)!"); } // // Step 6. Build polygons mesh from contours. // // Build polygon navmesh from the contours. m_pmesh = rcAllocPolyMesh(); if (!m_pmesh) { m_pLog->logMessage("ERROR: buildNavigation: Out of memory 'pmesh'."); return false; } if (!rcBuildPolyMesh(m_ctx, *m_cset, m_cfg.maxVertsPerPoly, *m_pmesh)) { // Try modifying the parameters. I experienced this error when setting agentMaxClimb too high. m_pLog->logMessage("ERROR: buildNavigation: Could not triangulate contours."); return false; } // // Step 7. Create detail mesh which allows to access approximate height on each polygon. // m_dmesh = rcAllocPolyMeshDetail(); if (!m_dmesh) { m_pLog->logMessage("ERROR: buildNavigation: Out of memory 'pmdtl'."); return false; } if (!rcBuildPolyMeshDetail(m_ctx, *m_pmesh, *m_chf, m_cfg.detailSampleDist, m_cfg.detailSampleMaxError, *m_dmesh)) { m_pLog->logMessage("ERROR: buildNavigation: Could not build detail mesh."); return false; } if (!m_keepInterResults) { rcFreeCompactHeightfield(m_chf); m_chf = 0; rcFreeContourSet(m_cset); m_cset = 0; } // At this point the navigation mesh data is ready, you can access it from m_pmesh. // See duDebugDrawPolyMesh or dtCreateNavMeshData as examples how to access the data. // // (Optional) Step 8. Create Detour data from Recast poly mesh. // // The GUI may allow more max points per polygon than Detour can handle. // Only build the detour navmesh if we do not exceed the limit. if (m_cfg.maxVertsPerPoly <= DT_VERTS_PER_POLYGON) { m_pLog->logMessage("Detour 1000"); unsigned char* navData = 0; int navDataSize = 0; // Update poly flags from areas. for (int i = 0; i < m_pmesh->npolys; ++i) { if (m_pmesh->areas[i] == RC_WALKABLE_AREA) { m_pmesh->areas[i] = SAMPLE_POLYAREA_GROUND; m_pmesh->flags[i] = SAMPLE_POLYFLAGS_WALK; } } // Set navmesh params dtNavMeshCreateParams params; memset(¶ms, 0, sizeof(params)); params.verts = m_pmesh->verts; params.vertCount = m_pmesh->nverts; params.polys = m_pmesh->polys; params.polyAreas = m_pmesh->areas; params.polyFlags = m_pmesh->flags; params.polyCount = m_pmesh->npolys; params.nvp = m_pmesh->nvp; params.detailMeshes = m_dmesh->meshes; params.detailVerts = m_dmesh->verts; params.detailVertsCount = m_dmesh->nverts; params.detailTris = m_dmesh->tris; params.detailTriCount = m_dmesh->ntris; // no off mesh connections yet m_offMeshConCount=0 ; params.offMeshConVerts = m_offMeshConVerts ; params.offMeshConRad = m_offMeshConRads ; params.offMeshConDir = m_offMeshConDirs ; params.offMeshConAreas = m_offMeshConAreas ; params.offMeshConFlags = m_offMeshConFlags ; params.offMeshConUserID = m_offMeshConId ; params.offMeshConCount = m_offMeshConCount ; params.walkableHeight = m_agentHeight; params.walkableRadius = m_agentRadius; params.walkableClimb = m_agentMaxClimb; rcVcopy(params.bmin, m_pmesh->bmin); rcVcopy(params.bmax, m_pmesh->bmax); params.cs = m_cfg.cs; params.ch = m_cfg.ch; m_pLog->logMessage("Detour 2000"); if (!dtCreateNavMeshData(¶ms, &navData, &navDataSize)) { m_pLog->logMessage("ERROR: Could not build Detour navmesh."); return false; } m_pLog->logMessage("Detour 3000"); m_navMesh = dtAllocNavMesh(); if (!m_navMesh) { dtFree(navData); m_pLog->logMessage("ERROR: Could not create Detour navmesh"); return false; } m_pLog->logMessage("Detour 4000"); dtStatus status; status = m_navMesh->init(navData, navDataSize, DT_TILE_FREE_DATA); if (dtStatusFailed(status)) { dtFree(navData); m_pLog->logMessage("ERROR: Could not init Detour navmesh"); return false; } m_pLog->logMessage("Detour 5000"); m_navQuery = dtAllocNavMeshQuery(); status = m_navQuery->init(m_navMesh, 2048); m_pLog->logMessage("Detour 5500"); if (dtStatusFailed(status)) { m_pLog->logMessage("ERROR: Could not init Detour navmesh query"); return false; } m_pLog->logMessage("Detour 6000"); } m_ctx->stopTimer(RC_TIMER_TOTAL); /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cleanup stuff we don't need // delete [] rc_verts ; // delete [] rc_tris ; // delete [] rc_trinorms ; //CreateRecastPolyMesh(*m_pmesh) ; // Debug render it m_pLog->logMessage("NavMeshBuild End"); return true; }
bool OgreDetourTileCache::loadAll(Ogre::String filename) { FILE* fp = fopen(filename.data(), "rb"); if (!fp) { Ogre::LogManager::getSingletonPtr()->logMessage("Error: OgreDetourTileCache::loadAll("+filename+"). Could not open file."); return false; } // Read header. TileCacheSetHeader header; fread(&header, sizeof(TileCacheSetHeader), 1, fp); if (header.magic != TILECACHESET_MAGIC) { fclose(fp); Ogre::LogManager::getSingletonPtr()->logMessage("Error: OgreDetourTileCache::loadAll("+filename+"). File does not appear to contain valid tilecache data."); return false; } if (header.version != TILECACHESET_VERSION) { fclose(fp); Ogre::LogManager::getSingletonPtr()->logMessage("Error: OgreDetourTileCache::loadAll("+filename+"). File contains a different version of the tilecache data format ("+Ogre::StringConverter::toString(header.version)+" instead of "+Ogre::StringConverter::toString(TILECACHESET_VERSION)+")."); return false; } m_recast->m_navMesh = dtAllocNavMesh(); if (!m_recast->m_navMesh) { fclose(fp); Ogre::LogManager::getSingletonPtr()->logMessage("Error: OgreDetourTileCache::loadAll("+filename+"). Could not allocate navmesh."); return false; } dtStatus status = m_recast->m_navMesh->init(&header.meshParams); if (dtStatusFailed(status)) { fclose(fp); Ogre::LogManager::getSingletonPtr()->logMessage("Error: OgreDetourTileCache::loadAll("+filename+"). Could not init navmesh."); return false; } m_tileCache = dtAllocTileCache(); if (!m_tileCache) { fclose(fp); Ogre::LogManager::getSingletonPtr()->logMessage("Error: OgreDetourTileCache::loadAll("+filename+"). Could not allocate tilecache."); return false; } status = m_tileCache->init(&header.cacheParams, m_talloc, m_tcomp, m_tmproc); if (dtStatusFailed(status)) { fclose(fp); Ogre::LogManager::getSingletonPtr()->logMessage("Error: OgreDetourTileCache::loadAll("+filename+"). Could not init tilecache."); return false; } memcpy(&m_cfg, &header.recastConfig, sizeof(rcConfig)); // Read tiles. for (int i = 0; i < header.numTiles; ++i) { TileCacheTileHeader tileHeader; fread(&tileHeader, sizeof(tileHeader), 1, fp); if (!tileHeader.tileRef || !tileHeader.dataSize) break; unsigned char* data = (unsigned char*)dtAlloc(tileHeader.dataSize, DT_ALLOC_PERM); if (!data) break; memset(data, 0, tileHeader.dataSize); fread(data, tileHeader.dataSize, 1, fp); dtCompressedTileRef tile = 0; m_tileCache->addTile(data, tileHeader.dataSize, DT_COMPRESSEDTILE_FREE_DATA, &tile); if (tile) m_tileCache->buildNavMeshTile(tile, m_recast->m_navMesh); } fclose(fp); // Init recast navmeshquery with created navmesh (in OgreRecast component) m_recast->m_navQuery = dtAllocNavMeshQuery(); m_recast->m_navQuery->init(m_recast->m_navMesh, 2048); // Config // TODO handle this nicer, also inputGeom is not inited, making some functions crash m_cellSize = m_cfg.cs; m_tileSize = m_cfg.tileSize; // cache bounding box const float* bmin = m_cfg.bmin; const float* bmax = m_cfg.bmax; // Copy loaded config back to recast module memcpy(&m_recast->m_cfg, &m_cfg, sizeof(rcConfig)); m_tileSize = m_cfg.tileSize; m_cellSize = m_cfg.cs; m_tcparams = header.cacheParams; // Determine grid size (number of tiles) based on bounding box and grid cell size int gw = 0, gh = 0; rcCalcGridSize(bmin, bmax, m_cellSize, &gw, &gh); // Calculates total size of voxel grid const int ts = m_tileSize; const int tw = (gw + ts-1) / ts; // Tile width const int th = (gh + ts-1) / ts; // Tile height m_tw = tw; m_th = th; Ogre::LogManager::getSingletonPtr()->logMessage("Total Voxels: "+Ogre::StringConverter::toString(gw) + " x " + Ogre::StringConverter::toString(gh)); Ogre::LogManager::getSingletonPtr()->logMessage("Tilesize: "+Ogre::StringConverter::toString(m_tileSize)+" Cellsize: "+Ogre::StringConverter::toString(m_cellSize)); Ogre::LogManager::getSingletonPtr()->logMessage("Tiles: "+Ogre::StringConverter::toString(m_tw)+" x "+Ogre::StringConverter::toString(m_th)); // Max tiles and max polys affect how the tile IDs are caculated. // There are 22 bits available for identifying a tile and a polygon. int tileBits = rcMin((int)dtIlog2(dtNextPow2(tw*th*EXPECTED_LAYERS_PER_TILE)), 14); if (tileBits > 14) tileBits = 14; int polyBits = 22 - tileBits; m_maxTiles = 1 << tileBits; m_maxPolysPerTile = 1 << polyBits; Ogre::LogManager::getSingletonPtr()->logMessage("Max Tiles: " + Ogre::StringConverter::toString(m_maxTiles)); Ogre::LogManager::getSingletonPtr()->logMessage("Max Polys: " + Ogre::StringConverter::toString(m_maxPolysPerTile)); // End config //// buildInitialNavmesh(); return true; }
void NavMeshCreator::computeNavMesh() { // Reset build times gathering. m_context->resetTimers(); // Start the build process. m_context->startTimer(RC_TIMER_TOTAL); m_context->log(RC_LOG_PROGRESS, "NavMesh computation start"); m_context->log(RC_LOG_PROGRESS, " - %.1fK vertices, %.1fK triangles", m_inputVerticesCount/1000.0f, m_inputTrianglesCount/1000.0f); if (m_success) { //Compute the grid size rcCalcGridSize( m_min, m_max, m_voxelSize, &m_intermediateHeightfieldWidth, &m_intermediateHeightfieldHeight); } m_context->log(RC_LOG_PROGRESS, " - %d x %d = %d voxels", m_intermediateHeightfieldWidth, m_intermediateHeightfieldHeight, m_intermediateHeightfieldHeight * m_intermediateHeightfieldWidth); //Mark the walkable m_inputTriangles rcMarkWalkableTriangles( m_context, m_maximumSlope, m_inputVertices, m_inputVerticesCount, m_inputTriangles, m_inputTrianglesCount, m_intermediateTriangleTags); // Build the heightfield m_success = m_success && (rcCreateHeightfield( m_context, *m_intermediateHeightfield, m_intermediateHeightfieldWidth, m_intermediateHeightfieldHeight, m_min, m_max, m_voxelSize, m_voxelHeight)); // rasterize m_inputTriangles. rcRasterizeTriangles( m_context, m_inputVertices, m_inputVerticesCount, m_inputTriangles, m_intermediateTriangleTags, m_inputTrianglesCount, *m_intermediateHeightfield, static_cast<int>(floor(m_maximumStepHeight / m_voxelHeight))); // Filter voxels rcFilterLowHangingWalkableObstacles( m_context, static_cast<int>(floor(m_maximumStepHeight / m_voxelHeight)), *m_intermediateHeightfield); rcFilterLedgeSpans( m_context, static_cast<int>(ceil(m_minimumCeilingClearance / m_voxelHeight)), static_cast<int>(floor(m_maximumStepHeight / m_voxelHeight)), *m_intermediateHeightfield); rcFilterWalkableLowHeightSpans( m_context, static_cast<int>(ceil(m_minimumCeilingClearance / m_voxelHeight)), *m_intermediateHeightfield); // Build the compact representation for the heightfield m_success = m_success && (rcBuildCompactHeightfield( m_context, static_cast<int>(ceil(m_minimumCeilingClearance / m_voxelHeight)), static_cast<int>(floor(m_maximumStepHeight / m_voxelHeight)), *m_intermediateHeightfield, *m_intermediateCompactHeightfield)); // Erode the navigatble area by minimum clearance to obstacles m_success = m_success && (rcErodeWalkableArea( m_context, static_cast<int>(ceil(m_minimumObstacleClearance / m_voxelSize)), *m_intermediateCompactHeightfield)); // Prepare for region partitioning, by calculating distance field along the walkable surface. m_success = m_success && (rcBuildDistanceField( m_context, *m_intermediateCompactHeightfield)); // Partition the walkable surface into simple regions without holes. m_success = m_success && (rcBuildRegions( m_context, *m_intermediateCompactHeightfield, 0, rcSqr<int>(m_regionMinSize), rcSqr<int>(m_regionMergeSize))); // Build the contours of the walkable surface m_success = m_success && (rcBuildContours( m_context, *m_intermediateCompactHeightfield, m_edgeMaxError, static_cast<int>(ceil(m_edgeMaxLength / m_voxelSize)), *m_intermediateContourSet)); // Build the polygon mesh, i.e. the navigation mesh geometry m_success = m_success && (rcBuildPolyMesh( m_context, *m_intermediateContourSet, m_polyMaxNbVertices, *m_intermediatePolyMesh)); //Build the detailed polygon mesh, i.e. the detail for the ground. m_success = m_success && (rcBuildPolyMeshDetail( m_context, *m_intermediatePolyMesh, *m_intermediateCompactHeightfield, m_sampleDist, m_sampleMaxError, *m_intermediatePolyMeshDetail)); // Update poly flags from areas. for (int i = 0; m_success && i < m_intermediatePolyMesh->npolys; ++i) { switch(m_intermediatePolyMesh->areas[i]) { case RC_WALKABLE_AREA: m_intermediatePolyMesh->areas[i] = area::Ground; break; case RC_NULL_AREA: default: m_intermediatePolyMesh->areas[i] = area::Obstacle; break; } switch(m_intermediatePolyMesh->areas[i]) { case area::Ground: m_intermediatePolyMesh->flags[i] = navigationFlags::Walkable; break; case area::Obstacle: default: m_intermediatePolyMesh->flags[i] = navigationFlags::NonWalkable; break; } } if (m_polyMaxNbVertices > DT_VERTS_PER_POLYGON) { m_context->log(RC_LOG_ERROR, "NavMeshCreator: unable to create Detour NavMesh, the configured maximum number of vertex per polygon (%d) is over Detour's limit (%d).",m_polyMaxNbVertices,DT_VERTS_PER_POLYGON); m_success = false; } if (m_success) { memset(m_intermediateNavMeshCreateParams, 0, sizeof(dtNavMeshCreateParams)); m_intermediateNavMeshCreateParams->verts = m_intermediatePolyMesh->verts; m_intermediateNavMeshCreateParams->vertCount = m_intermediatePolyMesh->nverts; m_intermediateNavMeshCreateParams->polys = m_intermediatePolyMesh->polys; m_intermediateNavMeshCreateParams->polyAreas = m_intermediatePolyMesh->areas; m_intermediateNavMeshCreateParams->polyFlags = m_intermediatePolyMesh->flags; m_intermediateNavMeshCreateParams->polyCount = m_intermediatePolyMesh->npolys; m_intermediateNavMeshCreateParams->nvp = m_intermediatePolyMesh->nvp; m_intermediateNavMeshCreateParams->detailMeshes = m_intermediatePolyMeshDetail->meshes; m_intermediateNavMeshCreateParams->detailVerts = m_intermediatePolyMeshDetail->verts; m_intermediateNavMeshCreateParams->detailVertsCount = m_intermediatePolyMeshDetail->nverts; m_intermediateNavMeshCreateParams->detailTris = m_intermediatePolyMeshDetail->tris; m_intermediateNavMeshCreateParams->detailTriCount = m_intermediatePolyMeshDetail->ntris; m_intermediateNavMeshCreateParams->offMeshConVerts = 0; m_intermediateNavMeshCreateParams->offMeshConRad = 0; m_intermediateNavMeshCreateParams->offMeshConDir = 0; m_intermediateNavMeshCreateParams->offMeshConAreas = 0; m_intermediateNavMeshCreateParams->offMeshConFlags = 0; m_intermediateNavMeshCreateParams->offMeshConUserID = 0; m_intermediateNavMeshCreateParams->offMeshConCount = 0; m_intermediateNavMeshCreateParams->walkableHeight = m_minimumCeilingClearance; m_intermediateNavMeshCreateParams->walkableRadius = m_minimumObstacleClearance; m_intermediateNavMeshCreateParams->walkableClimb = m_maximumStepHeight; rcVcopy(m_intermediateNavMeshCreateParams->bmin, m_intermediatePolyMesh->bmin); rcVcopy(m_intermediateNavMeshCreateParams->bmax, m_intermediatePolyMesh->bmax); m_intermediateNavMeshCreateParams->cs = m_voxelSize; m_intermediateNavMeshCreateParams->ch = m_voxelHeight; m_intermediateNavMeshCreateParams->buildBvTree = true; } if (m_success) { m_outputNavMeshBuffer = 0; m_outputNavMeshBufferSize = 0; if (!dtCreateNavMeshData(m_intermediateNavMeshCreateParams, &m_outputNavMeshBuffer, &m_outputNavMeshBufferSize)) { m_context->log(RC_LOG_ERROR, "NavMeshCreator: unable to create the detour navmesh data."); m_success = false; } } m_context->stopTimer(RC_TIMER_TOTAL); if (m_success) { duLogBuildTimes(*m_context, m_context->getAccumulatedTime(RC_TIMER_TOTAL)); } }
uint8* TileBuilder::BuildInstance( dtNavMeshParams& navMeshParams ) { float* bmin = NULL, *bmax = NULL; _Geometry->CalculateBoundingBox(bmin, bmax); rcVcopy(InstanceConfig.bmax, bmax); rcVcopy(InstanceConfig.bmin, bmin); uint32 numVerts = _Geometry->Vertices.size(); uint32 numTris = _Geometry->Triangles.size(); float* vertices; int* triangles; uint8* areas; _Geometry->GetRawData(vertices, triangles, areas); // this sets the dimensions of the heightfield rcCalcGridSize(InstanceConfig.bmin, InstanceConfig.bmax, InstanceConfig.cs, &InstanceConfig.width, &InstanceConfig.height); rcHeightfield* hf = rcAllocHeightfield(); rcCreateHeightfield(Context, *hf, InstanceConfig.width, InstanceConfig.height, InstanceConfig.bmin, InstanceConfig.bmax, InstanceConfig.cs, InstanceConfig.ch); rcClearUnwalkableTriangles(Context, InstanceConfig.walkableSlopeAngle, vertices, numVerts, triangles, numTris, areas); rcRasterizeTriangles(Context, vertices, numVerts, triangles, areas, numTris, *hf, InstanceConfig.walkableClimb); rcFilterLowHangingWalkableObstacles(Context, InstanceConfig.walkableClimb, *hf); rcFilterLedgeSpans(Context, InstanceConfig.walkableHeight, InstanceConfig.walkableClimb, *hf); rcFilterWalkableLowHeightSpans(Context, InstanceConfig.walkableHeight, *hf); rcCompactHeightfield* chf = rcAllocCompactHeightfield(); rcBuildCompactHeightfield(Context, InstanceConfig.walkableHeight, InstanceConfig.walkableClimb, *hf, *chf); rcErodeWalkableArea(Context, InstanceConfig.walkableRadius, *chf); rcBuildDistanceField(Context, *chf); rcBuildRegions(Context, *chf, InstanceConfig.borderSize, InstanceConfig.minRegionArea, InstanceConfig.minRegionArea); rcContourSet* contours = rcAllocContourSet(); rcBuildContours(Context, *chf, InstanceConfig.maxSimplificationError, InstanceConfig.maxEdgeLen, *contours); rcPolyMesh* pmesh = rcAllocPolyMesh(); rcBuildPolyMesh(Context, *contours, InstanceConfig.maxVertsPerPoly, *pmesh); rcPolyMeshDetail* dmesh = rcAllocPolyMeshDetail(); rcBuildPolyMeshDetail(Context, *pmesh, *chf, InstanceConfig.detailSampleDist, InstanceConfig.detailSampleMaxError, *dmesh); // Set flags according to area types (e.g. Swim for Water) for (int i = 0; i < pmesh->npolys; i++) { if (pmesh->areas[i] == Constants::POLY_AREA_ROAD || pmesh->areas[i] == Constants::POLY_AREA_TERRAIN) pmesh->flags[i] = Constants::POLY_FLAG_WALK; else if (pmesh->areas[i] == Constants::POLY_AREA_WATER) pmesh->flags[i] = Constants::POLY_FLAG_SWIM; } dtNavMeshCreateParams params; memset(¶ms, 0, sizeof(params)); // PolyMesh data params.verts = pmesh->verts; params.vertCount = pmesh->nverts; params.polys = pmesh->polys; params.polyAreas = pmesh->areas; params.polyFlags = pmesh->flags; params.polyCount = pmesh->npolys; params.nvp = pmesh->nvp; // PolyMeshDetail data params.detailMeshes = dmesh->meshes; params.detailVerts = dmesh->verts; params.detailVertsCount = dmesh->nverts; params.detailTris = dmesh->tris; params.detailTriCount = dmesh->ntris; rcVcopy(params.bmin, pmesh->bmin); rcVcopy(params.bmax, pmesh->bmax); // General settings params.ch = InstanceConfig.ch; params.cs = InstanceConfig.cs; params.walkableClimb = InstanceConfig.walkableClimb * InstanceConfig.ch; params.walkableHeight = InstanceConfig.walkableHeight * InstanceConfig.ch; params.walkableRadius = InstanceConfig.walkableRadius * InstanceConfig.cs; params.tileX = X; params.tileY = Y; params.tileLayer = 0; params.buildBvTree = true; rcVcopy(params.bmax, bmax); rcVcopy(params.bmin, bmin); // Offmesh-connection settings params.offMeshConCount = 0; // none for now rcFreeHeightField(hf); rcFreeCompactHeightfield(chf); rcFreeContourSet(contours); delete vertices; delete triangles; delete areas; delete bmin; delete bmax; if (!params.polyCount || !params.polys || Constants::TilesPerMap * Constants::TilesPerMap == params.polyCount) { // we have flat tiles with no actual geometry - don't build those, its useless // keep in mind that we do output those into debug info // drop tiles with only exact count - some tiles may have geometry while having less tiles printf("No polygons to build on tile, skipping.\n"); rcFreePolyMesh(pmesh); rcFreePolyMeshDetail(dmesh); return NULL; } int navDataSize; uint8* navData; printf("Creating the navmesh with %i vertices, %i polys, %i triangles!\n", params.vertCount, params.polyCount, params.detailTriCount); bool result = dtCreateNavMeshData(¶ms, &navData, &navDataSize); rcFreePolyMesh(pmesh); rcFreePolyMeshDetail(dmesh); if (result) { printf("NavMesh created, size %i!\n", navDataSize); DataSize = navDataSize; return navData; } return NULL; }
bool Sample_SoloMesh::handleBuild() { if (!m_geom || !m_geom->getMesh()) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Input mesh is not specified."); return false; } cleanup(); const float* bmin = m_geom->getMeshBoundsMin(); const float* bmax = m_geom->getMeshBoundsMax(); const float* verts = m_geom->getMesh()->getVerts(); const int nverts = m_geom->getMesh()->getVertCount(); const int* tris = m_geom->getMesh()->getTris(); const int ntris = m_geom->getMesh()->getTriCount(); // // Step 1. Initialize build config. // // Init build configuration from GUI memset(&m_cfg, 0, sizeof(m_cfg)); m_cfg.cs = m_cellSize; m_cfg.ch = m_cellHeight; m_cfg.walkableSlopeAngle = m_agentMaxSlope; m_cfg.walkableHeight = (int)ceilf(m_agentHeight / m_cfg.ch); m_cfg.walkableClimb = (int)floorf(m_agentMaxClimb / m_cfg.ch); m_cfg.walkableRadius = (int)ceilf(m_agentRadius / m_cfg.cs); m_cfg.maxEdgeLen = (int)(m_edgeMaxLen / m_cellSize); m_cfg.maxSimplificationError = m_edgeMaxError; m_cfg.minRegionArea = (int)rcSqr(m_regionMinSize); // Note: area = size*size m_cfg.mergeRegionArea = (int)rcSqr(m_regionMergeSize); // Note: area = size*size m_cfg.maxVertsPerPoly = (int)m_vertsPerPoly; m_cfg.detailSampleDist = m_detailSampleDist < 0.9f ? 0 : m_cellSize * m_detailSampleDist; m_cfg.detailSampleMaxError = m_cellHeight * m_detailSampleMaxError; // Set the area where the navigation will be build. // Here the bounds of the input mesh are used, but the // area could be specified by an user defined box, etc. rcVcopy(m_cfg.bmin, bmin); rcVcopy(m_cfg.bmax, bmax); rcCalcGridSize(m_cfg.bmin, m_cfg.bmax, m_cfg.cs, &m_cfg.width, &m_cfg.height); // Reset build times gathering. m_ctx->resetTimers(); // Start the build process. m_ctx->startTimer(RC_TIMER_TOTAL); m_ctx->log(RC_LOG_PROGRESS, "Building navigation:"); m_ctx->log(RC_LOG_PROGRESS, " - %d x %d cells", m_cfg.width, m_cfg.height); m_ctx->log(RC_LOG_PROGRESS, " - %.1fK verts, %.1fK tris", nverts/1000.0f, ntris/1000.0f); // // Step 2. Rasterize input polygon soup. // // Allocate voxel heightfield where we rasterize our input data to. m_solid = rcAllocHeightfield(); if (!m_solid) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'solid'."); return false; } if (!rcCreateHeightfield(m_ctx, *m_solid, m_cfg.width, m_cfg.height, m_cfg.bmin, m_cfg.bmax, m_cfg.cs, m_cfg.ch)) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not create solid heightfield."); return false; } // Allocate array that can hold triangle area types. // If you have multiple meshes you need to process, allocate // and array which can hold the max number of triangles you need to process. m_triareas = new unsigned char[ntris]; if (!m_triareas) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'm_triareas' (%d).", ntris); return false; } // Find triangles which are walkable based on their slope and rasterize them. // If your input data is multiple meshes, you can transform them here, calculate // the are type for each of the meshes and rasterize them. memset(m_triareas, 0, ntris*sizeof(unsigned char)); rcMarkWalkableTriangles(m_ctx, m_cfg.walkableSlopeAngle, verts, nverts, tris, ntris, m_triareas); rcRasterizeTriangles(m_ctx, verts, nverts, tris, m_triareas, ntris, *m_solid, m_cfg.walkableClimb); if (!m_keepInterResults) { delete [] m_triareas; m_triareas = 0; } // // Step 3. Filter walkables surfaces. // // Once all geoemtry is rasterized, we do initial pass of filtering to // remove unwanted overhangs caused by the conservative rasterization // as well as filter spans where the character cannot possibly stand. rcFilterLowHangingWalkableObstacles(m_ctx, m_cfg.walkableClimb, *m_solid); rcFilterLedgeSpans(m_ctx, m_cfg.walkableHeight, m_cfg.walkableClimb, *m_solid); rcFilterWalkableLowHeightSpans(m_ctx, m_cfg.walkableHeight, *m_solid); // // Step 4. Partition walkable surface to simple regions. // // Compact the heightfield so that it is faster to handle from now on. // This will result more cache coherent data as well as the neighbours // between walkable cells will be calculated. m_chf = rcAllocCompactHeightfield(); if (!m_chf) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'chf'."); return false; } if (!rcBuildCompactHeightfield(m_ctx, m_cfg.walkableHeight, m_cfg.walkableClimb, *m_solid, *m_chf)) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build compact data."); return false; } if (!m_keepInterResults) { rcFreeHeightField(m_solid); m_solid = 0; } // Erode the walkable area by agent radius. if (!rcErodeWalkableArea(m_ctx, m_cfg.walkableRadius, *m_chf)) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not erode."); return false; } // (Optional) Mark areas. const ConvexVolume* vols = m_geom->getConvexVolumes(); for (int i = 0; i < m_geom->getConvexVolumeCount(); ++i) rcMarkConvexPolyArea(m_ctx, vols[i].verts, vols[i].nverts, vols[i].hmin, vols[i].hmax, (unsigned char)vols[i].area, *m_chf); // Partition the heightfield so that we can use simple algorithm later to triangulate the walkable areas. // There are 3 martitioning methods, each with some pros and cons: // 1) Watershed partitioning // - the classic Recast partitioning // - creates the nicest tessellation // - usually slowest // - partitions the heightfield into nice regions without holes or overlaps // - the are some corner cases where this method creates produces holes and overlaps // - holes may appear when a small obstacles is close to large open area (triangulation can handle this) // - overlaps may occur if you have narrow spiral corridors (i.e stairs), this make triangulation to fail // * generally the best choice if you precompute the nacmesh, use this if you have large open areas // 2) Monotone partioning // - fastest // - partitions the heightfield into regions without holes and overlaps (guaranteed) // - creates long thin polygons, which sometimes causes paths with detours // * use this if you want fast navmesh generation // 3) Layer partitoining // - quite fast // - partitions the heighfield into non-overlapping regions // - relies on the triangulation code to cope with holes (thus slower than monotone partitioning) // - produces better triangles than monotone partitioning // - does not have the corner cases of watershed partitioning // - can be slow and create a bit ugly tessellation (still better than monotone) // if you have large open areas with small obstacles (not a problem if you use tiles) // * good choice to use for tiled navmesh with medium and small sized tiles if (m_partitionType == SAMPLE_PARTITION_WATERSHED) { // Prepare for region partitioning, by calculating distance field along the walkable surface. if (!rcBuildDistanceField(m_ctx, *m_chf)) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build distance field."); return false; } // Partition the walkable surface into simple regions without holes. if (!rcBuildRegions(m_ctx, *m_chf, 0, m_cfg.minRegionArea, m_cfg.mergeRegionArea)) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build watershed regions."); return false; } } else if (m_partitionType == SAMPLE_PARTITION_MONOTONE) { // Partition the walkable surface into simple regions without holes. // Monotone partitioning does not need distancefield. if (!rcBuildRegionsMonotone(m_ctx, *m_chf, 0, m_cfg.minRegionArea, m_cfg.mergeRegionArea)) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build monotone regions."); return false; } } else // SAMPLE_PARTITION_LAYERS { // Partition the walkable surface into simple regions without holes. if (!rcBuildLayerRegions(m_ctx, *m_chf, 0, m_cfg.minRegionArea)) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build layer regions."); return false; } } // // Step 5. Trace and simplify region contours. // // Create contours. m_cset = rcAllocContourSet(); if (!m_cset) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'cset'."); return false; } if (!rcBuildContours(m_ctx, *m_chf, m_cfg.maxSimplificationError, m_cfg.maxEdgeLen, *m_cset)) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not create contours."); return false; } // // Step 6. Build polygons mesh from contours. // // Build polygon navmesh from the contours. m_pmesh = rcAllocPolyMesh(); if (!m_pmesh) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'pmesh'."); return false; } if (!rcBuildPolyMesh(m_ctx, *m_cset, m_cfg.maxVertsPerPoly, *m_pmesh)) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not triangulate contours."); return false; } // // Step 7. Create detail mesh which allows to access approximate height on each polygon. // m_dmesh = rcAllocPolyMeshDetail(); if (!m_dmesh) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'pmdtl'."); return false; } if (!rcBuildPolyMeshDetail(m_ctx, *m_pmesh, *m_chf, m_cfg.detailSampleDist, m_cfg.detailSampleMaxError, *m_dmesh)) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build detail mesh."); return false; } if (!m_keepInterResults) { rcFreeCompactHeightfield(m_chf); m_chf = 0; rcFreeContourSet(m_cset); m_cset = 0; } // At this point the navigation mesh data is ready, you can access it from m_pmesh. // See duDebugDrawPolyMesh or dtCreateNavMeshData as examples how to access the data. // // (Optional) Step 8. Create Detour data from Recast poly mesh. // // The GUI may allow more max points per polygon than Detour can handle. // Only build the detour navmesh if we do not exceed the limit. if (m_cfg.maxVertsPerPoly <= DT_VERTS_PER_POLYGON) { unsigned char* navData = 0; int navDataSize = 0; // Update poly flags from areas. for (int i = 0; i < m_pmesh->npolys; ++i) { if (m_pmesh->areas[i] == RC_WALKABLE_AREA) m_pmesh->areas[i] = SAMPLE_POLYAREA_GROUND; if (m_pmesh->areas[i] == SAMPLE_POLYAREA_GROUND || m_pmesh->areas[i] == SAMPLE_POLYAREA_GRASS || m_pmesh->areas[i] == SAMPLE_POLYAREA_ROAD) { m_pmesh->flags[i] = SAMPLE_POLYFLAGS_WALK; } else if (m_pmesh->areas[i] == SAMPLE_POLYAREA_WATER) { m_pmesh->flags[i] = SAMPLE_POLYFLAGS_SWIM; } else if (m_pmesh->areas[i] == SAMPLE_POLYAREA_DOOR) { m_pmesh->flags[i] = SAMPLE_POLYFLAGS_WALK | SAMPLE_POLYFLAGS_DOOR; } } dtNavMeshCreateParams params; memset(¶ms, 0, sizeof(params)); params.verts = m_pmesh->verts; params.vertCount = m_pmesh->nverts; params.polys = m_pmesh->polys; params.polyAreas = m_pmesh->areas; params.polyFlags = m_pmesh->flags; params.polyCount = m_pmesh->npolys; params.nvp = m_pmesh->nvp; params.detailMeshes = m_dmesh->meshes; params.detailVerts = m_dmesh->verts; params.detailVertsCount = m_dmesh->nverts; params.detailTris = m_dmesh->tris; params.detailTriCount = m_dmesh->ntris; params.offMeshConVerts = m_geom->getOffMeshConnectionVerts(); params.offMeshConRad = m_geom->getOffMeshConnectionRads(); params.offMeshConDir = m_geom->getOffMeshConnectionDirs(); params.offMeshConAreas = m_geom->getOffMeshConnectionAreas(); params.offMeshConFlags = m_geom->getOffMeshConnectionFlags(); params.offMeshConUserID = m_geom->getOffMeshConnectionId(); params.offMeshConCount = m_geom->getOffMeshConnectionCount(); params.walkableHeight = m_agentHeight; params.walkableRadius = m_agentRadius; params.walkableClimb = m_agentMaxClimb; rcVcopy(params.bmin, m_pmesh->bmin); rcVcopy(params.bmax, m_pmesh->bmax); params.cs = m_cfg.cs; params.ch = m_cfg.ch; params.buildBvTree = true; if (!dtCreateNavMeshData(¶ms, &navData, &navDataSize)) { m_ctx->log(RC_LOG_ERROR, "Could not build Detour navmesh."); return false; } m_navMesh = dtAllocNavMesh(); if (!m_navMesh) { dtFree(navData); m_ctx->log(RC_LOG_ERROR, "Could not create Detour navmesh"); return false; } dtStatus status; status = m_navMesh->init(navData, navDataSize, DT_TILE_FREE_DATA); if (dtStatusFailed(status)) { dtFree(navData); m_ctx->log(RC_LOG_ERROR, "Could not init Detour navmesh"); return false; } status = m_navQuery->init(m_navMesh, 2048); if (dtStatusFailed(status)) { m_ctx->log(RC_LOG_ERROR, "Could not init Detour navmesh query"); return false; } } m_ctx->stopTimer(RC_TIMER_TOTAL); // Show performance stats. duLogBuildTimes(*m_ctx, m_ctx->getAccumulatedTime(RC_TIMER_TOTAL)); m_ctx->log(RC_LOG_PROGRESS, ">> Polymesh: %d vertices %d polygons", m_pmesh->nverts, m_pmesh->npolys); m_totalBuildTimeMs = m_ctx->getAccumulatedTime(RC_TIMER_TOTAL)/1000.0f; if (m_tool) m_tool->init(this); initToolStates(this); return true; }