Пример #1
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);
        }
Пример #2
0
        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);
        }