Ejemplo n.º 1
0
        public int GetTileRef(CompressedTile tile)
        {
            if (tile == null)
            {
                return(0);
            }
            int it = Array.IndexOf(m_tiles, tile);

            return(EncodeTileId(tile.Salt, it));
        }
Ejemplo n.º 2
0
        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");
            }
        }
Ejemplo n.º 3
0
        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);
        }
Ejemplo n.º 4
0
        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);
        }
Ejemplo n.º 5
0
        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);
        }
Ejemplo n.º 6
0
        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);
        }
Ejemplo n.º 7
0
        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);
        }