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; }
void Sample_SoloMesh::handleMeshChanged(class InputGeom* geom) { Sample::handleMeshChanged(geom); dtFreeNavMesh(m_navMesh); m_navMesh = 0; if (m_tool) { m_tool->reset(); m_tool->init(this); } resetToolStates(); initToolStates(this); }
void CMaNGOS_Map::ClearAllGeoms() { delete m_MapInfos; m_navMesh = NULL; m_navQuery = NULL; setTool(new NavMeshTesterTool); if (m_SelectedTile) { delete m_SelectedTile; m_SelectedTile = NULL; } initToolStates(); resetToolStates(); }
void Sample_TempObstacles::handleMeshChanged(class InputGeom* geom) { Sample::handleMeshChanged(geom); dtFreeTileCache(m_tileCache); m_tileCache = 0; dtFreeNavMesh(m_navMesh); m_navMesh = 0; if (m_tool) { m_tool->reset(); m_tool->init(this); m_tmproc->init(m_geom); } resetToolStates(); initToolStates(this); }
void Sample_TileMesh::handleMeshChanged(InputGeom* geom) { Sample::handleMeshChanged(geom); const BuildSettings* buildSettings = geom->getBuildSettings(); if (buildSettings && buildSettings->tileSize > 0) m_tileSize = buildSettings->tileSize; cleanup(); dtFreeNavMesh(m_navMesh); m_navMesh = 0; if (m_tool) { m_tool->reset(); m_tool->init(this); } resetToolStates(); initToolStates(this); }
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 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_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; }