public int GetTileRef(CompressedTile tile) { if (tile == null) { return(0); } int it = Array.IndexOf(m_tiles, tile); return(EncodeTileId(tile.Salt, it)); }
public void Init(TileCacheParams tcparams, TileCacheMeshProcess tmproc) { m_params = tcparams; m_tmproc = tmproc; // Alloc space for obstacles. m_obstacles = new TileCacheObstacle[tcparams.MaxObstacles]; m_nextFreeObstacle = -1; for (int i = tcparams.MaxObstacles - 1; i >= 0; i--) { m_obstacles[i] = new TileCacheObstacle { Salt = 1, Next = m_nextFreeObstacle }; m_nextFreeObstacle = i; } // Init tiles var m_tileLutSize = Helper.NextPowerOfTwo(tcparams.MaxTiles / 4); if (m_tileLutSize == 0) { m_tileLutSize = 1; } m_tileLutMask = m_tileLutSize - 1; m_tiles = new CompressedTile[tcparams.MaxTiles]; m_posLookup = new CompressedTile[m_tileLutSize]; for (int i = tcparams.MaxTiles - 1; i >= 0; i--) { m_tiles[i] = new CompressedTile { Salt = 1, Next = m_nextFreeTile }; m_nextFreeTile = m_tiles[i]; } // Init ID generator values. m_tileBits = (int)Math.Log(Helper.NextPowerOfTwo(tcparams.MaxTiles), 2); // Only allow 31 salt bits, since the salt mask is calculated using 32bit uint and it will overflow. m_saltBits = Math.Min(31, 32 - m_tileBits); if (m_saltBits < 10) { throw new EngineException("NavMesh DT_INVALID_PARAM"); } }
public CompressedTile AddTile(TileCacheData data, CompressedTileFlagTypes flags) { // Make sure the data is in right format. var header = data.Header; if (header.Magic != DetourTileCache.DT_TILECACHE_MAGIC) { throw new EngineException("DT_WRONG_MAGIC"); } if (header.Version != DetourTileCache.DT_TILECACHE_VERSION) { throw new EngineException("DT_WRONG_VERSION"); } // Make sure the location is free. if (GetTileAt(header.TX, header.TY, header.TLayer) != null) { throw new EngineException("DT_FAILURE"); } // Allocate a tile. CompressedTile tile = null; if (m_nextFreeTile != null) { tile = m_nextFreeTile; m_nextFreeTile = tile.Next; tile.Next = null; } else { tile = new CompressedTile(); } // Insert tile into the position lut. int h = Detour.ComputeTileHash(header.TX, header.TY, m_tileLutMask); tile.Next = m_posLookup[h]; m_posLookup[h] = tile; // Init tile. tile.Header = data.Header; tile.Data = data.Data; tile.Flags = flags; return(tile); }
public int GetTilesAt(int tx, int ty, out CompressedTile[] tiles, int maxTiles) { tiles = new CompressedTile[maxTiles]; int n = 0; // Find tile based on hash. int h = Detour.ComputeTileHash(tx, ty, m_tileLutMask); var tile = m_posLookup[h]; while (tile != null) { if (tile.Header.TX == tx && tile.Header.TY == ty && n < maxTiles) { tiles[n++] = tile; } tile = tile.Next; } return(n); }
public Status QueryTiles(Vector3 bmin, Vector3 bmax, int maxResults, out CompressedTile[] results, out int resultCount) { results = new CompressedTile[maxResults]; int MAX_TILES = 32; int n = 0; float tw = m_params.Width * m_params.CellSize; float th = m_params.Height * m_params.CellSize; int tx0 = (int)Math.Floor((bmin.X - m_params.Origin.X) / tw); int tx1 = (int)Math.Floor((bmax.X - m_params.Origin.X) / tw); int ty0 = (int)Math.Floor((bmin.Z - m_params.Origin.Z) / th); int ty1 = (int)Math.Floor((bmax.Z - m_params.Origin.Z) / th); for (int ty = ty0; ty <= ty1; ++ty) { for (int tx = tx0; tx <= tx1; ++tx) { int ntiles = GetTilesAt(tx, ty, out CompressedTile[] tiles, MAX_TILES); for (int i = 0; i < ntiles; ++i) { var tile = tiles[i]; CalcTightTileBounds(tile.Header, out Vector3 tbmin, out Vector3 tbmax); if (Detour.OverlapBounds(bmin, bmax, tbmin, tbmax) && n < maxResults) { results[n++] = tiles[i]; } } } } resultCount = n; return(Status.DT_SUCCESS); }
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 RemoveTile(int r, out TileCacheLayerData data, out int dataSize) { data = TileCacheLayerData.Empty; dataSize = 0; if (r == 0) { return(Status.DT_FAILURE | Status.DT_INVALID_PARAM); } int tileIndex = DecodeTileIdTile(r); int tileSalt = DecodeTileIdSalt(r); if (tileIndex >= m_params.MaxTiles) { return(Status.DT_FAILURE | Status.DT_INVALID_PARAM); } var tile = m_tiles[tileIndex]; if (tile.Salt != tileSalt) { return(Status.DT_FAILURE | Status.DT_INVALID_PARAM); } // Remove tile from hash lookup. int h = DetourTileCache.ComputeTileHash(tile.Header.TX, tile.Header.TY, m_tileLutMask); CompressedTile prev = null; CompressedTile cur = m_posLookup[h]; while (cur != null) { if (cur == tile) { if (prev != null) { prev.Next = cur.Next; } else { m_posLookup[h] = cur.Next; } break; } prev = cur; cur = cur.Next; } // Reset tile. if ((tile.Flags & CompressedTileFlagTypes.DT_COMPRESSEDTILE_FREE_DATA) != 0) { // Owns data tile.Data = TileCacheLayerData.Empty; } else { data = tile.Data; } tile.Header = new TileCacheLayerHeader(); tile.Data = new TileCacheLayerData(); tile.Flags = 0; // Update salt, salt should never be zero. tile.Salt = (tile.Salt + 1) & ((1 << m_saltBits) - 1); if (tile.Salt == 0) { tile.Salt++; } // Add to free list. tile.Next = m_nextFreeTile; m_nextFreeTile = tile; return(Status.DT_SUCCESS); }