Ejemplo n.º 1
0
        public bool unloadMap(uint mapId)
        {
            if (!loadedMMaps.ContainsKey(mapId))
            {
                // file may not exist, therefore not loaded
                Log.outDebug(LogFilter.Maps, "MMAP:unloadMap: Asked to unload not loaded navmesh map {0:D4}", mapId);
                return(false);
            }

            // unload all tiles from given map
            MMapData mmap = loadedMMaps.LookupByKey(mapId);

            foreach (var i in mmap.loadedTileRefs)
            {
                uint x = (i.Key >> 16);
                uint y = (i.Key & 0x0000FFFF);
                Detour.dtRawTileData data;
                if (Detour.dtStatusFailed(mmap.navMesh.removeTile(i.Value, out data)))
                {
                    Log.outError(LogFilter.Maps, "MMAP:unloadMap: Could not unload {0:D4}{1:D2}{2:D2}.mmtile from navmesh", mapId, x, y);
                }
                else
                {
                    --loadedTiles;
                    Log.outInfo(LogFilter.Maps, "MMAP:unloadMap: Unloaded mmtile {0:D4} [{1:D2}, {2:D2}] from {3:D4}", mapId, x, y, mapId);
                }
            }

            loadedMMaps.Remove(mapId);
            Log.outInfo(LogFilter.Maps, "MMAP:unloadMap: Unloaded {0:D4}.mmap", mapId);

            return(true);
        }
Ejemplo n.º 2
0
        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]);
        }
Ejemplo n.º 3
0
        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);
        }
Ejemplo n.º 4
0
    // 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);
    }
Ejemplo n.º 5
0
        void AddSwap(PhasedTile ptile, uint swap, uint packedXY)
        {
            uint x = (packedXY >> 16);
            uint y = (packedXY & 0x0000FFFF);

            if (!loadedTileRefs.ContainsKey(packedXY))
            {
                Log.outDebug(LogFilter.Maps, "MMapData.AddSwap: phased mmtile {0:D4}[{1:D2}, {2:D2}] load skipped, due to not loaded base tile on map {3}", swap, x, y, _mapId);
                return;
            }
            if (loadedPhasedTiles[swap].Contains(packedXY))
            {
                Log.outDebug(LogFilter.Maps, "MMapData.AddSwap: WARNING! phased mmtile {0:D4}[{1:D2}, {2:D2}] load skipped, due to already loaded on map {3}", swap, x, y, _mapId);
                return;
            }

            Detour.dtMeshHeader header = ptile.data.header;

            Detour.dtMeshTile oldTile = navMesh.getTileByRef(loadedTileRefs[packedXY]);
            if (oldTile == null)
            {
                Log.outDebug(LogFilter.Maps, "MMapData.AddSwap: phased mmtile {0:D4}[{1:D2}, {2:D2}] load skipped, due to not loaded base tile ref on map {3}", swap, x, y, _mapId);
                return;
            }

            // header xy is based on the swap map's tile set, wich doesn't have all the same tiles as root map, so copy the xy from the orignal header
            header.x = oldTile.header.x;
            header.y = oldTile.header.y;

            Detour.dtRawTileData data;
            // remove old tile
            if (Detour.dtStatusFailed(navMesh.removeTile(loadedTileRefs[packedXY], out data)))
            {
                Log.outError(LogFilter.Maps, "MMapData.AddSwap: Could not unload {0:D4}{1:D2}{2:D2}.mmtile from navmesh", _mapId, x, y);
            }
            else
            {
                Log.outDebug(LogFilter.Maps, "MMapData.AddSwap: Unloaded {0:D4}{1:D2}{2:D2}.mmtile from navmesh", _mapId, x, y);

                _activeSwaps.Add(swap);
                loadedPhasedTiles.Add(swap, packedXY);

                // add new swapped tile
                ulong loadedRef = 0;
                if (Detour.dtStatusSucceed(navMesh.addTile(ptile.data, 0, 0, ref loadedRef)))
                {
                    Log.outDebug(LogFilter.Maps, "MMapData.AddSwap: Loaded phased mmtile {0:D4}[{1:D2}, {2:D2}] into {0:D4}[{1:D2}, {2:D2}]", swap, x, y, _mapId, header.x, header.y);
                }
                else
                {
                    Log.outError(LogFilter.Maps, "MMapData.AddSwap: Could not load {0:D4}{1:D2}{2:D2}.mmtile to navmesh", swap, x, y);
                }

                loadedTileRefs[packedXY] = loadedRef;
            }
        }
Ejemplo n.º 6
0
        public bool unloadMap(uint mapId, uint x, uint y)
        {
            // check if we have this map loaded
            MMapData mmap = GetMMapData(mapId);

            if (mmap == null)
            {
                // file may not exist, therefore not loaded
                Log.outDebug(LogFilter.Maps, "MMAP:unloadMap: Asked to unload not loaded navmesh map. {0:D4}{1:D2}{2:D2}.mmtile", mapId, x, y);
                return(false);
            }

            // check if we have this tile loaded
            uint packedGridPos = packTileID((int)x, (int)y);

            if (!mmap.loadedTileRefs.ContainsKey(packedGridPos))
            {
                // file may not exist, therefore not loaded
                Log.outDebug(LogFilter.Maps, "MMAP:unloadMap: Asked to unload not loaded navmesh tile. {0:D4}{1:D2}{2:D2}.mmtile", mapId, x, y);
                return(false);
            }

            ulong tileRef = mmap.loadedTileRefs.LookupByKey(packedGridPos);

            // unload, and mark as non loaded
            Detour.dtRawTileData data;
            if (Detour.dtStatusFailed(mmap.navMesh.removeTile(tileRef, out data)))
            {
                // this is technically a memory leak
                // if the grid is later reloaded, dtNavMesh.addTile will return error but no extra memory is used
                // we cannot recover from this error - assert out
                Log.outError(LogFilter.Maps, "MMAP:unloadMap: Could not unload {0:D4}{1:D2}{2:D2}.mmtile from navmesh", mapId, x, y);
                Contract.Assert(false);
            }
            else
            {
                mmap.loadedTileRefs.Remove(packedGridPos);
                --loadedTiles;
                Log.outInfo(LogFilter.Maps, "MMAP:unloadMap: Unloaded mmtile {0:D4}[{1:D2}, {2:D2}] from {3:D4}", mapId, x, y, mapId);

                var phasedMaps = phaseMapData.LookupByKey(mapId);
                if (!phasedMaps.Empty())
                {
                    mmap.DeleteBaseTile(packedGridPos);
                    UnloadPhaseTile(phasedMaps, (int)x, (int)y);
                }
                return(true);
            }

            return(false);
        }
Ejemplo n.º 7
0
        bool GetSteerTarget(float[] startPos, float[] endPos, float minTargetDist, ulong[] path, uint pathSize, out float[] steerPos, out Detour.dtStraightPathFlags steerPosFlag, out ulong steerPosRef)
        {
            steerPosRef  = 0;
            steerPos     = new float[3];
            steerPosFlag = 0;

            // Find steer target.
            float[] steerPath      = new float[3 * 3];
            byte[]  steerPathFlags = new byte[3];
            ulong[] steerPathPolys = new ulong[3];
            int     nsteerPath     = 0;
            uint    dtResult       = _navMeshQuery.findStraightPath(startPos, endPos, path, (int)pathSize, steerPath, steerPathFlags, steerPathPolys, ref nsteerPath, 3, 0);

            if (nsteerPath == 0 || Detour.dtStatusFailed(dtResult))
            {
                return(false);
            }

            // Find vertex far enough to steer to.
            uint ns = 0;

            while (ns < nsteerPath)
            {
                Span <float> span = steerPath;
                // Stop at Off-Mesh link or when point is further than slop away.
                if ((steerPathFlags[ns].HasAnyFlag((byte)Detour.dtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION) ||
                     !InRangeYZX(span.Slice((int)ns * 3).ToArray(), startPos, minTargetDist, 1000.0f)))
                {
                    break;
                }
                ns++;
            }
            // Failed to find good point to steer to.
            if (ns >= nsteerPath)
            {
                return(false);
            }

            Detour.dtVcopy(steerPos, 0, steerPath, (int)ns * 3);
            steerPos[1]  = startPos[1]; // keep Z value
            steerPosFlag = (Detour.dtStraightPathFlags)steerPathFlags[ns];
            steerPosRef  = steerPathPolys[ns];

            return(true);
        }
Ejemplo n.º 8
0
        void RemoveSwap(PhasedTile ptile, uint swap, uint packedXY)
        {
            uint x = (packedXY >> 16);
            uint y = (packedXY & 0x0000FFFF);

            if (!loadedPhasedTiles[swap].Contains(packedXY))
            {
                Log.outDebug(LogFilter.Maps, "MMapData.RemoveSwap: mmtile {0:D4}[{1:D2}, {2:D2}] unload skipped, due to not loaded", swap, x, y);
                return;
            }
            Detour.dtMeshHeader header = ptile.data.header;

            Detour.dtRawTileData data;
            // remove old tile
            if (Detour.dtStatusFailed(navMesh.removeTile(loadedTileRefs[packedXY], out data)))
            {
                Log.outError(LogFilter.Maps, "MMapData.RemoveSwap: Could not unload phased {0:D4}{1:D2}{2:D2}.mmtile from navmesh", swap, x, y);
            }
            else
            {
                Log.outDebug(LogFilter.Maps, "MMapData.RemoveSwap: Unloaded phased {0:D4}{1:D2}{2:D2}.mmtile from navmesh", swap, x, y);

                // restore base tile
                ulong loadedRef = 0;
                if (Detour.dtStatusSucceed(navMesh.addTile(_baseTiles[packedXY].data, 0, 0, ref loadedRef)))
                {
                    Log.outDebug(LogFilter.Maps, "MMapData.RemoveSwap: Loaded base mmtile {0:D4}[{1:D2}, {2:D2}] into {0:D4}[{1:D2}, {2:D2}]", _mapId, x, y, _mapId, header.x, header.y);
                }
                else
                {
                    Log.outError(LogFilter.Maps, "MMapData.RemoveSwap: Could not load base {0:D4}{1:D2}{2:D2}.mmtile to navmesh", _mapId, x, y);
                }

                loadedTileRefs[packedXY] = loadedRef;
            }

            loadedPhasedTiles.Remove(swap, packedXY);

            if (loadedPhasedTiles[swap].Empty())
            {
                _activeSwaps.Remove(swap);
                Log.outDebug(LogFilter.Maps, "MMapData.RemoveSwap: Fully removed swap {0} from map {1}", swap, _mapId);
            }
        }
Ejemplo n.º 9
0
        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);
        }
Ejemplo n.º 10
0
        bool loadMapData(uint mapId)
        {
            // we already have this map loaded?
            if (loadedMMaps.ContainsKey(mapId) && loadedMMaps[mapId] != null)
            {
                return(true);
            }

            // load and init dtNavMesh - read parameters from file
            string filename = string.Format(MAP_FILE_NAME_FORMAT, Global.WorldMgr.GetDataPath(), mapId);

            if (!File.Exists(filename))
            {
                Log.outError(LogFilter.Maps, "Could not open mmap file {0}", filename);
                return(false);
            }

            using (BinaryReader reader = new BinaryReader(new FileStream(filename, FileMode.Open, FileAccess.Read), Encoding.UTF8))
            {
                Detour.dtNavMeshParams Params = new Detour.dtNavMeshParams();
                Params.orig[0] = reader.ReadSingle();
                Params.orig[1] = reader.ReadSingle();
                Params.orig[2] = reader.ReadSingle();

                Params.tileWidth  = reader.ReadSingle();
                Params.tileHeight = reader.ReadSingle();
                Params.maxTiles   = reader.ReadInt32();
                Params.maxPolys   = reader.ReadInt32();

                Detour.dtNavMesh mesh = new Detour.dtNavMesh();
                if (Detour.dtStatusFailed(mesh.init(Params)))
                {
                    Log.outError(LogFilter.Maps, "MMAP:loadMapData: Failed to initialize dtNavMesh for mmap {0:D4} from file {1}", mapId, filename);
                    return(false);
                }

                Log.outInfo(LogFilter.Maps, "MMAP:loadMapData: Loaded {0:D4}.mmap", mapId);

                // store inside our map list
                loadedMMaps[mapId] = new MMapData(mesh, mapId);
                return(true);
            }
        }
Ejemplo n.º 11
0
        ulong GetPathPolyByPosition(ulong[] polyPath, uint polyPathSize, float[] point, ref float distance)
        {
            if (polyPath == null || polyPathSize == 0)
            {
                return(0);
            }

            ulong nearestPoly = 0;
            float minDist2d   = float.MaxValue;
            float minDist3d   = 0.0f;

            for (uint i = 0; i < polyPathSize; ++i)
            {
                float[] closestPoint = new float[3];
                bool    posOverPoly  = false;
                if (Detour.dtStatusFailed(_navMeshQuery.closestPointOnPoly(polyPath[i], point, closestPoint, ref posOverPoly)))
                {
                    continue;
                }

                float d = Detour.dtVdist2DSqr(point, closestPoint);
                if (d < minDist2d)
                {
                    minDist2d   = d;
                    nearestPoly = polyPath[i];
                    minDist3d   = Detour.dtVdistSqr(point, closestPoint);
                }

                if (minDist2d < 1.0f) // shortcut out - close enough for us
                {
                    break;
                }
            }

            distance = (float)Math.Sqrt(minDist3d);

            return((minDist2d < 3.0f) ? nearestPoly : 0u);
        }
Ejemplo n.º 12
0
        uint FindSmoothPath(float[] startPos, float[] endPos, ulong[] polyPath, uint polyPathSize, out float[] smoothPath, out int smoothPathSize, uint maxSmoothPathSize)
        {
            smoothPathSize = 0;
            int nsmoothPath = 0;

            smoothPath = new float[74 * 3];

            ulong[] polys = new ulong[74];
            Array.Copy(polyPath, polys, polyPathSize);
            uint npolys = polyPathSize;

            float[] iterPos   = new float[3];
            float[] targetPos = new float[3];
            if (Detour.dtStatusFailed(_navMeshQuery.closestPointOnPolyBoundary(polys[0], startPos, iterPos)))
            {
                return(Detour.DT_FAILURE);
            }

            if (Detour.dtStatusFailed(_navMeshQuery.closestPointOnPolyBoundary(polys[npolys - 1], endPos, targetPos)))
            {
                return(Detour.DT_FAILURE);
            }

            Detour.dtVcopy(smoothPath, nsmoothPath * 3, iterPos, 0);
            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 && nsmoothPath < maxSmoothPathSize)
            {
                // Find location to steer towards.
                float[] steerPos;
                Detour.dtStraightPathFlags steerPosFlag;
                ulong steerPosRef = 0;

                if (!GetSteerTarget(iterPos, targetPos, 0.3f, polys, npolys, out steerPos, out steerPosFlag, out steerPosRef))
                {
                    break;
                }

                bool endOfPath         = steerPosFlag.HasAnyFlag(Detour.dtStraightPathFlags.DT_STRAIGHTPATH_END);
                bool offMeshConnection = steerPosFlag.HasAnyFlag(Detour.dtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION);

                // Find movement delta.
                float[] delta = new float[3];
                Detour.dtVsub(delta, steerPos, iterPos);
                float len = (float)Math.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 < 4.0f)
                {
                    len = 1.0f;
                }
                else
                {
                    len = 4.0f / len;
                }

                float[] moveTgt = new float[3];
                Detour.dtVmad(moveTgt, iterPos, delta, len);

                // Move
                float[] result         = new float[3];
                int     MAX_VISIT_POLY = 16;
                ulong[] visited        = new ulong[MAX_VISIT_POLY];

                int nvisited = 0;
                _navMeshQuery.moveAlongSurface(polys[0], iterPos, moveTgt, _filter, result, visited, ref nvisited, MAX_VISIT_POLY);
                npolys = FixupCorridor(polys, npolys, 74, visited, nvisited);

                _navMeshQuery.getPolyHeight(polys[0], result, ref result[1]);
                result[1] += 0.5f;
                Detour.dtVcopy(iterPos, result);

                // Handle end of path and off-mesh links when close enough.
                if (endOfPath && InRangeYZX(iterPos, steerPos, 0.3f, 1.0f))
                {
                    // Reached end of path.
                    Detour.dtVcopy(iterPos, targetPos);
                    if (nsmoothPath < maxSmoothPathSize)
                    {
                        Detour.dtVcopy(smoothPath, nsmoothPath * 3, iterPos, 0);
                        nsmoothPath++;
                    }
                    break;
                }
                else if (offMeshConnection && InRangeYZX(iterPos, steerPos, 0.3f, 1.0f))
                {
                    // Advance the path up to and over the off-mesh connection.
                    ulong prevRef = 0;
                    ulong polyRef = polys[0];
                    uint  npos    = 0;
                    while (npos < npolys && polyRef != steerPosRef)
                    {
                        prevRef = polyRef;
                        polyRef = polys[npos];
                        npos++;
                    }

                    for (uint i = npos; i < npolys; ++i)
                    {
                        polys[i - npos] = polys[i];
                    }

                    npolys -= npos;

                    // Handle the connection.
                    float[] connectionStartPos = new float[3];
                    float[] connectionEndPos   = new float[3];
                    if (Detour.dtStatusSucceed(_navMesh.getOffMeshConnectionPolyEndPoints(prevRef, polyRef, connectionStartPos, connectionEndPos)))
                    {
                        if (nsmoothPath < maxSmoothPathSize)
                        {
                            Detour.dtVcopy(smoothPath, nsmoothPath * 3, connectionStartPos, 0);
                            nsmoothPath++;
                        }
                        // Move position at the other side of the off-mesh link.
                        Detour.dtVcopy(iterPos, connectionEndPos);
                        _navMeshQuery.getPolyHeight(polys[0], iterPos, ref iterPos[1]);
                        iterPos[1] += 0.5f;
                    }
                }

                // Store results.
                if (nsmoothPath < maxSmoothPathSize)
                {
                    Detour.dtVcopy(smoothPath, nsmoothPath * 3, iterPos, 0);
                    nsmoothPath++;
                }
            }

            smoothPathSize = nsmoothPath;

            // this is most likely a loop
            return(nsmoothPath < 74 ? Detour.DT_SUCCESS : Detour.DT_FAILURE);
        }
Ejemplo n.º 13
0
    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);
    }
Ejemplo n.º 14
0
        void BuildPolyPath(Vector3 startPos, Vector3 endPos)
        {
            // *** getting start/end poly logic ***

            float distToStartPoly = 0;
            float distToEndPoly   = 0;

            float[] startPoint = { startPos.Y, startPos.Z, startPos.X };
            float[] endPoint   = { endPos.Y, endPos.Z, endPos.X };

            ulong startPoly = GetPolyByLocation(startPoint, ref distToStartPoly);
            ulong endPoly   = GetPolyByLocation(endPoint, ref distToEndPoly);

            // we have a hole in our mesh
            // make shortcut path and mark it as NOPATH ( with flying and swimming exception )
            // its up to caller how he will use this info
            if (startPoly == 0 || endPoly == 0)
            {
                Log.outDebug(LogFilter.Maps, "++ BuildPolyPath . (startPoly == 0 || endPoly == 0)\n");
                BuildShortcut();
                bool path = _sourceUnit.IsTypeId(TypeId.Unit) && _sourceUnit.ToCreature().CanFly();

                bool waterPath = _sourceUnit.IsTypeId(TypeId.Unit) && _sourceUnit.ToCreature().CanSwim();
                if (waterPath)
                {
                    // Check both start and end points, if they're both in water, then we can *safely* let the creature move
                    for (uint i = 0; i < _pathPoints.Length; ++i)
                    {
                        ZLiquidStatus status = _sourceUnit.GetMap().getLiquidStatus(_pathPoints[i].X, _pathPoints[i].Y, _pathPoints[i].Z, MapConst.MapAllLiquidTypes);
                        // One of the points is not in the water, cancel movement.
                        if (status == ZLiquidStatus.NoWater)
                        {
                            waterPath = false;
                            break;
                        }
                    }
                }

                pathType = (path || waterPath) ? (PathType.Normal | PathType.NotUsingPath) : PathType.NoPath;
                return;
            }

            // we may need a better number here
            bool farFromPoly = (distToStartPoly > 7.0f || distToEndPoly > 7.0f);

            if (farFromPoly)
            {
                Log.outDebug(LogFilter.Maps, "++ BuildPolyPath . farFromPoly distToStartPoly={0:F3} distToEndPoly={1:F3}\n", distToStartPoly, distToEndPoly);

                bool buildShotrcut = false;
                if (_sourceUnit.IsTypeId(TypeId.Unit))
                {
                    Creature owner = _sourceUnit.ToCreature();

                    Vector3 p = (distToStartPoly > 7.0f) ? startPos : endPos;
                    if (_sourceUnit.GetMap().IsUnderWater(p.X, p.Y, p.Z))
                    {
                        Log.outDebug(LogFilter.Maps, "++ BuildPolyPath . underWater case\n");
                        if (owner.CanSwim())
                        {
                            buildShotrcut = true;
                        }
                    }
                    else
                    {
                        Log.outDebug(LogFilter.Maps, "++ BuildPolyPath . flying case\n");
                        if (owner.CanFly())
                        {
                            buildShotrcut = true;
                        }
                    }
                }

                if (buildShotrcut)
                {
                    BuildShortcut();
                    pathType = (PathType.Normal | PathType.NotUsingPath);
                    return;
                }
                else
                {
                    float[] closestPoint = new float[3];
                    // we may want to use closestPointOnPolyBoundary instead
                    bool posOverPoly = false;
                    if (Detour.dtStatusSucceed(_navMeshQuery.closestPointOnPoly(endPoly, endPoint, closestPoint, ref posOverPoly)))
                    {
                        Detour.dtVcopy(endPoint, closestPoint);
                        SetActualEndPosition(new Vector3(endPoint[2], endPoint[0], endPoint[1]));
                    }

                    pathType = PathType.Incomplete;
                }
            }

            // *** poly path generating logic ***

            // start and end are on same polygon
            // just need to move in straight line
            if (startPoly == endPoly)
            {
                Log.outDebug(LogFilter.Maps, "++ BuildPolyPath . (startPoly == endPoly)\n");

                BuildShortcut();

                _pathPolyRefs[0] = startPoly;
                _polyLength      = 1;

                pathType = farFromPoly ? PathType.Incomplete : PathType.Normal;
                Log.outDebug(LogFilter.Maps, "BuildPolyPath . path type {0}\n", pathType);
                return;
            }

            // look for startPoly/endPoly in current path
            /// @todo we can merge it with getPathPolyByPosition() loop
            bool startPolyFound = false;
            bool endPolyFound   = false;
            uint pathStartIndex = 0;
            uint pathEndIndex   = 0;

            if (_polyLength != 0)
            {
                for (; pathStartIndex < _polyLength; ++pathStartIndex)
                {
                    // here to carch few bugs
                    if (_pathPolyRefs[pathStartIndex] == 0)
                    {
                        Log.outError(LogFilter.Maps, "Invalid poly ref in BuildPolyPath. _polyLength: {0}, pathStartIndex: {1}," +
                                     " startPos: {2}, endPos: {3}, mapid: {4}", _polyLength, pathStartIndex, startPos, endPos, _sourceUnit.GetMapId());
                        break;
                    }

                    if (_pathPolyRefs[pathStartIndex] == startPoly)
                    {
                        startPolyFound = true;
                        break;
                    }
                }

                for (pathEndIndex = _polyLength - 1; pathEndIndex > pathStartIndex; --pathEndIndex)
                {
                    if (_pathPolyRefs[pathEndIndex] == endPoly)
                    {
                        endPolyFound = true;
                        break;
                    }
                }
            }

            if (startPolyFound && endPolyFound)
            {
                Log.outDebug(LogFilter.Maps, "BuildPolyPath : (startPolyFound && endPolyFound)\n");

                // we moved along the path and the target did not move out of our old poly-path
                // our path is a simple subpath case, we have all the data we need
                // just "cut" it out

                _polyLength = pathEndIndex - pathStartIndex + 1;
                Array.Copy(_pathPolyRefs, pathStartIndex, _pathPolyRefs, 0, _polyLength);
            }
            else if (startPolyFound && !endPolyFound)
            {
                Log.outDebug(LogFilter.Maps, "BuildPolyPath : (startPolyFound && !endPolyFound)\n");

                // we are moving on the old path but target moved out
                // so we have atleast part of poly-path ready

                _polyLength -= pathStartIndex;

                // try to adjust the suffix of the path instead of recalculating entire length
                // at given interval the target cannot get too far from its last location
                // thus we have less poly to cover
                // sub-path of optimal path is optimal

                // take ~80% of the original length
                /// @todo play with the values here
                uint prefixPolyLength = (uint)(_polyLength * 0.8f + 0.5f);
                Array.Copy(_pathPolyRefs, pathStartIndex, _pathPolyRefs, 0, prefixPolyLength);

                ulong suffixStartPoly = _pathPolyRefs[prefixPolyLength - 1];

                // we need any point on our suffix start poly to generate poly-path, so we need last poly in prefix data
                float[] suffixEndPoint = new float[3];
                bool    posOverPoly    = false;
                if (Detour.dtStatusFailed(_navMeshQuery.closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint, ref posOverPoly)))
                {
                    // we can hit offmesh connection as last poly - closestPointOnPoly() don't like that
                    // try to recover by using prev polyref
                    --prefixPolyLength;
                    suffixStartPoly = _pathPolyRefs[prefixPolyLength - 1];
                    if (Detour.dtStatusFailed(_navMeshQuery.closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint, ref posOverPoly)))
                    {
                        // suffixStartPoly is still invalid, error state
                        BuildShortcut();
                        pathType = PathType.NoPath;
                        return;
                    }
                }

                // generate suffix
                uint suffixPolyLength = 0;

                uint dtResult;
                if (_straightLine)
                {
                    float   hit       = 0;
                    float[] hitNormal = new float[3];

                    dtResult = _navMeshQuery.raycast(
                        suffixStartPoly,
                        suffixEndPoint,
                        endPoint,
                        _filter,
                        ref hit,
                        hitNormal,
                        _pathPolyRefs,
                        ref suffixPolyLength,
                        74 - (int)prefixPolyLength);

                    // raycast() sets hit to FLT_MAX if there is a ray between start and end
                    if (hit != float.MaxValue)
                    {
                        // the ray hit something, return no path instead of the incomplete one
                        pathType = PathType.NoPath;
                        return;
                    }
                }
                else
                {
                    dtResult = _navMeshQuery.findPath(
                        suffixStartPoly,    // start polygon
                        endPoly,            // end polygon
                        suffixEndPoint,     // start position
                        endPoint,           // end position
                        _filter,            // polygon search filter
                        _pathPolyRefs,
                        ref suffixPolyLength,
                        74 - (int)prefixPolyLength);
                }

                if (suffixPolyLength == 0 || Detour.dtStatusFailed(dtResult))
                {
                    // this is probably an error state, but we'll leave it
                    // and hopefully recover on the next Update
                    // we still need to copy our preffix
                    Log.outError(LogFilter.Maps, "{0}'s Path Build failed: 0 length path", _sourceUnit.GetGUID().ToString());
                }

                Log.outDebug(LogFilter.Maps, "m_polyLength={0} prefixPolyLength={1} suffixPolyLength={2} \n", _polyLength, prefixPolyLength, suffixPolyLength);

                // new path = prefix + suffix - overlap
                _polyLength = prefixPolyLength + suffixPolyLength - 1;
            }
            else
            {
                Log.outDebug(LogFilter.Maps, "++ BuildPolyPath . (!startPolyFound && !endPolyFound)\n");

                // either we have no path at all . first run
                // or something went really wrong . we aren't moving along the path to the target
                // just generate new path

                // free and invalidate old path data
                Clear();

                uint dtResult;
                if (_straightLine)
                {
                    float   hit       = 0;
                    float[] hitNormal = new float[3];

                    dtResult = _navMeshQuery.raycast(
                        startPoly,
                        startPoint,
                        endPoint,
                        _filter,
                        ref hit,
                        hitNormal,
                        _pathPolyRefs,
                        ref _polyLength,
                        74);

                    // raycast() sets hit to FLT_MAX if there is a ray between start and end
                    if (hit != float.MaxValue)
                    {
                        // the ray hit something, return no path instead of the incomplete one
                        pathType = PathType.NoPath;
                        return;
                    }
                }
                else
                {
                    dtResult = _navMeshQuery.findPath(
                        startPoly,     // start polygon
                        endPoly,       // end polygon
                        startPoint,    // start position
                        endPoint,      // end position
                        _filter,       // polygon search filter
                        _pathPolyRefs, // [out] path
                        ref _polyLength,
                        74);           // max number of polygons in output path
                }

                if (_polyLength == 0 || Detour.dtStatusFailed(dtResult))
                {
                    // only happens if we passed bad data to findPath(), or navmesh is messed up
                    Log.outError(LogFilter.Maps, "{0}'s Path Build failed: 0 length path", _sourceUnit.GetGUID().ToString());
                    BuildShortcut();
                    pathType = PathType.NoPath;
                    return;
                }
            }

            // by now we know what type of path we can get
            if (_pathPolyRefs[_polyLength - 1] == endPoly && !pathType.HasAnyFlag(PathType.Incomplete))
            {
                pathType = PathType.Normal;
            }
            else
            {
                pathType = PathType.Incomplete;
            }

            // generate the point-path out of our up-to-date poly-path
            BuildPointPath(startPoint, endPoint);
        }
Ejemplo n.º 15
0
        void BuildPointPath(float[] startPoint, float[] endPoint)
        {
            float[] pathPoints = new float[74 * 3];
            int     pointCount = 0;
            uint    dtResult   = Detour.DT_FAILURE;

            if (_straightLine)
            {
                dtResult   = Detour.DT_SUCCESS;
                pointCount = 1;
                Array.Copy(startPoint, pathPoints, 3); // first point

                // path has to be split into polygons with dist SMOOTH_PATH_STEP_SIZE between them
                Vector3 startVec = new Vector3(startPoint[0], startPoint[1], startPoint[2]);
                Vector3 endVec   = new Vector3(endPoint[0], endPoint[1], endPoint[2]);
                Vector3 diffVec  = (endVec - startVec);
                Vector3 prevVec  = startVec;
                float   len      = diffVec.GetLength();
                diffVec *= 4.0f / len;
                while (len > 4.0f)
                {
                    len     -= 4.0f;
                    prevVec += diffVec;
                    pathPoints[3 * pointCount + 0] = prevVec.X;
                    pathPoints[3 * pointCount + 1] = prevVec.Y;
                    pathPoints[3 * pointCount + 2] = prevVec.Z;
                    ++pointCount;
                }

                Array.Copy(endPoint, 0, pathPoints, 3 * pointCount, 3); // last point
                ++pointCount;
            }
            else if (_useStraightPath)
            {
                dtResult = _navMeshQuery.findStraightPath(
                    startPoint,         // start position
                    endPoint,           // end position
                    _pathPolyRefs,
                    (int)_polyLength,
                    pathPoints,         // [out] path corner points
                    null,               // [out] flags
                    null,               // [out] shortened path
                    ref pointCount,
                    (int)_pointPathLimit,
                    0);   // maximum number of points/polygons to use
            }
            else
            {
                dtResult = FindSmoothPath(
                    startPoint,        // start position
                    endPoint,          // end position
                    _pathPolyRefs,     // current path
                    _polyLength,       // length of current path
                    out pathPoints,    // [out] path corner points
                    out pointCount,
                    _pointPathLimit);  // maximum number of points
            }

            if (pointCount < 2 || Detour.dtStatusFailed(dtResult))
            {
                // only happens if pass bad data to findStraightPath or navmesh is broken
                // single point paths can be generated here
                /// @todo check the exact cases
                Log.outDebug(LogFilter.Maps, "++ PathGenerator.BuildPointPath FAILED! path sized {0} returned\n", pointCount);
                BuildShortcut();
                pathType = PathType.NoPath;
                return;
            }
            else if (pointCount == _pointPathLimit)
            {
                Log.outDebug(LogFilter.Maps, "++ PathGenerator.BuildPointPath FAILED! path sized {0} returned, lower than limit set to {1}\n", pointCount, _pointPathLimit);
                BuildShortcut();
                pathType = PathType.Short;
                return;
            }

            _pathPoints = new Vector3[pointCount];
            for (uint i = 0; i < pointCount; ++i)
            {
                _pathPoints[i] = new Vector3(pathPoints[i * 3 + 2], pathPoints[i * 3], pathPoints[i * 3 + 1]);
            }

            NormalizePath();

            // first point is always our current location - we need the next one
            SetActualEndPosition(_pathPoints[pointCount - 1]);

            // force the given destination, if needed
            if (_forceDestination && (!pathType.HasAnyFlag(PathType.Normal) || !InRange(GetEndPosition(), GetActualEndPosition(), 1.0f, 1.0f)))
            {
                // we may want to keep partial subpath
                if (Dist3DSqr(GetActualEndPosition(), GetEndPosition()) < 0.3f * Dist3DSqr(GetStartPosition(), GetEndPosition()))
                {
                    SetActualEndPosition(GetEndPosition());
                    _pathPoints[_pathPoints.Length - 1] = GetEndPosition();
                }
                else
                {
                    SetActualEndPosition(GetEndPosition());
                    BuildShortcut();
                }

                pathType = (PathType.Normal | PathType.NotUsingPath);
            }
            Log.outDebug(LogFilter.Maps, "PathGenerator.BuildPointPath path type {0} size {1} poly-size {2}\n", pathType, pointCount, _polyLength);
        }
Ejemplo n.º 16
0
    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);
    }
Ejemplo n.º 17
0
    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);
    }
Ejemplo n.º 18
0
        void BuildPointPath(float[] startPoint, float[] endPoint)
        {
            float[] pathPoints = new float[74 * 3];
            int     pointCount = 0;
            uint    dtResult;

            if (_useRaycast)
            {
                // _straightLine uses raycast and it currently doesn't support building a point path, only a 2-point path with start and hitpoint/end is returned
                Log.outError(LogFilter.Maps, $"PathGenerator::BuildPointPath() called with _useRaycast for unit {_source.GetGUID()}");
                BuildShortcut();
                pathType = PathType.NoPath;
                return;
            }
            else if (_useStraightPath)
            {
                dtResult = _navMeshQuery.findStraightPath(
                    startPoint,         // start position
                    endPoint,           // end position
                    _pathPolyRefs,
                    (int)_polyLength,
                    pathPoints,         // [out] path corner points
                    null,               // [out] flags
                    null,               // [out] shortened path
                    ref pointCount,
                    (int)_pointPathLimit,
                    0);   // maximum number of points/polygons to use
            }
            else
            {
                dtResult = FindSmoothPath(
                    startPoint,        // start position
                    endPoint,          // end position
                    _pathPolyRefs,     // current path
                    _polyLength,       // length of current path
                    out pathPoints,    // [out] path corner points
                    out pointCount,
                    _pointPathLimit);  // maximum number of points
            }

            // Special case with start and end positions very close to each other
            if (_polyLength == 1 && pointCount == 1)
            {
                // First point is start position, append end position
                Detour.dtVcopy(pathPoints, 1 * 3, endPoint, 0);
                pointCount++;
            }
            else if (pointCount < 2 || Detour.dtStatusFailed(dtResult))
            {
                // only happens if pass bad data to findStraightPath or navmesh is broken
                // single point paths can be generated here
                // @todo check the exact cases
                Log.outDebug(LogFilter.Maps, "++ PathGenerator.BuildPointPath FAILED! path sized {0} returned\n", pointCount);
                BuildShortcut();
                pathType |= PathType.NoPath;
                return;
            }
            else if (pointCount == _pointPathLimit)
            {
                Log.outDebug(LogFilter.Maps, "++ PathGenerator.BuildPointPath FAILED! path sized {0} returned, lower than limit set to {1}\n", pointCount, _pointPathLimit);
                BuildShortcut();
                pathType |= PathType.Short;
                return;
            }

            _pathPoints = new Vector3[pointCount];
            for (uint i = 0; i < pointCount; ++i)
            {
                _pathPoints[i] = new Vector3(pathPoints[i * 3 + 2], pathPoints[i * 3], pathPoints[i * 3 + 1]);
            }

            NormalizePath();

            // first point is always our current location - we need the next one
            SetActualEndPosition(_pathPoints[pointCount - 1]);

            // force the given destination, if needed
            if (_forceDestination && (!pathType.HasAnyFlag(PathType.Normal) || !InRange(GetEndPosition(), GetActualEndPosition(), 1.0f, 1.0f)))
            {
                // we may want to keep partial subpath
                if (Dist3DSqr(GetActualEndPosition(), GetEndPosition()) < 0.3f * Dist3DSqr(GetStartPosition(), GetEndPosition()))
                {
                    SetActualEndPosition(GetEndPosition());
                    _pathPoints[^ 1] = GetEndPosition();
Ejemplo n.º 19
0
        //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);
        }
Ejemplo n.º 20
0
        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);
        }