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;
}