public Detour.dtNavMeshQuery GetNavMeshQuery(uint mapId, uint instanceId, List <uint> swaps) { MMapData mmap = GetMMapData(mapId); if (mmap == null) { return(null); } if (!mmap.navMeshQueries.ContainsKey(instanceId)) { // allocate mesh query Detour.dtNavMeshQuery query = new Detour.dtNavMeshQuery(); if (Detour.dtStatusFailed(query.init(mmap.GetNavMesh(swaps), 1024))) { Log.outError(LogFilter.Maps, "MMAP:GetNavMeshQuery: Failed to initialize dtNavMeshQuery for mapId {0} instanceId {1}", mapId, instanceId); return(null); } Log.outInfo(LogFilter.Maps, "MMAP:GetNavMeshQuery: created dtNavMeshQuery for mapId {0} instanceId {1}", mapId, instanceId); mmap.navMeshQueries.Add(instanceId, query); } return(mmap.navMeshQueries[instanceId]); }
bool loadMapInstanceImpl(string basePath, uint mapId, uint instanceId) { if (!loadMapData(basePath, mapId)) { return(false); } MMapData mmap = loadedMMaps[mapId]; if (mmap.navMeshQueries.ContainsKey(instanceId)) { return(true); } // allocate mesh query Detour.dtNavMeshQuery query = new Detour.dtNavMeshQuery(); if (Detour.dtStatusFailed(query.init(mmap.navMesh, 1024))) { Log.outError(LogFilter.Maps, "MMAP.GetNavMeshQuery: Failed to initialize dtNavMeshQuery for mapId {0:D4} instanceId {1}", mapId, instanceId); return(false); } Log.outDebug(LogFilter.Maps, "MMAP.GetNavMeshQuery: created dtNavMeshQuery for mapId {0:D4} instanceId {1}", mapId, instanceId); mmap.navMeshQueries.Add(instanceId, query); return(true); }
// This function checks if the path has a small U-turn, that is, // a polygon further in the path is adjacent to the first polygon // in the path. If that happens, a shortcut is taken. // This can happen if the target (T) location is at tile boundary, // and we're (S) approaching it parallel to the tile edge. // The choice at the vertex can be arbitrary, // +---+---+ // |:::|:::| // +-S-+-T-+ // |:::| | <-- the step can end up in here, resulting U-turn path. // +---+---+ static int fixupShortcuts(dtPolyRef[] path, int npath, Detour.dtNavMeshQuery navQuery) { if (npath < 3) { return(npath); } // Get connected polygons const int maxNeis = 16; dtPolyRef[] neis = new dtPolyRef[maxNeis]; int nneis = 0; Detour.dtMeshTile tile = null; Detour.dtPoly poly = null; if (Detour.dtStatusFailed(navQuery.getAttachedNavMesh().getTileAndPolyByRef(path[0], ref tile, ref poly))) { return(npath); } for (uint k = poly.firstLink; k != Detour.DT_NULL_LINK; k = tile.links[k].next) { Detour.dtLink link = tile.links[k]; if (link.polyRef != 0) { if (nneis < maxNeis) { neis[nneis++] = link.polyRef; } } } // If any of the neighbour polygons is within the next few polygons // in the path, short cut to that polygon directly. const int maxLookAhead = 6; int cut = 0; for (int i = Math.Min(maxLookAhead, npath) - 1; i > 1 && cut == 0; i--) { for (int j = 0; j < nneis; j++) { if (path[i] == neis[j]) { cut = i; break; } } } if (cut > 1) { int offset = cut - 1; npath -= offset; for (int i = 1; i < npath; i++) { path[i] = path[i + offset]; } } return(npath); }
static bool LoadedTilesCommand(StringArguments args, CommandHandler handler) { Player player = handler.GetSession().GetPlayer(); uint terrainMapId = PhasingHandler.GetTerrainMapId(player.GetPhaseShift(), player.GetMap(), player.GetPositionX(), player.GetPositionY()); Detour.dtNavMesh navmesh = Global.MMapMgr.GetNavMesh(terrainMapId); Detour.dtNavMeshQuery navmeshquery = Global.MMapMgr.GetNavMeshQuery(terrainMapId, handler.GetPlayer().GetInstanceId()); if (navmesh == null || navmeshquery == null) { handler.SendSysMessage("NavMesh not loaded for current map."); return(true); } handler.SendSysMessage("mmap loadedtiles:"); for (int i = 0; i < navmesh.getMaxTiles(); ++i) { Detour.dtMeshTile tile = navmesh.getTile(i); if (tile.header == null) { continue; } handler.SendSysMessage("[{0:D2}, {1:D2}]", tile.header.x, tile.header.y); } return(true); }
static bool LoadedTilesCommand(StringArguments args, CommandHandler handler) { uint mapid = handler.GetPlayer().GetMapId(); Detour.dtNavMesh navmesh = Global.MMapMgr.GetNavMesh(mapid); Detour.dtNavMeshQuery navmeshquery = Global.MMapMgr.GetNavMeshQuery(mapid, handler.GetPlayer().GetInstanceId()); if (navmesh == null || navmeshquery == null) { handler.SendSysMessage("NavMesh not loaded for current map."); return(true); } handler.SendSysMessage("mmap loadedtiles:"); for (int i = 0; i < navmesh.getMaxTiles(); ++i) { Detour.dtMeshTile tile = navmesh.getTile(i); if (tile == null) { continue; } handler.SendSysMessage("[{0:D2}, {1:D2}]", tile.header.x, tile.header.y); } return(true); }
static bool getSteerTarget(Detour.dtNavMeshQuery navQuery, float[] startPos, float[] endPos, float minTargetDist, dtPolyRef[] path, int pathSize, float[] steerPos, ref byte steerPosFlag, ref dtPolyRef steerPosRef, ref float[] outPoints, ref int outPointCount) { // Find steer target. const int MAX_STEER_POINTS = 3; float[] steerPath = new float[MAX_STEER_POINTS * 3]; byte[] steerPathFlags = new byte[MAX_STEER_POINTS]; dtPolyRef[] steerPathPolys = new dtPolyRef[MAX_STEER_POINTS]; int nsteerPath = 0; navQuery.findStraightPath(startPos, endPos, path, pathSize, steerPath, steerPathFlags, steerPathPolys, ref nsteerPath, MAX_STEER_POINTS, 0); if (nsteerPath == 0) { return(false); } //if (outPoints && outPointCount) //{ outPointCount = nsteerPath; for (int i = 0; i < nsteerPath; ++i) { Detour.dtVcopy(outPoints, i * 3, steerPath, i * 3); } //} // Find vertex far enough to steer to. int ns = 0; while (ns < nsteerPath) { // Stop at Off-Mesh link or when point is further than slop away. if ((steerPathFlags[ns] & (byte)Detour.dtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION) != 0 || !inRange(steerPath, ns * 3, startPos, 0, minTargetDist, 1000.0f)) { break; } ns++; } // Failed to find good point to steer to. if (ns >= nsteerPath) { return(false); } Detour.dtVcopy(steerPos, 0, steerPath, ns * 3); steerPos[1] = startPos[1]; steerPosFlag = steerPathFlags[ns]; steerPosRef = steerPathPolys[ns]; return(true); }
public static StraightPath ComputeStraightPath(Detour.dtNavMeshQuery navQuery, float[] startPos, float[] endPos, float distance = 10) { //m_ComputedPathType = PathType.Straight; StraightPath path = new StraightPath(); float[] extents = new float[3]; for (int i = 0; i < 3; ++i) { extents[i] = distance; } dtPolyRef startRef = 0; dtPolyRef endRef = 0; float[] startPt = new float[3]; float[] endPt = new float[3]; Detour.dtQueryFilter filter = new Detour.dtQueryFilter(); navQuery.findNearestPoly(startPos, extents, filter, ref startRef, ref startPt); navQuery.findNearestPoly(endPos, extents, filter, ref endRef, ref endPt); int pathCount = -1; navQuery.findPath(startRef, endRef, startPt, endPt, filter, path.m_RawPathPolys, ref pathCount, StraightPath.MAX_POLYS); path.m_RawPathLength = pathCount; if (pathCount > 0) { // In case of partial path, make sure the end point is clamped to the last polygon. float[] epos = new float[3]; Detour.dtVcopy(epos, endPt); if (path.m_RawPathPolys[pathCount - 1] != endRef) { bool posOverPoly = false; navQuery.closestPointOnPoly(path.m_RawPathPolys[pathCount - 1], endPt, epos, ref posOverPoly); } navQuery.findStraightPath(startPt, endPt, path.m_RawPathPolys, pathCount, path.m_straightPath, path.m_straightPathFlags, path.m_straightPathPolys, ref path.m_straightPathCount, StraightPath.MAX_POLYS, path.m_straightPathOptions); } return(path); }
public static uint GetClosestPointOnNavMesh(Detour.dtNavMeshQuery navQuery, float[] pos, ref float[] resPos, float distance = 10) { float[] extents = new float[3]; for (int i = 0; i < 3; ++i) { extents[i] = distance; } Detour.dtQueryFilter filter = new Detour.dtQueryFilter(); dtPolyRef startRef = 0; resPos = new float[3]; var res = navQuery.findNearestPoly(pos, extents, filter, ref startRef, ref resPos); return(res); }
public static float[] GetClosestPointOnNavMesh(Detour.dtNavMeshQuery navQuery, float[] pos) { float[] extents = new float[3]; for (int i = 0; i < 3; ++i) { extents[i] = 10.0f; } Detour.dtQueryFilter filter = new Detour.dtQueryFilter(); dtPolyRef startRef = 0; float[] res = new float[3]; navQuery.findNearestPoly(pos, extents, filter, ref startRef, ref res); return(res); }
public void ClearComputedData() { m_ctx = new Recast.BuildContext(); m_triareas = null; m_solid = null; m_chf = null; m_cset = null; m_pmesh = null; m_cfg = null; m_dmesh = null; m_navMesh = null; m_navQuery = null; m_bmin = new float[3]; m_bmax = new float[3]; }
public bool ComputeSystem(byte[] tileRawData, int start) { m_ctx.enableLog(true); m_ctx.resetTimers(); // Start the build process. m_ctx.startTimer(Recast.rcTimerLabel.RC_TIMER_TOTAL); m_rawTileData = new Detour.dtRawTileData(); m_rawTileData.FromBytes(tileRawData, start); m_navMesh = new Detour.dtNavMesh(); if (m_navMesh == null) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "Could not create Detour navmesh"); return(false); } dtStatus status; status = m_navMesh.init(m_rawTileData, (int)Detour.dtTileFlags.DT_TILE_FREE_DATA); if (Detour.dtStatusFailed(status)) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "Could not init Detour navmesh"); return(false); } m_navQuery = new Detour.dtNavMeshQuery(); status = m_navQuery.init(m_navMesh, 2048); if (Detour.dtStatusFailed(status)) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "Could not init Detour navmesh query"); return(false); } m_ctx.stopTimer(Recast.rcTimerLabel.RC_TIMER_TOTAL); //m_ctx.log(Recast.rcLogCategory.RC_LOG_PROGRESS, ">> Polymesh: " + m_pmesh.nverts + " vertices " + m_pmesh.npolys + " polygons"); m_totalBuildTimeMs = (float)m_ctx.getAccumulatedTime(Recast.rcTimerLabel.RC_TIMER_TOTAL); return(true); }
public static uint GetClosestPointOnNavMesh(Detour.dtNavMeshQuery navQuery, Vector3 pos, ref float[] resPos, float distance = 10) { return(GetClosestPointOnNavMesh(navQuery, new[] { pos.x, pos.y, pos.z }, ref resPos, distance)); }
public static StraightPath ComputeStraightPath(Detour.dtNavMeshQuery navQuery, Vector3 startPos, Vector3 endPos) { return(ComputeStraightPath(navQuery, Vector3ToArray(startPos), Vector3ToArray(endPos))); }
public bool ComputeSystem(byte[] tileRawData, int start) { m_ctx.enableLog(true); m_ctx.resetTimers(); // Start the build process. m_ctx.startTimer(Recast.rcTimerLabel.RC_TIMER_TOTAL); m_rawTileData = new Detour.dtRawTileData(); m_rawTileData.FromBytes(tileRawData, start); m_navMesh = new Detour.dtNavMesh(); if (m_navMesh == null) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "Could not create Detour navmesh"); return false; } dtStatus status; status = m_navMesh.init(m_rawTileData, (int)Detour.dtTileFlags.DT_TILE_FREE_DATA); if (Detour.dtStatusFailed(status)) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "Could not init Detour navmesh"); return false; } m_navQuery = new Detour.dtNavMeshQuery(); status = m_navQuery.init(m_navMesh, 2048); if (Detour.dtStatusFailed(status)) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "Could not init Detour navmesh query"); return false; } m_ctx.stopTimer(Recast.rcTimerLabel.RC_TIMER_TOTAL); //m_ctx.log(Recast.rcLogCategory.RC_LOG_PROGRESS, ">> Polymesh: " + m_pmesh.nverts + " vertices " + m_pmesh.npolys + " polygons"); m_totalBuildTimeMs = (float) m_ctx.getAccumulatedTime(Recast.rcTimerLabel.RC_TIMER_TOTAL); return true; }
//Compute Recast and Detour navmesh public bool ComputeSystem() { ClearComputedData(); Recast.rcCalcBounds(m_verts, m_vertCount, m_bmin, m_bmax); // // Step 1. Initialize build config. // // Init build configuration from GUI m_cfg = new Recast.rcConfig(); m_cfg.cs = m_RecastMeshParams.m_cellSize; m_cfg.ch = m_RecastMeshParams.m_cellHeight; m_cfg.walkableSlopeAngle = m_RecastMeshParams.m_agentMaxSlope; m_cfg.walkableHeight = (int)Math.Ceiling(m_RecastMeshParams.m_agentHeight / m_cfg.ch); m_cfg.walkableClimb = (int)Math.Floor(m_RecastMeshParams.m_agentMaxClimb / m_cfg.ch); m_cfg.walkableRadius = (int)Math.Ceiling(m_RecastMeshParams.m_agentRadius / m_cfg.cs); m_cfg.maxEdgeLen = (int)(m_RecastMeshParams.m_edgeMaxLen / m_RecastMeshParams.m_cellSize); m_cfg.maxSimplificationError = m_RecastMeshParams.m_edgeMaxError; m_cfg.minRegionArea = (int)(m_RecastMeshParams.m_regionMinSize * m_RecastMeshParams.m_regionMinSize); // Note: area = size*size m_cfg.mergeRegionArea = (int)(m_RecastMeshParams.m_regionMergeSize * m_RecastMeshParams.m_regionMergeSize); // Note: area = size*size m_cfg.maxVertsPerPoly = (int)m_RecastMeshParams.m_vertsPerPoly; m_cfg.detailSampleDist = m_RecastMeshParams.m_detailSampleDist < 0.9f ? 0 : m_RecastMeshParams.m_cellSize * m_RecastMeshParams.m_detailSampleDist; m_cfg.detailSampleMaxError = m_RecastMeshParams.m_cellHeight * m_RecastMeshParams.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. Recast.rcVcopy(m_cfg.bmin, m_bmin); Recast.rcVcopy(m_cfg.bmax, m_bmax); Recast.rcCalcGridSize(m_cfg.bmin, m_cfg.bmax, m_cfg.cs, out m_cfg.width, out m_cfg.height); // Reset build times gathering. m_ctx.resetTimers(); // Start the build process. m_ctx.startTimer(Recast.rcTimerLabel.RC_TIMER_TOTAL); m_ctx.log(Recast.rcLogCategory.RC_LOG_PROGRESS, "Building navigation:"); m_ctx.log(Recast.rcLogCategory.RC_LOG_PROGRESS, " - " + m_cfg.width + " x " + m_cfg.height + " cells"); m_ctx.log(Recast.rcLogCategory.RC_LOG_PROGRESS, " - " + m_vertCount / 1000.0f + "K verts, " + m_triCount / 1000.0f + "K tris"); // // Step 2. Rasterize input polygon soup. // // Allocate voxel heightfield where we rasterize our input data to. m_solid = new Recast.rcHeightfield(); if (m_solid == null) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "buildNavigation: Out of memory 'solid'."); return false; } if (!Recast.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(Recast.rcLogCategory.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 byte[m_triCount]; if (m_triareas == null) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "buildNavigation: Out of memory 'm_triareas' (" + m_triCount + ")."); 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(byte)); Recast.rcMarkWalkableTriangles(m_ctx, m_cfg.walkableSlopeAngle, m_verts, m_vertCount, m_tris, m_triCount, m_triareas); Recast.rcRasterizeTriangles(m_ctx, m_verts, m_vertCount, m_tris, m_triareas, m_triCount, m_solid, m_cfg.walkableClimb); if (!m_keepInterResults) { m_triareas = null; } // // 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. Recast.rcFilterLowHangingWalkableObstacles(m_ctx, m_cfg.walkableClimb, m_solid); Recast.rcFilterLedgeSpans(m_ctx, m_cfg.walkableHeight, m_cfg.walkableClimb, m_solid); Recast.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 = new Recast.rcCompactHeightfield(); if (m_chf == null) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "buildNavigation: Out of memory 'chf'."); return false; } if (!Recast.rcBuildCompactHeightfield(m_ctx, m_cfg.walkableHeight, m_cfg.walkableClimb, m_solid, m_chf)) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "buildNavigation: Could not build compact data."); return false; } if (!m_keepInterResults) { m_solid = null; } // Erode the walkable area by agent radius. if (!Recast.rcErodeWalkableArea(m_ctx, m_cfg.walkableRadius, m_chf)) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "buildNavigation: Could not erode."); return false; } /* // (Optional) Mark areas. 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, (byte)vols[i].area, *m_chf); */ if (m_RecastMeshParams.m_monotonePartitioning) { // Partition the walkable surface into simple regions without holes. // Monotone partitioning does not need distancefield. if (!Recast.rcBuildRegionsMonotone(m_ctx, m_chf, 0, m_cfg.minRegionArea, m_cfg.mergeRegionArea)) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "buildNavigation: Could not build regions."); return false; } } else { // Prepare for region partitioning, by calculating distance field along the walkable surface. if (!Recast.rcBuildDistanceField(m_ctx, m_chf)) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "buildNavigation: Could not build distance field."); return false; } // Partition the walkable surface into simple regions without holes. if (!Recast.rcBuildRegions(m_ctx, m_chf, 0, m_cfg.minRegionArea, m_cfg.mergeRegionArea)) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "buildNavigation: Could not build regions."); return false; } } // // Step 5. Trace and simplify region contours. // // Create contours. m_cset = new Recast.rcContourSet(); if (m_cset == null) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "buildNavigation: Out of memory 'cset'."); return false; } if (!Recast.rcBuildContours(m_ctx, m_chf, m_cfg.maxSimplificationError, m_cfg.maxEdgeLen, m_cset, -1)) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "buildNavigation: Could not create contours."); return false; } //m_cset.dumpToTxt("Data/CSET_dump.txt"); // // Step 6. Build polygons mesh from contours. // // Build polygon navmesh from the contours. m_pmesh = new Recast.rcPolyMesh(); if (m_pmesh == null) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "buildNavigation: Out of memory 'pmesh'."); return false; } if (!Recast.rcBuildPolyMesh(m_ctx, m_cset, m_cfg.maxVertsPerPoly, m_pmesh)) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "buildNavigation: Could not triangulate contours."); return false; } //m_pmesh.dumpToObj("Data/navmesh.obj"); //m_pmesh.dumpToText("Data/navmesh.txt"); // // Step 7. Create detail mesh which allows to access approximate height on each polygon. // m_dmesh = new Recast.rcPolyMeshDetail();//rcAllocPolyMeshDetail(); if (m_dmesh == null) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "buildNavigation: Out of memory 'pmdtl'."); return false; } if (!Recast.rcBuildPolyMeshDetail(m_ctx, m_pmesh, m_chf, m_cfg.detailSampleDist, m_cfg.detailSampleMaxError, m_dmesh)) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "buildNavigation: Could not build detail mesh."); return false; } //m_dmesh.dumpToText("Data/polymeshdetail_cs.txt"); //m_dmesh.dumpToObj("Data/polymeshdetail_cs.obj"); if (!m_keepInterResults) { m_chf = null; m_cset = null; } // 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 <= Detour.DT_VERTS_PER_POLYGON) { //unsigned char* navData = 0; Detour.dtRawTileData navData = null; //int navDataSize = 0; // Update poly flags from areas. for (int i = 0; i < m_pmesh.npolys; ++i) { if (m_pmesh.areas[i] == Recast.RC_WALKABLE_AREA) m_pmesh.areas[i] = (byte)SamplePolyAreas.GROUND; if (m_pmesh.areas[i] == (byte)SamplePolyAreas.GROUND) { m_pmesh.flags[i] = (ushort)SamplePolyFlags.WALK; } /* if (m_pmesh.areas[i] == Recast.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; }*/ } Detour.dtNavMeshCreateParams navMeshCreateParams = new Detour.dtNavMeshCreateParams(); navMeshCreateParams.verts = m_pmesh.verts; navMeshCreateParams.vertCount = m_pmesh.nverts; navMeshCreateParams.polys = m_pmesh.polys; navMeshCreateParams.polyAreas = m_pmesh.areas; navMeshCreateParams.polyFlags = m_pmesh.flags; navMeshCreateParams.polyCount = m_pmesh.npolys; navMeshCreateParams.nvp = m_pmesh.nvp; navMeshCreateParams.detailMeshes = m_dmesh.meshes; navMeshCreateParams.detailVerts = m_dmesh.verts; navMeshCreateParams.detailVertsCount = m_dmesh.nverts; navMeshCreateParams.detailTris = m_dmesh.tris; navMeshCreateParams.detailTriCount = m_dmesh.ntris; navMeshCreateParams.offMeshConVerts = null;//m_geom.getOffMeshConnectionVerts(); navMeshCreateParams.offMeshConRad = null;//m_geom.getOffMeshConnectionRads(); navMeshCreateParams.offMeshConDir = null;//m_geom.getOffMeshConnectionDirs(); navMeshCreateParams.offMeshConAreas = null;//m_geom.getOffMeshConnectionAreas(); navMeshCreateParams.offMeshConFlags = null;//m_geom.getOffMeshConnectionFlags(); navMeshCreateParams.offMeshConUserID = null;//m_geom.getOffMeshConnectionId(); navMeshCreateParams.offMeshConCount = 0;//m_geom.getOffMeshConnectionCount(); navMeshCreateParams.walkableHeight = m_RecastMeshParams.m_agentHeight; navMeshCreateParams.walkableRadius = m_RecastMeshParams.m_agentRadius; navMeshCreateParams.walkableClimb = m_RecastMeshParams.m_agentMaxClimb; Recast.rcVcopy(navMeshCreateParams.bmin, m_pmesh.bmin); Recast.rcVcopy(navMeshCreateParams.bmax, m_pmesh.bmax); navMeshCreateParams.cs = m_cfg.cs; navMeshCreateParams.ch = m_cfg.ch; navMeshCreateParams.buildBvTree = true; if (!Detour.dtCreateNavMeshData(navMeshCreateParams, out navData)) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "Could not build Detour navmesh."); return false; } m_navMesh = new Detour.dtNavMesh(); if (m_navMesh == null) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "Could not create Detour navmesh"); return false; } dtStatus status; status = m_navMesh.init(navData, (int)Detour.dtTileFlags.DT_TILE_FREE_DATA); if (Detour.dtStatusFailed(status)) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "Could not init Detour navmesh"); return false; } m_navQuery = new Detour.dtNavMeshQuery(); status = m_navQuery.init(m_navMesh, 2048); if (Detour.dtStatusFailed(status)) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "Could not init Detour navmesh query"); return false; } m_rawTileData = navData; } else { m_ctx.log(Recast.rcLogCategory.RC_LOG_WARNING, "Detour does not support more than " + Detour.DT_VERTS_PER_POLYGON + " verts per polygon. A navmesh has not been generated."); } m_ctx.stopTimer(Recast.rcTimerLabel.RC_TIMER_TOTAL); // Show performance stats. m_ctx.logBuildTimes(); m_ctx.log(Recast.rcLogCategory.RC_LOG_PROGRESS, ">> Polymesh: " + m_pmesh.nverts + " vertices " + m_pmesh.npolys + " polygons"); m_totalBuildTimeMs = (float) m_ctx.getAccumulatedTime(Recast.rcTimerLabel.RC_TIMER_TOTAL); return true; }
public Queue <Position> FindPathBetween(Position start, Position end, bool useStraightPath = false) { var path = new Queue <Position>(); if (dtNavMesh == null) { return(path); } var startDetourPosition = start.ToDetourPosition(); var endDetourPosition = end.ToDetourPosition(); var queryFilter = new Detour.dtQueryFilter(); var navMeshQuery = new Detour.dtNavMeshQuery(); var status = navMeshQuery.init(dtNavMesh, MAX_PATH); if (Detour.dtStatusFailed(status)) { return(path); } queryFilter.setIncludeFlags(0xffff); queryFilter.setExcludeFlags(0x0); uint startRef = 0; uint endRef = 0; float[] startNearest = new float[3]; float[] endNearest = new float[3]; float[] extents = new float[] { 10.0F, 25.0F, 10.0F }; status = navMeshQuery.findNearestPoly(startDetourPosition, extents, queryFilter, ref startRef, ref startNearest); if (Detour.dtStatusFailed(status)) { return(path); } status = navMeshQuery.findNearestPoly(endDetourPosition, extents, queryFilter, ref endRef, ref endNearest); if (Detour.dtStatusFailed(status)) { return(path); } if (!dtNavMesh.isValidPolyRef(startRef) || !dtNavMesh.isValidPolyRef(endRef)) { return(path); } uint[] pathPolys = new uint[MAX_PATH]; int pathCount = 0; float[] straightPath = new float[MAX_PATH * 3]; byte[] straightPathFlags = new byte[MAX_PATH]; uint[] straightPathPolys = new uint[MAX_PATH]; int straightPathCount = 0; status = navMeshQuery.findPath( startRef, endRef, startNearest, endNearest, queryFilter, pathPolys, ref pathCount, MAX_PATH ); if (Detour.dtStatusFailed(status)) { path.Enqueue(start); path.Enqueue(end); return(path); } status = navMeshQuery.findStraightPath( startNearest, endNearest, pathPolys, pathCount, straightPath, straightPathFlags, straightPathPolys, ref straightPathCount, MAX_PATH, (int)Detour.dtStraightPathOptions.DT_STRAIGHTPATH_ALL_CROSSINGS ); if (Detour.dtStatusFailed(status)) { path.Enqueue(start); path.Enqueue(end); return(path); } if (straightPathCount > 0) { if (Detour.dtStatusFailed(status)) { return(path); } for (int i = 3; i < straightPathCount * 3;) { float[] pathPos = new float[3]; pathPos[0] = straightPath[i++]; pathPos[1] = straightPath[i++]; pathPos[2] = straightPath[i++]; var position = ToFFXIPosition(pathPos); path.Enqueue(position); } } else { for (int i = 1; i < pathCount; i++) { float[] pathPos = new float[3]; bool posOverPoly = false; if (Detour.dtStatusFailed(navMeshQuery.closestPointOnPoly(pathPolys[i], startDetourPosition, pathPos, ref posOverPoly))) { return(path); } if (path.Count < 1) { if (Detour.dtStatusFailed(navMeshQuery.closestPointOnPolyBoundary(pathPolys[i], startDetourPosition, pathPos))) { return(path); } } var position = ToFFXIPosition(pathPos); path.Enqueue(position); } } if (path.Count < 1) { path.Enqueue(end); } return(path); }
static bool LocCommand(StringArguments args, CommandHandler handler) { handler.SendSysMessage("mmap tileloc:"); // grid tile location Player player = handler.GetPlayer(); int gx = (int)(32 - player.GetPositionX() / MapConst.SizeofGrids); int gy = (int)(32 - player.GetPositionY() / MapConst.SizeofGrids); float x, y, z; player.GetPosition(out x, out y, out z); handler.SendSysMessage("{0:D4}{1:D2}{2:D2}.mmtile", player.GetMapId(), gy, gx); handler.SendSysMessage("gridloc [{0}, {1}]", gx, gy); // calculate navmesh tile location uint terrainMapId = PhasingHandler.GetTerrainMapId(player.GetPhaseShift(), player.GetMap(), x, y); Detour.dtNavMesh navmesh = Global.MMapMgr.GetNavMesh(terrainMapId); Detour.dtNavMeshQuery navmeshquery = Global.MMapMgr.GetNavMeshQuery(terrainMapId, player.GetInstanceId()); if (navmesh == null || navmeshquery == null) { handler.SendSysMessage("NavMesh not loaded for current map."); return(true); } float[] min = navmesh.getParams().orig; float[] location = { y, z, x }; float[] extents = { 3.0f, 5.0f, 3.0f }; int tilex = (int)((y - min[0]) / MapConst.SizeofGrids); int tiley = (int)((x - min[2]) / MapConst.SizeofGrids); handler.SendSysMessage("Calc [{0:D2}, {1:D2}]", tilex, tiley); // navmesh poly . navmesh tile location Detour.dtQueryFilter filter = new Detour.dtQueryFilter(); float[] nothing = new float[3]; ulong polyRef = 0; if (Detour.dtStatusFailed(navmeshquery.findNearestPoly(location, extents, filter, ref polyRef, ref nothing))) { handler.SendSysMessage("Dt [??,??] (invalid poly, probably no tile loaded)"); return(true); } if (polyRef == 0) { handler.SendSysMessage("Dt [??, ??] (invalid poly, probably no tile loaded)"); } else { Detour.dtMeshTile tile = new Detour.dtMeshTile(); Detour.dtPoly poly = new Detour.dtPoly(); if (Detour.dtStatusSucceed(navmesh.getTileAndPolyByRef(polyRef, ref tile, ref poly))) { if (tile != null) { handler.SendSysMessage("Dt [{0:D2},{1:D2}]", tile.header.x, tile.header.y); return(false); } } handler.SendSysMessage("Dt [??,??] (no tile loaded)"); } return(true); }
public Queue <Position> FindPathBetween(Position start, Position end, bool useStraightPath = false) { var path = new Queue <Position>(); if (dtNavMesh == null) { EasyFarm.ViewModels.LogViewModel.Write("FindPathBetween: Unable to path due to lacking navigation mesh for zone " + _zone.ToString()); return(path); } var startDetourPosition = start.ToDetourPosition(); var endDetourPosition = end.ToDetourPosition(); var queryFilter = new Detour.dtQueryFilter(); var navMeshQuery = new Detour.dtNavMeshQuery(); var status = navMeshQuery.init(dtNavMesh, 256); if (Detour.dtStatusFailed(status)) { return(path); } queryFilter.setIncludeFlags(0xffff); queryFilter.setExcludeFlags(0x0); uint startRef = 0; uint endRef = 0; float[] startNearest = new float[3]; float[] endNearest = new float[3]; float[] extents = new float[] { 10.0F, (float)EasyFarm.UserSettings.Config.Instance.HeightThreshold, 10.0F }; status = navMeshQuery.findNearestPoly(startDetourPosition, extents, queryFilter, ref startRef, ref startNearest); if (Detour.dtStatusFailed(status)) { return(path); } status = navMeshQuery.findNearestPoly(endDetourPosition, extents, queryFilter, ref endRef, ref endNearest); if (Detour.dtStatusFailed(status)) { return(path); } if (!dtNavMesh.isValidPolyRef(startRef) || !dtNavMesh.isValidPolyRef(endRef)) { return(path); } uint[] pathPolys = new uint[256]; int pathCount = 0; status = navMeshQuery.findPath(startRef, endRef, startNearest, endNearest, queryFilter, pathPolys, ref pathCount, 256); if (Detour.dtStatusFailed(status)) { return(path); } if (path.Count < 1) { float[] straightPath = new float[256 * 3]; byte[] straightPathFlags = new byte[256]; uint[] straightPathPolys = new uint[256]; int straightPathCount = 256 * 3; status = navMeshQuery.findStraightPath( startNearest, endNearest, pathPolys, pathCount, straightPath, straightPathFlags, straightPathPolys, ref straightPathCount, 256, 0 ); if (straightPathCount > 1) { if (Detour.dtStatusFailed(status)) { return(path); } path.Clear(); // i starts at 3 so the start position is ignored for (int i = 3; i < straightPathCount * 3;) { float[] pathPos = new float[3]; pathPos[0] = straightPath[i++]; pathPos[1] = straightPath[i++]; pathPos[2] = straightPath[i++]; var position = ToFFXIPosition(pathPos); path.Enqueue(position); } } } else { // i starts at 3 so the start position is ignored for (int i = 1; i < pathCount; i++) { float[] pathPos = new float[3]; bool posOverPoly = false; if (Detour.dtStatusFailed(navMeshQuery.closestPointOnPoly(pathPolys[i], startDetourPosition, pathPos, ref posOverPoly))) { return(path); } if (path.Count < 1) { if (Detour.dtStatusFailed(navMeshQuery.closestPointOnPolyBoundary(pathPolys[i], startDetourPosition, pathPos))) { return(path); } } var position = ToFFXIPosition(pathPos); path.Enqueue(position); } } return(path); }
public static SmoothPath ComputeSmoothPath(Detour.dtNavMeshQuery navQuery, Vector3 startPos, Vector3 endPos, float distance = 10) { return(ComputeSmoothPath(navQuery, Vector3ToArray(startPos), Vector3ToArray(endPos), distance)); }
public bool LoadZone(Zone zone) { if (zone == Zone.Unknown) { Unload(); return(false); } string path = "navmeshes\\" + zone.ToString() + ".nav"; if (_zone == zone && dtNavMesh != null) { return(true); } else { Unload(); } _zone = zone; var headerBufferSize = NavMeshSetHeader.ByteSize(); var headerBuffer = new byte[headerBufferSize]; if (!File.Exists(path)) { return(false); } var file = File.OpenRead(path); file.Read(headerBuffer, 0, headerBufferSize); NavMeshSetHeader header = new NavMeshSetHeader(); var headerBytesRead = header.FromBytes(headerBuffer, 0); if (header.magic != NAVMESHSET_MAGIC) { return(false); } if (header.version != NAVMESHSET_VERSION) { return(false); } var navMesh = new Detour.dtNavMesh(); navMesh.init(header.meshParams); for (int i = 0; i < header.numTiles; ++i) { var tileHeaderBuffer = new byte[NavMeshTileHeader.ByteSize()]; file.Read(tileHeaderBuffer, 0, tileHeaderBuffer.Length); var tileHeader = new NavMeshTileHeader(); tileHeader.FromBytes(tileHeaderBuffer, 0); if (tileHeader.dataSize == 0 || tileHeader.tileRef == 0) { break; } var rawTileData = new Detour.dtRawTileData(); var data = new byte[tileHeader.dataSize]; file.Read(data, 0, data.Length); rawTileData.FromBytes(data, 0); uint result = 0; navMesh.addTile(rawTileData, tileHeader.dataSize, 0x01 /*DT_TILE_FREE_DATA*/, tileHeader.tileRef, ref result); if (Detour.dtStatusFailed(result)) { return(false); } } // hard-code to make sure it is compatible with expectation. var maxPolys = header.meshParams.maxPolys; var status = new Detour.dtNavMeshQuery().init(navMesh, maxPolys); if (Detour.dtStatusFailed(status)) { return(false); } dtNavMesh = navMesh; return(headerBytesRead > 0); }
public static SmoothPath ComputeSmoothPath(Detour.dtNavMeshQuery navQuery, float[] startWorldPos, float[] endWorldPos, float distance = 10) { SmoothPath smoothPath = new SmoothPath(); if (navQuery == null) { return(smoothPath); } float[] extents = new float[3]; for (int i = 0; i < 3; ++i) { extents[i] = distance; } dtPolyRef startRef = 0; dtPolyRef endRef = 0; float[] startPt = new float[3]; float[] endPt = new float[3]; Detour.dtQueryFilter filter = new Detour.dtQueryFilter(); navQuery.findNearestPoly(startWorldPos, extents, filter, ref startRef, ref startPt); navQuery.findNearestPoly(endWorldPos, extents, filter, ref endRef, ref endPt); const int maxPath = SmoothPath.MAX_POLYS; dtPolyRef[] path = new dtPolyRef[maxPath]; int pathCount = -1; navQuery.findPath(startRef, endRef, startPt, endPt, filter, path, ref pathCount, maxPath); smoothPath.m_nsmoothPath = 0; if (pathCount > 0) { // Iterate over the path to find smooth path on the detail mesh surface. dtPolyRef[] polys = new dtPolyRef[SmoothPath.MAX_POLYS]; for (int i = 0; i < pathCount; ++i) { polys[i] = path[i]; } int npolys = pathCount; float[] iterPos = new float[3]; float[] targetPos = new float[3]; bool posOverPoly_dummy = false; navQuery.closestPointOnPoly(startRef, startPt, iterPos, ref posOverPoly_dummy); navQuery.closestPointOnPoly(polys[npolys - 1], endPt, targetPos, ref posOverPoly_dummy); const float STEP_SIZE = 0.5f; const float SLOP = 0.01f; smoothPath.m_nsmoothPath = 0; Detour.dtVcopy(smoothPath.m_smoothPath, smoothPath.m_nsmoothPath * 3, iterPos, 0); smoothPath.m_nsmoothPath++; // Move towards target a small advancement at a time until target reached or // when ran out of memory to store the path. while (npolys != 0 && smoothPath.m_nsmoothPath < SmoothPath.MAX_SMOOTH) { // Find location to steer towards. float[] steerPos = new float[3]; byte steerPosFlag = 0; dtPolyRef steerPosRef = 0; if (!getSteerTarget(navQuery, iterPos, targetPos, SLOP, polys, npolys, steerPos, ref steerPosFlag, ref steerPosRef)) { break; } bool endOfPath = (steerPosFlag & (byte)Detour.dtStraightPathFlags.DT_STRAIGHTPATH_END) != 0 ? true : false; bool offMeshConnection = (steerPosFlag & (byte)Detour.dtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION) != 0 ? true : false; // Find movement delta. float[] delta = new float[3]; //, len; float len = .0f; Detour.dtVsub(delta, steerPos, iterPos); len = (float)Mathf.Sqrt(Detour.dtVdot(delta, delta)); // If the steer target is end of path or off-mesh link, do not move past the location. if ((endOfPath || offMeshConnection) && len < STEP_SIZE) { len = 1; } else { len = STEP_SIZE / len; } float[] moveTgt = new float[3]; Detour.dtVmad(moveTgt, iterPos, delta, len); // Move float[] result = new float[3]; dtPolyRef[] visited = new dtPolyRef[16]; int nvisited = 0; navQuery.moveAlongSurface(polys[0], iterPos, moveTgt, filter, result, visited, ref nvisited, 16); npolys = fixupCorridor(polys, npolys, SmoothPath.MAX_POLYS, visited, nvisited); npolys = fixupShortcuts(polys, npolys, navQuery); float h = 0; dtStatus getHeightStatus = navQuery.getPolyHeight(polys[0], result, ref h); result[1] = h; if ((getHeightStatus & Detour.DT_FAILURE) != 0) { Debug.LogError("Failed to getPolyHeight " + polys[0] + " pos " + result[0] + " " + result[1] + " " + result[2] + " h " + h); } Detour.dtVcopy(iterPos, result); // Handle end of path and off-mesh links when close enough. if (endOfPath && inRange(iterPos, 0, steerPos, 0, SLOP, 1.0f)) { // Reached end of path. Detour.dtVcopy(iterPos, targetPos); if (smoothPath.m_nsmoothPath < SmoothPath.MAX_SMOOTH) { Detour.dtVcopy(smoothPath.m_smoothPath, smoothPath.m_nsmoothPath * 3, iterPos, 0); smoothPath.m_nsmoothPath++; } break; } else if (offMeshConnection && inRange(iterPos, 0, steerPos, 0, SLOP, 1.0f)) { // Reached off-mesh connection. float[] startPos = new float[3]; //, endPos[3]; float[] endPos = new float[3]; // Advance the path up to and over the off-mesh connection. dtPolyRef prevRef = 0, polyRef = polys[0]; int npos = 0; while (npos < npolys && polyRef != steerPosRef) { prevRef = polyRef; polyRef = polys[npos]; npos++; } for (int i = npos; i < npolys; ++i) { polys[i - npos] = polys[i]; } npolys -= npos; // Handle the connection. dtStatus status = navQuery.getAttachedNavMesh().getOffMeshConnectionPolyEndPoints(prevRef, polyRef, startPos, endPos); if (Detour.dtStatusSucceed(status)) { if (smoothPath.m_nsmoothPath < SmoothPath.MAX_SMOOTH) { Detour.dtVcopy(smoothPath.m_smoothPath, smoothPath.m_nsmoothPath * 3, startPos, 0); smoothPath.m_nsmoothPath++; // Hack to make the dotted path not visible during off-mesh connection. if ((smoothPath.m_nsmoothPath & 1) != 0) { Detour.dtVcopy(smoothPath.m_smoothPath, smoothPath.m_nsmoothPath * 3, startPos, 0); smoothPath.m_nsmoothPath++; } } // Move position at the other side of the off-mesh link. Detour.dtVcopy(iterPos, endPos); float eh = 0.0f; navQuery.getPolyHeight(polys[0], iterPos, ref eh); iterPos[1] = eh; } } // Store results. if (smoothPath.m_nsmoothPath < SmoothPath.MAX_SMOOTH) { Detour.dtVcopy(smoothPath.m_smoothPath, smoothPath.m_nsmoothPath * 3, iterPos, 0); smoothPath.m_nsmoothPath++; } } } return(smoothPath); }
//Compute Recast and Detour navmesh public bool ComputeSystem() { ClearComputedData(); Recast.rcCalcBounds(m_verts, m_vertCount, m_bmin, m_bmax); // // Step 1. Initialize build config. // // Init build configuration from GUI m_cfg = new Recast.rcConfig(); m_cfg.cs = m_RecastMeshParams.m_cellSize; m_cfg.ch = m_RecastMeshParams.m_cellHeight; m_cfg.walkableSlopeAngle = m_RecastMeshParams.m_agentMaxSlope; m_cfg.walkableHeight = (int)Math.Ceiling(m_RecastMeshParams.m_agentHeight / m_cfg.ch); m_cfg.walkableClimb = (int)Math.Floor(m_RecastMeshParams.m_agentMaxClimb / m_cfg.ch); m_cfg.walkableRadius = (int)Math.Ceiling(m_RecastMeshParams.m_agentRadius / m_cfg.cs); m_cfg.maxEdgeLen = (int)(m_RecastMeshParams.m_edgeMaxLen / m_RecastMeshParams.m_cellSize); m_cfg.maxSimplificationError = m_RecastMeshParams.m_edgeMaxError; m_cfg.minRegionArea = (int)(m_RecastMeshParams.m_regionMinSize * m_RecastMeshParams.m_regionMinSize); // Note: area = size*size m_cfg.mergeRegionArea = (int)(m_RecastMeshParams.m_regionMergeSize * m_RecastMeshParams.m_regionMergeSize); // Note: area = size*size m_cfg.maxVertsPerPoly = (int)m_RecastMeshParams.m_vertsPerPoly; m_cfg.detailSampleDist = m_RecastMeshParams.m_detailSampleDist < 0.9f ? 0 : m_RecastMeshParams.m_cellSize * m_RecastMeshParams.m_detailSampleDist; m_cfg.detailSampleMaxError = m_RecastMeshParams.m_cellHeight * m_RecastMeshParams.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. Recast.rcVcopy(m_cfg.bmin, m_bmin); Recast.rcVcopy(m_cfg.bmax, m_bmax); Recast.rcCalcGridSize(m_cfg.bmin, m_cfg.bmax, m_cfg.cs, out m_cfg.width, out m_cfg.height); // Reset build times gathering. m_ctx.resetTimers(); // Start the build process. m_ctx.startTimer(Recast.rcTimerLabel.RC_TIMER_TOTAL); m_ctx.log(Recast.rcLogCategory.RC_LOG_PROGRESS, "Building navigation:"); m_ctx.log(Recast.rcLogCategory.RC_LOG_PROGRESS, " - " + m_cfg.width + " x " + m_cfg.height + " cells"); m_ctx.log(Recast.rcLogCategory.RC_LOG_PROGRESS, " - " + m_vertCount / 1000.0f + "K verts, " + m_triCount / 1000.0f + "K tris"); // // Step 2. Rasterize input polygon soup. // // Allocate voxel heightfield where we rasterize our input data to. m_solid = new Recast.rcHeightfield(); if (m_solid == null) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "buildNavigation: Out of memory 'solid'."); return(false); } if (!Recast.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(Recast.rcLogCategory.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 byte[m_triCount]; if (m_triareas == null) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "buildNavigation: Out of memory 'm_triareas' (" + m_triCount + ")."); 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(byte)); Recast.rcMarkWalkableTriangles(m_ctx, m_cfg.walkableSlopeAngle, m_verts, m_vertCount, m_tris, m_triCount, m_triareas); Recast.rcRasterizeTriangles(m_ctx, m_verts, m_vertCount, m_tris, m_triareas, m_triCount, m_solid, m_cfg.walkableClimb); if (!m_keepInterResults) { m_triareas = null; } // // 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. Recast.rcFilterLowHangingWalkableObstacles(m_ctx, m_cfg.walkableClimb, m_solid); Recast.rcFilterLedgeSpans(m_ctx, m_cfg.walkableHeight, m_cfg.walkableClimb, m_solid); Recast.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 = new Recast.rcCompactHeightfield(); if (m_chf == null) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "buildNavigation: Out of memory 'chf'."); return(false); } if (!Recast.rcBuildCompactHeightfield(m_ctx, m_cfg.walkableHeight, m_cfg.walkableClimb, m_solid, m_chf)) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "buildNavigation: Could not build compact data."); return(false); } if (!m_keepInterResults) { m_solid = null; } // Erode the walkable area by agent radius. if (!Recast.rcErodeWalkableArea(m_ctx, m_cfg.walkableRadius, m_chf)) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "buildNavigation: Could not erode."); return(false); } /* * // (Optional) Mark areas. * 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, (byte)vols[i].area, *m_chf); */ if (m_RecastMeshParams.m_monotonePartitioning) { // Partition the walkable surface into simple regions without holes. // Monotone partitioning does not need distancefield. if (!Recast.rcBuildRegionsMonotone(m_ctx, m_chf, 0, m_cfg.minRegionArea, m_cfg.mergeRegionArea)) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "buildNavigation: Could not build regions."); return(false); } } else { // Prepare for region partitioning, by calculating distance field along the walkable surface. if (!Recast.rcBuildDistanceField(m_ctx, m_chf)) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "buildNavigation: Could not build distance field."); return(false); } // Partition the walkable surface into simple regions without holes. if (!Recast.rcBuildRegions(m_ctx, m_chf, 0, m_cfg.minRegionArea, m_cfg.mergeRegionArea)) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "buildNavigation: Could not build regions."); return(false); } } // // Step 5. Trace and simplify region contours. // // Create contours. m_cset = new Recast.rcContourSet(); if (m_cset == null) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "buildNavigation: Out of memory 'cset'."); return(false); } if (!Recast.rcBuildContours(m_ctx, m_chf, m_cfg.maxSimplificationError, m_cfg.maxEdgeLen, m_cset, -1)) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "buildNavigation: Could not create contours."); return(false); } //m_cset.dumpToTxt("Data/CSET_dump.txt"); // // Step 6. Build polygons mesh from contours. // // Build polygon navmesh from the contours. m_pmesh = new Recast.rcPolyMesh(); if (m_pmesh == null) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "buildNavigation: Out of memory 'pmesh'."); return(false); } if (!Recast.rcBuildPolyMesh(m_ctx, m_cset, m_cfg.maxVertsPerPoly, m_pmesh)) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "buildNavigation: Could not triangulate contours."); return(false); } //m_pmesh.dumpToObj("Data/navmesh.obj"); //m_pmesh.dumpToText("Data/navmesh.txt"); // // Step 7. Create detail mesh which allows to access approximate height on each polygon. // m_dmesh = new Recast.rcPolyMeshDetail(); //rcAllocPolyMeshDetail(); if (m_dmesh == null) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "buildNavigation: Out of memory 'pmdtl'."); return(false); } if (!Recast.rcBuildPolyMeshDetail(m_ctx, m_pmesh, m_chf, m_cfg.detailSampleDist, m_cfg.detailSampleMaxError, m_dmesh)) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "buildNavigation: Could not build detail mesh."); return(false); } //m_dmesh.dumpToText("Data/polymeshdetail_cs.txt"); //m_dmesh.dumpToObj("Data/polymeshdetail_cs.obj"); if (!m_keepInterResults) { m_chf = null; m_cset = null; } // 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 <= Detour.DT_VERTS_PER_POLYGON) { //unsigned char* navData = 0; Detour.dtRawTileData navData = null; //int navDataSize = 0; // Update poly flags from areas. for (int i = 0; i < m_pmesh.npolys; ++i) { if (m_pmesh.areas[i] == Recast.RC_WALKABLE_AREA) { m_pmesh.areas[i] = (byte)SamplePolyAreas.GROUND; } if (m_pmesh.areas[i] == (byte)SamplePolyAreas.GROUND) { m_pmesh.flags[i] = (ushort)SamplePolyFlags.WALK; } /* * if (m_pmesh.areas[i] == Recast.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; * }*/ } Detour.dtNavMeshCreateParams navMeshCreateParams = new Detour.dtNavMeshCreateParams(); navMeshCreateParams.verts = m_pmesh.verts; navMeshCreateParams.vertCount = m_pmesh.nverts; navMeshCreateParams.polys = m_pmesh.polys; navMeshCreateParams.polyAreas = m_pmesh.areas; navMeshCreateParams.polyFlags = m_pmesh.flags; navMeshCreateParams.polyCount = m_pmesh.npolys; navMeshCreateParams.nvp = m_pmesh.nvp; navMeshCreateParams.detailMeshes = m_dmesh.meshes; navMeshCreateParams.detailVerts = m_dmesh.verts; navMeshCreateParams.detailVertsCount = m_dmesh.nverts; navMeshCreateParams.detailTris = m_dmesh.tris; navMeshCreateParams.detailTriCount = m_dmesh.ntris; navMeshCreateParams.offMeshConVerts = null; //m_geom.getOffMeshConnectionVerts(); navMeshCreateParams.offMeshConRad = null; //m_geom.getOffMeshConnectionRads(); navMeshCreateParams.offMeshConDir = null; //m_geom.getOffMeshConnectionDirs(); navMeshCreateParams.offMeshConAreas = null; //m_geom.getOffMeshConnectionAreas(); navMeshCreateParams.offMeshConFlags = null; //m_geom.getOffMeshConnectionFlags(); navMeshCreateParams.offMeshConUserID = null; //m_geom.getOffMeshConnectionId(); navMeshCreateParams.offMeshConCount = 0; //m_geom.getOffMeshConnectionCount(); navMeshCreateParams.walkableHeight = m_RecastMeshParams.m_agentHeight; navMeshCreateParams.walkableRadius = m_RecastMeshParams.m_agentRadius; navMeshCreateParams.walkableClimb = m_RecastMeshParams.m_agentMaxClimb; Recast.rcVcopy(navMeshCreateParams.bmin, m_pmesh.bmin); Recast.rcVcopy(navMeshCreateParams.bmax, m_pmesh.bmax); navMeshCreateParams.cs = m_cfg.cs; navMeshCreateParams.ch = m_cfg.ch; navMeshCreateParams.buildBvTree = true; if (!Detour.dtCreateNavMeshData(navMeshCreateParams, out navData)) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "Could not build Detour navmesh."); return(false); } m_navMesh = new Detour.dtNavMesh(); if (m_navMesh == null) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "Could not create Detour navmesh"); return(false); } dtStatus status; status = m_navMesh.init(navData, (int)Detour.dtTileFlags.DT_TILE_FREE_DATA); if (Detour.dtStatusFailed(status)) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "Could not init Detour navmesh"); return(false); } m_navQuery = new Detour.dtNavMeshQuery(); status = m_navQuery.init(m_navMesh, 2048); if (Detour.dtStatusFailed(status)) { m_ctx.log(Recast.rcLogCategory.RC_LOG_ERROR, "Could not init Detour navmesh query"); return(false); } m_rawTileData = navData; } else { m_ctx.log(Recast.rcLogCategory.RC_LOG_WARNING, "Detour does not support more than " + Detour.DT_VERTS_PER_POLYGON + " verts per polygon. A navmesh has not been generated."); } m_ctx.stopTimer(Recast.rcTimerLabel.RC_TIMER_TOTAL); // Show performance stats. m_ctx.logBuildTimes(); m_ctx.log(Recast.rcLogCategory.RC_LOG_PROGRESS, ">> Polymesh: " + m_pmesh.nverts + " vertices " + m_pmesh.npolys + " polygons"); m_totalBuildTimeMs = (float)m_ctx.getAccumulatedTime(Recast.rcTimerLabel.RC_TIMER_TOTAL); return(true); }