EXPORT_API dtStatus dtnmBuildSingleTileMesh(dtNavMeshCreateParams* params , dtNavMesh** ppNavMesh) { if (!params) return DT_FAILURE + DT_INVALID_PARAM; unsigned char* navData = 0; int navDataSize = 0; if (!dtCreateNavMeshData(params, &navData, &navDataSize)) return DT_FAILURE + DT_INVALID_PARAM; dtNavMesh* pNavMesh = dtAllocNavMesh(); if (!pNavMesh) { dtFree(navData); return DT_FAILURE + DT_OUT_OF_MEMORY; } dtStatus status = pNavMesh->init(navData, navDataSize, DT_TILE_FREE_DATA); if (dtStatusFailed(status)) { dtFreeNavMesh(pNavMesh); dtFree(navData); return status; } *ppNavMesh = pNavMesh; return DT_SUCCESS; }
NavMeshPtr makeEmptyNavMesh(const Settings& settings) { // Max tiles and max polys affect how the tile IDs are caculated. // There are 22 bits available for identifying a tile and a polygon. const int polysAndTilesBits = 22; const auto polysBits = getMinValuableBitsNumber(settings.mMaxPolys); if (polysBits >= polysAndTilesBits) throw InvalidArgument("Too many polygons per tile"); const auto tilesBits = polysAndTilesBits - polysBits; dtNavMeshParams params; std::fill_n(params.orig, 3, 0.0f); params.tileWidth = settings.mTileSize * settings.mCellSize; params.tileHeight = settings.mTileSize * settings.mCellSize; params.maxTiles = 1 << tilesBits; params.maxPolys = 1 << polysBits; NavMeshPtr navMesh(dtAllocNavMesh(), &dtFreeNavMesh); const auto status = navMesh->init(¶ms); if (!dtStatusSucceed(status)) throw NavigatorException("Failed to init navmesh"); return navMesh; }
void CCollideInterface::ActivateMap(uint32 mapid) { m_navmaplock.Acquire(); std::map<uint32, NavMeshData*>::iterator itr = m_navdata.find(mapid); if(itr != m_navdata.end()) ++itr->second->refs; else { //load params char filename[1024]; sprintf(filename, "mmaps/%03i.mmap", mapid); FILE* f = fopen(filename, "rb"); if(f == NULL) { m_navmaplock.Release(); return; } dtNavMeshParams params; fread(¶ms, sizeof(params), 1, f); fclose(f); NavMeshData* d = new NavMeshData; d->mesh = dtAllocNavMesh(); d->query = dtAllocNavMeshQuery(); d->mesh->init(¶ms); d->query->init(d->mesh, 1024); d->AddRef(); m_navdata.insert(std::make_pair(mapid, d)); } m_navmaplock.Release(); }
bool OgreDetourTileCache::initTileCache() { // BUILD TileCache dtFreeTileCache(m_tileCache); dtStatus status; m_tileCache = dtAllocTileCache(); if (!m_tileCache) { m_recast->m_pLog->logMessage("ERROR: buildTiledNavigation: Could not allocate tile cache."); return false; } status = m_tileCache->init(&m_tcparams, m_talloc, m_tcomp, m_tmproc); if (dtStatusFailed(status)) { m_recast->m_pLog->logMessage("ERROR: buildTiledNavigation: Could not init tile cache."); return false; } dtFreeNavMesh(m_recast->m_navMesh); m_recast->m_navMesh = dtAllocNavMesh(); if (!m_recast->m_navMesh) { m_recast->m_pLog->logMessage("ERROR: buildTiledNavigation: Could not allocate navmesh."); return false; } // Init multi-tile navmesh parameters dtNavMeshParams params; memset(¶ms, 0, sizeof(params)); rcVcopy(params.orig, m_tcparams.orig); // Set world-space origin of tile grid params.tileWidth = m_tileSize*m_tcparams.cs; params.tileHeight = m_tileSize*m_tcparams.cs; params.maxTiles = m_maxTiles; params.maxPolys = m_maxPolysPerTile; status = m_recast->m_navMesh->init(¶ms); if (dtStatusFailed(status)) { m_recast->m_pLog->logMessage("ERROR: buildTiledNavigation: Could not init navmesh."); return false; } // Init recast navmeshquery with created navmesh (in OgreRecast component) m_recast->m_navQuery = dtAllocNavMeshQuery(); status = m_recast->m_navQuery->init(m_recast->m_navMesh, 2048); if (dtStatusFailed(status)) { m_recast->m_pLog->logMessage("ERROR: buildTiledNavigation: Could not init Detour navmesh query"); return false; } return true; }
dtNavMesh* Sample_TileMesh::loadAll(const char* path) { FILE* fp = fopen(path, "rb"); if (!fp) return 0; // Read header. NavMeshSetHeader header; fread(&header, sizeof(NavMeshSetHeader), 1, fp); if (header.magic != NAVMESHSET_MAGIC) { fclose(fp); return 0; } if (header.version != NAVMESHSET_VERSION) { fclose(fp); return 0; } dtNavMesh* mesh = dtAllocNavMesh(); if (!mesh) { fclose(fp); return 0; } dtStatus status = mesh->init(&header.params); if (dtStatusFailed(status)) { fclose(fp); return 0; } // Read tiles. for (int i = 0; i < header.numTiles; ++i) { NavMeshTileHeader 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); mesh->addTile(data, tileHeader.dataSize, DT_TILE_FREE_DATA, tileHeader.tileRef, 0); } fclose(fp); return mesh; }
bool Sample_TileMesh::handleBuild() { if (!m_geom || !m_geom->getMesh()) { m_ctx->log(RC_LOG_ERROR, "buildTiledNavigation: No vertices and triangles."); 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; rcVcopy(params.orig, m_geom->getNavMeshBoundsMin()); params.tileWidth = m_tileSize*m_cellSize; params.tileHeight = m_tileSize*m_cellSize; params.maxTiles = m_maxTiles; params.maxPolys = m_maxPolysPerTile; dtStatus status; 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; } if (m_buildAll) buildAllTiles(); if (m_tool) m_tool->init(this); initToolStates(this); return true; }
bool NavMesh::createNavMesh(dtNavMeshCreateParams ¶ms) { unsigned char *tileData = NULL; S32 tileDataSize = 0; if(!dtCreateNavMeshData(¶ms, &tileData, &tileDataSize)) { Con::errorf("Could not construct NavMeshData for NavMesh %s", getIdString()); return false; } tnm = dtAllocNavMesh(); if(!tnm) { Con::errorf("Out of memory allocating dtNavMesh for NavMesh %s", getIdString()); return false; } dtStatus s = tnm->init(tileData, tileDataSize, DT_TILE_FREE_DATA); if(dtStatusFailed(s)) { Con::errorf("Could not initialise dtNavMesh for NavMesh %s", getIdString()); return false; } // Initialise all flags to something helpful. for(U32 i = 0; i < tnm->getMaxTiles(); ++i) { const dtMeshTile* tile = ((const dtNavMesh*)tnm)->getTile(i); if(!tile->header) continue; const dtPolyRef base = tnm->getPolyRefBase(tile); for(U32 j = 0; j < tile->header->polyCount; ++j) { const dtPolyRef ref = base | j; unsigned short f = 0; tnm->getPolyFlags(ref, &f); tnm->setPolyFlags(ref, f | 1); } } return true; }
EXPORT_API dtStatus dtnmInitTiledNavMesh(dtNavMeshParams* params , dtNavMesh** ppNavMesh) { if (!params) return DT_FAILURE + DT_INVALID_PARAM; dtNavMesh* pNavMesh = dtAllocNavMesh(); if (!pNavMesh) return DT_FAILURE + DT_OUT_OF_MEMORY; dtStatus status = pNavMesh->init(params); if (dtStatusFailed(status)) { dtFreeNavMesh(pNavMesh); return status; } *ppNavMesh = pNavMesh; return DT_SUCCESS; }
void Sample_TempObstacles::loadAll(const char* path) { FILE* fp = fopen(path, "rb"); if (!fp) return; // Read header. TileCacheSetHeader header; fread(&header, sizeof(TileCacheSetHeader), 1, fp); if (header.magic != TILECACHESET_MAGIC) { fclose(fp); return; } if (header.version != TILECACHESET_VERSION) { fclose(fp); return; } m_navMesh = dtAllocNavMesh(); if (!m_navMesh) { fclose(fp); return; } dtStatus status = m_navMesh->init(&header.meshParams); if (dtStatusFailed(status)) { fclose(fp); return; } m_tileCache = dtAllocTileCache(); if (!m_tileCache) { fclose(fp); return; } status = m_tileCache->init(&header.cacheParams, m_talloc, m_tcomp, m_tmproc); if (dtStatusFailed(status)) { fclose(fp); return; } // 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_navMesh); } fclose(fp); }
bool Sample_TileMesh::handleLoadSubTiles() { if (!m_geom || !m_geom->getMesh()) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Input mesh is not specified."); return false; } cleanup(); const char* meshFilePath = m_geom->getMesh()->getFileName(); char charBuff[4]; memset(charBuff, 0, sizeof(charBuff)); memcpy(charBuff, &meshFilePath[10], sizeof(char) * 3); int mapId = atoi(charBuff); // load and init dtNavMesh - read parameters from file int pathLen = strlen("Meshes/%03i.mmap") + 1; char *fileName = new char[pathLen]; snprintf(fileName, pathLen, "Meshes/%03i.mmap", mapId); FILE* file = fopen(fileName, "rb"); if (!file) { delete[] fileName; return false; } dtNavMeshParams params; int count = fread(¶ms, sizeof(dtNavMeshParams), 1, file); fclose(file); if (count != 1) { delete[] fileName; return false; } params.maxTiles = 25 * 25 * 9; dtNavMesh* mesh = dtAllocNavMesh(); if (dtStatusFailed(mesh->init(¶ms))) { dtFreeNavMesh(mesh); delete[] fileName; return false; } delete[] fileName; memset(charBuff, 0, sizeof(charBuff)); memcpy(charBuff, &meshFilePath[13], sizeof(char) * 2); int x = atoi(charBuff); memset(charBuff, 0, sizeof(charBuff)); memcpy(charBuff, &meshFilePath[15], sizeof(char) * 2); int y = atoi(charBuff); for (int subRow = 0; subRow < 25; subRow++) { for (int subCol = 0; subCol < 25; subCol++) { // load this tile :: Meshes/MMMXXYY.mmtile pathLen = strlen("Meshes/%03i%02i%02i_____%02i%02i.mmtile") + 1; fileName = new char[pathLen]; snprintf(fileName, pathLen, "Meshes/%03i%02i%02i_____%02i%02i.mmtile", mapId, x, y, subRow, subCol); file = fopen(fileName, "rb"); if (!file) { delete[] fileName; continue; } delete[] fileName; // read header MmapTileHeader fileHeader; if (fread(&fileHeader, sizeof(MmapTileHeader), 1, file) != 1 || fileHeader.mmapMagic != MMAP_MAGIC) { fclose(file); continue; } unsigned char* data = (unsigned char*)dtAlloc(fileHeader.size, DT_ALLOC_PERM); size_t result = fread(data, fileHeader.size, 1, file); if (!result) { fclose(file); continue; } // Fix x/y dtMeshHeader* header = (dtMeshHeader*)data; header->x = header->x * 25 + subRow; header->y = header->y * 25 + subCol; fclose(file); dtTileRef tileRef = 0; // memory allocated for data is now managed by detour, and will be deallocated when the tile is removed if (!dtStatusSucceed(mesh->addTile(data, fileHeader.size, DT_TILE_FREE_DATA, 0, &tileRef))) { dtFree(data); continue; } } } m_navMesh = mesh; 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_tool) m_tool->init(this); initToolStates(this); return true; }
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; }
bool BotLoadNavMesh( const char *filename, NavData_t &nav ) { char mapname[ MAX_QPATH ]; char filePath[ MAX_QPATH ]; char gameName[ MAX_STRING_CHARS ]; fileHandle_t f = 0; BotLoadOffMeshConnections( filename, &nav ); Cvar_VariableStringBuffer( "mapname", mapname, sizeof( mapname ) ); Cvar_VariableStringBuffer( "fs_game", gameName, sizeof( gameName ) ); Com_sprintf( filePath, sizeof( filePath ), "maps/%s-%s.navMesh", mapname, filename ); Com_Printf( " loading navigation mesh file '%s'...\n", filePath ); int len = FS_FOpenFileRead( filePath, &f, qtrue ); if ( !f ) { Com_Printf( S_COLOR_RED "ERROR: Cannot open Navigaton Mesh file\n" ); return false; } if ( len < 0 ) { Com_Printf( S_COLOR_RED "ERROR: Negative Length for Navigation Mesh file\n" ); return false; } NavMeshSetHeader header; FS_Read( &header, sizeof( header ), f ); SwapNavMeshSetHeader( header ); if ( header.magic != NAVMESHSET_MAGIC ) { Com_Printf( S_COLOR_RED "ERROR: File is wrong magic\n" ); FS_FCloseFile( f ); return false; } if ( header.version != NAVMESHSET_VERSION ) { Com_Printf( S_COLOR_RED "ERROR: File is wrong version found: %d want: %d\n", header.version, NAVMESHSET_VERSION ); FS_FCloseFile( f ); return false; } nav.mesh = dtAllocNavMesh(); if ( !nav.mesh ) { Com_Printf( S_COLOR_RED "ERROR: Unable to allocate nav mesh\n" ); FS_FCloseFile( f ); return false; } dtStatus status = nav.mesh->init( &header.params ); if ( dtStatusFailed( status ) ) { Com_Printf( S_COLOR_RED "ERROR: Could not init navmesh\n" ); dtFreeNavMesh( nav.mesh ); nav.mesh = NULL; FS_FCloseFile( f ); return false; } nav.cache = dtAllocTileCache(); if ( !nav.cache ) { Com_Printf( S_COLOR_RED "ERROR: Could not allocate tile cache\n" ); dtFreeNavMesh( nav.mesh ); nav.mesh = NULL; FS_FCloseFile( f ); return false; } status = nav.cache->init( &header.cacheParams, &alloc, &comp, &nav.process ); if ( dtStatusFailed( status ) ) { Com_Printf( S_COLOR_RED "ERROR: Could not init tile cache\n" ); dtFreeNavMesh( nav.mesh ); dtFreeTileCache( nav.cache ); nav.mesh = NULL; nav.cache = NULL; FS_FCloseFile( f ); return false; } for ( int i = 0; i < header.numTiles; i++ ) { NavMeshTileHeader tileHeader; FS_Read( &tileHeader, sizeof( tileHeader ), f ); SwapNavMeshTileHeader( tileHeader ); if ( !tileHeader.tileRef || !tileHeader.dataSize ) { Com_Printf( S_COLOR_RED "ERROR: NUll Tile in navmesh\n" ); dtFreeNavMesh( nav.mesh ); dtFreeTileCache( nav.cache ); nav.cache = NULL; nav.mesh = NULL; FS_FCloseFile( f ); return false; } unsigned char *data = ( unsigned char * ) dtAlloc( tileHeader.dataSize, DT_ALLOC_PERM ); if ( !data ) { Com_Printf( S_COLOR_RED "ERROR: Failed to allocate memory for tile data\n" ); dtFreeNavMesh( nav.mesh ); dtFreeTileCache( nav.cache ); nav.cache = NULL; nav.mesh = NULL; FS_FCloseFile( f ); return false; } memset( data, 0, tileHeader.dataSize ); FS_Read( data, tileHeader.dataSize, f ); if ( LittleLong( 1 ) != 1 ) { dtTileCacheHeaderSwapEndian( data, tileHeader.dataSize ); } dtCompressedTileRef tile = 0; dtStatus status = nav.cache->addTile( data, tileHeader.dataSize, DT_TILE_FREE_DATA, &tile ); if ( dtStatusFailed( status ) ) { Com_Printf( S_COLOR_RED "ERROR: Failed to add tile to navmesh\n" ); dtFree( data ); dtFreeTileCache( nav.cache ); dtFreeNavMesh( nav.mesh ); nav.cache = NULL; nav.mesh = NULL; FS_FCloseFile( f ); return false; } if ( tile ) { nav.cache->buildNavMeshTile( tile, nav.mesh ); } } FS_FCloseFile( f ); return true; }
//------------------------------------------------------------------------------------- bool NavMeshHandle::_create(int layer, const std::string& resPath, const std::string& res, NavMeshHandle* pNavMeshHandle) { KBE_ASSERT(pNavMeshHandle); FILE* fp = fopen(res.c_str(), "rb"); if (!fp) { ERROR_MSG(fmt::format("NavMeshHandle::create: open({}) is error!\n", Resmgr::getSingleton().matchRes(res))); return false; } DEBUG_MSG(fmt::format("NavMeshHandle::create: ({}), layer={}\n", res, layer)); bool safeStorage = true; int pos = 0; int size = sizeof(NavMeshSetHeader); fseek(fp, 0, SEEK_END); size_t flen = ftell(fp); fseek(fp, 0, SEEK_SET); uint8* data = new uint8[flen]; if(data == NULL) { ERROR_MSG(fmt::format("NavMeshHandle::create: open({}), memory(size={}) error!\n", Resmgr::getSingleton().matchRes(res), flen)); fclose(fp); SAFE_RELEASE_ARRAY(data); return false; } size_t readsize = fread(data, 1, flen, fp); if(readsize != flen) { ERROR_MSG(fmt::format("NavMeshHandle::create: open({}), read(size={} != {}) error!\n", Resmgr::getSingleton().matchRes(res), readsize, flen)); fclose(fp); SAFE_RELEASE_ARRAY(data); return false; } if (readsize < sizeof(NavMeshSetHeader)) { ERROR_MSG(fmt::format("NavMeshHandle::create: open({}), NavMeshSetHeader is error!\n", Resmgr::getSingleton().matchRes(res))); fclose(fp); SAFE_RELEASE_ARRAY(data); return false; } NavMeshSetHeader header; memcpy(&header, data, size); pos += size; if (header.version != NavMeshHandle::RCN_NAVMESH_VERSION) { ERROR_MSG(fmt::format("NavMeshHandle::create: navmesh version({}) is not match({})!\n", header.version, ((int)NavMeshHandle::RCN_NAVMESH_VERSION))); fclose(fp); SAFE_RELEASE_ARRAY(data); return false; } dtNavMesh* mesh = dtAllocNavMesh(); if (!mesh) { ERROR_MSG("NavMeshHandle::create: dtAllocNavMesh is failed!\n"); fclose(fp); SAFE_RELEASE_ARRAY(data); return false; } dtStatus status = mesh->init(&header.params); if (dtStatusFailed(status)) { ERROR_MSG(fmt::format("NavMeshHandle::create: mesh init is error({})!\n", status)); fclose(fp); SAFE_RELEASE_ARRAY(data); return false; } // Read tiles. bool success = true; for (int i = 0; i < header.tileCount; ++i) { NavMeshTileHeader tileHeader; size = sizeof(NavMeshTileHeader); memcpy(&tileHeader, &data[pos], size); pos += size; size = tileHeader.dataSize; if (!tileHeader.tileRef || !tileHeader.dataSize) { success = false; status = DT_FAILURE + DT_INVALID_PARAM; break; } unsigned char* tileData = (unsigned char*)dtAlloc(size, DT_ALLOC_PERM); if (!tileData) { success = false; status = DT_FAILURE + DT_OUT_OF_MEMORY; break; } memcpy(tileData, &data[pos], size); pos += size; status = mesh->addTile(tileData , size , (safeStorage ? DT_TILE_FREE_DATA : 0) , tileHeader.tileRef , 0); if (dtStatusFailed(status)) { success = false; break; } } fclose(fp); SAFE_RELEASE_ARRAY(data); if (!success) { ERROR_MSG(fmt::format("NavMeshHandle::create: error({})!\n", status)); dtFreeNavMesh(mesh); return false; } dtNavMeshQuery* pMavmeshQuery = new dtNavMeshQuery(); pMavmeshQuery->init(mesh, 1024); pNavMeshHandle->resPath = resPath; pNavMeshHandle->navmeshLayer[layer].pNavmeshQuery = pMavmeshQuery; pNavMeshHandle->navmeshLayer[layer].pNavmesh = mesh; uint32 tileCount = 0; uint32 nodeCount = 0; uint32 polyCount = 0; uint32 vertCount = 0; uint32 triCount = 0; uint32 triVertCount = 0; uint32 dataSize = 0; const dtNavMesh* navmesh = mesh; for (int32 i = 0; i < navmesh->getMaxTiles(); ++i) { const dtMeshTile* tile = navmesh->getTile(i); if (!tile || !tile->header) continue; tileCount ++; nodeCount += tile->header->bvNodeCount; polyCount += tile->header->polyCount; vertCount += tile->header->vertCount; triCount += tile->header->detailTriCount; triVertCount += tile->header->detailVertCount; dataSize += tile->dataSize; // DEBUG_MSG(fmt::format("NavMeshHandle::create: verts({}, {}, {})\n", tile->verts[0], tile->verts[1], tile->verts[2])); } DEBUG_MSG(fmt::format("\t==> tiles loaded: {}\n", tileCount)); DEBUG_MSG(fmt::format("\t==> BVTree nodes: {}\n", nodeCount)); DEBUG_MSG(fmt::format("\t==> {} polygons ({} vertices)\n", polyCount, vertCount)); DEBUG_MSG(fmt::format("\t==> {} triangles ({} vertices)\n", triCount, triVertCount)); DEBUG_MSG(fmt::format("\t==> {:.2f} MB of data (not including pointers)\n", (((float)dataSize / sizeof(unsigned char)) / 1048576))); return 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; }
DetourInterface::DetourInterface(rcPolyMesh* polyMesh,rcPolyMeshDetail* detailMesh,rcdtConfig& config) : _navMesh(nullptr), _navQuery(nullptr), _isMeshBuilt(false) { detourCleanup(); unsigned char* navData = 0; int navDataSize = 0; if(config.recastConfig->maxVertsPerPoly > DT_VERTS_PER_POLYGON) { return; } #if defined(DEBUG) || defined(_DEBUG) std::cout << "Detour - Stage 1" << std::endl; #endif for(int i = 0; i < polyMesh->npolys; ++i) { if(polyMesh->areas[i] == RC_WALKABLE_AREA) { polyMesh->areas[i] = DT_PA_GROUND; polyMesh->flags[i] = DT_PF_WALK; } } dtNavMeshCreateParams params; memset(¶ms,0,sizeof(params)); params.verts = polyMesh->verts; params.vertCount = polyMesh->nverts; params.polys = polyMesh->polys; params.polyAreas = polyMesh->areas; params.polyFlags = polyMesh->flags; params.polyCount = polyMesh->npolys; params.nvp = polyMesh->nvp; params.detailMeshes = detailMesh->meshes; params.detailVerts = detailMesh->verts; params.detailVertsCount = detailMesh->nverts; params.detailTris = detailMesh->tris; params.detailTriCount = detailMesh->ntris; //offmesh connections are not implemented. I don't even know what they are! params.offMeshConCount = 0; params.walkableHeight = config.userConfig->getAgentHeight(); params.walkableRadius = config.userConfig->getAgentRadius(); params.walkableClimb = config.userConfig->getAgentMaxClimb(); params.buildBvTree = true; rcVcopy(params.bmin, config.recastConfig->bmin); rcVcopy(params.bmax, config.recastConfig->bmax); params.cs = config.recastConfig->cs; params.ch = config.recastConfig->ch; #if defined(DEBUG) || defined(_DEBUG) std::cout << "Detour - Stage 2" << std::endl; #endif if(!dtCreateNavMeshData(¶ms,&navData,&navDataSize)) { std::cout << "Error! Detour - could not build navmesh!" << std::endl; std::cout << " - " << params.cs << std::endl; std::cout << " - " << params.ch << std::endl; std::cout << " - " << params.walkableRadius << std::endl; //_isMeshBuilt = false; return; } #if defined(DEBUG) || defined(_DEBUG) std::cout << "Detour - Stage 3" << std::endl; #endif _navMesh = dtAllocNavMesh(); if(!_navMesh) { dtFree(_navMesh); std::cout << "Error! Detour - could not create Detour navmesh!" << std::endl; return; } #if defined(DEBUG) || defined(_DEBUG) std::cout << "Detour - Stage 4" << std::endl; #endif dtStatus status; status = _navMesh->init(navData,navDataSize,DT_TILE_FREE_DATA); if(dtStatusFailed(status)) { dtFree(navData); std::cout << "Error! Could not initialize Detour NavMesh." << std::endl; return; } #if defined(DEBUG) || defined(_DEBUG) std::cout << "Detour - Stage 5" << std::endl; #endif _navQuery = dtAllocNavMeshQuery(); status = _navQuery->init(_navMesh,2048); if(dtStatusFailed(status)) { std::cout << "Error! Detour - could not initialize Detour navmesh query." << std::endl; return; } _isMeshBuilt = true; }
void NavigationMesh::SetNavigationDataAttr(PODVector<unsigned char> value) { ReleaseNavigationMesh(); if (value.Empty()) return; MemoryBuffer buffer(value); boundingBox_ = buffer.ReadBoundingBox(); numTilesX_ = buffer.ReadInt(); numTilesZ_ = buffer.ReadInt(); dtNavMeshParams params; rcVcopy(params.orig, &boundingBox_.min_.x_); params.tileWidth = buffer.ReadFloat(); params.tileHeight = buffer.ReadFloat(); params.maxTiles = buffer.ReadInt(); params.maxPolys = buffer.ReadInt(); navMesh_ = dtAllocNavMesh(); if (!navMesh_) { LOGERROR("Could not allocate navigation mesh"); return; } if (dtStatusFailed(navMesh_->init(¶ms))) { LOGERROR("Could not initialize navigation mesh"); ReleaseNavigationMesh(); return; } unsigned numTiles = 0; while (!buffer.IsEof()) { /*int x =*/ buffer.ReadInt(); /*int z =*/ buffer.ReadInt(); /*dtTileRef tileRef =*/ buffer.ReadUInt(); unsigned navDataSize = buffer.ReadUInt(); unsigned char* navData = (unsigned char*)dtAlloc(navDataSize, DT_ALLOC_PERM); if (!navData) { LOGERROR("Could not allocate data for navigation mesh tile"); return; } buffer.Read(navData, navDataSize); if (dtStatusFailed(navMesh_->addTile(navData, navDataSize, DT_TILE_FREE_DATA, 0, 0))) { LOGERROR("Failed to add navigation mesh tile"); dtFree(navData); return; } else ++numTiles; } LOGDEBUG("Created navigation mesh with " + String(numTiles) + " tiles from serialized data"); }
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; }
bool NavMesh::BuildMesh() { dtStatus status; if (!m_geom || !m_geom->getMesh()) return false; m_tmproc->init(m_geom); // Init cache 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; // 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) return false; status = m_tileCache->init(&tcparams, m_talloc, m_tcomp, m_tmproc); if (dtStatusFailed(status)) return false; dtFreeNavMesh(m_navMesh); m_navMesh = dtAllocNavMesh(); if (!m_navMesh) return false; dtNavMeshParams params; memset(¶ms, 0, sizeof(params)); rcVcopy(params.orig, m_geom->getMeshBoundsMin()); 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)) return false; status = m_navQuery->init(m_navMesh, 2048); if (dtStatusFailed(status)) return false; for (int y = 0; y < th; ++y) { for (int x = 0; x < tw; ++x) { TileCacheData tiles[MAX_LAYERS]; memset(tiles, 0, sizeof(tiles)); int n = rasterizeTileLayers(m_geom, x, y, cfg, tiles, MAX_LAYERS); for (int i = 0; i < n; ++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; } } } } for (int y = 0; y < th; ++y) for (int x = 0; x < tw; ++x) m_tileCache->buildNavMeshTilesAt(x,y, m_navMesh); }
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; }
/*! 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; }
bool NavMeshGenerator::handleBuild(const Array2D<int>& tab) { cleanup(); // // Step 1. Initialize build config. // // Init build configuration from GUI memset(&m_cfg, 0, sizeof(m_cfg)); const int voxels_per_tile = 3; //3 m_cfg.width = tab.size().x*3*voxels_per_tile; m_cfg.height = tab.size().x*3*voxels_per_tile; m_cfg.cs = pixels_per_tile/float(voxels_per_tile); m_cfg.walkableRadius = voxels_per_tile == 1 ? 0 : 1; m_cfg.maxEdgeLen = 0;//20; // m_cfg.maxSimplificationError = 0.f; // 0 or 1, no need because we are working on tiles m_cfg.minRegionArea = 64;//(int)rcSqr(m_regionMinSize); // Note: area = size*size m_cfg.mergeRegionArea = 10000;//(int)rcSqr(m_regionMergeSize); // Note: area = size*size m_cfg.maxVertsPerPoly = 4;//(int)m_vertsPerPoly; m_cfg.ch = 0.2f; // < height info, not used m_cfg.detailSampleDist = 20.f; // < height info, not used m_cfg.detailSampleMaxError = 0.2f; // < height info, not used m_cfg.walkableSlopeAngle = 0; // < height info, not used m_cfg.walkableHeight = 0; // < height info, not used m_cfg.walkableClimb = 0; // < height info, not used // Set the area where the navigation will be build. m_cfg.bmin[0] = 0; m_cfg.bmin[1] = 0; m_cfg.bmin[2] = 0; m_cfg.bmax[0] = tab.size().x*3*pixels_per_tile; m_cfg.bmax[1] = 3; m_cfg.bmax[2] = tab.size().y*3*pixels_per_tile; // 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); // // Step 2. Create Heightfield // // Allocate voxel heightfield 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; } for(int i = 0; i < m_solid->width; ++i) { for(int j = 0; j < m_solid->height; ++j) { bool colide = tab(int(i*tab.size().x/m_solid->width) , int(j*tab.size().y/m_solid->height)) != 0 ; rcAddSpan(NULL, *m_solid, i, j, 0, colide ? 10: 0, colide ? RC_NULL_AREA : RC_WALKABLE_AREA, 1); } } // // Step 3. Filter walkables surfaces. //----> Not done for 2D // // 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 (true) //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 (false)//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] == RC_WALKABLE_AREA) { m_pmesh->flags[i] = 1; } else { m_pmesh->flags[i] = 0; } } 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.offMeshConCount = 0; /* unused since offMeshConCount is null 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.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. m_ctx->log(RC_LOG_PROGRESS, ">> Polymesh: %d vertices %d polygons", m_pmesh->nverts, m_pmesh->npolys); return true; }
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; }
//----------------------------------------------------------------------------- // 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; }
bool NavigationMesh::Build() { PROFILE(BuildNavigationMesh); // Release existing navigation data and zero the bounding box ReleaseNavigationMesh(); if (!node_) return false; if (!node_->GetWorldScale().Equals(Vector3::ONE)) LOGWARNING("Navigation mesh root node has scaling. Agent parameters may not work as intended"); Vector<NavigationGeometryInfo> geometryList; CollectGeometries(geometryList); if (geometryList.Empty()) return true; // Nothing to do // Build the combined bounding box for (unsigned i = 0; i < geometryList.Size(); ++i) boundingBox_.Merge(geometryList[i].boundingBox_); // Expand bounding box by padding boundingBox_.min_ -= padding_; boundingBox_.max_ += padding_; { PROFILE(BuildNavigationMesh); // Calculate number of tiles int gridW = 0, gridH = 0; float tileEdgeLength = (float)tileSize_ * cellSize_; rcCalcGridSize(&boundingBox_.min_.x_, &boundingBox_.max_.x_, cellSize_, &gridW, &gridH); numTilesX_ = (gridW + tileSize_ - 1) / tileSize_; numTilesZ_ = (gridH + tileSize_ - 1) / tileSize_; // Calculate max. number of tiles and polygons, 22 bits available to identify both tile & polygon within tile unsigned maxTiles = NextPowerOfTwo(numTilesX_ * numTilesZ_); unsigned tileBits = 0; unsigned temp = maxTiles; while (temp > 1) { temp >>= 1; ++tileBits; } unsigned maxPolys = 1 << (22 - tileBits); dtNavMeshParams params; rcVcopy(params.orig, &boundingBox_.min_.x_); params.tileWidth = tileEdgeLength; params.tileHeight = tileEdgeLength; params.maxTiles = maxTiles; params.maxPolys = maxPolys; navMesh_ = dtAllocNavMesh(); if (!navMesh_) { LOGERROR("Could not allocate navigation mesh"); return false; } if (dtStatusFailed(navMesh_->init(¶ms))) { LOGERROR("Could not initialize navigation mesh"); ReleaseNavigationMesh(); return false; } // Build each tile unsigned numTiles = 0; for (int z = 0; z < numTilesZ_; ++z) { for (int x = 0; x < numTilesX_; ++x) { if (BuildTile(geometryList, x, z)) ++numTiles; } } LOGDEBUG("Built navigation mesh with " + String(numTiles) + " tiles"); return true; } }
bool CNavMesh::load(char* path) { this->path = path; this->unload(); m_navMesh = new dtNavMesh(); FILE* fp = fopen(path, "rb"); if (!fp) { ShowError("CNavMesh::load Error loading navmesh (%s)\n", path); return false; } // Read header. NavMeshSetHeader header; fread(&header, sizeof(NavMeshSetHeader), 1, fp); if (header.magic != NAVMESHSET_MAGIC) { fclose(fp); return false; } if (header.version != NAVMESHSET_VERSION) { fclose(fp); return false; } m_navMesh = dtAllocNavMesh(); if (!m_navMesh) { fclose(fp); return false; } dtStatus status = m_navMesh->init(&header.params); if (dtStatusFailed(status)) { ShowError("CNavMesh::load Could not initialize detour for (%s)", path); outputError(status); fclose(fp); return false; } // Read tiles. for (int i = 0; i < header.numTiles; ++i) { NavMeshTileHeader 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); m_navMesh->addTile(data, tileHeader.dataSize, DT_TILE_FREE_DATA, tileHeader.tileRef, 0); } fclose(fp); m_navMeshQuery = new dtNavMeshQuery(); // init detour nav mesh path finder status = m_navMeshQuery->init(m_navMesh, MAX_NAV_POLYS); if(dtStatusFailed(status)) { ShowError("CNavMesh::load Error loading navmeshquery (%s)\n", path); outputError(status); return false; } return true; }
bool NavMesh::loadNavMeshFile() { auto data = FileUtils::getInstance()->getDataFromFile(_navFilePath); if (data.isNull()) return false; // Read header. unsigned int offset = 0; TileCacheSetHeader header = *((TileCacheSetHeader*)(data.getBytes() + offset)); offset += sizeof(TileCacheSetHeader); if (header.magic != TILECACHESET_MAGIC) { return false; } if (header.version != TILECACHESET_VERSION) { return false; } _navMesh = dtAllocNavMesh(); if (!_navMesh) { return false; } dtStatus status = _navMesh->init(&header.meshParams); if (dtStatusFailed(status)) { return false; } _tileCache = dtAllocTileCache(); if (!_tileCache) { return false; } _allocator = new LinearAllocator(32000); _compressor = new FastLZCompressor; _meshProcess = new MeshProcess(_geomData); status = _tileCache->init(&header.cacheParams, _allocator, _compressor, _meshProcess); if (dtStatusFailed(status)) { return false; } // Read tiles. for (int i = 0; i < header.numTiles; ++i) { TileCacheTileHeader tileHeader = *((TileCacheTileHeader*)(data.getBytes() + offset)); offset += sizeof(TileCacheTileHeader); if (!tileHeader.tileRef || !tileHeader.dataSize) break; unsigned char* tileData = (unsigned char*)dtAlloc(tileHeader.dataSize, DT_ALLOC_PERM); if (!tileData) break; memcpy(tileData, (data.getBytes() + offset), tileHeader.dataSize); offset += tileHeader.dataSize; dtCompressedTileRef tile = 0; _tileCache->addTile(tileData, tileHeader.dataSize, DT_COMPRESSEDTILE_FREE_DATA, &tile); if (tile) _tileCache->buildNavMeshTile(tile, _navMesh); } //create crowed _crowed = dtAllocCrowd(); _crowed->init(MAX_AGENTS, header.cacheParams.walkableRadius, _navMesh); //create NavMeshQuery _navMeshQuery = dtAllocNavMeshQuery(); _navMeshQuery->init(_navMesh, 2048); _agentList.assign(MAX_AGENTS, nullptr); _obstacleList.assign(header.cacheParams.maxObstacles, nullptr); //duDebugDrawNavMesh(&_debugDraw, *_navMesh, DU_DRAWNAVMESH_OFFMESHCONS); return true; }
void MapBuilder::buildNavMesh(uint32 mapID, dtNavMesh* &navMesh) { std::set<uint32>* tiles = getTileList(mapID); // old code for non-statically assigned bitmask sizes: ///*** calculate number of bits needed to store tiles & polys ***/ //int tileBits = dtIlog2(dtNextPow2(tiles->size())); //if (tileBits < 1) tileBits = 1; // need at least one bit! //int polyBits = sizeof(dtPolyRef)*8 - SALT_MIN_BITS - tileBits; int polyBits = STATIC_POLY_BITS; int maxTiles = tiles->size(); int maxPolysPerTile = 1 << polyBits; /*** calculate bounds of map ***/ uint32 tileXMin = 64, tileYMin = 64, tileXMax = 0, tileYMax = 0, tileX, tileY; for (std::set<uint32>::iterator it = tiles->begin(); it != tiles->end(); ++it) { StaticMapTree::unpackTileID(*it, tileX, tileY); if (tileX > tileXMax) tileXMax = tileX; else if (tileX < tileXMin) tileXMin = tileX; if (tileY > tileYMax) tileYMax = tileY; else if (tileY < tileYMin) tileYMin = tileY; } // use Max because '32 - tileX' is negative for values over 32 float bmin[3], bmax[3]; getTileBounds(tileXMax, tileYMax, NULL, 0, bmin, bmax); /*** now create the navmesh ***/ // navmesh creation params dtNavMeshParams navMeshParams; memset(&navMeshParams, 0, sizeof(dtNavMeshParams)); navMeshParams.tileWidth = GRID_SIZE; navMeshParams.tileHeight = GRID_SIZE; rcVcopy(navMeshParams.orig, bmin); navMeshParams.maxTiles = maxTiles; navMeshParams.maxPolys = maxPolysPerTile; navMesh = dtAllocNavMesh(); printf("[Map %04u] Creating navMesh...\n", mapID); if (!navMesh->init(&navMeshParams)) { printf("[Map %04u] Failed creating navmesh! \n", mapID); return; } char fileName[25]; sprintf(fileName, "mmaps/%04u.mmap", mapID); FILE* file = fopen(fileName, "wb"); if (!file) { dtFreeNavMesh(navMesh); char message[1024]; sprintf(message, "[Map %04u] Failed to open %s for writing!\n", mapID, fileName); perror(message); return; } // now that we know navMesh params are valid, we can write them to file fwrite(&navMeshParams, sizeof(dtNavMeshParams), 1, file); fclose(file); }
//------------------------------------------------------------------------------------- NavMeshHandle* NavMeshEx::loadNavmesh(std::string name) { if(name == "") return NULL; KBEngine::thread::ThreadGuard tg(&mutex_); std::string path = Resmgr::getSingleton().matchRes("spaces/" + name + "/" + name + ".navmesh_srv"); if(navmeshs_.find(name) != navmeshs_.end()) { return NULL; } FILE* fp = fopen(path.c_str(), "rb"); if (!fp) { ERROR_MSG(boost::format("NavMeshEx::loadNavmesh: open(%1%) is error!\n") % path); return NULL; } bool safeStorage = true; int pos = 0; int size = sizeof(NavMeshSetHeader); fseek(fp, 0, SEEK_END); size_t flen = ftell(fp); fseek(fp, 0, SEEK_SET); uint8* data = new uint8[flen]; if(data == NULL) { ERROR_MSG(boost::format("NavMeshEx::loadNavmesh: open(%1%), memory(size=%2%) error!\n") % path % flen); fclose(fp); SAFE_RELEASE_ARRAY(data); return NULL; } size_t readsize = fread(data, 1, flen, fp); if(readsize != flen) { ERROR_MSG(boost::format("NavMeshEx::loadNavmesh: open(%1%), read(size=%2% != %3%) error!\n") % path % readsize % flen); fclose(fp); SAFE_RELEASE_ARRAY(data); return NULL; } if (readsize < sizeof(NavMeshSetHeader)) { ERROR_MSG(boost::format("NavMeshEx::loadNavmesh: open(%1%), NavMeshSetHeader is error!\n") % path); fclose(fp); SAFE_RELEASE_ARRAY(data); return NULL; } NavMeshSetHeader header; memcpy(&header, data, size); pos += size; if (header.version != RCN_NAVMESH_VERSION) { ERROR_MSG(boost::format("NavMeshEx::loadNavmesh: version(%1%) is not match(%2%)!\n") % header.version % RCN_NAVMESH_VERSION); fclose(fp); SAFE_RELEASE_ARRAY(data); return NULL; } dtNavMesh* mesh = dtAllocNavMesh(); if (!mesh) { ERROR_MSG("NavMeshEx::loadNavmesh: dtAllocNavMesh is failed!\n"); fclose(fp); SAFE_RELEASE_ARRAY(data); return NULL; } dtStatus status = mesh->init(&header.params); if (dtStatusFailed(status)) { ERROR_MSG(boost::format("NavMeshEx::loadNavmesh: mesh init is error(%1%)!\n") % status); fclose(fp); SAFE_RELEASE_ARRAY(data); return NULL; } // Read tiles. bool success = true; for (int i = 0; i < header.tileCount; ++i) { NavMeshTileHeader tileHeader; size = sizeof(NavMeshTileHeader); memcpy(&tileHeader, &data[pos], size); pos += size; size = tileHeader.dataSize; if (!tileHeader.tileRef || !tileHeader.dataSize) { success = false; status = DT_FAILURE + DT_INVALID_PARAM; break; } unsigned char* tileData = (unsigned char*)dtAlloc(size, DT_ALLOC_PERM); if (!tileData) { success = false; status = DT_FAILURE + DT_OUT_OF_MEMORY; break; } memcpy(tileData, &data[pos], size); pos += size; status = mesh->addTile(tileData , size , (safeStorage ? DT_TILE_FREE_DATA : 0) , tileHeader.tileRef , 0); if (dtStatusFailed(status)) { success = false; break; } } fclose(fp); SAFE_RELEASE_ARRAY(data); if (!success) { ERROR_MSG(boost::format("NavMeshEx::loadNavmesh: error(%1%)!\n") % status); dtFreeNavMesh(mesh); return NULL; } NavMeshHandle* pNavMeshHandle = new NavMeshHandle(); pNavMeshHandle->navmesh = mesh; pNavMeshHandle->navmeshQuery = new dtNavMeshQuery(); pNavMeshHandle->navmeshQuery->init(mesh, 1024); pNavMeshHandle->name = name; navmeshs_[name] = pNavMeshHandle; uint32 tileCount = 0; uint32 nodeCount = 0; uint32 polyCount = 0; uint32 vertCount = 0; uint32 triCount = 0; uint32 triVertCount = 0; uint32 dataSize = 0; const dtNavMesh* navmesh = mesh; for (int32 i = 0; i < navmesh->getMaxTiles(); ++i) { const dtMeshTile* tile = navmesh->getTile(i); if (!tile || !tile->header) continue; tileCount ++; nodeCount += tile->header->bvNodeCount; polyCount += tile->header->polyCount; vertCount += tile->header->vertCount; triCount += tile->header->detailTriCount; triVertCount += tile->header->detailVertCount; dataSize += tile->dataSize; // DEBUG_MSG(boost::format("NavMeshEx::loadNavmesh: verts(%1%, %2%, %3%)\n") % tile->verts[0] % tile->verts[1] % tile->verts[2]); } DEBUG_MSG(boost::format("NavMeshEx::loadNavmesh: (%1%)\n") % name); DEBUG_MSG(boost::format("\t==> tiles loaded: %1%\n") % tileCount); DEBUG_MSG(boost::format("\t==> BVTree nodes: %1%\n") % nodeCount); DEBUG_MSG(boost::format("\t==> %1% polygons (%2% vertices)\n") % polyCount % vertCount); DEBUG_MSG(boost::format("\t==> %1% triangles (%2% vertices)\n") % triCount % triVertCount); DEBUG_MSG(boost::format("\t==> %.2f MB of data (not including pointers)\n") % (((float)dataSize / sizeof(unsigned char)) / 1048576)); return pNavMeshHandle; }
void MapBuilder::buildNavMesh(int mapID, dtNavMesh*& navMesh, dtNavMeshParams*& navMeshParams) { bool isFirstNavMesh = navMeshParams ? false : true; if (isFirstNavMesh) { set<uint32>* tiles = getTileList(mapID); int polyBits = DT_POLY_BITS; int maxTiles = tiles->size(); int maxPolysPerTile = 1 << polyBits; /*** calculate bounds of map ***/ uint32 tileXMin = 64, tileYMin = 64, tileXMax = 0, tileYMax = 0, tileX, tileY; for (set<uint32>::iterator it = tiles->begin(); it != tiles->end(); ++it) { StaticMapTree::unpackTileID((*it), tileX, tileY); if (tileX > tileXMax) { tileXMax = tileX; } else if (tileX < tileXMin) { tileXMin = tileX; } if (tileY > tileYMax) { tileYMax = tileY; } else if (tileY < tileYMin) { tileYMin = tileY; } } // use Max because '32 - tileX' is negative for values over 32 float bmin[3], bmax[3]; getTileBounds(tileXMax, tileYMax, NULL, 0, bmin, bmax); /*** now create the navmesh ***/ // navmesh creation params navMeshParams = new dtNavMeshParams(); memset(navMeshParams, 0, sizeof(dtNavMeshParams)); navMeshParams->tileWidth = GRID_SIZE; navMeshParams->tileHeight = GRID_SIZE; rcVcopy(navMeshParams->orig, bmin); navMeshParams->maxTiles = maxTiles; navMeshParams->maxPolys = maxPolysPerTile; } navMesh = dtAllocNavMesh(); if (!navMesh->init(navMeshParams)) { printf("Failed creating navmesh! \n"); return; } if (isFirstNavMesh) { char fileName[25]; sprintf(fileName, "mmaps/%03u.mmap", mapID); FILE* file = fopen(fileName, "wb"); if (!file) { dtFreeNavMesh(navMesh); char message[1024]; sprintf(message, "Failed to open %s for writing!\n", fileName); perror(message); return; } // now that we know navMesh params are valid, we can write them to file fwrite(navMeshParams, sizeof(dtNavMeshParams), 1, file); fclose(file); } }
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; }