From 9fc9382e58f86f3b344d74e7ee6ad992df521ff2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 30 Jan 2026 13:11:56 +0100 Subject: [PATCH] Refactor navmesh building to support updating all scenes automatically without specifying one #3744 --- Source/Editor/Modules/SceneEditingModule.cs | 2 +- Source/Editor/SceneGraph/Actors/SplineNode.cs | 2 +- Source/Editor/Tools/Terrain/EditTab.cs | 2 +- .../Editor/Tools/Terrain/EditTerrainGizmo.cs | 2 +- .../Terrain/Undo/EditTerrainMapAction.cs | 5 +- .../Editor/Undo/Actions/DeleteActorsAction.cs | 2 +- .../Undo/Actions/TransformObjectsAction.cs | 6 +- .../Engine/Navigation/NavMeshBoundsVolume.cpp | 29 +++- .../Engine/Navigation/NavMeshBoundsVolume.h | 1 + Source/Engine/Navigation/NavMeshBuilder.cpp | 154 +++++++++--------- Source/Engine/Navigation/NavMeshBuilder.h | 4 - .../Engine/Navigation/NavModifierVolume.cpp | 4 +- Source/Engine/Navigation/Navigation.cpp | 24 --- Source/Engine/Navigation/Navigation.h | 43 +++-- 14 files changed, 145 insertions(+), 135 deletions(-) diff --git a/Source/Editor/Modules/SceneEditingModule.cs b/Source/Editor/Modules/SceneEditingModule.cs index c36866bc3..b1a5be6f1 100644 --- a/Source/Editor/Modules/SceneEditingModule.cs +++ b/Source/Editor/Modules/SceneEditingModule.cs @@ -229,7 +229,7 @@ namespace FlaxEditor.Modules if (!isPlayMode && options.General.AutoRebuildNavMesh && actor.Scene && node.AffectsNavigationWithChildren) { var bounds = actor.BoxWithChildren; - Navigation.BuildNavMesh(actor.Scene, bounds, options.General.AutoRebuildNavMeshTimeoutMs); + Navigation.BuildNavMesh(bounds, options.General.AutoRebuildNavMeshTimeoutMs); } } diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs index de319ca1d..515939526 100644 --- a/Source/Editor/SceneGraph/Actors/SplineNode.cs +++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs @@ -555,7 +555,7 @@ namespace FlaxEditor.SceneGraph.Actors var options = Editor.Instance.Options.Options.General; if (options.AutoRebuildNavMesh) { - Navigation.BuildNavMesh(collider.Scene, collider.Box, options.AutoRebuildNavMeshTimeoutMs); + Navigation.BuildNavMesh(collider.Box, options.AutoRebuildNavMeshTimeoutMs); } } } diff --git a/Source/Editor/Tools/Terrain/EditTab.cs b/Source/Editor/Tools/Terrain/EditTab.cs index 551a47974..6a6191122 100644 --- a/Source/Editor/Tools/Terrain/EditTab.cs +++ b/Source/Editor/Tools/Terrain/EditTab.cs @@ -192,7 +192,7 @@ namespace FlaxEditor.Tools.Terrain { if (terrain.Scene && terrain.HasStaticFlag(StaticFlags.Navigation)) { - Navigation.BuildNavMesh(terrain.Scene, patchBounds, editorOptions.General.AutoRebuildNavMeshTimeoutMs); + Navigation.BuildNavMesh(patchBounds, editorOptions.General.AutoRebuildNavMeshTimeoutMs); } } } diff --git a/Source/Editor/Tools/Terrain/EditTerrainGizmo.cs b/Source/Editor/Tools/Terrain/EditTerrainGizmo.cs index 54a6d7fa4..5fc0e894f 100644 --- a/Source/Editor/Tools/Terrain/EditTerrainGizmo.cs +++ b/Source/Editor/Tools/Terrain/EditTerrainGizmo.cs @@ -209,7 +209,7 @@ namespace FlaxEditor.Tools.Terrain { if (terrain.Scene && terrain.HasStaticFlag(StaticFlags.Navigation)) { - Navigation.BuildNavMesh(terrain.Scene, patchBounds, editorOptions.General.AutoRebuildNavMeshTimeoutMs); + Navigation.BuildNavMesh(patchBounds, editorOptions.General.AutoRebuildNavMeshTimeoutMs); } } } diff --git a/Source/Editor/Tools/Terrain/Undo/EditTerrainMapAction.cs b/Source/Editor/Tools/Terrain/Undo/EditTerrainMapAction.cs index 87b0c3cc9..afac0948e 100644 --- a/Source/Editor/Tools/Terrain/Undo/EditTerrainMapAction.cs +++ b/Source/Editor/Tools/Terrain/Undo/EditTerrainMapAction.cs @@ -172,7 +172,7 @@ namespace FlaxEditor.Tools.Terrain.Undo if (_navmeshBoundsModifications != null) { foreach (var bounds in _navmeshBoundsModifications) - Navigation.BuildNavMesh(scene, bounds, _dirtyNavMeshTimeoutMs); + Navigation.BuildNavMesh(bounds, _dirtyNavMeshTimeoutMs); } Editor.Instance.Scene.MarkSceneEdited(scene); @@ -217,11 +217,10 @@ namespace FlaxEditor.Tools.Terrain.Undo } // Update navmesh - var scene = Terrain.Scene; if (_navmeshBoundsModifications != null) { foreach (var bounds in _navmeshBoundsModifications) - Navigation.BuildNavMesh(scene, bounds, _dirtyNavMeshTimeoutMs); + Navigation.BuildNavMesh(bounds, _dirtyNavMeshTimeoutMs); } Editor.Instance.Scene.MarkSceneEdited(Terrain.Scene); diff --git a/Source/Editor/Undo/Actions/DeleteActorsAction.cs b/Source/Editor/Undo/Actions/DeleteActorsAction.cs index 75594ecb9..19ffb1e3f 100644 --- a/Source/Editor/Undo/Actions/DeleteActorsAction.cs +++ b/Source/Editor/Undo/Actions/DeleteActorsAction.cs @@ -303,7 +303,7 @@ namespace FlaxEditor.Actions if (_nodeParents[i] is ActorNode node && node.Actor && node.Actor.Scene && node.AffectsNavigationWithChildren) { var bounds = node.Actor.BoxWithChildren; - Navigation.BuildNavMesh(node.Actor.Scene, bounds, options.General.AutoRebuildNavMeshTimeoutMs); + Navigation.BuildNavMesh(bounds, options.General.AutoRebuildNavMeshTimeoutMs); } } } diff --git a/Source/Editor/Undo/Actions/TransformObjectsAction.cs b/Source/Editor/Undo/Actions/TransformObjectsAction.cs index ebed61174..df013e20e 100644 --- a/Source/Editor/Undo/Actions/TransformObjectsAction.cs +++ b/Source/Editor/Undo/Actions/TransformObjectsAction.cs @@ -121,12 +121,12 @@ namespace FlaxEditor // Handle simple case where objects were moved just a little and use one navmesh build request to improve performance if (data.BeforeBounds.Intersects(ref data.AfterBounds)) { - Navigation.BuildNavMesh(data.Scene, BoundingBox.Merge(data.BeforeBounds, data.AfterBounds), options.General.AutoRebuildNavMeshTimeoutMs); + Navigation.BuildNavMesh(BoundingBox.Merge(data.BeforeBounds, data.AfterBounds), options.General.AutoRebuildNavMeshTimeoutMs); } else { - Navigation.BuildNavMesh(data.Scene, data.BeforeBounds, options.General.AutoRebuildNavMeshTimeoutMs); - Navigation.BuildNavMesh(data.Scene, data.AfterBounds, options.General.AutoRebuildNavMeshTimeoutMs); + Navigation.BuildNavMesh(data.BeforeBounds, options.General.AutoRebuildNavMeshTimeoutMs); + Navigation.BuildNavMesh(data.AfterBounds, options.General.AutoRebuildNavMeshTimeoutMs); } } } diff --git a/Source/Engine/Navigation/NavMeshBoundsVolume.cpp b/Source/Engine/Navigation/NavMeshBoundsVolume.cpp index 56351fded..c54f2f072 100644 --- a/Source/Engine/Navigation/NavMeshBoundsVolume.cpp +++ b/Source/Engine/Navigation/NavMeshBoundsVolume.cpp @@ -6,7 +6,7 @@ #if USE_EDITOR #include "Editor/Editor.h" #include "Editor/Managed/ManagedEditor.h" -#include "NavMeshBuilder.h" +#include "Navigation.h" #endif NavMeshBoundsVolume::NavMeshBoundsVolume(const SpawnParams& params) @@ -55,9 +55,30 @@ void NavMeshBoundsVolume::OnBoundsChanged(const BoundingBox& prevBounds) // Auto-rebuild modified navmesh area if (IsDuringPlay() && IsActiveInHierarchy() && !Editor::IsPlayMode && Editor::Managed->CanAutoBuildNavMesh()) { - BoundingBox dirtyBounds; - BoundingBox::Merge(prevBounds, _box, dirtyBounds); - NavMeshBuilder::Build(GetScene(), dirtyBounds, ManagedEditor::ManagedEditorOptions.AutoRebuildNavMeshTimeoutMs); + if (_box.Intersects(prevBounds)) + { + // Bounds were moved a bit so merge into a single request (for performance reasons) + BoundingBox dirtyBounds; + BoundingBox::Merge(prevBounds, _box, dirtyBounds); + Navigation::BuildNavMesh(dirtyBounds, ManagedEditor::ManagedEditorOptions.AutoRebuildNavMeshTimeoutMs); + } + else + { + // Dirty each bounds in separate + Navigation::BuildNavMesh(prevBounds, ManagedEditor::ManagedEditorOptions.AutoRebuildNavMeshTimeoutMs); + Navigation::BuildNavMesh(_box, ManagedEditor::ManagedEditorOptions.AutoRebuildNavMeshTimeoutMs); + } + } +} + +void NavMeshBoundsVolume::OnActiveInTreeChanged() +{ + BoxVolume::OnActiveInTreeChanged(); + + // Auto-rebuild + if (IsDuringPlay() && !Editor::IsPlayMode && Editor::Managed->CanAutoBuildNavMesh()) + { + Navigation::BuildNavMesh(_box, ManagedEditor::ManagedEditorOptions.AutoRebuildNavMeshTimeoutMs); } } diff --git a/Source/Engine/Navigation/NavMeshBoundsVolume.h b/Source/Engine/Navigation/NavMeshBoundsVolume.h index c04bc0483..80df5035a 100644 --- a/Source/Engine/Navigation/NavMeshBoundsVolume.h +++ b/Source/Engine/Navigation/NavMeshBoundsVolume.h @@ -30,6 +30,7 @@ protected: void OnDisable() override; #if USE_EDITOR void OnBoundsChanged(const BoundingBox& prevBounds) override; + void OnActiveInTreeChanged() override; Color GetWiresColor() override; #endif }; diff --git a/Source/Engine/Navigation/NavMeshBuilder.cpp b/Source/Engine/Navigation/NavMeshBuilder.cpp index 91a4b1c20..896cf4217 100644 --- a/Source/Engine/Navigation/NavMeshBuilder.cpp +++ b/Source/Engine/Navigation/NavMeshBuilder.cpp @@ -3,6 +3,7 @@ #if COMPILE_WITH_NAV_MESH_BUILDER #include "NavMeshBuilder.h" +#include "Navigation.h" #include "NavMesh.h" #include "NavigationSettings.h" #include "NavMeshBoundsVolume.h" @@ -706,6 +707,7 @@ struct BuildRequest ScriptingObjectReference Scene; DateTime Time; BoundingBox DirtyBounds; + bool SpecificScene; }; CriticalSection NavBuildQueueLocker; @@ -713,6 +715,7 @@ Array NavBuildQueue; CriticalSection NavBuildTasksLocker; int32 NavBuildTasksMaxCount = 0; +bool NavBuildCheckMissingNavMeshes = false; Array NavBuildTasks; class NavMeshTileBuildTask : public ThreadPoolTask @@ -776,13 +779,13 @@ void CancelNavMeshTileBuildTasks(NavMeshRuntime* runtime) NavBuildTasksLocker.Unlock(); } -void CancelNavMeshTileBuildTasks(NavMeshRuntime* runtime, int32 x, int32 y) +void CancelNavMeshTileBuildTasks(NavMeshRuntime* runtime, int32 x, int32 y, NavMesh* navMesh) { NavBuildTasksLocker.Lock(); for (int32 i = 0; i < NavBuildTasks.Count(); i++) { auto task = NavBuildTasks[i]; - if (task->Runtime == runtime && task->X == x && task->Y == y) + if (task->Runtime == runtime && task->X == x && task->Y == y && task->NavMesh == navMesh) { NavBuildTasksLocker.Unlock(); @@ -838,7 +841,7 @@ void NavMeshBuilder::Init() Level::SceneUnloading.Bind(); } -bool NavMeshBuilder::IsBuildingNavMesh() +bool Navigation::IsBuildingNavMesh() { NavBuildTasksLocker.Lock(); const bool hasAnyTask = NavBuildTasks.HasItems(); @@ -847,7 +850,7 @@ bool NavMeshBuilder::IsBuildingNavMesh() return hasAnyTask; } -float NavMeshBuilder::GetNavMeshBuildingProgress() +float Navigation::GetNavMeshBuildingProgress() { NavBuildTasksLocker.Lock(); float result = 1.0f; @@ -1023,7 +1026,7 @@ void BuildDirtyBounds(Scene* scene, NavMesh* navMesh, const BoundingBox& dirtyBo for (const auto& tile : unusedTiles) { // Wait for any async tasks that are producing this tile - CancelNavMeshTileBuildTasks(runtime, tile.X, tile.Y); + CancelNavMeshTileBuildTasks(runtime, tile.X, tile.Y, navMesh); } runtime->Locker.Lock(); for (const auto& tile : unusedTiles) @@ -1106,31 +1109,6 @@ void BuildDirtyBounds(Scene* scene, const BoundingBox& dirtyBounds, bool rebuild { BuildDirtyBounds(scene, navMesh, dirtyBounds, rebuild); } - - // Remove unused navmeshes - if (settings->AutoRemoveMissingNavMeshes) - { - for (NavMesh* navMesh : scene->Navigation.Meshes) - { - // Skip used navmeshes - if (navMesh->Data.Tiles.HasItems()) - continue; - - // Skip navmeshes during async building - int32 usageCount = 0; - NavBuildTasksLocker.Lock(); - for (int32 i = 0; i < NavBuildTasks.Count(); i++) - { - if (NavBuildTasks.Get()[i]->NavMesh == navMesh) - usageCount++; - } - NavBuildTasksLocker.Unlock(); - if (usageCount != 0) - continue; - - navMesh->DeleteObject(); - } - } } void ClearNavigation(Scene* scene) @@ -1144,6 +1122,40 @@ void ClearNavigation(Scene* scene) } } +void BuildNavigation(BuildRequest& request) +{ + // If scene is not specified then build all loaded scenes + if (!request.Scene) + { + for (Scene* scene : Level::Scenes) + { + request.Scene = scene; + BuildNavigation(request); + } + return; + } + + // Early out if scene is not using navigation + if (request.Scene->Navigation.Volumes.IsEmpty()) + { + ClearNavigation(request.Scene); + return; + } + + // Check if similar request is already in a queue + for (auto& e : NavBuildQueue) + { + if (e.Scene == request.Scene && (e.DirtyBounds == request.DirtyBounds || request.DirtyBounds == BoundingBox::Empty)) + { + e = request; + return; + } + } + + // Enqueue request + NavBuildQueue.Add(request); +} + void NavMeshBuilder::Update() { PROFILE_MEM(NavigationBuilding); @@ -1158,9 +1170,10 @@ void NavMeshBuilder::Update() if (now - req.Time >= 0) { NavBuildQueue.RemoveAt(i--); - const auto scene = req.Scene.Get(); + Scene* scene = req.Scene.Get(); if (!scene) continue; + bool rebuild = req.DirtyBounds == BoundingBox::Empty; // Early out if scene has no bounds volumes to define nav mesh area if (scene->Navigation.Volumes.IsEmpty()) @@ -1170,7 +1183,6 @@ void NavMeshBuilder::Update() } // Check if build a custom dirty bounds or whole scene - bool rebuild = req.DirtyBounds == BoundingBox::Empty; if (rebuild) req.DirtyBounds = scene->Navigation.GetNavigationBounds(); // Compute total navigation area bounds if (didRebuild) @@ -1178,26 +1190,37 @@ void NavMeshBuilder::Update() else didRebuild = true; BuildDirtyBounds(scene, req.DirtyBounds, rebuild); + NavBuildCheckMissingNavMeshes = true; + } + } + + // Remove unused navmeshes (when all active tasks are done) + // TODO: ignore AutoRemoveMissingNavMeshes in game and make it editor-only? + if (NavBuildCheckMissingNavMeshes && NavBuildTasksMaxCount == 0 && NavigationSettings::Get()->AutoRemoveMissingNavMeshes) + { + NavBuildCheckMissingNavMeshes = false; + NavBuildTasksLocker.Lock(); + int32 taskCount = NavBuildTasks.Count(); + NavBuildTasksLocker.Unlock(); + if (taskCount == 0) + { + for (Scene* scene : Level::Scenes) + { + for (NavMesh* navMesh : scene->Navigation.Meshes) + { + if (!navMesh->Data.Tiles.HasItems()) + { + navMesh->DeleteObject(); + } + } + } } } } -void NavMeshBuilder::Build(Scene* scene, float timeoutMs) +void Navigation::BuildNavMesh(Scene* scene, float timeoutMs) { - if (!scene) - { - LOG(Warning, "Could not generate navmesh without scene."); - return; - } - - // Early out if scene is not using navigation - if (scene->Navigation.Volumes.IsEmpty()) - { - ClearNavigation(scene); - return; - } - - PROFILE_CPU_NAMED("NavMeshBuilder"); + PROFILE_CPU(); PROFILE_MEM(NavigationBuilding); ScopeLock lock(NavBuildQueueLocker); @@ -1205,36 +1228,15 @@ void NavMeshBuilder::Build(Scene* scene, float timeoutMs) req.Scene = scene; req.Time = DateTime::NowUTC() + TimeSpan::FromMilliseconds(timeoutMs); req.DirtyBounds = BoundingBox::Empty; - - for (int32 i = 0; i < NavBuildQueue.Count(); i++) - { - auto& e = NavBuildQueue.Get()[i]; - if (e.Scene == scene && e.DirtyBounds == req.DirtyBounds) - { - e = req; - return; - } - } - - NavBuildQueue.Add(req); + req.SpecificScene = scene != nullptr; + BuildNavigation(req); } -void NavMeshBuilder::Build(Scene* scene, const BoundingBox& dirtyBounds, float timeoutMs) +void Navigation::BuildNavMesh(const BoundingBox& dirtyBounds, Scene* scene, float timeoutMs) { - if (!scene) - { - LOG(Warning, "Could not generate navmesh without scene."); - return; - } - - // Early out if scene is not using navigation - if (scene->Navigation.Volumes.IsEmpty()) - { - ClearNavigation(scene); - return; - } - - PROFILE_CPU_NAMED("NavMeshBuilder"); + if (dirtyBounds.GetVolume() <= ZeroTolerance) + return; // Skip updating empty bounds + PROFILE_CPU(); PROFILE_MEM(NavigationBuilding); ScopeLock lock(NavBuildQueueLocker); @@ -1242,8 +1244,8 @@ void NavMeshBuilder::Build(Scene* scene, const BoundingBox& dirtyBounds, float t req.Scene = scene; req.Time = DateTime::NowUTC() + TimeSpan::FromMilliseconds(timeoutMs); req.DirtyBounds = dirtyBounds; - - NavBuildQueue.Add(req); + req.SpecificScene = scene != nullptr; + BuildNavigation(req); } #endif diff --git a/Source/Engine/Navigation/NavMeshBuilder.h b/Source/Engine/Navigation/NavMeshBuilder.h index a3477db27..355bac7de 100644 --- a/Source/Engine/Navigation/NavMeshBuilder.h +++ b/Source/Engine/Navigation/NavMeshBuilder.h @@ -15,11 +15,7 @@ class FLAXENGINE_API NavMeshBuilder { public: static void Init(); - static bool IsBuildingNavMesh(); - static float GetNavMeshBuildingProgress(); static void Update(); - static void Build(Scene* scene, float timeoutMs); - static void Build(Scene* scene, const BoundingBox& dirtyBounds, float timeoutMs); }; #endif diff --git a/Source/Engine/Navigation/NavModifierVolume.cpp b/Source/Engine/Navigation/NavModifierVolume.cpp index 9e1295f70..aa71e7aa1 100644 --- a/Source/Engine/Navigation/NavModifierVolume.cpp +++ b/Source/Engine/Navigation/NavModifierVolume.cpp @@ -2,7 +2,7 @@ #include "NavModifierVolume.h" #include "NavigationSettings.h" -#include "NavMeshBuilder.h" +#include "Navigation.h" #include "Engine/Level/Scene/Scene.h" #include "Engine/Serialization/Serialization.h" #if USE_EDITOR @@ -83,7 +83,7 @@ void NavModifierVolume::OnBoundsChanged(const BoundingBox& prevBounds) #else const float timeoutMs = 0.0f; #endif - NavMeshBuilder::Build(GetScene(), dirtyBounds, timeoutMs); + Navigation::BuildNavMesh(dirtyBounds, GetScene(), timeoutMs); } #endif } diff --git a/Source/Engine/Navigation/Navigation.cpp b/Source/Engine/Navigation/Navigation.cpp index 06413ea7f..908819765 100644 --- a/Source/Engine/Navigation/Navigation.cpp +++ b/Source/Engine/Navigation/Navigation.cpp @@ -382,30 +382,6 @@ bool Navigation::RayCast(const Vector3& startPosition, const Vector3& endPositio return NavMeshes.First()->RayCast(startPosition, endPosition, hitInfo); } -#if COMPILE_WITH_NAV_MESH_BUILDER - -bool Navigation::IsBuildingNavMesh() -{ - return NavMeshBuilder::IsBuildingNavMesh(); -} - -float Navigation::GetNavMeshBuildingProgress() -{ - return NavMeshBuilder::GetNavMeshBuildingProgress(); -} - -void Navigation::BuildNavMesh(Scene* scene, float timeoutMs) -{ - NavMeshBuilder::Build(scene, timeoutMs); -} - -void Navigation::BuildNavMesh(Scene* scene, const BoundingBox& dirtyBounds, float timeoutMs) -{ - NavMeshBuilder::Build(scene, dirtyBounds, timeoutMs); -} - -#endif - #if COMPILE_WITH_DEBUG_DRAW void Navigation::DrawNavMesh() diff --git a/Source/Engine/Navigation/Navigation.h b/Source/Engine/Navigation/Navigation.h index 434817ca1..80c8eb84a 100644 --- a/Source/Engine/Navigation/Navigation.h +++ b/Source/Engine/Navigation/Navigation.h @@ -84,9 +84,7 @@ public: /// True if ray hits a matching object, otherwise false. API_FUNCTION() static bool RayCast(const Vector3& startPosition, const Vector3& endPosition, API_PARAM(Out) NavMeshHit& hitInfo); -public: #if COMPILE_WITH_NAV_MESH_BUILDER - /// /// Returns true if navigation system is during navmesh building (any request is valid or async task active). /// @@ -100,32 +98,49 @@ public: /// /// Builds the Nav Mesh for the given scene (discards all its tiles). /// - /// - /// Requests are enqueued till the next game scripts update. Actual navmesh building in done via Thread Pool tasks in a background to prevent game thread stalls. - /// - /// The scene. + /// Requests are enqueued till the next game scripts update. Actual navmesh building in done via Thread Pool tasks in a background to prevent game thread stalls. + /// The scene. Pass null to build navmesh for all loaded scenes. /// The timeout to wait before building Nav Mesh (in milliseconds). - API_FUNCTION() static void BuildNavMesh(Scene* scene, float timeoutMs = 50); + API_FUNCTION() static void BuildNavMesh(Scene* scene = nullptr, float timeoutMs = 50); /// /// Builds the Nav Mesh for the given scene (builds only the tiles overlapping the given bounding box). /// - /// - /// Requests are enqueued till the next game scripts update. Actual navmesh building in done via Thread Pool tasks in a background to prevent game thread stalls. - /// + /// Requests are enqueued till the next game scripts update. Actual navmesh building in done via Thread Pool tasks in a background to prevent game thread stalls. + /// The bounds in world-space to build overlapping tiles. + /// The scene. Pass null to build navmesh for all loaded scenes that intersect with a given bounds. + /// The timeout to wait before building Nav Mesh (in milliseconds). + API_FUNCTION() static void BuildNavMesh(const BoundingBox& dirtyBounds, Scene* scene = nullptr, float timeoutMs = 50); + + /// + /// Builds the Nav Mesh for all the loaded scenes (builds only the tiles overlapping the given bounding box). + /// + /// Requests are enqueued till the next game scripts update. Actual navmesh building in done via Thread Pool tasks in a background to prevent game thread stalls. + /// The bounds in world-space to build overlapping tiles. + /// The timeout to wait before building Nav Mesh (in milliseconds). + API_FUNCTION() static void BuildNavMesh(const BoundingBox& dirtyBounds, float timeoutMs = 50) + { + BuildNavMesh(dirtyBounds, nullptr, timeoutMs); + } + + /// + /// Builds the Nav Mesh for the given scene (builds only the tiles overlapping the given bounding box). + /// [Deprecated in v1.12] + /// + /// Requests are enqueued till the next game scripts update. Actual navmesh building in done via Thread Pool tasks in a background to prevent game thread stalls. /// The scene. /// The bounds in world-space to build overlapping tiles. /// The timeout to wait before building Nav Mesh (in milliseconds). - API_FUNCTION() static void BuildNavMesh(Scene* scene, const BoundingBox& dirtyBounds, float timeoutMs = 50); - + API_FUNCTION() DEPRECATED("Use BuildNavMesh with reordered arguments instead") static void BuildNavMesh(Scene* scene, const BoundingBox& dirtyBounds, float timeoutMs = 50) + { + BuildNavMesh(dirtyBounds, scene, timeoutMs); + } #endif #if COMPILE_WITH_DEBUG_DRAW - /// /// Draws the navigation for all the scenes (uses DebugDraw interface). /// static void DrawNavMesh(); - #endif };