unsigned char* Sample_TileMesh::buildTileMesh(const int tx, const int ty, const float* bmin, const float* bmax, int& dataSize) { if (!m_geom || !m_geom->getMesh() || !m_geom->getChunkyMesh()) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Input mesh is not specified."); return 0; } m_tileMemUsage = 0; m_tileBuildTime = 0; cleanup(); const float* verts = m_geom->getMesh()->getVerts(); const int nverts = m_geom->getMesh()->getVertCount(); const int ntris = m_geom->getMesh()->getTriCount(); const rcChunkyTriMesh* chunkyMesh = m_geom->getChunkyMesh(); // 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.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; // Expand the heighfield bounding box by border size to find the extents of geometry we need to build this tile. // // This is done in order to make sure that the navmesh tiles connect correctly at the borders, // and the obstacles close to the border work correctly with the dilation process. // No polygons (or contours) will be created on the border area. // // IMPORTANT! // // :''''''''': // : +-----+ : // : | | : // : | |<--- tile to build // : | | : // : +-----+ :<-- geometry needed // :.........: // // You should use this bounding box to query your input geometry. // // For example if you build a navmesh for terrain, and want the navmesh tiles to match the terrain tile size // you will need to pass in data from neighbour terrain tiles too! In a simple case, just pass in all the 8 neighbours, // or use the bounding box below to only pass in a sliver of each of the 8 neighbours. rcVcopy(m_cfg.bmin, bmin); rcVcopy(m_cfg.bmax, bmax); m_cfg.bmin[0] -= m_cfg.borderSize*m_cfg.cs; m_cfg.bmin[2] -= m_cfg.borderSize*m_cfg.cs; m_cfg.bmax[0] += m_cfg.borderSize*m_cfg.cs; m_cfg.bmax[2] += m_cfg.borderSize*m_cfg.cs; // 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); // 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 0; } 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 0; } // Allocate array that can hold triangle flags. // If you have multiple meshes you need to process, allocate // and array which can hold the max number of triangles you need to process. m_triareas = new AreaType[chunkyMesh->maxTrisPerChunk]; if (!m_triareas) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'm_triareas' (%d).", chunkyMesh->maxTrisPerChunk); return 0; } float tbmin[2], tbmax[2]; tbmin[0] = m_cfg.bmin[0]; tbmin[1] = m_cfg.bmin[2]; tbmax[0] = m_cfg.bmax[0]; tbmax[1] = m_cfg.bmax[2]; int cid[512];// TODO: Make grow when returning too many items. const int ncid = rcGetChunksOverlappingRect(chunkyMesh, tbmin, tbmax, cid, 512); if (!ncid) return 0; m_tileTriCount = 0; for (int i = 0; i < ncid; ++i) { const rcChunkyTriMeshNode& node = chunkyMesh->nodes[cid[i]]; const int* ctris = &chunkyMesh->tris[node.i*3]; const int nctris = node.n; m_tileTriCount += nctris; memset(m_triareas, 0, nctris*sizeof(unsigned char)); rcMarkWalkableTriangles(m_ctx, m_cfg.walkableSlopeAngle, verts, nverts, ctris, nctris, m_triareas); if (!rcRasterizeTriangles(m_ctx, verts, nverts, ctris, m_triareas, nctris, *m_solid, m_cfg.walkableClimb)) return 0; } if (!m_keepInterResults) { delete [] m_triareas; m_triareas = 0; } // Once all geometry is rasterized, we do initial pass of filtering to // remove unwanted overhangs caused by the conservative rasterization // as well as filter spans where the character cannot possibly stand. rcFilterLowHangingWalkableObstacles(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); // 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 0; } 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 0; } 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 0; } // (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 0; } // 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_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build watershed regions."); return 0; } } 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, m_cfg.borderSize, m_cfg.minRegionArea, m_cfg.mergeRegionArea)) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build monotone regions."); return 0; } } else // SAMPLE_PARTITION_LAYERS { // Partition the walkable surface into simple regions without holes. if (!rcBuildLayerRegions(m_ctx, *m_chf, m_cfg.borderSize, m_cfg.minRegionArea)) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build layer regions."); return 0; } } // Create contours. m_cset = rcAllocContourSet(); if (!m_cset) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'cset'."); return 0; } 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 0; } if (m_cset->nconts == 0) { return 0; } // Build polygon navmesh from the contours. m_pmesh = rcAllocPolyMesh(); if (!m_pmesh) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'pmesh'."); return 0; } if (!rcBuildPolyMesh(m_ctx, *m_cset, m_cfg.maxVertsPerPoly, *m_pmesh)) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not triangulate contours."); return 0; } // Build detail mesh. m_dmesh = rcAllocPolyMeshDetail(); if (!m_dmesh) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'dmesh'."); return 0; } if (!rcBuildPolyMeshDetail(m_ctx, *m_pmesh, *m_chf, m_cfg.detailSampleDist, m_cfg.detailSampleMaxError, *m_dmesh)) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could build polymesh detail."); return 0; } if (!m_keepInterResults) { rcFreeCompactHeightfield(m_chf); m_chf = 0; rcFreeContourSet(m_cset); m_cset = 0; } unsigned char* navData = 0; int navDataSize = 0; if (m_cfg.maxVertsPerPoly <= DT_VERTS_PER_POLYGON) { if (m_pmesh->nverts >= 0xffff) { // The vertex indices are ushorts, and cannot point to more than 0xffff vertices. m_ctx->log(RC_LOG_ERROR, "Too many vertices per tile %d (max: %d).", m_pmesh->nverts, 0xffff); return 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.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; params.tileX = tx; params.tileY = ty; params.tileLayer = 0; 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 0; } } m_tileMemUsage = navDataSize/1024.0f; 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_tileBuildTime = m_ctx->getAccumulatedTime(RC_TIMER_TOTAL)/1000.0f; dataSize = navDataSize; return navData; }
unsigned char* Sample_TileMesh::buildTileMesh(const int tx, const int ty, const float* bmin, const float* bmax, int& dataSize) { if (!m_geom || !m_geom->getMesh() || !m_geom->getChunkyMesh()) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Input mesh is not specified."); return 0; } m_tileMemUsage = 0; m_tileBuildTime = 0; cleanup(); const float* verts = m_geom->getMesh()->getVerts(); const int nverts = m_geom->getMesh()->getVertCount(); const int ntris = m_geom->getMesh()->getTriCount(); const rcChunkyTriMesh* chunkyMesh = m_geom->getChunkyMesh(); // 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.minRegionSize = (int)rcSqr(m_regionMinSize); m_cfg.mergeRegionSize = (int)rcSqr(m_regionMergeSize); 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; rcVcopy(m_cfg.bmin, bmin); rcVcopy(m_cfg.bmax, bmax); m_cfg.bmin[0] -= m_cfg.borderSize*m_cfg.cs; m_cfg.bmin[2] -= m_cfg.borderSize*m_cfg.cs; m_cfg.bmax[0] += m_cfg.borderSize*m_cfg.cs; m_cfg.bmax[2] += m_cfg.borderSize*m_cfg.cs; // Reset build times gathering. m_ctx->resetBuildTimes(); // Start the build process. rcTimeVal totStartTime = m_ctx->getTime(); 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); // Allocate voxel heighfield 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 0; } 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 0; } // Allocate array that can hold triangle flags. // If you have multiple meshes you need to process, allocate // and array which can hold the max number of triangles you need to process. m_triareas = new unsigned char[chunkyMesh->maxTrisPerChunk]; if (!m_triareas) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'm_triareas' (%d).", chunkyMesh->maxTrisPerChunk); return 0; } float tbmin[2], tbmax[2]; tbmin[0] = m_cfg.bmin[0]; tbmin[1] = m_cfg.bmin[2]; tbmax[0] = m_cfg.bmax[0]; tbmax[1] = m_cfg.bmax[2]; int cid[512];// TODO: Make grow when returning too many items. const int ncid = rcGetChunksInRect(chunkyMesh, tbmin, tbmax, cid, 512); if (!ncid) return 0; m_tileTriCount = 0; for (int i = 0; i < ncid; ++i) { const rcChunkyTriMeshNode& node = chunkyMesh->nodes[cid[i]]; const int* tris = &chunkyMesh->tris[node.i*3]; const int ntris = node.n; m_tileTriCount += ntris; 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; } // Once all geometry is rasterized, we do initial pass of filtering to // remove unwanted overhangs caused by the conservative rasterization // as well as filter spans where the character cannot possibly stand. rcFilterLowHangingWalkableObstacles(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); // 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 0; } 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 0; } 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); // 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 0; } // Partition the walkable surface into simple regions without holes. if (!rcBuildRegions(m_ctx, *m_chf, m_cfg.borderSize, m_cfg.minRegionSize, m_cfg.mergeRegionSize)) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build regions."); return 0; } // Create contours. m_cset = rcAllocContourSet(); if (!m_cset) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'cset'."); return 0; } 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 0; } if (m_cset->nconts == 0) { return 0; } // Build polygon navmesh from the contours. m_pmesh = rcAllocPolyMesh(); if (!m_pmesh) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'pmesh'."); return 0; } if (!rcBuildPolyMesh(m_ctx, *m_cset, m_cfg.maxVertsPerPoly, *m_pmesh)) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not triangulate contours."); return 0; } // Build detail mesh. m_dmesh = rcAllocPolyMeshDetail(); if (!m_dmesh) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'dmesh'."); return 0; } if (!rcBuildPolyMeshDetail(m_ctx, *m_pmesh, *m_chf, m_cfg.detailSampleDist, m_cfg.detailSampleMaxError, *m_dmesh)) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could build polymesh detail."); return 0; } if (!m_keepInterResults) { rcFreeCompactHeightfield(m_chf); m_chf = 0; rcFreeContourSet(m_cset); m_cset = 0; } unsigned char* navData = 0; int navDataSize = 0; if (m_cfg.maxVertsPerPoly <= DT_VERTS_PER_POLYGON) { // Remove padding from the polymesh data. TODO: Remove this odditity. for (int i = 0; i < m_pmesh->nverts; ++i) { unsigned short* v = &m_pmesh->verts[i*3]; v[0] -= (unsigned short)m_cfg.borderSize; v[2] -= (unsigned short)m_cfg.borderSize; } if (m_pmesh->nverts >= 0xffff) { // The vertex indices are ushorts, and cannot point to more than 0xffff vertices. m_ctx->log(RC_LOG_ERROR, "Too many vertices per tile %d (max: %d).", m_pmesh->nverts, 0xffff); return false; } // 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.offMeshConCount = m_geom->getOffMeshConnectionCount(); params.walkableHeight = m_agentHeight; params.walkableRadius = m_agentRadius; params.walkableClimb = m_agentMaxClimb; params.tileX = tx; params.tileY = ty; rcVcopy(params.bmin, bmin); rcVcopy(params.bmax, bmax); params.cs = m_cfg.cs; params.ch = m_cfg.ch; params.tileSize = m_cfg.tileSize; if (!dtCreateNavMeshData(¶ms, &navData, &navDataSize)) { m_ctx->log(RC_LOG_ERROR, "Could not build Detour navmesh."); return 0; } // Restore padding so that the debug visualization is correct. for (int i = 0; i < m_pmesh->nverts; ++i) { unsigned short* v = &m_pmesh->verts[i*3]; v[0] += (unsigned short)m_cfg.borderSize; v[2] += (unsigned short)m_cfg.borderSize; } } m_tileMemUsage = navDataSize/1024.0f; rcTimeVal totEndTime = m_ctx->getTime(); // Show performance stats. duLogBuildTimes(m_ctx, m_ctx->getDeltaTimeUsec(totStartTime, totEndTime)); m_ctx->log(RC_LOG_PROGRESS, ">> Polymesh: %d vertices %d polygons", m_pmesh->nverts, m_pmesh->npolys); m_tileBuildTime = m_ctx->getDeltaTimeUsec(totStartTime, totEndTime)/1000.0f; dataSize = navDataSize; return navData; }
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); 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 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); 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, Object *obj, unsigned int mesh_index ) { unsigned int i = 0, j = 0, k = 0, triangle_count = 0; int *indices = NULL; Mesh *objmesh = obj->meshList[ mesh_index ]; // vec3 *vertex_array = ( vec3 * ) malloc( objmesh->n_objvertexdata * sizeof( vec3 ) ), vec3 *vertex_array = ( vec3 * ) malloc( objmesh->uniqueVertexUVIndexList.size() * sizeof( vec3 ) ), *vertex_start = vertex_array; rcHeightfield *rcheightfield; rcCompactHeightfield *rccompactheightfield; rcContourSet *rccontourset; rcPolyMesh *rcpolymesh; rcPolyMeshDetail *rcpolymeshdetail; while( i != objmesh->uniqueVertexUVIndexList.size() ) { memcpy( vertex_array, &obj->vertexMgr.uniqueVertexList[objmesh->uniqueVertexUVIndexList[i].vertexIndex], sizeof( vec3 ) ); *vertex_array = vertex_array->toRecast(); // vec3_to_recast( vertex_array ); ++vertex_array; ++i; } // 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; // } triangle_count += objmesh->vertexIndexList.size(); indices = ( int * ) realloc( indices, triangle_count * sizeof( int ) ); j = 0; while( j != objmesh->vertexIndexList.size() ) { indices[ k ] = objmesh->vertexIndexList[ j ]; ++k; ++j; } // // 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, (int)objmesh->uniqueVertexUVIndexList.size(), // 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, (int)objmesh->uniqueVertexUVIndexList.size(), // objmesh->n_objvertexdata, indices, triangle_count, navigation->triangle_flags ); rcRasterizeTriangles( ( float * )vertex_start, (int)objmesh->uniqueVertexUVIndexList.size(), // 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; }
static int rasterizeTileLayers(BuildContext* ctx, InputGeom* geom, const int tx, const int ty, const rcConfig& cfg, TileCacheData* tiles, const int maxTiles) { if (!geom || !geom->getMesh() || !geom->getChunkyMesh()) { ctx->log(RC_LOG_ERROR, "buildTile: Input mesh is not specified."); return 0; } FastLZCompressor comp; RasterizationContext rc; const float* verts = geom->getMesh()->getVerts(); const int nverts = geom->getMesh()->getVertCount(); const rcChunkyTriMesh* chunkyMesh = geom->getChunkyMesh(); // Tile bounds. const float tcs = cfg.tileSize * cfg.cellSizeXZ; rcConfig tcfg; memcpy(&tcfg, &cfg, sizeof(tcfg)); tcfg.bmin[0] = cfg.bmin[0] + tx*tcs; tcfg.bmin[1] = cfg.bmin[1]; tcfg.bmin[2] = cfg.bmin[2] + ty*tcs; tcfg.bmax[0] = cfg.bmin[0] + (tx+1)*tcs; tcfg.bmax[1] = cfg.bmax[1]; tcfg.bmax[2] = cfg.bmin[2] + (ty+1)*tcs; tcfg.bmin[0] -= tcfg.borderSize*tcfg.cellSizeXZ; tcfg.bmin[2] -= tcfg.borderSize*tcfg.cellSizeXZ; tcfg.bmax[0] += tcfg.borderSize*tcfg.cellSizeXZ; tcfg.bmax[2] += tcfg.borderSize*tcfg.cellSizeXZ; // Allocate voxel heightfield where we rasterize our input data to. rc.solid = rcAllocHeightfield(); if (!rc.solid) { ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'solid'."); return 0; } if (!rcCreateHeightfield(ctx, *rc.solid, tcfg.width, tcfg.height, tcfg.bmin, tcfg.bmax, tcfg.cellSizeXZ, tcfg.cellSizeY)) { ctx->log(RC_LOG_ERROR, "buildNavigation: Could not create solid heightfield."); return 0; } // Allocate array that can hold triangle flags. // If you have multiple meshes you need to process, allocate // and array which can hold the max number of triangles you need to process. rc.triareaMask = new navAreaMask[chunkyMesh->maxTrisPerChunk]; if (!rc.triareaMask) { ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'm_triareas' (%d).", chunkyMesh->maxTrisPerChunk); return 0; } float tbmin[2], tbmax[2]; tbmin[0] = tcfg.bmin[0]; tbmin[1] = tcfg.bmin[2]; tbmax[0] = tcfg.bmax[0]; tbmax[1] = tcfg.bmax[2]; int cid[512];// TODO: Make grow when returning too many items. const int ncid = rcGetChunksOverlappingRect(chunkyMesh, tbmin, tbmax, cid, 512); if (!ncid) { return 0; // empty } for (int i = 0; i < ncid; ++i) { const rcChunkyTriMeshNode& node = chunkyMesh->nodes[cid[i]]; const int* tris = &chunkyMesh->tris[node.i*3]; const int ntris = node.n; memset(rc.triareaMask, 0, ntris*sizeof(navAreaMask)); rcMarkWalkableTriangles(ctx, tcfg.walkableSlopeAngle, verts, nverts, tris, ntris, rc.triareaMask); rcRasterizeTriangles(ctx, verts, nverts, tris, rc.triareaMask, ntris, *rc.solid, tcfg.walkableClimb); } // Once all geometry is rasterized, we do initial pass of filtering to // remove unwanted overhangs caused by the conservative rasterization // as well as filter spans where the character cannot possibly stand. rcFilterLowHangingWalkableObstacles(ctx, tcfg.walkableClimb, *rc.solid); rcFilterLedgeSpans(ctx, tcfg.walkableHeight, tcfg.walkableClimb, *rc.solid); rcFilterWalkableLowHeightSpans(ctx, tcfg.walkableHeight, *rc.solid); rc.chf = rcAllocCompactHeightfield(); if (!rc.chf) { ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'chf'."); return 0; } if (!rcBuildCompactHeightfield(ctx, tcfg.walkableHeight, tcfg.walkableClimb, *rc.solid, *rc.chf)) { ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build compact data."); return 0; } // Erode the walkable area by agent radius. if (!rcErodeWalkableArea(ctx, tcfg.walkableRadius, *rc.chf)) { ctx->log(RC_LOG_ERROR, "buildNavigation: Could not erode."); return 0; } // (Optional) Mark areas. const ConvexVolume* vols = geom->getConvexVolumes(); for (int i = 0; i < geom->getConvexVolumeCount(); ++i) { rcMarkConvexPolyArea(ctx, vols[i].verts, vols[i].nverts, vols[i].hmin, vols[i].hmax, vols[i].areaMask, *rc.chf); } rc.lset = rcAllocHeightfieldLayerSet(); if (!rc.lset) { ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'lset'."); return 0; } if (!rcBuildHeightfieldLayers(ctx, *rc.chf, tcfg.borderSize, tcfg.walkableHeight, *rc.lset)) { ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build heighfield layers."); return 0; } rc.ntiles = 0; for (int i = 0; i < rcMin(rc.lset->nlayers, MAX_LAYERS); ++i) { TileCacheData* tile = &rc.tiles[rc.ntiles++]; const rcHeightfieldLayer* layer = &rc.lset->layers[i]; // Store header dtTileCacheLayerHeader header; header.magic = DT_TILECACHE_MAGIC; header.version = DT_TILECACHE_VERSION; // Tile layer location in the navmesh. header.tx = tx; header.ty = ty; header.tlayer = i; dtVcopy(header.bmin, layer->bmin); dtVcopy(header.bmax, layer->bmax); // Tile info. header.width = (unsigned char)layer->width; header.height = (unsigned char)layer->height; header.minx = (unsigned char)layer->minx; header.maxx = (unsigned char)layer->maxx; header.miny = (unsigned char)layer->miny; header.maxy = (unsigned char)layer->maxy; header.hmin = (unsigned short)layer->hmin; header.hmax = (unsigned short)layer->hmax; dtStatus status = dtBuildTileCacheLayer(&comp, &header, layer->heights, layer->areaMasks, layer->cons, &tile->data, &tile->dataSize); if (dtStatusFailed(status)) { return 0; } } // Transfer ownsership of tile data from build context to the caller. int n = 0; for (int i = 0; i < rcMin(rc.ntiles, maxTiles); ++i) { tiles[n++] = rc.tiles[i]; rc.tiles[i].data = 0; rc.tiles[i].dataSize = 0; } return n; }
bool NavigationMesh::BuildTile(Vector<NavigationGeometryInfo>& geometryList, int x, int z) { PROFILE(BuildNavigationMeshTile); // Remove previous tile (if any) navMesh_->removeTile(navMesh_->getTileRefAt(x, z, 0), 0, 0); float tileEdgeLength = (float)tileSize_ * cellSize_; BoundingBox tileBoundingBox(Vector3( boundingBox_.min_.x_ + tileEdgeLength * (float)x, boundingBox_.min_.y_, boundingBox_.min_.z_ + tileEdgeLength * (float)z ), Vector3( boundingBox_.min_.x_ + tileEdgeLength * (float)(x + 1), boundingBox_.max_.y_, boundingBox_.min_.z_ + tileEdgeLength * (float)(z + 1) )); NavigationBuildData build; rcConfig cfg; memset(&cfg, 0, sizeof cfg); cfg.cs = cellSize_; cfg.ch = cellHeight_; cfg.walkableSlopeAngle = agentMaxSlope_; cfg.walkableHeight = (int)ceilf(agentHeight_ / cfg.ch); cfg.walkableClimb = (int)floorf(agentMaxClimb_ / cfg.ch); cfg.walkableRadius = (int)ceilf(agentRadius_ / cfg.cs); cfg.maxEdgeLen = (int)(edgeMaxLength_ / cellSize_); cfg.maxSimplificationError = edgeMaxError_; cfg.minRegionArea = (int)sqrtf(regionMinSize_); cfg.mergeRegionArea = (int)sqrtf(regionMergeSize_); cfg.maxVertsPerPoly = 6; cfg.tileSize = tileSize_; cfg.borderSize = cfg.walkableRadius + 3; // Add padding cfg.width = cfg.tileSize + cfg.borderSize * 2; cfg.height = cfg.tileSize + cfg.borderSize * 2; cfg.detailSampleDist = detailSampleDistance_ < 0.9f ? 0.0f : cellSize_ * detailSampleDistance_; cfg.detailSampleMaxError = cellHeight_ * detailSampleMaxError_; rcVcopy(cfg.bmin, &tileBoundingBox.min_.x_); rcVcopy(cfg.bmax, &tileBoundingBox.max_.x_); cfg.bmin[0] -= cfg.borderSize * cfg.cs; cfg.bmin[2] -= cfg.borderSize * cfg.cs; cfg.bmax[0] += cfg.borderSize * cfg.cs; cfg.bmax[2] += cfg.borderSize * cfg.cs; BoundingBox expandedBox(*reinterpret_cast<Vector3*>(cfg.bmin), *reinterpret_cast<Vector3*>(cfg.bmax)); GetTileGeometry(build, geometryList, expandedBox); if (build.vertices_.Empty() || build.indices_.Empty()) return true; // Nothing to do build.heightField_ = rcAllocHeightfield(); if (!build.heightField_) { LOGERROR("Could not allocate heightfield"); return false; } if (!rcCreateHeightfield(build.ctx_, *build.heightField_, cfg.width, cfg.height, cfg.bmin, cfg.bmax, cfg.cs, cfg.ch)) { LOGERROR("Could not create heightfield"); return false; } unsigned numTriangles = build.indices_.Size() / 3; SharedArrayPtr<unsigned char> triAreas(new unsigned char[numTriangles]); memset(triAreas.Get(), 0, numTriangles); rcMarkWalkableTriangles(build.ctx_, cfg.walkableSlopeAngle, &build.vertices_[0].x_, build.vertices_.Size(), &build.indices_[0], numTriangles, triAreas.Get()); rcRasterizeTriangles(build.ctx_, &build.vertices_[0].x_, build.vertices_.Size(), &build.indices_[0], triAreas.Get(), numTriangles, *build.heightField_, cfg.walkableClimb); rcFilterLowHangingWalkableObstacles(build.ctx_, cfg.walkableClimb, *build.heightField_); rcFilterLedgeSpans(build.ctx_, cfg.walkableHeight, cfg.walkableClimb, *build.heightField_); rcFilterWalkableLowHeightSpans(build.ctx_, cfg.walkableHeight, *build.heightField_); build.compactHeightField_ = rcAllocCompactHeightfield(); if (!build.compactHeightField_) { LOGERROR("Could not allocate create compact heightfield"); return false; } if (!rcBuildCompactHeightfield(build.ctx_, cfg.walkableHeight, cfg.walkableClimb, *build.heightField_, *build.compactHeightField_)) { LOGERROR("Could not build compact heightfield"); return false; } if (!rcErodeWalkableArea(build.ctx_, cfg.walkableRadius, *build.compactHeightField_)) { LOGERROR("Could not erode compact heightfield"); return false; } if (!rcBuildDistanceField(build.ctx_, *build.compactHeightField_)) { LOGERROR("Could not build distance field"); return false; } if (!rcBuildRegions(build.ctx_, *build.compactHeightField_, cfg.borderSize, cfg.minRegionArea, cfg.mergeRegionArea)) { LOGERROR("Could not build regions"); return false; } build.contourSet_ = rcAllocContourSet(); if (!build.contourSet_) { LOGERROR("Could not allocate contour set"); return false; } if (!rcBuildContours(build.ctx_, *build.compactHeightField_, cfg.maxSimplificationError, cfg.maxEdgeLen, *build.contourSet_)) { LOGERROR("Could not create contours"); return false; } build.polyMesh_ = rcAllocPolyMesh(); if (!build.polyMesh_) { LOGERROR("Could not allocate poly mesh"); return false; } if (!rcBuildPolyMesh(build.ctx_, *build.contourSet_, cfg.maxVertsPerPoly, *build.polyMesh_)) { LOGERROR("Could not triangulate contours"); return false; } build.polyMeshDetail_ = rcAllocPolyMeshDetail(); if (!build.polyMeshDetail_) { LOGERROR("Could not allocate detail mesh"); return false; } if (!rcBuildPolyMeshDetail(build.ctx_, *build.polyMesh_, *build.compactHeightField_, cfg.detailSampleDist, cfg.detailSampleMaxError, *build.polyMeshDetail_)) { LOGERROR("Could not build detail mesh"); return false; } // Set polygon flags /// \todo Allow to define custom flags for (int i = 0; i < build.polyMesh_->npolys; ++i) { if (build.polyMesh_->areas[i] == RC_WALKABLE_AREA) build.polyMesh_->flags[i] = 0x1; } unsigned char* navData = 0; int navDataSize = 0; dtNavMeshCreateParams params; memset(¶ms, 0, sizeof params); params.verts = build.polyMesh_->verts; params.vertCount = build.polyMesh_->nverts; params.polys = build.polyMesh_->polys; params.polyAreas = build.polyMesh_->areas; params.polyFlags = build.polyMesh_->flags; params.polyCount = build.polyMesh_->npolys; params.nvp = build.polyMesh_->nvp; params.detailMeshes = build.polyMeshDetail_->meshes; params.detailVerts = build.polyMeshDetail_->verts; params.detailVertsCount = build.polyMeshDetail_->nverts; params.detailTris = build.polyMeshDetail_->tris; params.detailTriCount = build.polyMeshDetail_->ntris; params.walkableHeight = agentHeight_; params.walkableRadius = agentRadius_; params.walkableClimb = agentMaxClimb_; params.tileX = x; params.tileY = z; rcVcopy(params.bmin, build.polyMesh_->bmin); rcVcopy(params.bmax, build.polyMesh_->bmax); params.cs = cfg.cs; params.ch = cfg.ch; params.buildBvTree = true; // Add off-mesh connections if have them if (build.offMeshRadii_.Size()) { params.offMeshConCount = build.offMeshRadii_.Size(); params.offMeshConVerts = &build.offMeshVertices_[0].x_; params.offMeshConRad = &build.offMeshRadii_[0]; params.offMeshConFlags = &build.offMeshFlags_[0]; params.offMeshConAreas = &build.offMeshAreas_[0]; params.offMeshConDir = &build.offMeshDir_[0]; } if (!dtCreateNavMeshData(¶ms, &navData, &navDataSize)) { LOGERROR("Could not build navigation mesh tile data"); return false; } if (dtStatusFailed(navMesh_->addTile(navData, navDataSize, DT_TILE_FREE_DATA, 0, 0))) { LOGERROR("Failed to add navigation mesh tile"); dtFree(navData); return false; } return true; }
bool NavMesh::createPolyMesh(rcConfig &cfg, NavModelData &data, rcContext *ctx) { // Create a heightfield to voxelise our input geometry hf = rcAllocHeightfield(); if(!hf || !rcCreateHeightfield(ctx, *hf, cfg.width, cfg.height, cfg.bmin, cfg.bmax, cfg.cs, cfg.ch)) { Con::errorf("Could not generate rcHeightField for NavMesh %s", getIdString()); return false; } unsigned char *areas = new unsigned char[data.getTriCount()]; if (!areas) { Con::errorf("Out of memory (area flags) for NavMesh %s", getIdString()); return false; } memset(areas, 0, data.getTriCount()*sizeof(unsigned char)); // Subtract 1 from all indices! for(U32 i = 0; i < data.getTriCount(); i++) { data.tris[i*3]--; data.tris[i*3+1]--; data.tris[i*3+2]--; } // Filter triangles by angle and rasterize rcMarkWalkableTriangles(ctx, cfg.walkableSlopeAngle, data.getVerts(), data.getVertCount(), data.getTris(), data.getTriCount(), areas); rcRasterizeTriangles(ctx, data.getVerts(), data.getVertCount(), data.getTris(), areas, data.getTriCount(), *hf, cfg.walkableClimb); delete [] areas; // Filter out areas with low ceilings and other stuff rcFilterLowHangingWalkableObstacles(ctx, cfg.walkableClimb, *hf); rcFilterLedgeSpans(ctx, cfg.walkableHeight, cfg.walkableClimb, *hf); rcFilterWalkableLowHeightSpans(ctx, cfg.walkableHeight, *hf); chf = rcAllocCompactHeightfield(); if(!chf || !rcBuildCompactHeightfield(ctx, cfg.walkableHeight, cfg.walkableClimb, *hf, *chf)) { Con::errorf("Could not generate rcCompactHeightField for NavMesh %s", getIdString()); return false; } if(!rcErodeWalkableArea(ctx, cfg.walkableRadius, *chf)) { Con::errorf("Could not erode walkable area for NavMesh %s", getIdString()); return false; } if(false) { if(!rcBuildRegionsMonotone(ctx, *chf, cfg.borderSize, cfg.minRegionArea, cfg.mergeRegionArea)) { Con::errorf("Could not build regions for NavMesh %s", getIdString()); return false; } } else { if(!rcBuildDistanceField(ctx, *chf)) return false; if(!rcBuildRegions(ctx, *chf, cfg.borderSize, cfg.minRegionArea, cfg.mergeRegionArea)) return false; } cs = rcAllocContourSet(); if(!cs || !rcBuildContours(ctx, *chf, cfg.maxSimplificationError, cfg.maxEdgeLen, *cs)) { Con::errorf("Could not construct rcContourSet for NavMesh %s", getIdString()); return false; } pm = rcAllocPolyMesh(); if(!pm || !rcBuildPolyMesh(ctx, *cs, cfg.maxVertsPerPoly, *pm)) { Con::errorf("Could not construct rcPolyMesh for NavMesh %s", getIdString()); return false; } pmd = rcAllocPolyMeshDetail(); if(!pmd || !rcBuildPolyMeshDetail(ctx, *pm, *chf, cfg.detailSampleDist, cfg.detailSampleMaxError, *pmd)) { Con::errorf("Could not construct rcPolyMeshDetail for NavMesh %s", getIdString()); return false; } return true; }
/* * Tries to load vmap and tilemap for a gridtile and creates a navmesh for it. * */ bool ModelContainerView::generateMoveMapForTile (int pMapId, int x, int y) { bool result = iVMapManager.loadMap (gVMapDataDir.c_str (), pMapId, x, y) == VMAP_LOAD_RESULT_OK; if (result == VMAP_LOAD_RESULT_OK) { //VMap loaded. Add data from vmap to global Triangle-Array parseVMap (pMapId, x, y); } // Add data from Height-Map to global Triangle-Array generateHeightMap(pMapId,x,y); // We will now add all triangles inside the given zone to the vectormap. // We could also do additional checks here. double x_max = (32-x)*SIZE_OF_GRIDS + 50; double y_max = (32-y)*SIZE_OF_GRIDS + 50; double x_min = x_max - SIZE_OF_GRIDS - 100; double y_min = y_max - SIZE_OF_GRIDS - 100; Vector3 low = Vector3(x_min,y_min,-inf()); Vector3 high = Vector3(x_max,y_max,inf()); AABox checkBox = AABox(low,high); AABox check; Triangle t; //each triangle has mangos format. for (int i = 0; i < globalTriangleArray.size(); i++) { t = globalTriangleArray[i]; t.getBounds(check); if (checkBox.contains(check)) { // Write it down in detour format. iGlobArray.append(t.vertex(0).y,t.vertex(0).z,t.vertex(0).x); iGlobArray.append(t.vertex(1).y,t.vertex(1).z,t.vertex(1).x); iGlobArray.append(t.vertex(2).y,t.vertex(2).z,t.vertex(2).x); } } if (iGlobArray.size() == 0) { printf("No models - check your mmap.datadir in your config"); return true; } if(gMakeObjFile) debugGenerateObjFile(); // create obj file for Recast Demo viewer //return true; float bmin[3], bmax[3]; /* * The format looks like this * Verticle = float[3] * Triangle = Verticle[3] * So there are * array.size() floats * that means there are * nverts = array.size()/3 Verticles * that means there are * ntris = nverts/3 */ //array/3 verticles const int nverts = iGlobArray.size()/3; // because 1 vert is 3 float. // -> vert = float[3] const float* verts = iGlobArray.getCArray(); rcCalcBounds(verts,nverts,bmin,bmax); // nverts/3 triangles // -> Triangle = vert[3] = float[9] int* tris = new int[nverts];// because 1 triangle is 3 verts for (int i = 0; i< nverts; i++) tris[i] = i; /* tris[i] = 1,2,3;4,5,6;7,8,9; * */ const int ntris = (nverts/3); rcConfig m_cfg; // // Step 1. Initialize build config. // // Init build configuration from GUI memset(&m_cfg, 0, sizeof(m_cfg)); // Change config settings here! m_cfg.cs = 0.3f; m_cfg.ch = 0.2f; m_cfg.walkableSlopeAngle = 50.0f; m_cfg.walkableHeight = 10; m_cfg.walkableClimb = 4; m_cfg.walkableRadius = 2; m_cfg.maxEdgeLen = (int)(12 / 0.3f); m_cfg.maxSimplificationError = 1.3f; m_cfg.minRegionSize = (int)rcSqr(50); m_cfg.mergeRegionSize = (int)rcSqr(20); m_cfg.maxVertsPerPoly = (int)6; m_cfg.detailSampleDist = 1.8f; m_cfg.detailSampleMaxError = 0.2f * 1; bool m_keepInterResults = false; printf("CellSize : %.2f\n",m_cfg.cs); printf("CellHeight : %.2f\n",m_cfg.ch); printf("WalkableSlope : %.2f\n",m_cfg.walkableSlopeAngle); printf("WalkableHeight : %i\n",m_cfg.walkableHeight); printf("walkableClimb : %i\n",m_cfg.walkableClimb); printf("walkableRadius : %i\n",m_cfg.walkableRadius); printf("maxEdgeLen : %i\n",m_cfg.maxEdgeLen); printf("maxSimplific.Er.: %.2f\n",m_cfg.maxSimplificationError); printf("minRegionSize : %i\n",m_cfg.minRegionSize); printf("mergedRegSize : %i\n",m_cfg.mergeRegionSize); printf("maxVertsPerPoly : %i\n",m_cfg.maxVertsPerPoly); printf("detailSampledist: %.2f\n",m_cfg.detailSampleDist); printf("det.Samp.max.err: %.2f\n",m_cfg.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. vcopy(m_cfg.bmin, bmin); vcopy(m_cfg.bmax, bmax); rcCalcGridSize(m_cfg.bmin, m_cfg.bmax, m_cfg.cs, &m_cfg.width, &m_cfg.height); // // Step 2. Rasterize input polygon soup. // // Allocate voxel heighfield where we rasterize our input data to. rcHeightfield* m_solid = new rcHeightfield; if (!m_solid) { printf("buildNavigation: Out of memory 'solid'.\n"); return false; } if (!rcCreateHeightfield(*m_solid, m_cfg.width, m_cfg.height, m_cfg.bmin, m_cfg.bmax, m_cfg.cs, m_cfg.ch)) { printf("buildNavigation: Could not create solid heightfield.\n"); return false; } // Allocate array that can hold triangle flags. // If you have multiple meshes you need to process, allocate // and array which can hold the max number of triangles you need to process. unsigned char* m_triflags = new unsigned char[ntris]; if (!m_triflags) { printf("buildNavigation: Out of memory 'triangleFlags' (%d).\n", 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 flags for each of the meshes and rasterize them. memset(m_triflags, 0, ntris*sizeof(unsigned char)); rcMarkWalkableTriangles(m_cfg.walkableSlopeAngle, verts, nverts, tris, ntris, m_triflags); rcRasterizeTriangles(verts, nverts, tris, m_triflags, ntris, *m_solid, m_cfg.walkableClimb); // should delete [] verts? - probably not, this is just pointer to data in a G3D Array // should delete [] tris? if (!m_keepInterResults) { delete [] m_triflags; m_triflags = 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_cfg.walkableClimb, *m_solid); rcFilterLedgeSpans(m_cfg.walkableHeight, m_cfg.walkableClimb, *m_solid); rcFilterWalkableLowHeightSpans(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. rcCompactHeightfield* m_chf = new rcCompactHeightfield; if (!m_chf) { printf("buildNavigation: Out of memory 'chf'.\n"); return false; } if (!rcBuildCompactHeightfield(m_cfg.walkableHeight, m_cfg.walkableClimb, RC_WALKABLE, *m_solid, *m_chf)) { printf( "buildNavigation: Could not build compact data.\n"); return false; } if (!m_keepInterResults) { delete m_solid; m_solid = 0; } // Erode the walkable area by agent radius. if (!rcErodeArea(RC_WALKABLE_AREA, m_cfg.walkableRadius, *m_chf)) { printf("buildNavigation: Could not erode.\n"); return false; } // (Optional) Mark areas. //const ConvexVolume* vols = m_geom->getConvexVolumes(); //for (int i = 0; i < m_geom->getConvexVolumeCount(); ++i) // rcMarkConvexPolyArea(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_chf)) { printf("buildNavigation: Could not build distance field.\n"); return false; } // Partition the walkable surface into simple regions without holes. if (!rcBuildRegions(*m_chf, m_cfg.borderSize, m_cfg.minRegionSize, m_cfg.mergeRegionSize)) { printf("buildNavigation: Could not build regions.\n"); } // // Step 5. Trace and simplify region contours. // // Create contours. rcContourSet* m_cset = new rcContourSet; if (!m_cset) { printf("buildNavigation: Out of memory 'cset'.\n"); return false; } if (!rcBuildContours(*m_chf, m_cfg.maxSimplificationError, m_cfg.maxEdgeLen, *m_cset)) { printf("buildNavigation: Could not create contours.\n"); return false; } // // Step 6. Build polygons mesh from contours. // // Build polygon navmesh from the contours. rcPolyMesh* m_pmesh = new rcPolyMesh; if (!m_pmesh) { printf("buildNavigation: Out of memory 'pmesh'.\n"); return false; } if (!rcBuildPolyMesh(*m_cset, m_cfg.maxVertsPerPoly, *m_pmesh)) { printf( "buildNavigation: Could not triangulate contours.\n"); return false; } // // Step 7. Create detail mesh which allows to access approximate height on each polygon. // rcPolyMeshDetail* m_dmesh = new rcPolyMeshDetail; if (!m_dmesh) { printf("buildNavigation: Out of memory 'pmdtl'.\n"); return false; } if (!rcBuildPolyMeshDetail(*m_pmesh, *m_chf, m_cfg.detailSampleDist, m_cfg.detailSampleMaxError, *m_dmesh)) { printf("buildNavigation: Could not build detail mesh.\n"); } if (!m_keepInterResults) { delete m_chf; m_chf = 0; delete 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) { // for now all generated navmesh is walkable by everyone. // else there will be no pathfinding at all! m_pmesh->flags[i] = RC_WALKABLE_AREA; } 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 = 0; params.offMeshConRad = 0; params.offMeshConDir = 0; params.offMeshConAreas = 0; params.offMeshConFlags = 0; params.offMeshConCount = 0; params.walkableHeight = 2.0f; params.walkableRadius = 0.6f; params.walkableClimb = 0.9f; vcopy(params.bmin, m_pmesh->bmin); vcopy(params.bmax, m_pmesh->bmax); params.cs = m_cfg.cs; params.ch = m_cfg.ch; printf("vertcount : %05u\n",params.vertCount); printf("polycount : %05u\n",params.polyCount); printf("detailVertsCount: %05u\n",params.detailVertsCount); printf("detailTriCount : %05u\n",params.detailTriCount); printf("walkableClimb : %.2f\n",params.walkableClimb); printf("walkableRadius : %.2f\n",params.walkableRadius); printf("walkableHeight : %.2f\n",params.walkableHeight); if (!dtCreateNavMeshData(¶ms, &navData, &navDataSize)) { printf("Could not build Detour navmesh.\n"); return false; } // navData now contains the MoveMap printf("Generated Navigation Mesh! Size: %i bytes/ %i kB / %i MB\n",navDataSize,navDataSize/1024,navDataSize/(1024*1024)); char tmp[14]; sprintf(tmp, "%03u%02u%02u.mmap",iMap,ix,iy); std::string savefilepath = gMMapDataDir + "/" + tmp; ofstream inf( savefilepath.c_str(),ofstream::binary ); if( inf ) { inf.write( (char*)( &navData[0] ), navDataSize ) ; } printf("MoveMap saved under %s\n", savefilepath.c_str()); delete [] navData; } // debugLoadNavMesh(); return (result); }