From 449923cb3ebc91039b44ccf20c793c2961db5e50 Mon Sep 17 00:00:00 2001
From: Doprez <73259914+Doprez@users.noreply.github.com>
Date: Mon, 29 Dec 2025 17:01:46 -0700
Subject: [PATCH 1/5] compiling update
---
sources/Directory.Packages.props | 10 ++---
.../Stride.Navigation/DtMeshDataSerializer.cs | 32 ++++++++-------
.../DynamicNavigationMeshSystem.cs | 4 +-
.../InternalNavigationMesh.cs | 25 ++++++------
.../Stride.Navigation/NavigationBuilder.cs | 40 +++++++++++++++++--
.../Stride.Navigation.csproj | 1 +
6 files changed, 76 insertions(+), 36 deletions(-)
diff --git a/sources/Directory.Packages.props b/sources/Directory.Packages.props
index d80c39307b..4c59a529c8 100644
--- a/sources/Directory.Packages.props
+++ b/sources/Directory.Packages.props
@@ -5,10 +5,10 @@
-
-
-
-
+
+
+
+
@@ -148,4 +148,4 @@
-
\ No newline at end of file
+
diff --git a/sources/engine/Stride.Navigation/DtMeshDataSerializer.cs b/sources/engine/Stride.Navigation/DtMeshDataSerializer.cs
index 9c4b722a91..c1ee633279 100644
--- a/sources/engine/Stride.Navigation/DtMeshDataSerializer.cs
+++ b/sources/engine/Stride.Navigation/DtMeshDataSerializer.cs
@@ -1,6 +1,7 @@
-// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net)
+// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net)
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
+using System.Diagnostics;
using DotRecast.Core.Numerics;
using DotRecast.Detour;
using Stride.Core.Serialization;
@@ -40,14 +41,14 @@ internal void WriteDtMeshBvTree(DtBVNode[] dataBvTree)
if (isNull)
continue;
- for (int i = 0; i < 3; i++)
- {
- stream.Write(t.bmin[i]);
- }
- for (int i = 0; i < 3; i++)
- {
- stream.Write(t.bmax[i]);
- }
+ stream.Write(t.bmin.X);
+ stream.Write(t.bmin.Y);
+ stream.Write(t.bmin.Z);
+
+ stream.Write(t.bmax.X);
+ stream.Write(t.bmax.Y);
+ stream.Write(t.bmax.Z);
+
stream.Write(t.i);
}
}
@@ -173,10 +174,13 @@ internal DtBVNode[] ReadDtMeshBvTree()
continue;
}
var node = new DtBVNode();
- for (int j = 0; j < 3; j++)
- node.bmin[j] = stream.Read();
- for (int j = 0; j < 3; j++)
- node.bmax[j] = stream.Read();
+ node.bmin.X = stream.Read();
+ node.bmin.Y = stream.Read();
+ node.bmin.Z = stream.Read();
+
+ node.bmax.X = stream.Read();
+ node.bmax.Y = stream.Read();
+ node.bmax.Z = stream.Read();
node.i = stream.Read();
arr[i] = node;
@@ -207,7 +211,7 @@ internal DtPolyDetail[] ReadDtMeshDetailMeshes()
int count = stream.Read();
var arr = new DtPolyDetail[count];
for (int i = 0; i < count; i++)
- arr[i] = new DtPolyDetail(stream.Read(),stream.Read(),stream.Read(),stream.Read());
+ arr[i] = new DtPolyDetail(stream.Read(),stream.Read(),stream.Read(),stream.Read());
return arr;
}
diff --git a/sources/engine/Stride.Navigation/DynamicNavigationMeshSystem.cs b/sources/engine/Stride.Navigation/DynamicNavigationMeshSystem.cs
index 8728879126..e906ca708f 100644
--- a/sources/engine/Stride.Navigation/DynamicNavigationMeshSystem.cs
+++ b/sources/engine/Stride.Navigation/DynamicNavigationMeshSystem.cs
@@ -176,7 +176,7 @@ public async Task Rebuild()
});
await result;
- FinilizeRebuild(result);
+ FinalizeRebuild(result);
return result.Result;
}
@@ -191,7 +191,7 @@ internal void InitializeSettingsFromNavigationSettings(NavigationSettings naviga
pendingRebuild = true;
}
- private void FinilizeRebuild(Task resultTask)
+ private void FinalizeRebuild(Task resultTask)
{
var result = resultTask.Result;
if (result.Success)
diff --git a/sources/engine/Stride.Navigation/InternalNavigationMesh.cs b/sources/engine/Stride.Navigation/InternalNavigationMesh.cs
index 3de1af7e15..d56b73da5a 100644
--- a/sources/engine/Stride.Navigation/InternalNavigationMesh.cs
+++ b/sources/engine/Stride.Navigation/InternalNavigationMesh.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
+using System.Runtime.InteropServices;
using DotRecast.Core.Numerics;
using DotRecast.Detour;
using Stride.Core.Mathematics;
@@ -40,7 +41,8 @@ public InternalNavigationMesh(float cellTileSize)
meshParams.maxPolys = 1 << polyBits;
// Initialize the query object
- navMesh = new DtNavMesh(meshParams, 2048);
+ navMesh = new DtNavMesh();
+ navMesh.Init(meshParams, 2048);
navQuery = new DtNavMeshQuery(navMesh);
}
@@ -51,7 +53,7 @@ public bool LoadTile(DtMeshData navData)
if (navData == null)
return false;
- long tileRef = navMesh.AddTile(navData, 0, 0);
+ var status = navMesh.AddTile(navData, 0, 0, out long tileRef);
return tileRef != 0;
}
@@ -75,13 +77,14 @@ public void DoPathFindQuery(PathFindQuery query, ref PathFindResult result)
status = navQuery.FindNearestPoly(query.Target.ToDotRecastVector(), query.FindNearestPolyExtent.ToDotRecastVector(), filter, out long endPoly, out RcVec3f endPoint, out _);
if (status.Failed())
return;
-
- List polys = new(query.MaxPathPoints);
- status = navQuery.FindPath(startPoly, endPoly, startPoint, endPoint, filter, ref polys, DtFindPathOption.NoOption);
+
+ long[] polys = new long[query.MaxPathPoints];
+ status = navQuery.FindPath(startPoly, endPoly, startPoint, endPoint, filter, polys, out var pathCount, query.MaxPathPoints);
if (status.Failed() || status.IsPartial())
return;
-
- status = navQuery.FindStraightPath(startPoint, endPoint, polys, ref result.PathPoints, query.MaxPathPoints, 0);
+
+ var pathPointsSpan = CollectionsMarshal.AsSpan(result.PathPoints);
+ status = navQuery.FindStraightPath(startPoint, endPoint, polys, polys.Length, pathPointsSpan, out var straightPathCount, query.MaxPathPoints, 0);
if (status.Failed())
return;
result.PathFound = true;
@@ -96,12 +99,10 @@ public void DoRaycastQuery(RaycastQuery query, out NavigationRaycastResult resul
DtStatus status = navQuery.FindNearestPoly(query.Source.ToDotRecastVector(), query.FindNearestPolyExtent.ToDotRecastVector(), filter, out long startPoly, out _, out _);
if (status.Failed())
return;
-
- List polys = new (query.MaxPathPoints);
+
+ long[] polys = new long[query.MaxPathPoints];
var normal = result.Normal.ToDotRecastVector();
- status = navQuery.Raycast(startPoly, query.Source.ToDotRecastVector(),
- query.Target.ToDotRecastVector(), filter,
- out float t, out normal, ref polys);
+ status = navQuery.Raycast(startPoly, query.Source.ToDotRecastVector(), query.Target.ToDotRecastVector(), filter, out float t, out normal, polys, out var pathCount, query.MaxPathPoints);
result.Normal = new(normal.X, normal.Y, normal.Z);
if (status.Failed())
diff --git a/sources/engine/Stride.Navigation/NavigationBuilder.cs b/sources/engine/Stride.Navigation/NavigationBuilder.cs
index 0bcbd120c9..380a7cbc19 100644
--- a/sources/engine/Stride.Navigation/NavigationBuilder.cs
+++ b/sources/engine/Stride.Navigation/NavigationBuilder.cs
@@ -1,4 +1,4 @@
-// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net)
+// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net)
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
using System;
@@ -96,7 +96,7 @@ public GeneratedData BuildNavmesh(ref Vector3[] vertices, ref int[] indices)
}
// Find walkable triangles and rasterize into heightfield
- triAreas = RcCommons.MarkWalkableTriangles(context, buildSettings.AgentMaxSlope, verts, indices, numTriangles, new RcAreaModification(RcAreaModification.RC_AREA_FLAGS_MASK));
+ triAreas = MarkWalkableTriangles(context, buildSettings.AgentMaxSlope, verts, indices, numTriangles, new RcAreaModification(RcAreaModification.RC_AREA_FLAGS_MASK));
RcRasterizations.RasterizeTriangles(context, verts, indices, triAreas, numTriangles, solid, walkableClimb);
// Filter walkable surfaces.
@@ -129,7 +129,7 @@ public GeneratedData BuildNavmesh(ref Vector3[] vertices, ref int[] indices)
// Update poly flags from areas.
for (int i = 0; i < polyMesh.npolys; ++i)
{
- if (polyMesh.areas[i] == RcConstants.RC_WALKABLE_AREA)
+ if (polyMesh.areas[i] == 63)
polyMesh.areas[i] = 0;
if (polyMesh.areas[i] == 0)
@@ -189,4 +189,38 @@ private bool CreateDetourMesh()
return navmeshData != null;
}
+
+ private static int[] MarkWalkableTriangles(RcContext ctx, float walkableSlopeAngle, float[] verts, int[] tris, int nt, RcAreaModification areaMod)
+ {
+ int[] array = new int[nt];
+ float num = MathF.Cos(walkableSlopeAngle / 180f * (float)Math.PI);
+ Vector3 norm = default(Vector3);
+ for (int i = 0; i < nt; i++)
+ {
+ int num2 = i * 3;
+ Vector3 v = ToVec3(verts, tris[num2] * 3);
+ Vector3 v2 = ToVec3(verts, tris[num2 + 1] * 3);
+ Vector3 v3 = ToVec3(verts, tris[num2 + 2] * 3);
+ CalcTriNormal(v, v2, v3, ref norm);
+ if (norm.Y > num)
+ {
+ array[i] = areaMod.Apply(array[i]);
+ }
+ }
+
+ return array;
+ }
+
+ private static void CalcTriNormal(Vector3 v0, Vector3 v1, Vector3 v2, ref Vector3 norm)
+ {
+ Vector3 v3 = v1 - v0;
+ Vector3 v4 = v2 - v0;
+ norm = Vector3.Cross(v3, v4);
+ norm = Vector3.Normalize(norm);
+ }
+
+ private static Vector3 ToVec3(float[] values, int n)
+ {
+ return new Vector3(values[n], values[n + 1], values[n + 2]);
+ }
}
diff --git a/sources/engine/Stride.Navigation/Stride.Navigation.csproj b/sources/engine/Stride.Navigation/Stride.Navigation.csproj
index 9378f597ca..b20ba7bb08 100644
--- a/sources/engine/Stride.Navigation/Stride.Navigation.csproj
+++ b/sources/engine/Stride.Navigation/Stride.Navigation.csproj
@@ -24,6 +24,7 @@
+
From b085faa23e540deaa8fe5c5a4d24e3005b2c5bc9 Mon Sep 17 00:00:00 2001
From: Doprez <73259914+Doprez@users.noreply.github.com>
Date: Tue, 30 Dec 2025 12:23:14 -0700
Subject: [PATCH 2/5] Fixed missing constant
---
sources/engine/Stride.Navigation/NavigationBuilder.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sources/engine/Stride.Navigation/NavigationBuilder.cs b/sources/engine/Stride.Navigation/NavigationBuilder.cs
index 380a7cbc19..d962fb8606 100644
--- a/sources/engine/Stride.Navigation/NavigationBuilder.cs
+++ b/sources/engine/Stride.Navigation/NavigationBuilder.cs
@@ -129,7 +129,7 @@ public GeneratedData BuildNavmesh(ref Vector3[] vertices, ref int[] indices)
// Update poly flags from areas.
for (int i = 0; i < polyMesh.npolys; ++i)
{
- if (polyMesh.areas[i] == 63)
+ if (polyMesh.areas[i] == RcRecast.RC_WALKABLE_AREA)
polyMesh.areas[i] = 0;
if (polyMesh.areas[i] == 0)
From 37e084ee0e2c87e56f6451ef596ff5c7fb346c7d Mon Sep 17 00:00:00 2001
From: Doprez <73259914+Doprez@users.noreply.github.com>
Date: Tue, 30 Dec 2025 12:35:04 -0700
Subject: [PATCH 3/5] use moved RcRecast helpers
---
.../Stride.Navigation/NavigationBuilder.cs | 36 +------------------
1 file changed, 1 insertion(+), 35 deletions(-)
diff --git a/sources/engine/Stride.Navigation/NavigationBuilder.cs b/sources/engine/Stride.Navigation/NavigationBuilder.cs
index d962fb8606..26295c56fa 100644
--- a/sources/engine/Stride.Navigation/NavigationBuilder.cs
+++ b/sources/engine/Stride.Navigation/NavigationBuilder.cs
@@ -96,7 +96,7 @@ public GeneratedData BuildNavmesh(ref Vector3[] vertices, ref int[] indices)
}
// Find walkable triangles and rasterize into heightfield
- triAreas = MarkWalkableTriangles(context, buildSettings.AgentMaxSlope, verts, indices, numTriangles, new RcAreaModification(RcAreaModification.RC_AREA_FLAGS_MASK));
+ triAreas = RcRecast.MarkWalkableTriangles(context, buildSettings.AgentMaxSlope, verts, indices, numTriangles, new RcAreaModification(RcAreaModification.RC_AREA_FLAGS_MASK));
RcRasterizations.RasterizeTriangles(context, verts, indices, triAreas, numTriangles, solid, walkableClimb);
// Filter walkable surfaces.
@@ -189,38 +189,4 @@ private bool CreateDetourMesh()
return navmeshData != null;
}
-
- private static int[] MarkWalkableTriangles(RcContext ctx, float walkableSlopeAngle, float[] verts, int[] tris, int nt, RcAreaModification areaMod)
- {
- int[] array = new int[nt];
- float num = MathF.Cos(walkableSlopeAngle / 180f * (float)Math.PI);
- Vector3 norm = default(Vector3);
- for (int i = 0; i < nt; i++)
- {
- int num2 = i * 3;
- Vector3 v = ToVec3(verts, tris[num2] * 3);
- Vector3 v2 = ToVec3(verts, tris[num2 + 1] * 3);
- Vector3 v3 = ToVec3(verts, tris[num2 + 2] * 3);
- CalcTriNormal(v, v2, v3, ref norm);
- if (norm.Y > num)
- {
- array[i] = areaMod.Apply(array[i]);
- }
- }
-
- return array;
- }
-
- private static void CalcTriNormal(Vector3 v0, Vector3 v1, Vector3 v2, ref Vector3 norm)
- {
- Vector3 v3 = v1 - v0;
- Vector3 v4 = v2 - v0;
- norm = Vector3.Cross(v3, v4);
- norm = Vector3.Normalize(norm);
- }
-
- private static Vector3 ToVec3(float[] values, int n)
- {
- return new Vector3(values[n], values[n + 1], values[n + 2]);
- }
}
From b92ac1f407385ae2f8edd5ae00a4943d5dfc3d20 Mon Sep 17 00:00:00 2001
From: Doprez <73259914+Doprez@users.noreply.github.com>
Date: Wed, 31 Dec 2025 15:04:43 -0700
Subject: [PATCH 4/5] fixed broken path finding with new version
---
.../InternalNavigationMesh.cs | 31 +++++++++++++++----
.../engine/Stride.Navigation/Navigation.cs | 2 +-
.../Processors/RecastNavigationMesh.cs | 4 +--
3 files changed, 28 insertions(+), 9 deletions(-)
diff --git a/sources/engine/Stride.Navigation/InternalNavigationMesh.cs b/sources/engine/Stride.Navigation/InternalNavigationMesh.cs
index d56b73da5a..a7ed805dc7 100644
--- a/sources/engine/Stride.Navigation/InternalNavigationMesh.cs
+++ b/sources/engine/Stride.Navigation/InternalNavigationMesh.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Runtime.InteropServices;
using DotRecast.Core.Numerics;
using DotRecast.Detour;
@@ -79,14 +80,32 @@ public void DoPathFindQuery(PathFindQuery query, ref PathFindResult result)
return;
long[] polys = new long[query.MaxPathPoints];
- status = navQuery.FindPath(startPoly, endPoly, startPoint, endPoint, filter, polys, out var pathCount, query.MaxPathPoints);
- if (status.Failed() || status.IsPartial())
- return;
+ navQuery.FindPath(startPoly, endPoly, startPoint, endPoint, filter, polys, out var pathCount, polys.Length);
- var pathPointsSpan = CollectionsMarshal.AsSpan(result.PathPoints);
- status = navQuery.FindStraightPath(startPoint, endPoint, polys, polys.Length, pathPointsSpan, out var straightPathCount, query.MaxPathPoints, 0);
- if (status.Failed())
+ if (0 >= pathCount)
+ {
return;
+ }
+
+ // In case of partial path, make sure the end point is clamped to the last polygon.
+ var endPosition = new RcVec3f(endPoint.X, endPoint.Y, endPoint.Z);
+ if (polys[pathCount - 1] != endPoly)
+ {
+ status = navQuery.ClosestPointOnPoly(polys[pathCount - 1], endPoint, out var closest, out var _);
+ if (status.Succeeded())
+ {
+ endPosition = closest;
+ }
+ }
+
+ // Due to Dotrecast using Spans, we need to allocate the array with the max size possible then resize it later to remove empty entries.
+ // TODO: By default we allocate 1024 points which is way more than enough for most cases and should maybe be defaulted to a smaller value in the future.
+ result.PathPoints = new DtStraightPath[query.MaxPathPoints];
+ navQuery.FindStraightPath(startPoint, endPosition, polys, pathCount, result.PathPoints, out var straightPathCount, query.MaxPathPoints, 0);
+
+ // cut out the empty entries
+ Array.Resize(ref result.PathPoints, straightPathCount);
+
result.PathFound = true;
}
diff --git a/sources/engine/Stride.Navigation/Navigation.cs b/sources/engine/Stride.Navigation/Navigation.cs
index a873982379..8e712b35a0 100644
--- a/sources/engine/Stride.Navigation/Navigation.cs
+++ b/sources/engine/Stride.Navigation/Navigation.cs
@@ -27,7 +27,7 @@ internal struct PathFindQuery
internal struct PathFindResult
{
public bool PathFound;
- public List PathPoints;
+ public DtStraightPath[] PathPoints;
}
internal struct BuildSettings
diff --git a/sources/engine/Stride.Navigation/Processors/RecastNavigationMesh.cs b/sources/engine/Stride.Navigation/Processors/RecastNavigationMesh.cs
index f311af3c72..012a0a797a 100644
--- a/sources/engine/Stride.Navigation/Processors/RecastNavigationMesh.cs
+++ b/sources/engine/Stride.Navigation/Processors/RecastNavigationMesh.cs
@@ -86,12 +86,12 @@ public bool TryFindPath(Vector3 start, Vector3 end, ICollection path, N
query.FindNearestPolyExtent = querySettings.FindNearestPolyExtent;
PathFindResult queryResult = default;
- queryResult.PathPoints = new List(querySettings.MaxPathPoints);
+ queryResult.PathPoints = new DtStraightPath[querySettings.MaxPathPoints];
navmesh.DoPathFindQuery(query, ref queryResult);
if (!queryResult.PathFound)
return false;
- for (int i = 0; i < queryResult.PathPoints.Count; i++)
+ for (int i = 0; i < queryResult.PathPoints.Length; i++)
{
path.Add(queryResult.PathPoints[i].pos.ToStrideVector());
}
From c56a314f46653ee45b7354ee43738d4bcda71ad7 Mon Sep 17 00:00:00 2001
From: Doprez <73259914+Doprez@users.noreply.github.com>
Date: Mon, 5 Jan 2026 06:57:18 -0700
Subject: [PATCH 5/5] NavigationMeshAssetCompiler version bump
---
.../Stride.Assets/Navigation/NavigationMeshAssetCompiler.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sources/engine/Stride.Assets/Navigation/NavigationMeshAssetCompiler.cs b/sources/engine/Stride.Assets/Navigation/NavigationMeshAssetCompiler.cs
index 1619501cbb..ee72ebe622 100644
--- a/sources/engine/Stride.Assets/Navigation/NavigationMeshAssetCompiler.cs
+++ b/sources/engine/Stride.Assets/Navigation/NavigationMeshAssetCompiler.cs
@@ -121,7 +121,7 @@ public NavmeshBuildCommand(string url, AssetItem assetItem, NavigationMeshAsset
asset = value;
assetUrl = url;
- Version = 1; // Removed separate debug model stored in the navigation mesh
+ Version = 2;
}
protected override void ComputeParameterHash(BinarySerializationWriter writer)