forked from mirror/DotRecast
Compare commits
60 Commits
Author | SHA1 | Date |
---|---|---|
wrenge | 0b888b16fb | |
wrenge | 088edcd655 | |
wrenge | 12e09475f0 | |
wrenge | 7de4b51135 | |
wrenge | 4824f29db7 | |
wrenge | 2cf9d9de18 | |
wrenge | d2082c6586 | |
wrenge | 0876d3adcf | |
wrenge | 1fa0320845 | |
wrenge | 815a83e3cb | |
wrenge | 592ecebe1e | |
wrenge | b2a217d4a3 | |
wrenge | 5c0ba9dba1 | |
wrenge | 38a8029b6e | |
wrenge | 418a39a576 | |
wrenge | 05613f196f | |
wrenge | 4dee6b20b5 | |
wrenge | 49da3fb454 | |
wrenge | 2c6f6a50cc | |
wrenge | dbc92a9aef | |
wrenge | c7c6e53d61 | |
wrenge | 858e094ea0 | |
wrenge | acd3f8d879 | |
wrenge | fff0998613 | |
wrenge | 2397f23fc3 | |
wrenge | b6a1a81fdc | |
wrenge | c989276f5e | |
wrenge | c0b916a0d1 | |
wrenge | 3e754529e5 | |
wrenge | 8642d47738 | |
wrenge | ff930712ee | |
wrenge | 6fe6844efd | |
wrenge | c8104cec4d | |
wrenge | db0717a77e | |
wrenge | 724be6f3b2 | |
wrenge | 0f28fa26ec | |
Georgiy Sadovnikov | 20a86b5bae | |
Семенов Иван | c84fb0b11e | |
Семенов Иван | 4a1b430ee6 | |
Семенов Иван | 53aac60713 | |
wrenge | e67dbfa604 | |
wrenge | b09eada4cf | |
wrenge | b8832e50e7 | |
wrenge | 005ab583f7 | |
ikpil | 15384cacb0 | |
ikpil | 94e66ed318 | |
wrenge | 0ed414f08c | |
wrenge | 897748285f | |
wrenge | 331e35ecbb | |
wrenge | 3157c1e764 | |
wrenge | 7f1b9e63bb | |
wrenge | d625e83fad | |
wrenge | 838064f3b6 | |
wrenge | 8ca008a5c3 | |
ikpil | 6a57c067ac | |
ikpil | 0c6921ad6b | |
ikpil | b0644a70b7 | |
ikpil | 2f2d68c29f | |
ikpil | 30bce34eaf | |
ikpil | d8ae9f2297 |
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"name": "rnd/dotrecastnetsim",
|
||||||
|
"description": "DotRecast",
|
||||||
|
"homepage": "https://git.bit5.ru/rnd/DotRecastNetSim.git",
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.4"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace DotRecast.Core.Buffers
|
||||||
|
{
|
||||||
|
// This implementation is thread unsafe
|
||||||
|
public class RcObjectPool<T> where T : class
|
||||||
|
{
|
||||||
|
private readonly Queue<T> _items = new Queue<T>();
|
||||||
|
private readonly Func<T> _createFunc;
|
||||||
|
|
||||||
|
public RcObjectPool(Func<T> createFunc)
|
||||||
|
{
|
||||||
|
_createFunc = createFunc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T Get()
|
||||||
|
{
|
||||||
|
if (_items.TryDequeue(out var result))
|
||||||
|
return result;
|
||||||
|
|
||||||
|
return _createFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Return(T obj)
|
||||||
|
{
|
||||||
|
_items.Enqueue(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ namespace DotRecast.Core.Buffers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class RcRentedArray<T> : IDisposable
|
public struct RcRentedArray<T> : IDisposable
|
||||||
{
|
{
|
||||||
private ArrayPool<T> _owner;
|
private ArrayPool<T> _owner;
|
||||||
private T[] _array;
|
private T[] _array;
|
||||||
|
@ -43,6 +43,11 @@ namespace DotRecast.Core.Buffers
|
||||||
return _array;
|
return _array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Span<T> AsSpan()
|
||||||
|
{
|
||||||
|
return new Span<T>(_array, 0, Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
|
|
@ -45,5 +45,11 @@ namespace DotRecast.Core.Collections
|
||||||
(list[k], list[n]) = (list[n], list[k]);
|
(list[k], list[n]) = (list[n], list[k]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void AddRange<T>(this IList<T> list, Span<T> span)
|
||||||
|
{
|
||||||
|
foreach (var i in span)
|
||||||
|
list.Add(i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -27,12 +27,12 @@ namespace DotRecast.Core.Collections
|
||||||
{
|
{
|
||||||
private bool _dirty;
|
private bool _dirty;
|
||||||
private readonly List<T> _items;
|
private readonly List<T> _items;
|
||||||
private readonly Comparer<T> _comparer;
|
private readonly Comparison<T> _comparison;
|
||||||
|
|
||||||
public RcSortedQueue(Comparison<T> comp)
|
public RcSortedQueue(Comparison<T> comp)
|
||||||
{
|
{
|
||||||
_items = new List<T>();
|
_items = new List<T>();
|
||||||
_comparer = Comparer<T>.Create((x, y) => comp.Invoke(x, y) * -1);
|
_comparison = (x, y) => comp(x, y) * -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Count()
|
public int Count()
|
||||||
|
@ -55,7 +55,7 @@ namespace DotRecast.Core.Collections
|
||||||
{
|
{
|
||||||
if (_dirty)
|
if (_dirty)
|
||||||
{
|
{
|
||||||
_items.Sort(_comparer); // reverse
|
_items.Sort(_comparison); // reverse
|
||||||
_dirty = false;
|
_dirty = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,7 @@ namespace DotRecast.Core.Collections
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
//int idx = _items.BinarySearch(item, _comparer); // don't use this! Because reference types can be reused externally.
|
//int idx = _items.BinarySearch(item, _comparer); // don't use this! Because reference types can be reused externally.
|
||||||
int idx = _items.FindLastIndex(x => item.Equals(x));
|
int idx = _items.LastIndexOf(item);
|
||||||
if (0 > idx)
|
if (0 > idx)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"name": "DotRecast.Core",
|
||||||
|
"rootNamespace": "DotRecast.Core",
|
||||||
|
"references": [],
|
||||||
|
"includePlatforms": [],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": true
|
||||||
|
}
|
|
@ -205,7 +205,7 @@ namespace DotRecast.Core.Numerics
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static float Dist2DSqr(RcVec3f p, float[] verts, int i)
|
public static float Dist2DSqr(RcVec3f p, Span<float> verts, int i)
|
||||||
{
|
{
|
||||||
float dx = verts[i] - p.X;
|
float dx = verts[i] - p.X;
|
||||||
float dz = verts[i + 2] - p.Z;
|
float dz = verts[i + 2] - p.Z;
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"name": "DotRecast.Detour.Crowd",
|
||||||
|
"rootNamespace": "DotRecast.Detour.Crowd",
|
||||||
|
"references": [
|
||||||
|
"DotRecast.Core",
|
||||||
|
"DotRecast.Detour"
|
||||||
|
],
|
||||||
|
"includePlatforms": [],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": true
|
||||||
|
}
|
|
@ -22,6 +22,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using DotRecast.Core;
|
using DotRecast.Core;
|
||||||
|
using DotRecast.Core.Buffers;
|
||||||
using DotRecast.Core.Collections;
|
using DotRecast.Core.Collections;
|
||||||
using DotRecast.Core.Numerics;
|
using DotRecast.Core.Numerics;
|
||||||
|
|
||||||
|
@ -129,7 +130,7 @@ namespace DotRecast.Detour.Crowd
|
||||||
private readonly DtObstacleAvoidanceParams[] _obstacleQueryParams;
|
private readonly DtObstacleAvoidanceParams[] _obstacleQueryParams;
|
||||||
private readonly DtObstacleAvoidanceQuery _obstacleQuery;
|
private readonly DtObstacleAvoidanceQuery _obstacleQuery;
|
||||||
|
|
||||||
private DtProximityGrid _grid;
|
private readonly DtProximityGrid _grid;
|
||||||
|
|
||||||
private int _maxPathResult;
|
private int _maxPathResult;
|
||||||
private readonly RcVec3f _agentPlacementHalfExtents;
|
private readonly RcVec3f _agentPlacementHalfExtents;
|
||||||
|
@ -174,6 +175,7 @@ namespace DotRecast.Detour.Crowd
|
||||||
_agentIdx = new RcAtomicInteger(0);
|
_agentIdx = new RcAtomicInteger(0);
|
||||||
_agents = new Dictionary<int, DtCrowdAgent>();
|
_agents = new Dictionary<int, DtCrowdAgent>();
|
||||||
_activeAgents = new List<DtCrowdAgent>();
|
_activeAgents = new List<DtCrowdAgent>();
|
||||||
|
_grid = new DtProximityGrid(_config.maxAgentRadius * 3);
|
||||||
|
|
||||||
// The navQuery is mostly used for local searches, no need for large node pool.
|
// The navQuery is mostly used for local searches, no need for large node pool.
|
||||||
SetNavMesh(nav);
|
SetNavMesh(nav);
|
||||||
|
@ -564,14 +566,18 @@ namespace DotRecast.Detour.Crowd
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private readonly RcSortedQueue<DtCrowdAgent> UpdateMoveRequest_queue = new RcSortedQueue<DtCrowdAgent>((a1, a2) => a2.targetReplanTime.CompareTo(a1.targetReplanTime));
|
||||||
|
private readonly List<long> UpdateMoveRequest_reqPath = new List<long>();
|
||||||
private void UpdateMoveRequest(IList<DtCrowdAgent> agents, float dt)
|
private void UpdateMoveRequest(IList<DtCrowdAgent> agents, float dt)
|
||||||
{
|
{
|
||||||
using var timer = _telemetry.ScopedTimer(DtCrowdTimerLabel.UpdateMoveRequest);
|
using var timer = _telemetry.ScopedTimer(DtCrowdTimerLabel.UpdateMoveRequest);
|
||||||
|
|
||||||
RcSortedQueue<DtCrowdAgent> queue = new RcSortedQueue<DtCrowdAgent>((a1, a2) => a2.targetReplanTime.CompareTo(a1.targetReplanTime));
|
RcSortedQueue<DtCrowdAgent> queue = UpdateMoveRequest_queue;
|
||||||
|
queue.Clear();
|
||||||
|
|
||||||
// Fire off new requests.
|
// Fire off new requests.
|
||||||
List<long> reqPath = new List<long>();
|
List<long> reqPath = UpdateMoveRequest_reqPath;
|
||||||
|
reqPath.Clear();
|
||||||
for (var i = 0; i < agents.Count; i++)
|
for (var i = 0; i < agents.Count; i++)
|
||||||
{
|
{
|
||||||
var ag = agents[i];
|
var ag = agents[i];
|
||||||
|
@ -864,7 +870,7 @@ namespace DotRecast.Detour.Crowd
|
||||||
{
|
{
|
||||||
using var timer = _telemetry.ScopedTimer(DtCrowdTimerLabel.BuildProximityGrid);
|
using var timer = _telemetry.ScopedTimer(DtCrowdTimerLabel.BuildProximityGrid);
|
||||||
|
|
||||||
_grid = new DtProximityGrid(_config.maxAgentRadius * 3);
|
_grid.Clear();
|
||||||
|
|
||||||
for (var i = 0; i < agents.Count; i++)
|
for (var i = 0; i < agents.Count; i++)
|
||||||
{
|
{
|
||||||
|
@ -1051,7 +1057,7 @@ namespace DotRecast.Detour.Crowd
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check
|
// Check
|
||||||
float triggerRadius = ag.option.radius * 2.25f;
|
float triggerRadius = ag.option.radius * 0.25f;//todo make parameterizable
|
||||||
if (ag.OverOffmeshConnection(triggerRadius))
|
if (ag.OverOffmeshConnection(triggerRadius))
|
||||||
{
|
{
|
||||||
// Prepare to off-mesh connection.
|
// Prepare to off-mesh connection.
|
||||||
|
@ -1200,6 +1206,9 @@ namespace DotRecast.Detour.Crowd
|
||||||
for (int j = 0; j < ag.nneis; ++j)
|
for (int j = 0; j < ag.nneis; ++j)
|
||||||
{
|
{
|
||||||
DtCrowdAgent nei = ag.neis[j].agent;
|
DtCrowdAgent nei = ag.neis[j].agent;
|
||||||
|
if(!nei.option.contributeObstacleAvoidance || nei.option.obstacleAvoidanceWeight < ag.option.obstacleAvoidanceWeight)
|
||||||
|
continue;
|
||||||
|
|
||||||
_obstacleQuery.AddCircle(nei.npos, nei.option.radius, nei.vel, nei.dvel);
|
_obstacleQuery.AddCircle(nei.npos, nei.option.radius, nei.vel, nei.dvel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,9 @@ namespace DotRecast.Detour.Crowd
|
||||||
/// [Limits: 0 <= value < #DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS]
|
/// [Limits: 0 <= value < #DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS]
|
||||||
public int obstacleAvoidanceType;
|
public int obstacleAvoidanceType;
|
||||||
|
|
||||||
|
public bool contributeObstacleAvoidance;
|
||||||
|
public float obstacleAvoidanceWeight;
|
||||||
|
|
||||||
/// The index of the query filter used by this agent.
|
/// The index of the query filter used by this agent.
|
||||||
public int queryFilterType;
|
public int queryFilterType;
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ namespace DotRecast.Detour.Crowd
|
||||||
private RcVec3f m_pos;
|
private RcVec3f m_pos;
|
||||||
private RcVec3f m_target;
|
private RcVec3f m_target;
|
||||||
|
|
||||||
private List<long> m_path;
|
private List<long> m_path = new List<long>();
|
||||||
private int m_npath;
|
private int m_npath;
|
||||||
private int m_maxPath;
|
private int m_maxPath;
|
||||||
|
|
||||||
|
@ -89,7 +89,8 @@ namespace DotRecast.Detour.Crowd
|
||||||
/// @return True if the initialization succeeded.
|
/// @return True if the initialization succeeded.
|
||||||
public bool Init(int maxPath)
|
public bool Init(int maxPath)
|
||||||
{
|
{
|
||||||
m_path = new List<long>(maxPath);
|
if (m_path.Capacity < maxPath)
|
||||||
|
m_path.Capacity = maxPath;
|
||||||
m_npath = 0;
|
m_npath = 0;
|
||||||
m_maxPath = maxPath;
|
m_maxPath = maxPath;
|
||||||
return true;
|
return true;
|
||||||
|
@ -218,7 +219,7 @@ namespace DotRecast.Detour.Crowd
|
||||||
{
|
{
|
||||||
if (res.Count > 1 && t > 0.99f)
|
if (res.Count > 1 && t > 0.99f)
|
||||||
{
|
{
|
||||||
m_npath = DtPathUtils.MergeCorridorStartShortcut(ref m_path, m_npath, m_maxPath, res, res.Count);
|
m_npath = DtPathUtils.MergeCorridorStartShortcut(m_path, m_npath, m_maxPath, res, res.Count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -250,7 +251,7 @@ namespace DotRecast.Detour.Crowd
|
||||||
|
|
||||||
if (status.Succeeded() && res.Count > 0)
|
if (status.Succeeded() && res.Count > 0)
|
||||||
{
|
{
|
||||||
m_npath = DtPathUtils.MergeCorridorStartShortcut(ref m_path, m_npath, m_maxPath, res, res.Count);
|
m_npath = DtPathUtils.MergeCorridorStartShortcut(m_path, m_npath, m_maxPath, res, res.Count);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,7 +277,7 @@ namespace DotRecast.Detour.Crowd
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prune path
|
// Prune path
|
||||||
m_path = m_path.GetRange(npos, m_npath - npos);
|
m_path.RemoveRange(0, npos);
|
||||||
m_npath -= npos;
|
m_npath -= npos;
|
||||||
|
|
||||||
refs[0] = prevRef;
|
refs[0] = prevRef;
|
||||||
|
@ -322,7 +323,7 @@ namespace DotRecast.Detour.Crowd
|
||||||
var status = navquery.MoveAlongSurface(m_path[0], m_pos, npos, filter, out var result, visited, out var nvisited, MAX_VISITED);
|
var status = navquery.MoveAlongSurface(m_path[0], m_pos, npos, filter, out var result, visited, out var nvisited, MAX_VISITED);
|
||||||
if (status.Succeeded())
|
if (status.Succeeded())
|
||||||
{
|
{
|
||||||
m_npath = DtPathUtils.MergeCorridorStartMoved(ref m_path, m_npath, m_maxPath, visited, nvisited);
|
m_npath = DtPathUtils.MergeCorridorStartMoved(m_path, m_npath, m_maxPath, visited, nvisited);
|
||||||
|
|
||||||
// Adjust the position to stay on top of the navmesh.
|
// Adjust the position to stay on top of the navmesh.
|
||||||
m_pos = result;
|
m_pos = result;
|
||||||
|
@ -366,7 +367,7 @@ namespace DotRecast.Detour.Crowd
|
||||||
var status = navquery.MoveAlongSurface(m_path[^1], m_target, npos, filter, out var result, visited, out nvisited, MAX_VISITED);
|
var status = navquery.MoveAlongSurface(m_path[^1], m_target, npos, filter, out var result, visited, out nvisited, MAX_VISITED);
|
||||||
if (status.Succeeded())
|
if (status.Succeeded())
|
||||||
{
|
{
|
||||||
m_npath = DtPathUtils.MergeCorridorEndMoved(ref m_path, m_npath, m_maxPath, visited, nvisited);
|
m_npath = DtPathUtils.MergeCorridorEndMoved(m_path, m_npath, m_maxPath, visited, nvisited);
|
||||||
|
|
||||||
// TODO: should we do that?
|
// TODO: should we do that?
|
||||||
// Adjust the position to stay on top of the navmesh.
|
// Adjust the position to stay on top of the navmesh.
|
||||||
|
@ -394,7 +395,11 @@ namespace DotRecast.Detour.Crowd
|
||||||
public void SetCorridor(RcVec3f target, List<long> path)
|
public void SetCorridor(RcVec3f target, List<long> path)
|
||||||
{
|
{
|
||||||
m_target = target;
|
m_target = target;
|
||||||
m_path = new List<long>(path);
|
if(path != m_path)
|
||||||
|
{
|
||||||
|
m_path.Clear();
|
||||||
|
m_path.AddRange(path);
|
||||||
|
}
|
||||||
m_npath = path.Count;
|
m_npath = path.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -444,7 +449,7 @@ namespace DotRecast.Detour.Crowd
|
||||||
else if (n < m_npath)
|
else if (n < m_npath)
|
||||||
{
|
{
|
||||||
// The path is partially usable.
|
// The path is partially usable.
|
||||||
m_path = m_path.GetRange(0, n);
|
m_path.RemoveRange(n, m_path.Count - n);
|
||||||
m_npath = n;
|
m_npath = n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using DotRecast.Core.Buffers;
|
||||||
|
|
||||||
namespace DotRecast.Detour.Crowd
|
namespace DotRecast.Detour.Crowd
|
||||||
{
|
{
|
||||||
|
@ -30,12 +31,14 @@ namespace DotRecast.Detour.Crowd
|
||||||
private readonly float _cellSize;
|
private readonly float _cellSize;
|
||||||
private readonly float _invCellSize;
|
private readonly float _invCellSize;
|
||||||
private readonly Dictionary<long, List<DtCrowdAgent>> _items;
|
private readonly Dictionary<long, List<DtCrowdAgent>> _items;
|
||||||
|
private readonly RcObjectPool<List<DtCrowdAgent>> _listPool;
|
||||||
|
|
||||||
public DtProximityGrid(float cellSize)
|
public DtProximityGrid(float cellSize)
|
||||||
{
|
{
|
||||||
_cellSize = cellSize;
|
_cellSize = cellSize;
|
||||||
_invCellSize = 1.0f / cellSize;
|
_invCellSize = 1.0f / cellSize;
|
||||||
_items = new Dictionary<long, List<DtCrowdAgent>>();
|
_items = new Dictionary<long, List<DtCrowdAgent>>();
|
||||||
|
_listPool = new RcObjectPool<List<DtCrowdAgent>>(() => new List<DtCrowdAgent>());
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
@ -57,6 +60,8 @@ namespace DotRecast.Detour.Crowd
|
||||||
|
|
||||||
public void Clear()
|
public void Clear()
|
||||||
{
|
{
|
||||||
|
foreach (var pair in _items)
|
||||||
|
_listPool.Return(pair.Value);
|
||||||
_items.Clear();
|
_items.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +79,8 @@ namespace DotRecast.Detour.Crowd
|
||||||
long key = CombineKey(x, y);
|
long key = CombineKey(x, y);
|
||||||
if (!_items.TryGetValue(key, out var ids))
|
if (!_items.TryGetValue(key, out var ids))
|
||||||
{
|
{
|
||||||
ids = new List<DtCrowdAgent>();
|
ids = _listPool.Get();
|
||||||
|
ids.Clear();
|
||||||
_items.Add(key, ids);
|
_items.Add(key, ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"name": "DotRecast.Detour.Dynamic",
|
||||||
|
"rootNamespace": "DotRecast.Detour.Dynamic",
|
||||||
|
"references": [
|
||||||
|
"DotRecast.Detour",
|
||||||
|
"DotRecast.Recast",
|
||||||
|
"DotRecast.Core"
|
||||||
|
],
|
||||||
|
"includePlatforms": [],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": true
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"name": "DotRecast.Detour.Extras",
|
||||||
|
"rootNamespace": "DotRecast.Detour.Extras",
|
||||||
|
"references": [
|
||||||
|
"DotRecast.Core",
|
||||||
|
"DotRecast.Detour",
|
||||||
|
"DotRecast.Recast"
|
||||||
|
],
|
||||||
|
"includePlatforms": [],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": true
|
||||||
|
}
|
|
@ -52,25 +52,12 @@ namespace DotRecast.Detour.Extras.Jumplink
|
||||||
|
|
||||||
RcVec3f halfExtents = new RcVec3f { X = cs, Y = heightRange, Z = cs };
|
RcVec3f halfExtents = new RcVec3f { X = cs, Y = heightRange, Z = cs };
|
||||||
float maxHeight = pt.Y + heightRange;
|
float maxHeight = pt.Y + heightRange;
|
||||||
RcAtomicBoolean found = new RcAtomicBoolean();
|
var query = new DtHeightSamplePolyQuery(navMeshQuery, pt, pt.Y, maxHeight);
|
||||||
RcAtomicFloat minHeight = new RcAtomicFloat(pt.Y);
|
navMeshQuery.QueryPolygons(pt, halfExtents, DtQueryNoOpFilter.Shared, ref query);
|
||||||
|
|
||||||
navMeshQuery.QueryPolygons(pt, halfExtents, DtQueryNoOpFilter.Shared, new DtCallbackPolyQuery((tile, poly, refs) =>
|
if (query.Found)
|
||||||
{
|
{
|
||||||
var status = navMeshQuery.GetPolyHeight(refs, pt, out var h);
|
height = query.MinHeight;
|
||||||
if (status.Succeeded())
|
|
||||||
{
|
|
||||||
if (h > minHeight.Get() && h < maxHeight)
|
|
||||||
{
|
|
||||||
minHeight.Exchange(h);
|
|
||||||
found.Set(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
if (found.Get())
|
|
||||||
{
|
|
||||||
height = minHeight.Get();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"name": "DotRecast.Detour.TileCache",
|
||||||
|
"rootNamespace": "DotRecast.Detour.TileCache",
|
||||||
|
"references": [
|
||||||
|
"DotRecast.Core",
|
||||||
|
"DotRecast.Detour",
|
||||||
|
"DotRecast.Recast"
|
||||||
|
],
|
||||||
|
"includePlatforms": [],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": true
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"name": "DotRecast.Detour",
|
||||||
|
"rootNamespace": "DotRecast.Detour",
|
||||||
|
"references": [
|
||||||
|
"DotRecast.Core"
|
||||||
|
],
|
||||||
|
"includePlatforms": [],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": true
|
||||||
|
}
|
|
@ -2,7 +2,7 @@ using System;
|
||||||
|
|
||||||
namespace DotRecast.Detour
|
namespace DotRecast.Detour
|
||||||
{
|
{
|
||||||
public class DtCallbackPolyQuery : IDtPolyQuery
|
public struct DtCallbackPolyQuery : IDtPolyQuery
|
||||||
{
|
{
|
||||||
private readonly Action<DtMeshTile, DtPoly, long> _callback;
|
private readonly Action<DtMeshTile, DtPoly, long> _callback;
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ namespace DotRecast.Detour
|
||||||
_callback = callback;
|
_callback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Process(DtMeshTile tile, DtPoly[] poly, Span<long> refs, int count)
|
public void Process(DtMeshTile tile, Span<DtPoly> poly, Span<long> refs, int count)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < count; ++i)
|
for (int i = 0; i < count; ++i)
|
||||||
{
|
{
|
||||||
|
|
|
@ -26,7 +26,7 @@ namespace DotRecast.Detour
|
||||||
return m_overflow;
|
return m_overflow;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Process(DtMeshTile tile, DtPoly[] poly, Span<long> refs, int count)
|
public void Process(DtMeshTile tile, Span<DtPoly> poly, Span<long> refs, int count)
|
||||||
{
|
{
|
||||||
int numLeft = m_maxPolys - m_numCollected;
|
int numLeft = m_maxPolys - m_numCollected;
|
||||||
int toCopy = count;
|
int toCopy = count;
|
||||||
|
|
|
@ -27,7 +27,7 @@ namespace DotRecast.Detour
|
||||||
{
|
{
|
||||||
private const float EPSILON = 0.0001f;
|
private const float EPSILON = 0.0001f;
|
||||||
|
|
||||||
public static float[] Intersect(Span<float> p, Span<float> q)
|
public static Span<float> Intersect(Span<float> p, Span<float> q, Span<float> buffer)
|
||||||
{
|
{
|
||||||
int n = p.Length / 3;
|
int n = p.Length / 3;
|
||||||
int m = q.Length / 3;
|
int m = q.Length / 3;
|
||||||
|
@ -95,7 +95,7 @@ namespace DotRecast.Detour
|
||||||
/* Special case: A & B parallel and separated. */
|
/* Special case: A & B parallel and separated. */
|
||||||
if (parallel && aHB < 0f && bHA < 0f)
|
if (parallel && aHB < 0f && bHA < 0f)
|
||||||
{
|
{
|
||||||
return null;
|
return Span<float>.Empty;
|
||||||
}
|
}
|
||||||
/* Special case: A & B collinear. */
|
/* Special case: A & B collinear. */
|
||||||
else if (parallel && MathF.Abs(aHB) < EPSILON && MathF.Abs(bHA) < EPSILON)
|
else if (parallel && MathF.Abs(aHB) < EPSILON && MathF.Abs(bHA) < EPSILON)
|
||||||
|
@ -168,8 +168,9 @@ namespace DotRecast.Detour
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
float[] copied = inters.Slice(0, ii).ToArray();
|
Span<float> result = buffer.Slice(0, ii);
|
||||||
return copied;
|
inters.Slice(0, ii).CopyTo(result);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int AddVertex(Span<float> inters, int ii, RcVec3f p)
|
private static int AddVertex(Span<float> inters, int ii, RcVec3f p)
|
||||||
|
|
|
@ -3,7 +3,7 @@ using DotRecast.Core.Numerics;
|
||||||
|
|
||||||
namespace DotRecast.Detour
|
namespace DotRecast.Detour
|
||||||
{
|
{
|
||||||
public class DtFindNearestPolyQuery : IDtPolyQuery
|
public struct DtFindNearestPolyQuery : IDtPolyQuery
|
||||||
{
|
{
|
||||||
private readonly DtNavMeshQuery _query;
|
private readonly DtNavMeshQuery _query;
|
||||||
private readonly RcVec3f _center;
|
private readonly RcVec3f _center;
|
||||||
|
@ -18,9 +18,12 @@ namespace DotRecast.Detour
|
||||||
_center = center;
|
_center = center;
|
||||||
_nearestDistanceSqr = float.MaxValue;
|
_nearestDistanceSqr = float.MaxValue;
|
||||||
_nearestPoint = center;
|
_nearestPoint = center;
|
||||||
|
|
||||||
|
_nearestRef = default;
|
||||||
|
_overPoly = default;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Process(DtMeshTile tile, DtPoly[] poly, Span<long> refs, int count)
|
public void Process(DtMeshTile tile, Span<DtPoly> poly, Span<long> refs, int count)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < count; ++i)
|
for (int i = 0; i < count; ++i)
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
using System;
|
||||||
|
using DotRecast.Core;
|
||||||
|
using DotRecast.Core.Numerics;
|
||||||
|
|
||||||
|
namespace DotRecast.Detour
|
||||||
|
{
|
||||||
|
public struct DtHeightSamplePolyQuery : IDtPolyQuery
|
||||||
|
{
|
||||||
|
private readonly DtNavMeshQuery _navMeshQuery;
|
||||||
|
private readonly RcVec3f _pt;
|
||||||
|
private readonly float _maxHeight;
|
||||||
|
public float MinHeight { get; private set; }
|
||||||
|
public bool Found { get; private set; }
|
||||||
|
|
||||||
|
public DtHeightSamplePolyQuery(DtNavMeshQuery navMeshQuery, RcVec3f pt, float minHeight, float maxHeight)
|
||||||
|
{
|
||||||
|
_navMeshQuery = navMeshQuery;
|
||||||
|
_pt = pt;
|
||||||
|
MinHeight = minHeight;
|
||||||
|
_maxHeight = maxHeight;
|
||||||
|
Found = default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Process(DtMeshTile tile, Span<DtPoly> poly, Span<long> refs, int count)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
ProcessSingle(refs[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessSingle(long refs)
|
||||||
|
{
|
||||||
|
var status = _navMeshQuery.GetPolyHeight(refs, _pt, out var h);
|
||||||
|
if (!status.Succeeded())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!(h > MinHeight) || !(h < _maxHeight))
|
||||||
|
return;
|
||||||
|
|
||||||
|
MinHeight = h;
|
||||||
|
Found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,8 +18,11 @@ freely, subject to the following restrictions:
|
||||||
3. This notice may not be removed or altered from any source distribution.
|
3. This notice may not be removed or altered from any source distribution.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace DotRecast.Detour
|
namespace DotRecast.Detour
|
||||||
{
|
{
|
||||||
|
[Serializable]
|
||||||
public class DtMeshData
|
public class DtMeshData
|
||||||
{
|
{
|
||||||
public DtMeshHeader header; //< The tile header.
|
public DtMeshHeader header; //< The tile header.
|
||||||
|
|
|
@ -18,11 +18,13 @@ freely, subject to the following restrictions:
|
||||||
3. This notice may not be removed or altered from any source distribution.
|
3. This notice may not be removed or altered from any source distribution.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
using DotRecast.Core.Numerics;
|
using DotRecast.Core.Numerics;
|
||||||
|
|
||||||
namespace DotRecast.Detour
|
namespace DotRecast.Detour
|
||||||
{
|
{
|
||||||
/** Provides high level information related to a dtMeshTile object. */
|
/** Provides high level information related to a dtMeshTile object. */
|
||||||
|
[Serializable]
|
||||||
public class DtMeshHeader
|
public class DtMeshHeader
|
||||||
{
|
{
|
||||||
/** Tile magic number. (Used to identify the data format.) */
|
/** Tile magic number. (Used to identify the data format.) */
|
||||||
|
|
|
@ -1365,6 +1365,11 @@ namespace DotRecast.Detour
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetTilesAt(int x, int y, DtMeshTile[] tiles, int maxTiles)
|
public int GetTilesAt(int x, int y, DtMeshTile[] tiles, int maxTiles)
|
||||||
|
{
|
||||||
|
return GetTilesAt(x, y, (Span<DtMeshTile>)tiles, maxTiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetTilesAt(int x, int y, Span<DtMeshTile> tiles, int maxTiles)
|
||||||
{
|
{
|
||||||
int n = 0;
|
int n = 0;
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ freely, subject to the following restrictions:
|
||||||
3. This notice may not be removed or altered from any source distribution.
|
3. This notice may not be removed or altered from any source distribution.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
using DotRecast.Core.Numerics;
|
using DotRecast.Core.Numerics;
|
||||||
|
|
||||||
namespace DotRecast.Detour
|
namespace DotRecast.Detour
|
||||||
|
|
|
@ -21,7 +21,9 @@ freely, subject to the following restrictions:
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using DotRecast.Core;
|
using DotRecast.Core;
|
||||||
|
using DotRecast.Core.Buffers;
|
||||||
using DotRecast.Core.Numerics;
|
using DotRecast.Core.Numerics;
|
||||||
|
using CollectionExtensions = DotRecast.Core.Collections.CollectionExtensions;
|
||||||
|
|
||||||
namespace DotRecast.Detour
|
namespace DotRecast.Detour
|
||||||
{
|
{
|
||||||
|
@ -32,6 +34,8 @@ namespace DotRecast.Detour
|
||||||
/// @ingroup detour
|
/// @ingroup detour
|
||||||
public class DtNavMeshQuery
|
public class DtNavMeshQuery
|
||||||
{
|
{
|
||||||
|
public const int MAX_PATH_LENGTH = 256;
|
||||||
|
|
||||||
protected readonly DtNavMesh m_nav; //< Pointer to navmesh data.
|
protected readonly DtNavMesh m_nav; //< Pointer to navmesh data.
|
||||||
protected DtQueryData m_query; //< Sliced query state.
|
protected DtQueryData m_query; //< Sliced query state.
|
||||||
|
|
||||||
|
@ -229,6 +233,7 @@ namespace DotRecast.Detour
|
||||||
IDtQueryFilter filter, IRcRand frand, IDtPolygonByCircleConstraint constraint,
|
IDtQueryFilter filter, IRcRand frand, IDtPolygonByCircleConstraint constraint,
|
||||||
out long randomRef, out RcVec3f randomPt)
|
out long randomRef, out RcVec3f randomPt)
|
||||||
{
|
{
|
||||||
|
const int MAX_VERT_BUFFER_SIZE = 128;
|
||||||
randomRef = startRef;
|
randomRef = startRef;
|
||||||
randomPt = centerPos;
|
randomPt = centerPos;
|
||||||
|
|
||||||
|
@ -262,9 +267,13 @@ namespace DotRecast.Detour
|
||||||
float radiusSqr = maxRadius * maxRadius;
|
float radiusSqr = maxRadius * maxRadius;
|
||||||
float areaSum = 0.0f;
|
float areaSum = 0.0f;
|
||||||
|
|
||||||
|
using RcRentedArray<float> polyVertsBuffer = RcRentedArray.Rent<float>(MAX_VERT_BUFFER_SIZE);
|
||||||
|
using RcRentedArray<float> randomPolyVertsBuffer = RcRentedArray.Rent<float>(MAX_VERT_BUFFER_SIZE);
|
||||||
|
using RcRentedArray<float> constrainedVertsBuffer = RcRentedArray.Rent<float>(MAX_VERT_BUFFER_SIZE);
|
||||||
|
|
||||||
DtPoly randomPoly = null;
|
DtPoly randomPoly = null;
|
||||||
long randomPolyRef = 0;
|
long randomPolyRef = 0;
|
||||||
float[] randomPolyVerts = null;
|
Span<float> randomPolyVerts = Span<float>.Empty;
|
||||||
|
|
||||||
while (!m_openList.IsEmpty())
|
while (!m_openList.IsEmpty())
|
||||||
{
|
{
|
||||||
|
@ -282,14 +291,14 @@ namespace DotRecast.Detour
|
||||||
{
|
{
|
||||||
// Calc area of the polygon.
|
// Calc area of the polygon.
|
||||||
float polyArea = 0.0f;
|
float polyArea = 0.0f;
|
||||||
float[] polyVerts = new float[bestPoly.vertCount * 3];
|
Span<float> polyVerts = polyVertsBuffer.AsSpan().Slice(0, bestPoly.vertCount * 3);
|
||||||
for (int j = 0; j < bestPoly.vertCount; ++j)
|
for (int j = 0; j < bestPoly.vertCount; ++j)
|
||||||
{
|
{
|
||||||
RcArrays.Copy(bestTile.data.verts, bestPoly.verts[j] * 3, polyVerts, j * 3, 3);
|
RcSpans.Copy(bestTile.data.verts, bestPoly.verts[j] * 3, polyVerts, j * 3, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
float[] constrainedVerts = constraint.Apply(polyVerts, centerPos, maxRadius);
|
Span<float> constrainedVerts = constraint.Apply(polyVerts, centerPos, maxRadius, constrainedVertsBuffer.AsSpan());
|
||||||
if (constrainedVerts != null)
|
if (!constrainedVerts.IsEmpty)
|
||||||
{
|
{
|
||||||
int vertCount = constrainedVerts.Length / 3;
|
int vertCount = constrainedVerts.Length / 3;
|
||||||
for (int j = 2; j < vertCount; ++j)
|
for (int j = 2; j < vertCount; ++j)
|
||||||
|
@ -307,7 +316,8 @@ namespace DotRecast.Detour
|
||||||
{
|
{
|
||||||
randomPoly = bestPoly;
|
randomPoly = bestPoly;
|
||||||
randomPolyRef = bestRef;
|
randomPolyRef = bestRef;
|
||||||
randomPolyVerts = constrainedVerts;
|
randomPolyVerts = randomPolyVertsBuffer.AsSpan().Slice(0, constrainedVerts.Length);
|
||||||
|
constrainedVerts.CopyTo(randomPolyVerts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -579,7 +589,7 @@ namespace DotRecast.Detour
|
||||||
|
|
||||||
// Get nearby polygons from proximity grid.
|
// Get nearby polygons from proximity grid.
|
||||||
DtFindNearestPolyQuery query = new DtFindNearestPolyQuery(this, center);
|
DtFindNearestPolyQuery query = new DtFindNearestPolyQuery(this, center);
|
||||||
DtStatus status = QueryPolygons(center, halfExtents, filter, query);
|
DtStatus status = QueryPolygons(center, halfExtents, filter, ref query);
|
||||||
if (status.Failed())
|
if (status.Failed())
|
||||||
{
|
{
|
||||||
return status;
|
return status;
|
||||||
|
@ -593,11 +603,13 @@ namespace DotRecast.Detour
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Queries polygons within a tile.
|
/// Queries polygons within a tile.
|
||||||
protected void QueryPolygonsInTile(DtMeshTile tile, RcVec3f qmin, RcVec3f qmax, IDtQueryFilter filter, IDtPolyQuery query)
|
protected void QueryPolygonsInTile<TQuery>(DtMeshTile tile, RcVec3f qmin, RcVec3f qmax, IDtQueryFilter filter, ref TQuery query)
|
||||||
|
where TQuery : IDtPolyQuery
|
||||||
{
|
{
|
||||||
const int batchSize = 32;
|
const int batchSize = 32;
|
||||||
Span<long> polyRefs = stackalloc long[batchSize];
|
Span<long> polyRefs = stackalloc long[batchSize];
|
||||||
DtPoly[] polys = new DtPoly[batchSize];
|
using RcRentedArray<DtPoly> polysRent = RcRentedArray.Rent<DtPoly>(batchSize);
|
||||||
|
Span<DtPoly> polys = polysRent.AsSpan();
|
||||||
int n = 0;
|
int n = 0;
|
||||||
|
|
||||||
if (tile.data.bvTree != null)
|
if (tile.data.bvTree != null)
|
||||||
|
@ -748,7 +760,7 @@ namespace DotRecast.Detour
|
||||||
return DtStatus.DT_FAILURE | DtStatus.DT_INVALID_PARAM;
|
return DtStatus.DT_FAILURE | DtStatus.DT_INVALID_PARAM;
|
||||||
|
|
||||||
DtCollectPolysQuery collector = new DtCollectPolysQuery(polys, maxPolys);
|
DtCollectPolysQuery collector = new DtCollectPolysQuery(polys, maxPolys);
|
||||||
DtStatus status = QueryPolygons(center, halfExtents, filter, collector);
|
DtStatus status = QueryPolygons(center, halfExtents, filter, ref collector);
|
||||||
if (status.Failed())
|
if (status.Failed())
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
|
@ -770,7 +782,8 @@ namespace DotRecast.Detour
|
||||||
/// @param[in] halfExtents The search distance along each axis. [(x, y, z)]
|
/// @param[in] halfExtents The search distance along each axis. [(x, y, z)]
|
||||||
/// @param[in] filter The polygon filter to apply to the query.
|
/// @param[in] filter The polygon filter to apply to the query.
|
||||||
/// @param[in] query The query. Polygons found will be batched together and passed to this query.
|
/// @param[in] query The query. Polygons found will be batched together and passed to this query.
|
||||||
public DtStatus QueryPolygons(RcVec3f center, RcVec3f halfExtents, IDtQueryFilter filter, IDtPolyQuery query)
|
public DtStatus QueryPolygons<TQuery>(RcVec3f center, RcVec3f halfExtents, IDtQueryFilter filter, ref TQuery query)
|
||||||
|
where TQuery : IDtPolyQuery
|
||||||
{
|
{
|
||||||
if (!center.IsFinite() || !halfExtents.IsFinite() || null == filter)
|
if (!center.IsFinite() || !halfExtents.IsFinite() || null == filter)
|
||||||
{
|
{
|
||||||
|
@ -786,7 +799,8 @@ namespace DotRecast.Detour
|
||||||
m_nav.CalcTileLoc(bmax, out var maxx, out var maxy);
|
m_nav.CalcTileLoc(bmax, out var maxx, out var maxy);
|
||||||
|
|
||||||
const int MAX_NEIS = 32;
|
const int MAX_NEIS = 32;
|
||||||
DtMeshTile[] neis = new DtMeshTile[MAX_NEIS];
|
using RcRentedArray<DtMeshTile> neisRent = RcRentedArray.Rent<DtMeshTile>(MAX_NEIS);
|
||||||
|
Span<DtMeshTile> neis = neisRent.AsSpan();
|
||||||
|
|
||||||
for (int y = miny; y <= maxy; ++y)
|
for (int y = miny; y <= maxy; ++y)
|
||||||
{
|
{
|
||||||
|
@ -795,7 +809,7 @@ namespace DotRecast.Detour
|
||||||
int nneis = m_nav.GetTilesAt(x, y, neis, MAX_NEIS);
|
int nneis = m_nav.GetTilesAt(x, y, neis, MAX_NEIS);
|
||||||
for (int j = 0; j < nneis; ++j)
|
for (int j = 0; j < nneis; ++j)
|
||||||
{
|
{
|
||||||
QueryPolygonsInTile(neis[j], bmin, bmax, filter, query);
|
QueryPolygonsInTile(neis[j], bmin, bmax, filter, ref query);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -877,7 +891,8 @@ namespace DotRecast.Detour
|
||||||
float lastBestNodeCost = startNode.total;
|
float lastBestNodeCost = startNode.total;
|
||||||
|
|
||||||
DtRaycastHit rayHit = new DtRaycastHit();
|
DtRaycastHit rayHit = new DtRaycastHit();
|
||||||
rayHit.path = new List<long>();
|
using var pathRent = RcRentedArray.Rent<long>(MAX_PATH_LENGTH);
|
||||||
|
rayHit.path = pathRent.AsArray();
|
||||||
while (!m_openList.IsEmpty())
|
while (!m_openList.IsEmpty())
|
||||||
{
|
{
|
||||||
// Remove node from open list and put it in closed list.
|
// Remove node from open list and put it in closed list.
|
||||||
|
@ -1166,7 +1181,8 @@ namespace DotRecast.Detour
|
||||||
}
|
}
|
||||||
|
|
||||||
var rayHit = new DtRaycastHit();
|
var rayHit = new DtRaycastHit();
|
||||||
rayHit.path = new List<long>();
|
using var pathRent = RcRentedArray.Rent<long>(MAX_PATH_LENGTH);
|
||||||
|
rayHit.path = pathRent.AsArray();
|
||||||
|
|
||||||
int iter = 0;
|
int iter = 0;
|
||||||
while (iter < maxIter && !m_openList.IsEmpty())
|
while (iter < maxIter && !m_openList.IsEmpty())
|
||||||
|
@ -1837,6 +1853,7 @@ namespace DotRecast.Detour
|
||||||
return DtStatus.DT_SUCCESS | (straightPathCount >= maxStraightPath ? DtStatus.DT_BUFFER_TOO_SMALL : DtStatus.DT_STATUS_NOTHING);
|
return DtStatus.DT_SUCCESS | (straightPathCount >= maxStraightPath ? DtStatus.DT_BUFFER_TOO_SMALL : DtStatus.DT_STATUS_NOTHING);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private readonly Queue<DtNode> MoveAlongSurface_queue = new Queue<DtNode>();
|
||||||
/// @par
|
/// @par
|
||||||
///
|
///
|
||||||
/// This method is optimized for small delta movement and a small number of
|
/// This method is optimized for small delta movement and a small number of
|
||||||
|
@ -1892,8 +1909,8 @@ namespace DotRecast.Detour
|
||||||
startNode.total = 0;
|
startNode.total = 0;
|
||||||
startNode.id = startRef;
|
startNode.id = startRef;
|
||||||
startNode.flags = DtNodeFlags.DT_NODE_CLOSED;
|
startNode.flags = DtNodeFlags.DT_NODE_CLOSED;
|
||||||
LinkedList<DtNode> stack = new LinkedList<DtNode>();
|
MoveAlongSurface_queue.Clear();
|
||||||
stack.AddLast(startNode);
|
MoveAlongSurface_queue.Enqueue(startNode);
|
||||||
|
|
||||||
RcVec3f bestPos = new RcVec3f();
|
RcVec3f bestPos = new RcVec3f();
|
||||||
float bestDist = float.MaxValue;
|
float bestDist = float.MaxValue;
|
||||||
|
@ -1909,11 +1926,10 @@ namespace DotRecast.Detour
|
||||||
const int MAX_NEIS = 8;
|
const int MAX_NEIS = 8;
|
||||||
Span<long> neis = stackalloc long[MAX_NEIS];
|
Span<long> neis = stackalloc long[MAX_NEIS];
|
||||||
|
|
||||||
while (0 < stack.Count)
|
while (0 < MoveAlongSurface_queue.Count)
|
||||||
{
|
{
|
||||||
// Pop front.
|
// Pop front.
|
||||||
DtNode curNode = stack.First?.Value;
|
DtNode curNode = MoveAlongSurface_queue.Dequeue();
|
||||||
stack.RemoveFirst();
|
|
||||||
|
|
||||||
// Get poly and tile.
|
// Get poly and tile.
|
||||||
// The API input has been checked already, skip checking internal data.
|
// The API input has been checked already, skip checking internal data.
|
||||||
|
@ -2012,7 +2028,7 @@ namespace DotRecast.Detour
|
||||||
// Mark as the node as visited and push to queue.
|
// Mark as the node as visited and push to queue.
|
||||||
neighbourNode.pidx = m_tinyNodePool.GetNodeIdx(curNode);
|
neighbourNode.pidx = m_tinyNodePool.GetNodeIdx(curNode);
|
||||||
neighbourNode.flags |= DtNodeFlags.DT_NODE_CLOSED;
|
neighbourNode.flags |= DtNodeFlags.DT_NODE_CLOSED;
|
||||||
stack.AddLast(neighbourNode);
|
MoveAlongSurface_queue.Enqueue(neighbourNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2258,13 +2274,15 @@ namespace DotRecast.Detour
|
||||||
out float t, out RcVec3f hitNormal, ref List<long> path)
|
out float t, out RcVec3f hitNormal, ref List<long> path)
|
||||||
{
|
{
|
||||||
DtRaycastHit hit = new DtRaycastHit();
|
DtRaycastHit hit = new DtRaycastHit();
|
||||||
hit.path = path;
|
using var pathRent = RcRentedArray.Rent<long>(MAX_PATH_LENGTH);
|
||||||
|
hit.path = pathRent.AsArray();
|
||||||
|
|
||||||
DtStatus status = Raycast(startRef, startPos, endPos, filter, 0, ref hit, 0);
|
DtStatus status = Raycast(startRef, startPos, endPos, filter, 0, ref hit, 0);
|
||||||
|
|
||||||
t = hit.t;
|
t = hit.t;
|
||||||
hitNormal = hit.hitNormal;
|
hitNormal = hit.hitNormal;
|
||||||
path = hit.path;
|
path.Clear();
|
||||||
|
CollectionExtensions.AddRange(path, new Span<long>(hit.path, 0, hit.pathCount));
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
@ -2330,7 +2348,7 @@ namespace DotRecast.Detour
|
||||||
}
|
}
|
||||||
|
|
||||||
hit.t = 0;
|
hit.t = 0;
|
||||||
hit.path.Clear();
|
hit.pathCount = 0;
|
||||||
hit.pathCost = 0;
|
hit.pathCost = 0;
|
||||||
|
|
||||||
Span<RcVec3f> verts = stackalloc RcVec3f[m_nav.GetMaxVertsPerPoly() + 1];
|
Span<RcVec3f> verts = stackalloc RcVec3f[m_nav.GetMaxVertsPerPoly() + 1];
|
||||||
|
@ -2383,7 +2401,7 @@ namespace DotRecast.Detour
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store visited polygons.
|
// Store visited polygons.
|
||||||
hit.path.Add(curRef);
|
hit.AddPathNode(curRef);
|
||||||
|
|
||||||
// Ray end is completely inside the polygon.
|
// Ray end is completely inside the polygon.
|
||||||
if (segMax == -1)
|
if (segMax == -1)
|
||||||
|
@ -2523,7 +2541,7 @@ namespace DotRecast.Detour
|
||||||
// int va = a * 3;
|
// int va = a * 3;
|
||||||
// int vb = b * 3;
|
// int vb = b * 3;
|
||||||
float dx = verts[b].X - verts[a].X;
|
float dx = verts[b].X - verts[a].X;
|
||||||
float dz = verts[b].Z - verts[a].X;
|
float dz = verts[b].Z - verts[a].Z;
|
||||||
hit.hitNormal = RcVec3f.Normalize(new RcVec3f(dz, 0, -dx));
|
hit.hitNormal = RcVec3f.Normalize(new RcVec3f(dz, 0, -dx));
|
||||||
return DtStatus.DT_SUCCESS;
|
return DtStatus.DT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -2903,6 +2921,7 @@ namespace DotRecast.Detour
|
||||||
return DtStatus.DT_SUCCESS;
|
return DtStatus.DT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private readonly Queue<DtNode> FindLocalNeighbourhood_queue = new Queue<DtNode>();
|
||||||
/// @par
|
/// @par
|
||||||
///
|
///
|
||||||
/// This method is optimized for a small search radius and small number of result
|
/// This method is optimized for a small search radius and small number of result
|
||||||
|
@ -2954,8 +2973,8 @@ namespace DotRecast.Detour
|
||||||
startNode.pidx = 0;
|
startNode.pidx = 0;
|
||||||
startNode.id = startRef;
|
startNode.id = startRef;
|
||||||
startNode.flags = DtNodeFlags.DT_NODE_CLOSED;
|
startNode.flags = DtNodeFlags.DT_NODE_CLOSED;
|
||||||
LinkedList<DtNode> stack = new LinkedList<DtNode>();
|
FindLocalNeighbourhood_queue.Clear();
|
||||||
stack.AddLast(startNode);
|
FindLocalNeighbourhood_queue.Enqueue(startNode);
|
||||||
|
|
||||||
resultRef.Add(startNode.id);
|
resultRef.Add(startNode.id);
|
||||||
resultParent.Add(0L);
|
resultParent.Add(0L);
|
||||||
|
@ -2965,11 +2984,11 @@ namespace DotRecast.Detour
|
||||||
Span<float> pa = stackalloc float[m_nav.GetMaxVertsPerPoly() * 3];
|
Span<float> pa = stackalloc float[m_nav.GetMaxVertsPerPoly() * 3];
|
||||||
Span<float> pb = stackalloc float[m_nav.GetMaxVertsPerPoly() * 3];
|
Span<float> pb = stackalloc float[m_nav.GetMaxVertsPerPoly() * 3];
|
||||||
|
|
||||||
while (0 < stack.Count)
|
while (0 < FindLocalNeighbourhood_queue.Count)
|
||||||
{
|
{
|
||||||
// Pop front.
|
// Pop front.
|
||||||
DtNode curNode = stack.First?.Value;
|
|
||||||
stack.RemoveFirst();
|
DtNode curNode = FindLocalNeighbourhood_queue.Dequeue();
|
||||||
|
|
||||||
// Get poly and tile.
|
// Get poly and tile.
|
||||||
// The API input has been checked already, skip checking internal data.
|
// The API input has been checked already, skip checking internal data.
|
||||||
|
@ -3082,7 +3101,7 @@ namespace DotRecast.Detour
|
||||||
|
|
||||||
resultRef.Add(neighbourRef);
|
resultRef.Add(neighbourRef);
|
||||||
resultParent.Add(curRef);
|
resultParent.Add(curRef);
|
||||||
stack.AddLast(neighbourNode);
|
FindLocalNeighbourhood_queue.Enqueue(neighbourNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System;
|
||||||
using DotRecast.Core.Numerics;
|
using DotRecast.Core.Numerics;
|
||||||
|
|
||||||
namespace DotRecast.Detour
|
namespace DotRecast.Detour
|
||||||
|
@ -10,9 +11,11 @@ namespace DotRecast.Detour
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public float[] Apply(float[] polyVerts, RcVec3f circleCenter, float radius)
|
public Span<float> Apply(Span<float> polyVerts, RcVec3f circleCenter, float radius, Span<float> resultBuffer)
|
||||||
{
|
{
|
||||||
return polyVerts;
|
var result = resultBuffer.Slice(0, polyVerts.Length);
|
||||||
|
polyVerts.CopyTo(result);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -18,8 +18,10 @@ freely, subject to the following restrictions:
|
||||||
3. This notice may not be removed or altered from any source distribution.
|
3. This notice may not be removed or altered from any source distribution.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using DotRecast.Core.Buffers;
|
||||||
|
|
||||||
namespace DotRecast.Detour
|
namespace DotRecast.Detour
|
||||||
{
|
{
|
||||||
|
@ -27,24 +29,68 @@ namespace DotRecast.Detour
|
||||||
{
|
{
|
||||||
private readonly Dictionary<long, List<DtNode>> m_map;
|
private readonly Dictionary<long, List<DtNode>> m_map;
|
||||||
|
|
||||||
private int m_nodeCount;
|
private int m_usedNodesCount;
|
||||||
private readonly List<DtNode> m_nodes;
|
private List<DtNode[]> m_buckets;
|
||||||
|
private readonly int m_initialBufferCapacityBase;
|
||||||
|
private readonly RcObjectPool<List<DtNode>> m_listPool;
|
||||||
|
|
||||||
public DtNodePool()
|
public DtNodePool(int initialBufferCapacityBase = 6) // initial size 64
|
||||||
{
|
{
|
||||||
m_map = new Dictionary<long, List<DtNode>>();
|
m_map = new Dictionary<long, List<DtNode>>();
|
||||||
m_nodes = new List<DtNode>();
|
m_listPool = new RcObjectPool<List<DtNode>>(() => new List<DtNode>());
|
||||||
|
m_buckets = new List<DtNode[]>();
|
||||||
|
m_initialBufferCapacityBase = initialBufferCapacityBase;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddNewBucket()
|
||||||
|
{
|
||||||
|
var bucketIndex = m_buckets.Count;
|
||||||
|
var bucket = new DtNode[1 << (bucketIndex + m_initialBufferCapacityBase)];
|
||||||
|
m_buckets.Add(bucket);
|
||||||
|
FillBucket(bucketIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FillBucket(int bucketIndex)
|
||||||
|
{
|
||||||
|
var bucket = m_buckets[bucketIndex];
|
||||||
|
var startIndex = GetBucketStartIndex(bucketIndex);
|
||||||
|
for (int i = 0; i < bucket.Length; i++)
|
||||||
|
{
|
||||||
|
bucket[i] = new DtNode(startIndex + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetBucketStartIndex(int bucketIndex)
|
||||||
|
{
|
||||||
|
return ((1 << (bucketIndex + m_initialBufferCapacityBase)) - 1) ^ ((1 << m_initialBufferCapacityBase) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DtNode[] EnsureBucket(int bucketIndex)
|
||||||
|
{
|
||||||
|
if (m_buckets.Count == bucketIndex)
|
||||||
|
AddNewBucket();
|
||||||
|
else if (m_buckets.Count < bucketIndex)
|
||||||
|
throw new Exception();
|
||||||
|
return m_buckets[bucketIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetBucketIndexByElementIndex(int elementIndex)
|
||||||
|
{
|
||||||
|
return DtUtils.Ilog2((elementIndex >> m_initialBufferCapacityBase) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Clear()
|
public void Clear()
|
||||||
{
|
{
|
||||||
|
foreach (var pair in m_map)
|
||||||
|
m_listPool.Return(pair.Value);
|
||||||
|
|
||||||
m_map.Clear();
|
m_map.Clear();
|
||||||
m_nodeCount = 0;
|
m_usedNodesCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetNodeCount()
|
public int GetNodeCount()
|
||||||
{
|
{
|
||||||
return m_nodeCount;
|
return m_usedNodesCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int FindNodes(long id, out List<DtNode> nodes)
|
public int FindNodes(long id, out List<DtNode> nodes)
|
||||||
|
@ -84,7 +130,8 @@ namespace DotRecast.Detour
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
nodes = new List<DtNode>();
|
nodes = m_listPool.Get();
|
||||||
|
nodes.Clear();
|
||||||
m_map.Add(id, nodes);
|
m_map.Add(id, nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,15 +140,10 @@ namespace DotRecast.Detour
|
||||||
|
|
||||||
private DtNode Create(long id, int state, List<DtNode> nodes)
|
private DtNode Create(long id, int state, List<DtNode> nodes)
|
||||||
{
|
{
|
||||||
if (m_nodes.Count <= m_nodeCount)
|
int i = m_usedNodesCount++;
|
||||||
{
|
int bucketIndex = GetBucketIndexByElementIndex(i);
|
||||||
var newNode = new DtNode(m_nodeCount);
|
int bucketStartIndex = GetBucketStartIndex(bucketIndex);
|
||||||
m_nodes.Add(newNode);
|
var node = EnsureBucket(bucketIndex)[i - bucketStartIndex];
|
||||||
}
|
|
||||||
|
|
||||||
int i = m_nodeCount;
|
|
||||||
m_nodeCount++;
|
|
||||||
var node = m_nodes[i];
|
|
||||||
node.pidx = 0;
|
node.pidx = 0;
|
||||||
node.cost = 0;
|
node.cost = 0;
|
||||||
node.total = 0;
|
node.total = 0;
|
||||||
|
@ -123,9 +165,16 @@ namespace DotRecast.Detour
|
||||||
|
|
||||||
public DtNode GetNodeAtIdx(int idx)
|
public DtNode GetNodeAtIdx(int idx)
|
||||||
{
|
{
|
||||||
return idx != 0
|
if (idx == 0)
|
||||||
? m_nodes[idx - 1]
|
return null;
|
||||||
: null;
|
|
||||||
|
int bucketIndex = GetBucketIndexByElementIndex(idx - 1);
|
||||||
|
if (m_buckets.Count <= bucketIndex)
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
|
||||||
|
int bucketStartIndex = GetBucketStartIndex(bucketIndex);
|
||||||
|
var node = EnsureBucket(bucketIndex)[idx - bucketStartIndex - 1];
|
||||||
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DtNode GetNode(long refs)
|
public DtNode GetNode(long refs)
|
||||||
|
@ -135,7 +184,7 @@ namespace DotRecast.Detour
|
||||||
|
|
||||||
public IEnumerable<DtNode> AsEnumerable()
|
public IEnumerable<DtNode> AsEnumerable()
|
||||||
{
|
{
|
||||||
return m_nodes.Take(m_nodeCount);
|
return m_buckets.SelectMany(x => x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -18,12 +18,14 @@ freely, subject to the following restrictions:
|
||||||
3. This notice may not be removed or altered from any source distribution.
|
3. This notice may not be removed or altered from any source distribution.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
using DotRecast.Core.Numerics;
|
using DotRecast.Core.Numerics;
|
||||||
|
|
||||||
namespace DotRecast.Detour
|
namespace DotRecast.Detour
|
||||||
{
|
{
|
||||||
/// Defines an navigation mesh off-mesh connection within a dtMeshTile object.
|
/// Defines an navigation mesh off-mesh connection within a dtMeshTile object.
|
||||||
/// An off-mesh connection is a user defined traversable connection made up to two vertices.
|
/// An off-mesh connection is a user defined traversable connection made up to two vertices.
|
||||||
|
[Serializable]
|
||||||
public class DtOffMeshConnection
|
public class DtOffMeshConnection
|
||||||
{
|
{
|
||||||
/// The endpoints of the connection. [(ax, ay, az, bx, by, bz)]
|
/// The endpoints of the connection. [(ax, ay, az, bx, by, bz)]
|
||||||
|
|
|
@ -20,7 +20,10 @@ freely, subject to the following restrictions:
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using DotRecast.Core.Buffers;
|
||||||
|
using DotRecast.Core.Collections;
|
||||||
using DotRecast.Core.Numerics;
|
using DotRecast.Core.Numerics;
|
||||||
|
using CollectionExtensions = DotRecast.Core.Collections.CollectionExtensions;
|
||||||
|
|
||||||
namespace DotRecast.Detour
|
namespace DotRecast.Detour
|
||||||
{
|
{
|
||||||
|
@ -148,7 +151,7 @@ namespace DotRecast.Detour
|
||||||
return npath;
|
return npath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int MergeCorridorStartMoved(ref List<long> path, int npath, int maxPath, Span<long> visited, int nvisited)
|
public static int MergeCorridorStartMoved(List<long> path, int npath, int maxPath, Span<long> visited, int nvisited)
|
||||||
{
|
{
|
||||||
int furthestPath = -1;
|
int furthestPath = -1;
|
||||||
int furthestVisited = -1;
|
int furthestVisited = -1;
|
||||||
|
@ -180,22 +183,23 @@ namespace DotRecast.Detour
|
||||||
}
|
}
|
||||||
|
|
||||||
// Concatenate paths.
|
// Concatenate paths.
|
||||||
|
var endIndex = nvisited - 1;
|
||||||
|
var length1 = endIndex - furthestVisited;
|
||||||
|
var length2 = npath - furthestPath;
|
||||||
|
using var result = RcRentedArray.Rent<long>(length1 + length2);
|
||||||
// Adjust beginning of the buffer to include the visited.
|
// Adjust beginning of the buffer to include the visited.
|
||||||
List<long> result = new List<long>();
|
|
||||||
// Store visited
|
// Store visited
|
||||||
for (int i = nvisited - 1; i > furthestVisited; --i)
|
for (int i = 0; i < length1; ++i)
|
||||||
{
|
result[i] = visited[endIndex - i];
|
||||||
result.Add(visited[i]);
|
|
||||||
|
path.CopyTo(furthestPath, result.AsArray(), length1, length2);
|
||||||
|
|
||||||
|
path.Clear();
|
||||||
|
CollectionExtensions.AddRange(path, result.AsSpan());
|
||||||
|
return result.Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
result.AddRange(path.GetRange(furthestPath, npath - furthestPath));
|
public static int MergeCorridorEndMoved(List<long> path, int npath, int maxPath, Span<long> visited, int nvisited)
|
||||||
|
|
||||||
path = result;
|
|
||||||
return result.Count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int MergeCorridorEndMoved(ref List<long> path, int npath, int maxPath, Span<long> visited, int nvisited)
|
|
||||||
{
|
{
|
||||||
int furthestPath = -1;
|
int furthestPath = -1;
|
||||||
int furthestVisited = -1;
|
int furthestVisited = -1;
|
||||||
|
@ -227,17 +231,18 @@ namespace DotRecast.Detour
|
||||||
}
|
}
|
||||||
|
|
||||||
// Concatenate paths.
|
// Concatenate paths.
|
||||||
List<long> result = path.GetRange(0, furthestPath);
|
var length1 = furthestPath;
|
||||||
foreach (var v in visited.Slice(furthestVisited, nvisited - furthestVisited))
|
var length2 = nvisited - furthestVisited;
|
||||||
{
|
using var result = RcRentedArray.Rent<long>(length1 + length2);
|
||||||
result.Add(v);
|
path.CopyTo(0, result.AsArray(), 0, length1);
|
||||||
|
visited.Slice(furthestVisited, nvisited - furthestVisited).CopyTo(result.AsSpan().Slice(length1, length2));
|
||||||
|
|
||||||
|
path.Clear();
|
||||||
|
CollectionExtensions.AddRange(path, result.AsSpan());
|
||||||
|
return path.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
path = result;
|
public static int MergeCorridorStartShortcut(List<long> path, int npath, int maxPath, List<long> visited, int nvisited)
|
||||||
return result.Count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int MergeCorridorStartShortcut(ref List<long> path, int npath, int maxPath, List<long> visited, int nvisited)
|
|
||||||
{
|
{
|
||||||
int furthestPath = -1;
|
int furthestPath = -1;
|
||||||
int furthestVisited = -1;
|
int furthestVisited = -1;
|
||||||
|
@ -271,11 +276,15 @@ namespace DotRecast.Detour
|
||||||
// Concatenate paths.
|
// Concatenate paths.
|
||||||
|
|
||||||
// Adjust beginning of the buffer to include the visited.
|
// Adjust beginning of the buffer to include the visited.
|
||||||
List<long> result = visited.GetRange(0, furthestVisited);
|
var length1 = furthestVisited;
|
||||||
result.AddRange(path.GetRange(furthestPath, npath - furthestPath));
|
var length2 = npath - furthestPath;
|
||||||
|
using var result = RcRentedArray.Rent<long>(length1 + length2);
|
||||||
|
visited.CopyTo(0, result.AsArray(), 0, length1);
|
||||||
|
path.CopyTo(furthestPath, result.AsArray(), length1, length2);
|
||||||
|
|
||||||
path = result;
|
path.Clear();
|
||||||
return result.Count;
|
CollectionExtensions.AddRange(path, result.AsSpan());
|
||||||
|
return path.Count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -38,9 +38,16 @@ namespace DotRecast.Detour
|
||||||
public int hitEdgeIndex;
|
public int hitEdgeIndex;
|
||||||
|
|
||||||
/// Pointer to an array of reference ids of the visited polygons. [opt]
|
/// Pointer to an array of reference ids of the visited polygons. [opt]
|
||||||
public List<long> path;
|
public long[] path;
|
||||||
|
|
||||||
|
public int pathCount;
|
||||||
|
|
||||||
/// The cost of the path until hit.
|
/// The cost of the path until hit.
|
||||||
public float pathCost;
|
public float pathCost;
|
||||||
|
|
||||||
|
public void AddPathNode(long nodeRef)
|
||||||
|
{
|
||||||
|
path[pathCount++] = nodeRef;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -39,8 +39,7 @@ namespace DotRecast.Detour
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Span<float> Apply(Span<float> verts, RcVec3f center, float radius, Span<float> resultBuffer)
|
||||||
public float[] Apply(float[] verts, RcVec3f center, float radius)
|
|
||||||
{
|
{
|
||||||
float radiusSqr = radius * radius;
|
float radiusSqr = radius * radius;
|
||||||
int outsideVertex = -1;
|
int outsideVertex = -1;
|
||||||
|
@ -56,19 +55,30 @@ namespace DotRecast.Detour
|
||||||
if (outsideVertex == -1)
|
if (outsideVertex == -1)
|
||||||
{
|
{
|
||||||
// polygon inside circle
|
// polygon inside circle
|
||||||
return verts;
|
var result = resultBuffer.Slice(0, verts.Length);
|
||||||
|
verts.CopyTo(result);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Span<float> qCircle = stackalloc float[UnitCircle.Length];
|
Span<float> qCircle = stackalloc float[UnitCircle.Length];
|
||||||
ScaleCircle(UnitCircle, center, radius, qCircle);
|
ScaleCircle(UnitCircle, center, radius, qCircle);
|
||||||
float[] intersection = DtConvexConvexIntersections.Intersect(verts, qCircle);
|
Span<float> intersection = DtConvexConvexIntersections.Intersect(verts, qCircle, resultBuffer);
|
||||||
if (intersection == null && DtUtils.PointInPolygon(center, verts, verts.Length / 3))
|
if (intersection.IsEmpty && DtUtils.PointInPolygon(center, verts, verts.Length / 3))
|
||||||
{
|
{
|
||||||
// circle inside polygon
|
// circle inside polygon
|
||||||
return qCircle.ToArray();
|
var result = resultBuffer.Slice(0, qCircle.Length);
|
||||||
|
qCircle.CopyTo(result);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
return intersection;
|
if(!intersection.IsEmpty)
|
||||||
|
{
|
||||||
|
var result = resultBuffer.Slice(0, intersection.Length);
|
||||||
|
// No need to copy, data is already in buffer
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Span<float>.Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -9,6 +9,6 @@ namespace DotRecast.Detour
|
||||||
{
|
{
|
||||||
/// Called for each batch of unique polygons touched by the search area in dtNavMeshQuery::queryPolygons.
|
/// Called for each batch of unique polygons touched by the search area in dtNavMeshQuery::queryPolygons.
|
||||||
/// This can be called multiple times for a single query.
|
/// This can be called multiple times for a single query.
|
||||||
void Process(DtMeshTile tile, DtPoly[] poly, Span<long> refs, int count);
|
void Process(DtMeshTile tile, Span<DtPoly> poly, Span<long> refs, int count);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -24,6 +24,6 @@ namespace DotRecast.Detour
|
||||||
{
|
{
|
||||||
public interface IDtPolygonByCircleConstraint
|
public interface IDtPolygonByCircleConstraint
|
||||||
{
|
{
|
||||||
float[] Apply(float[] polyVerts, RcVec3f circleCenter, float radius);
|
Span<float> Apply(Span<float> polyVerts, RcVec3f circleCenter, float radius, Span<float> resultBuffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
{
|
||||||
|
"name": "DotRecast.Recast.Demo",
|
||||||
|
"rootNamespace": "DotRecast.Recast.Demo",
|
||||||
|
"references": [
|
||||||
|
"DotRecast.Core"
|
||||||
|
],
|
||||||
|
"includePlatforms": [],
|
||||||
|
"excludePlatforms": [
|
||||||
|
"Android",
|
||||||
|
"Editor",
|
||||||
|
"EmbeddedLinux",
|
||||||
|
"GameCoreScarlett",
|
||||||
|
"GameCoreXboxOne",
|
||||||
|
"iOS",
|
||||||
|
"LinuxStandalone64",
|
||||||
|
"CloudRendering",
|
||||||
|
"macOSStandalone",
|
||||||
|
"PS4",
|
||||||
|
"PS5",
|
||||||
|
"QNX",
|
||||||
|
"Stadia",
|
||||||
|
"Switch",
|
||||||
|
"tvOS",
|
||||||
|
"WSA",
|
||||||
|
"VisionOS",
|
||||||
|
"WebGL",
|
||||||
|
"WindowsStandalone32",
|
||||||
|
"WindowsStandalone64",
|
||||||
|
"XboxOne"
|
||||||
|
],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [
|
||||||
|
{
|
||||||
|
"name": "Select...",
|
||||||
|
"expression": "",
|
||||||
|
"define": "USE_RECAST_DEMO"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"noEngineReferences": true
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"name": "DotRecast.Recast.Toolset",
|
||||||
|
"rootNamespace": "DotRecast.Recast.Toolset",
|
||||||
|
"references": [
|
||||||
|
"DotRecast.Core",
|
||||||
|
"DotRecast.Recast",
|
||||||
|
"DotRecast.Detour",
|
||||||
|
"DotRecast.Detour.Crowd",
|
||||||
|
"DotRecast.Detour.Dynamic",
|
||||||
|
"DotRecast.Detour.Extras",
|
||||||
|
"DotRecast.Detour.TileCache"
|
||||||
|
],
|
||||||
|
"includePlatforms": [],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": true
|
||||||
|
}
|
|
@ -46,13 +46,13 @@ namespace DotRecast.Recast.Toolset.Geom
|
||||||
if (null != _geom)
|
if (null != _geom)
|
||||||
{
|
{
|
||||||
var offMeshConnections = _geom.GetOffMeshConnections();
|
var offMeshConnections = _geom.GetOffMeshConnections();
|
||||||
|
option.offMeshConCount = offMeshConnections.Count;
|
||||||
option.offMeshConVerts = new float[option.offMeshConCount * 6];
|
option.offMeshConVerts = new float[option.offMeshConCount * 6];
|
||||||
option.offMeshConRad = new float[option.offMeshConCount];
|
option.offMeshConRad = new float[option.offMeshConCount];
|
||||||
option.offMeshConDir = new int[option.offMeshConCount];
|
option.offMeshConDir = new int[option.offMeshConCount];
|
||||||
option.offMeshConAreas = new int[option.offMeshConCount];
|
option.offMeshConAreas = new int[option.offMeshConCount];
|
||||||
option.offMeshConFlags = new int[option.offMeshConCount];
|
option.offMeshConFlags = new int[option.offMeshConCount];
|
||||||
option.offMeshConUserID = new int[option.offMeshConCount];
|
option.offMeshConUserID = new int[option.offMeshConCount];
|
||||||
option.offMeshConCount = offMeshConnections.Count;
|
|
||||||
for (int i = 0; i < option.offMeshConCount; i++)
|
for (int i = 0; i < option.offMeshConCount; i++)
|
||||||
{
|
{
|
||||||
RcOffMeshConnection offMeshCon = offMeshConnections[i];
|
RcOffMeshConnection offMeshCon = offMeshConnections[i];
|
||||||
|
@ -65,7 +65,7 @@ namespace DotRecast.Recast.Toolset.Geom
|
||||||
option.offMeshConDir[i] = offMeshCon.bidir ? 1 : 0;
|
option.offMeshConDir[i] = offMeshCon.bidir ? 1 : 0;
|
||||||
option.offMeshConAreas[i] = offMeshCon.area;
|
option.offMeshConAreas[i] = offMeshCon.area;
|
||||||
option.offMeshConFlags[i] = offMeshCon.flags;
|
option.offMeshConFlags[i] = offMeshCon.flags;
|
||||||
// option.offMeshConUserID[i] = offMeshCon.userId;
|
option.offMeshConUserID[i] = offMeshCon.userId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,7 +98,7 @@ namespace DotRecast.Recast.Toolset.Tools
|
||||||
|
|
||||||
iterPos = result;
|
iterPos = result;
|
||||||
|
|
||||||
pathIterPolyCount = DtPathUtils.MergeCorridorStartMoved(ref pathIterPolys, pathIterPolyCount, MAX_POLYS, visited, nvisited);
|
pathIterPolyCount = DtPathUtils.MergeCorridorStartMoved(pathIterPolys, pathIterPolyCount, MAX_POLYS, visited, nvisited);
|
||||||
pathIterPolyCount = DtPathUtils.FixupShortcuts(ref pathIterPolys, pathIterPolyCount, navQuery);
|
pathIterPolyCount = DtPathUtils.FixupShortcuts(ref pathIterPolys, pathIterPolyCount, navQuery);
|
||||||
|
|
||||||
var status = navQuery.GetPolyHeight(pathIterPolys[0], result, out var h);
|
var status = navQuery.GetPolyHeight(pathIterPolys[0], result, out var h);
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"name": "DotRecast.Recast",
|
||||||
|
"rootNamespace": "DotRecast.Recast",
|
||||||
|
"references": [
|
||||||
|
"DotRecast.Core"
|
||||||
|
],
|
||||||
|
"includePlatforms": [],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": true
|
||||||
|
}
|
|
@ -33,7 +33,7 @@ namespace DotRecast.Recast.Geom
|
||||||
public readonly int flags;
|
public readonly int flags;
|
||||||
public readonly int userId;
|
public readonly int userId;
|
||||||
|
|
||||||
public RcOffMeshConnection(RcVec3f start, RcVec3f end, float radius, bool bidir, int area, int flags)
|
public RcOffMeshConnection(RcVec3f start, RcVec3f end, float radius, bool bidir, int area, int flags, int userId = 0)
|
||||||
{
|
{
|
||||||
verts = new float[6];
|
verts = new float[6];
|
||||||
verts[0] = start.X;
|
verts[0] = start.X;
|
||||||
|
@ -46,6 +46,7 @@ namespace DotRecast.Recast.Geom
|
||||||
this.bidir = bidir;
|
this.bidir = bidir;
|
||||||
this.area = area;
|
this.area = area;
|
||||||
this.flags = flags;
|
this.flags = flags;
|
||||||
|
this.userId = userId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"name": "com.rnd.dotrecast",
|
||||||
|
"displayName": "DotRecast",
|
||||||
|
"version": "0.4.1"
|
||||||
|
}
|
|
@ -17,6 +17,7 @@ freely, subject to the following restrictions:
|
||||||
3. This notice may not be removed or altered from any source distribution.
|
3. This notice may not be removed or altered from any source distribution.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
|
||||||
namespace DotRecast.Detour.Test;
|
namespace DotRecast.Detour.Test;
|
||||||
|
@ -29,9 +30,10 @@ public class ConvexConvexIntersectionTest
|
||||||
{
|
{
|
||||||
float[] p = { -4, 0, 0, -3, 0, 3, 2, 0, 3, 3, 0, -3, -2, 0, -4 };
|
float[] p = { -4, 0, 0, -3, 0, 3, 2, 0, 3, 3, 0, -3, -2, 0, -4 };
|
||||||
float[] q = { -4, 0, 0, -3, 0, 3, 2, 0, 3, 3, 0, -3, -2, 0, -4 };
|
float[] q = { -4, 0, 0, -3, 0, 3, 2, 0, 3, 3, 0, -3, -2, 0, -4 };
|
||||||
float[] intersection = DtConvexConvexIntersections.Intersect(p, q);
|
float[] buffer = new float[128];
|
||||||
|
Span<float> intersection = DtConvexConvexIntersections.Intersect(p, q, buffer);
|
||||||
Assert.That(intersection.Length, Is.EqualTo(5 * 3));
|
Assert.That(intersection.Length, Is.EqualTo(5 * 3));
|
||||||
Assert.That(intersection, Is.EqualTo(p));
|
Assert.That(intersection.ToArray(), Is.EquivalentTo(p));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -39,8 +41,9 @@ public class ConvexConvexIntersectionTest
|
||||||
{
|
{
|
||||||
float[] p = { -5, 0, -5, -5, 0, 4, 1, 0, 4, 1, 0, -5 };
|
float[] p = { -5, 0, -5, -5, 0, 4, 1, 0, 4, 1, 0, -5 };
|
||||||
float[] q = { -4, 0, 0, -3, 0, 3, 2, 0, 3, 3, 0, -3, -2, 0, -4 };
|
float[] q = { -4, 0, 0, -3, 0, 3, 2, 0, 3, 3, 0, -3, -2, 0, -4 };
|
||||||
float[] intersection = DtConvexConvexIntersections.Intersect(p, q);
|
float[] buffer = new float[128];
|
||||||
|
Span<float> intersection = DtConvexConvexIntersections.Intersect(p, q, buffer);
|
||||||
Assert.That(intersection.Length, Is.EqualTo(5 * 3));
|
Assert.That(intersection.Length, Is.EqualTo(5 * 3));
|
||||||
Assert.That(intersection, Is.EqualTo(new[] { 1, 0, 3, 1, 0, -3.4f, -2, 0, -4, -4, 0, 0, -3, 0, 3 }));
|
Assert.That(intersection.ToArray(), Is.EquivalentTo(new[] { 1, 0, 3, 1, 0, -3.4f, -2, 0, -4, -4, 0, 0, -3, 0, 3 }));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -17,6 +17,7 @@ freely, subject to the following restrictions:
|
||||||
3. This notice may not be removed or altered from any source distribution.
|
3. This notice may not be removed or altered from any source distribution.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
using DotRecast.Core.Numerics;
|
using DotRecast.Core.Numerics;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
|
||||||
|
@ -32,9 +33,12 @@ public class PolygonByCircleConstraintTest
|
||||||
{
|
{
|
||||||
float[] polygon = { -2, 0, 2, 2, 0, 2, 2, 0, -2, -2, 0, -2 };
|
float[] polygon = { -2, 0, 2, 2, 0, 2, 2, 0, -2, -2, 0, -2 };
|
||||||
RcVec3f center = new RcVec3f(1, 0, 1);
|
RcVec3f center = new RcVec3f(1, 0, 1);
|
||||||
float[] constrained = _constraint.Apply(polygon, center, 6);
|
var radius = 6;
|
||||||
|
|
||||||
Assert.That(constrained, Is.EqualTo(polygon));
|
float[] buffer = new float[128];
|
||||||
|
Span<float> constrained = _constraint.Apply(polygon, center, radius, buffer);
|
||||||
|
|
||||||
|
Assert.That(constrained.ToArray(), Is.EquivalentTo(polygon));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -43,10 +47,13 @@ public class PolygonByCircleConstraintTest
|
||||||
int expectedSize = 21;
|
int expectedSize = 21;
|
||||||
float[] polygon = { -2, 0, 2, 2, 0, 2, 2, 0, -2, -2, 0, -2 };
|
float[] polygon = { -2, 0, 2, 2, 0, 2, 2, 0, -2, -2, 0, -2 };
|
||||||
RcVec3f center = new RcVec3f(2, 0, 0);
|
RcVec3f center = new RcVec3f(2, 0, 0);
|
||||||
|
var radius = 3;
|
||||||
|
|
||||||
|
float[] buffer = new float[128];
|
||||||
|
Span<float> constrained = _constraint.Apply(polygon, center, radius, buffer);
|
||||||
|
|
||||||
float[] constrained = _constraint.Apply(polygon, center, 3);
|
|
||||||
Assert.That(constrained.Length, Is.EqualTo(expectedSize));
|
Assert.That(constrained.Length, Is.EqualTo(expectedSize));
|
||||||
Assert.That(constrained, Is.SupersetOf(new[] { 2f, 0f, 2f, 2f, 0f, -2f }));
|
Assert.That(constrained.ToArray(), Is.SupersetOf(new[] { 2f, 0f, 2f, 2f, 0f, -2f }));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -55,7 +62,10 @@ public class PolygonByCircleConstraintTest
|
||||||
int expectedSize = 12 * 3;
|
int expectedSize = 12 * 3;
|
||||||
float[] polygon = { -4, 0, 0, -3, 0, 3, 2, 0, 3, 3, 0, -3, -2, 0, -4 };
|
float[] polygon = { -4, 0, 0, -3, 0, 3, 2, 0, 3, 3, 0, -3, -2, 0, -4 };
|
||||||
RcVec3f center = new RcVec3f(-1, 0, -1);
|
RcVec3f center = new RcVec3f(-1, 0, -1);
|
||||||
float[] constrained = _constraint.Apply(polygon, center, 2);
|
var radius = 2;
|
||||||
|
|
||||||
|
float[] buffer = new float[128];
|
||||||
|
Span<float> constrained = _constraint.Apply(polygon, center, radius, buffer);
|
||||||
|
|
||||||
Assert.That(constrained.Length, Is.EqualTo(expectedSize));
|
Assert.That(constrained.Length, Is.EqualTo(expectedSize));
|
||||||
|
|
||||||
|
@ -73,10 +83,13 @@ public class PolygonByCircleConstraintTest
|
||||||
int expectedSize = 9 * 3;
|
int expectedSize = 9 * 3;
|
||||||
float[] polygon = { -4, 0, 0, -3, 0, 3, 2, 0, 3, 3, 0, -3, -2, 0, -4 };
|
float[] polygon = { -4, 0, 0, -3, 0, 3, 2, 0, 3, 3, 0, -3, -2, 0, -4 };
|
||||||
RcVec3f center = new RcVec3f(-2, 0, -1);
|
RcVec3f center = new RcVec3f(-2, 0, -1);
|
||||||
float[] constrained = _constraint.Apply(polygon, center, 3);
|
var radius = 3;
|
||||||
|
|
||||||
|
float[] buffer = new float[128];
|
||||||
|
Span<float> constrained = _constraint.Apply(polygon, center, radius, buffer);
|
||||||
|
|
||||||
Assert.That(constrained.Length, Is.EqualTo(expectedSize));
|
Assert.That(constrained.Length, Is.EqualTo(expectedSize));
|
||||||
Assert.That(constrained, Is.SupersetOf(new[] { -2f, 0f, -4f, -4f, 0f, 0f, -3.4641016f, 0.0f, 1.60769534f, -2.0f, 0.0f, 2.0f }));
|
Assert.That(constrained.ToArray(), Is.SupersetOf(new[] { -2f, 0f, -4f, -4f, 0f, 0f, -3.4641016f, 0.0f, 1.60769534f, -2.0f, 0.0f, 2.0f }));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -85,9 +98,12 @@ public class PolygonByCircleConstraintTest
|
||||||
int expectedSize = 7 * 3;
|
int expectedSize = 7 * 3;
|
||||||
float[] polygon = { -4, 0, 0, -3, 0, 3, 2, 0, 3, 3, 0, -3, -2, 0, -4 };
|
float[] polygon = { -4, 0, 0, -3, 0, 3, 2, 0, 3, 3, 0, -3, -2, 0, -4 };
|
||||||
RcVec3f center = new RcVec3f(4, 0, 0);
|
RcVec3f center = new RcVec3f(4, 0, 0);
|
||||||
float[] constrained = _constraint.Apply(polygon, center, 4);
|
var radius = 4;
|
||||||
|
|
||||||
|
float[] buffer = new float[128];
|
||||||
|
Span<float> constrained = _constraint.Apply(polygon, center, radius, buffer);
|
||||||
|
|
||||||
Assert.That(constrained.Length, Is.EqualTo(expectedSize));
|
Assert.That(constrained.Length, Is.EqualTo(expectedSize));
|
||||||
Assert.That(constrained, Is.SupersetOf(new[] { 1.53589869f, 0f, 3f, 2f, 0f, 3f, 3f, 0f, -3f }));
|
Assert.That(constrained.ToArray(), Is.SupersetOf(new[] { 1.53589869f, 0f, 3f, 2f, 0f, 3f, 3f, 0f, -3f }));
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue