Files
sbox-public/engine/Sandbox.Engine/Game/Navigation/NavMesh/NavMesh.Path.cs
s&box team 71f266059a Open source release
This commit imports the C# engine code and game files, excluding C++ source code.

[Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
2025-11-24 09:05:18 +00:00

206 lines
6.3 KiB
C#

using DotRecast.Detour;
using System.Buffers;
namespace Sandbox.Navigation;
/// <summary>
/// Navigation Mesh - allowing AI to navigate a world
/// </summary>
public sealed partial class NavMesh
{
[Obsolete( "Use CalculatePath instead" )]
public unsafe List<Vector3> GetSimplePath( Vector3 from, Vector3 to )
{
var list = new List<Vector3>();
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<long> polyPath = new List<long>( 128 );
var polyPathFound = query.FindPath( fromPoly, toPoly, fromPoint, toPoint, DtQueryNoOpFilter.Shared, ref polyPath, DtFindPathOption.NoOption );
if ( polyPathFound.Failed() ) return list;
Span<DtStraightPath> 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;
}
/// <summary>
/// Computes a navigation path between the specified start and target positions on the navmesh.
/// Uses the same pathfinding algorithm as <see cref="NavMeshAgent"/>, taking agent configuration into account if provided.
/// The result is suitable for direct use with <see cref="NavMeshAgent.SetPath"/>.
/// If a complete path cannot be found, the result may indicate an incomplete or failed path.
/// </summary>
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<DtStraightPath>.Shared.Rent( 4096 );
dtStatus = query.FindStraightPath( startLocation, targetLocation, result.Polygons, result.Polygons.Count, straightPathCache, out var filledPointCount, straightPathCache.Length, 0 );
if ( dtStatus.Failed() )
{
ArrayPool<DtStraightPath>.Shared.Return( straightPathCache );
result.Status = NavMeshPathStatus.PathNotFound;
return result;
}
var points = new List<NavMeshPathPoint>( filledPointCount );
for ( int i = 0; i < filledPointCount; i++ )
{
points.Add( new NavMeshPathPoint { Position = FromNav( straightPathCache[i].pos ) } );
}
ArrayPool<DtStraightPath>.Shared.Return( straightPathCache );
result.Points = points;
if ( result.Polygons[^1] != targetPoly )
{
result.Status = NavMeshPathStatus.Partial;
}
else
{
result.Status = NavMeshPathStatus.Complete;
}
return result;
}
}
/// <summary>
/// Defines the input for a pathfinding request on the navmesh.
/// </summary>
public struct CalculatePathRequest
{
/// <summary>
/// Start position of the path, should be close to the navmesh.
/// </summary>
public Vector3 Start;
/// <summary>
/// Target/End position of the path, should be close to the navmesh.
/// </summary>
public Vector3 Target;
/// <summary>
/// Optional agent whose configuration is used for path calculation.
/// </summary>
public NavMeshAgent Agent;
}
/// <summary>
/// Contains the result of a pathfinding operation.
/// </summary>
public struct NavMeshPath : IValid
{
/// <summary>
/// The outcome of the path calculation.
/// </summary>
public NavMeshPathStatus Status { get; internal set; }
/// <summary>
/// True if a path was found.
/// </summary>
public readonly bool IsValid => Status == NavMeshPathStatus.Partial || Status == NavMeshPathStatus.Complete;
/// <summary>
/// Polygons traversed by the path.
/// Internal for now as you cannot do anything with polygon ids yet.
/// </summary>
internal List<long> Polygons;
/// <summary>
/// Points along the path.
/// </summary>
public IReadOnlyList<NavMeshPathPoint> Points { get; internal set; }
}
public enum NavMeshPathStatus
{
/// <summary>
/// Start location was not found on the navmesh.
/// </summary>
StartNotFound,
/// <summary>
/// Target location was not found on the navmesh.
/// </summary>
TargetNotFound,
/// <summary>
/// No path could be found.
/// </summary>
PathNotFound,
/// <summary>
/// Path found, but does not reach the target.
/// The returned path will be to the closest location that can be reached.
/// </summary>
Partial,
/// <summary>
/// Path found from start to target.
/// </summary>
Complete,
}
/// <summary>
/// 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.
/// </summary>
public struct NavMeshPathPoint
{
public Vector3 Position;
}