public bool BuildNavMeshTile(CompressedTile tile, NavMesh navmesh) { NavMeshTileBuildContext bc = new NavMeshTileBuildContext(); int walkableClimbVx = (int)(m_params.WalkableClimb / m_params.CellHeight); // Decompress tile layer data. if (!DetourTileCache.DecompressTileCacheLayer(tile.Header, tile.Data, 0, out var layer)) { return(false); } bc.Layer = layer; // Rasterize obstacles. for (int i = 0; i < m_params.MaxObstacles; ++i) { var ob = m_obstacles[i]; if (ob.State == ObstacleState.DT_OBSTACLE_EMPTY || ob.State == ObstacleState.DT_OBSTACLE_REMOVING) { continue; } if (DetourTileCache.Contains(ob.Touched, ob.NTouched, tile)) { if (ob.Type == ObstacleType.DT_OBSTACLE_CYLINDER) { DetourTileCache.MarkCylinderArea(bc, tile.Header.BBox.Minimum, m_params.CellSize, m_params.CellHeight, ob.Cylinder.Pos, ob.Cylinder.Radius, ob.Cylinder.Height, 0); } else if (ob.Type == ObstacleType.DT_OBSTACLE_BOX) { DetourTileCache.MarkBoxArea(bc, tile.Header.BBox.Minimum, m_params.CellSize, m_params.CellHeight, ob.Box.BMin, ob.Box.BMax, 0); } else if (ob.Type == ObstacleType.DT_OBSTACLE_ORIENTED_BOX) { DetourTileCache.MarkBoxArea(bc, tile.Header.BBox.Minimum, m_params.CellSize, m_params.CellHeight, ob.OrientedBox.Center, ob.OrientedBox.HalfExtents, ob.OrientedBox.RotAux, 0); } } } // Build navmesh if (!DetourTileCache.BuildTileCacheRegions(bc, walkableClimbVx)) { return(false); } if (!DetourTileCache.BuildTileCacheContours(bc, walkableClimbVx, m_params.MaxSimplificationError)) { return(false); } if (!DetourTileCache.BuildTileCachePolyMesh(bc)) { return(false); } // Early out if the mesh tile is empty. if (bc.LMesh.NPolys == 0) { // Remove existing tile. navmesh.RemoveTile(navmesh.GetTileRefAt(tile.Header.TX, tile.Header.TY, tile.Header.TLayer), null, 0); return(true); } var param = new NavMeshCreateParams { Verts = bc.LMesh.Verts, VertCount = bc.LMesh.NVerts, Polys = bc.LMesh.Polys, PolyAreas = bc.LMesh.Areas, PolyFlags = bc.LMesh.Flags, polyCount = bc.LMesh.NPolys, nvp = Detour.DT_VERTS_PER_POLYGON, walkableHeight = m_params.WalkableHeight, walkableRadius = m_params.WalkableRadius, walkableClimb = m_params.WalkableClimb, tileX = tile.Header.TX, tileY = tile.Header.TY, tileLayer = tile.Header.TLayer, cs = m_params.CellSize, ch = m_params.CellHeight, buildBvTree = false, bmin = tile.Header.BBox.Minimum, bmax = tile.Header.BBox.Maximum, }; if (m_tmproc != null) { m_tmproc.Process(ref param, bc); } if (!Detour.CreateNavMeshData(param, out MeshData navData)) { return(false); } // Remove existing tile. navmesh.RemoveTile(navmesh.GetTileRefAt(tile.Header.TX, tile.Header.TY, tile.Header.TLayer), null, 0); // Add new tile, or leave the location empty. if (navData != null && !navmesh.AddTile(navData, TileFlagTypes.DT_TILE_FREE_DATA, 0, out int result)) { // Let the navmesh own the data. navData = null; return(false); } return(true); }
public Status Update(float dt, NavMesh navmesh, out bool upToDate) { upToDate = false; if (m_nupdate == 0) { // Process requests. for (int i = 0; i < m_nreqs; ++i) { var req = m_reqs[i]; int idx = DecodeObstacleIdObstacle(req.NRef); if (idx >= m_params.MaxObstacles) { continue; } var ob = m_obstacles[idx]; int salt = DecodeObstacleIdSalt(req.NRef); if (ob.Salt != salt) { continue; } if (req.Action == ObstacleRequestAction.REQUEST_ADD) { // Find touched tiles. GetObstacleBounds(ob, out Vector3 bmin, out Vector3 bmax); QueryTiles(bmin, bmax, DetourTileCache.DT_MAX_TOUCHED_TILES, out var touched, out var nTouched); ob.Touched = touched; ob.NTouched = nTouched; // Add tiles to update list. ob.NPending = 0; for (int j = 0; j < ob.NTouched; ++j) { if (m_nupdate < DetourTileCache.MAX_UPDATE) { if (!DetourTileCache.Contains(m_update, m_nupdate, ob.Touched[j])) { m_update[m_nupdate++] = ob.Touched[j]; } ob.Pending[ob.NPending++] = ob.Touched[j]; } } } else if (req.Action == ObstacleRequestAction.REQUEST_REMOVE) { // Prepare to remove obstacle. ob.State = ObstacleState.DT_OBSTACLE_REMOVING; // Add tiles to update list. ob.NPending = 0; for (int j = 0; j < ob.NTouched; ++j) { if (m_nupdate < DetourTileCache.MAX_UPDATE) { if (!DetourTileCache.Contains(m_update, m_nupdate, ob.Touched[j])) { m_update[m_nupdate++] = ob.Touched[j]; } ob.Pending[ob.NPending++] = ob.Touched[j]; } } } } m_nreqs = 0; } Status status = Status.DT_SUCCESS; // Process updates if (m_nupdate != 0) { // Build mesh var r = m_update[0]; if (!BuildNavMeshTile(r, navmesh)) { status = Status.DT_FAILURE; } m_nupdate--; if (m_nupdate > 0) { Array.Copy(m_update, 1, m_update, 0, m_nupdate); } // Update obstacle states. for (int i = 0; i < m_params.MaxObstacles; ++i) { var ob = m_obstacles[i]; if (ob.State == ObstacleState.DT_OBSTACLE_PROCESSING || ob.State == ObstacleState.DT_OBSTACLE_REMOVING) { // Remove handled tile from pending list. for (int j = 0; j < ob.NPending; j++) { if (ob.Pending[j] == r) { ob.Pending[j] = ob.Pending[ob.NPending - 1]; ob.NPending--; break; } } // If all pending tiles processed, change state. if (ob.NPending == 0) { if (ob.State == ObstacleState.DT_OBSTACLE_PROCESSING) { ob.State = ObstacleState.DT_OBSTACLE_PROCESSED; } else if (ob.State == ObstacleState.DT_OBSTACLE_REMOVING) { ob.State = ObstacleState.DT_OBSTACLE_EMPTY; // Update salt, salt should never be zero. ob.Salt = (ob.Salt + 1) & ((1 << 16) - 1); if (ob.Salt == 0) { ob.Salt++; } // Return obstacle to free list. ob.Next = m_nextFreeObstacle; m_nextFreeObstacle = i; } } } } } upToDate = m_nupdate == 0 && m_nreqs == 0; return(status); }