using DotRecast.Detour; using System.Buffers; namespace Sandbox.Navigation; /// /// Navigation Mesh - allowing AI to navigate a world /// public sealed partial class NavMesh { [Obsolete( "Use CalculatePath instead" )] public unsafe List GetSimplePath( Vector3 from, Vector3 to ) { var list = new List(); int nodeCountMax = 128; // find polys var fromFound = query.FindNearestPoly( ToNav( from ), ToNav( TileSizeWorldSpace * 3 ), DtQueryNoOpFilter.Shared, out var fromPoly, out var fromPoint, out _ ); if ( !fromFound.Succeeded() ) return list; var toFound = query.FindNearestPoly( ToNav( to ), ToNav( TileSizeWorldSpace * 3 ), DtQueryNoOpFilter.Shared, out var toPoly, out var toPoint, out _ ); if ( toFound.Failed() ) return list; // find path List polyPath = new List( 128 ); var polyPathFound = query.FindPath( fromPoly, toPoly, fromPoint, toPoint, DtQueryNoOpFilter.Shared, ref polyPath, DtFindPathOption.NoOption ); if ( polyPathFound.Failed() ) return list; Span outNodes = stackalloc DtStraightPath[nodeCountMax]; var straightPathFound = query.FindStraightPath( fromPoint, toPoint, polyPath, polyPath.Count, outNodes, out var straightPathCount, 128, 0 ); if ( straightPathFound.Failed() ) return list; for ( int i = 0; i < straightPathCount; i++ ) { list.Add( FromNav( outNodes[i].pos ) ); } return list; } /// /// Computes a navigation path between the specified start and target positions on the navmesh. /// Uses the same pathfinding algorithm as , taking agent configuration into account if provided. /// The result is suitable for direct use with . /// If a complete path cannot be found, the result may indicate an incomplete or failed path. /// public NavMeshPath CalculatePath( CalculatePathRequest request ) { NavMeshPath result = new(); // In navspace var searchRadius = request.Agent != null ? new Vector3( request.Agent.Radius * 2.01f, request.Agent.Height * 1.51f, request.Agent.Radius * 2.01f ) : crowd._agentPlacementHalfExtents; var startFound = query.FindNearestPoly( ToNav( request.Start ), searchRadius, DtQueryNoOpFilter.Shared, out var startPoly, out var startLocation, out _ ); if ( !startFound.Succeeded() ) { result.Status = NavMeshPathStatus.StartNotFound; return result; } var targetFound = query.FindNearestPoly( ToNav( request.Target ), searchRadius, DtQueryNoOpFilter.Shared, out var targetPoly, out var targetLocation, out _ ); if ( !targetFound.Succeeded() ) { result.Status = NavMeshPathStatus.TargetNotFound; return result; } var filter = request.Agent != null ? request.Agent.agentInternal.option.filter : crowd.GetDefaultFilter(); // Quick search towards the goal. var dtStatus = query.InitSlicedFindPath( startPoly, targetPoly, startLocation, targetLocation, filter, 0 ); if ( dtStatus.Failed() ) { result.Status = NavMeshPathStatus.PathNotFound; return result; } dtStatus = query.UpdateSlicedFindPath( crowd.Config().maxFindPathIterations, out var _ ); if ( dtStatus.Failed() ) { result.Status = NavMeshPathStatus.PathNotFound; return result; } result.Polygons = new( 128 ); dtStatus = query.FinalizeSlicedFindPath( ref result.Polygons ); if ( dtStatus.Failed() || result.Polygons.Count == 0 ) { result.Status = NavMeshPathStatus.PathNotFound; return result; } var straightPathCache = ArrayPool.Shared.Rent( 4096 ); dtStatus = query.FindStraightPath( startLocation, targetLocation, result.Polygons, result.Polygons.Count, straightPathCache, out var filledPointCount, straightPathCache.Length, 0 ); if ( dtStatus.Failed() ) { ArrayPool.Shared.Return( straightPathCache ); result.Status = NavMeshPathStatus.PathNotFound; return result; } var points = new List( filledPointCount ); for ( int i = 0; i < filledPointCount; i++ ) { points.Add( new NavMeshPathPoint { Position = FromNav( straightPathCache[i].pos ) } ); } ArrayPool.Shared.Return( straightPathCache ); result.Points = points; if ( result.Polygons[^1] != targetPoly ) { result.Status = NavMeshPathStatus.Partial; } else { result.Status = NavMeshPathStatus.Complete; } return result; } } /// /// Defines the input for a pathfinding request on the navmesh. /// public struct CalculatePathRequest { /// /// Start position of the path, should be close to the navmesh. /// public Vector3 Start; /// /// Target/End position of the path, should be close to the navmesh. /// public Vector3 Target; /// /// Optional agent whose configuration is used for path calculation. /// public NavMeshAgent Agent; } /// /// Contains the result of a pathfinding operation. /// public struct NavMeshPath : IValid { /// /// The outcome of the path calculation. /// public NavMeshPathStatus Status { get; internal set; } /// /// True if a path was found. /// public readonly bool IsValid => Status == NavMeshPathStatus.Partial || Status == NavMeshPathStatus.Complete; /// /// Polygons traversed by the path. /// Internal for now as you cannot do anything with polygon ids yet. /// internal List Polygons; /// /// Points along the path. /// public IReadOnlyList Points { get; internal set; } } public enum NavMeshPathStatus { /// /// Start location was not found on the navmesh. /// StartNotFound, /// /// Target location was not found on the navmesh. /// TargetNotFound, /// /// No path could be found. /// PathNotFound, /// /// Path found, but does not reach the target. /// The returned path will be to the closest location that can be reached. /// Partial, /// /// Path found from start to target. /// Complete, } /// /// Represents a point in a navmesh path, including its position in 3D space. /// May be extended in the future to hold more information about the point. /// public struct NavMeshPathPoint { public Vector3 Position; }