diff --git a/Source/Engine/Level/Scene/Scene.cpp b/Source/Engine/Level/Scene/Scene.cpp index 0a2bdd387..5830cb95f 100644 --- a/Source/Engine/Level/Scene/Scene.cpp +++ b/Source/Engine/Level/Scene/Scene.cpp @@ -30,6 +30,12 @@ bool SceneAsset::IsInternalType() const return true; } +void SceneNavigation::Clear() +{ + Volumes.Clear(); + Actors.Clear(); +} + BoundingBox SceneNavigation::GetNavigationBounds() { if (Volumes.IsEmpty()) @@ -372,6 +378,7 @@ void Scene::EndPlay() // Improve scene cleanup performance by removing all data from scene rendering and ticking containers Ticking.Clear(); Rendering.Clear(); + Navigation.Clear(); // Base Actor::EndPlay(); diff --git a/Source/Engine/Level/Scene/SceneNavigation.h b/Source/Engine/Level/Scene/SceneNavigation.h index f8b5eef50..66b7ec2d0 100644 --- a/Source/Engine/Level/Scene/SceneNavigation.h +++ b/Source/Engine/Level/Scene/SceneNavigation.h @@ -23,6 +23,17 @@ public: /// Array Meshes; + /// + /// The list of registered navigation-relevant actors (on the scene). + /// + Array Actors; + +public: + /// + /// Clears this instance data. + /// + void Clear(); + /// /// Gets the total navigation volumes bounds. /// diff --git a/Source/Engine/Navigation/NavLink.cpp b/Source/Engine/Navigation/NavLink.cpp index ce85d63b6..c766cf867 100644 --- a/Source/Engine/Navigation/NavLink.cpp +++ b/Source/Engine/Navigation/NavLink.cpp @@ -1,6 +1,7 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. #include "NavLink.h" +#include "Engine/Level/Scene/Scene.h" #include "Engine/Serialization/Serialization.h" NavLink::NavLink(const SpawnParams& params) @@ -62,6 +63,20 @@ void NavLink::Deserialize(DeserializeStream& stream, ISerializeModifier* modifie DESERIALIZE(BiDirectional); } +void NavLink::OnEnable() +{ + GetScene()->Navigation.Actors.Add(this); + + Actor::OnEnable(); +} + +void NavLink::OnDisable() +{ + Actor::OnDisable(); + + GetScene()->Navigation.Actors.Remove(this); +} + void NavLink::OnTransformChanged() { // Base diff --git a/Source/Engine/Navigation/NavLink.h b/Source/Engine/Navigation/NavLink.h index c372e74b9..059d25f9f 100644 --- a/Source/Engine/Navigation/NavLink.h +++ b/Source/Engine/Navigation/NavLink.h @@ -46,6 +46,8 @@ public: #endif void Serialize(SerializeStream& stream, const void* otherObj) override; void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; + void OnEnable() override; + void OnDisable() override; protected: // [Actor] diff --git a/Source/Engine/Navigation/NavMeshBuilder.cpp b/Source/Engine/Navigation/NavMeshBuilder.cpp index 5bc5141fa..f81932837 100644 --- a/Source/Engine/Navigation/NavMeshBuilder.cpp +++ b/Source/Engine/Navigation/NavMeshBuilder.cpp @@ -23,7 +23,6 @@ #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Level/Scene/Scene.h" #include "Engine/Level/Level.h" -#include "Engine/Level/SceneQuery.h" #include #include #include @@ -68,7 +67,7 @@ struct Modifier NavAreaProperties* NavArea; }; -struct NavigationSceneRasterization +struct NavSceneRasterizer { NavMesh* NavMesh; BoundingBox TileBoundsNavMesh; @@ -83,7 +82,7 @@ struct NavigationSceneRasterization Array* Modifiers; const bool IsWorldToNavMeshIdentity; - NavigationSceneRasterization(::NavMesh* navMesh, const BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh, rcContext* context, rcConfig* config, rcHeightfield* heightfield, Array* offMeshLinks, Array* modifiers) + NavSceneRasterizer(::NavMesh* navMesh, const BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh, rcContext* context, rcConfig* config, rcHeightfield* heightfield, Array* offMeshLinks, Array* modifiers) : TileBoundsNavMesh(tileBoundsNavMesh) , WorldToNavMesh(worldToNavMesh) , IsWorldToNavMeshIdentity(worldToNavMesh.IsIdentity()) @@ -103,35 +102,20 @@ struct NavigationSceneRasterization auto& ib = IndexBuffer; if (vb.IsEmpty() || ib.IsEmpty()) return; + PROFILE_CPU(); // Rasterize triangles + const Float3* vbData = vb.Get(); + const int32* ibData = ib.Get(); + Float3 v0, v1, v2; if (IsWorldToNavMeshIdentity) { // Faster path for (int32 i0 = 0; i0 < ib.Count();) { - auto v0 = vb[ib[i0++]]; - auto v1 = vb[ib[i0++]]; - auto v2 = vb[ib[i0++]]; -#if NAV_MESH_BUILD_DEBUG_DRAW_GEOMETRY - DEBUG_DRAW_TRIANGLE(v0, v1, v2, Color::Orange.AlphaMultiplied(0.3f), 1.0f, true); -#endif - - auto n = Float3::Cross(v0 - v1, v0 - v2); - n.Normalize(); - const char area = n.Y > WalkableThreshold ? RC_WALKABLE_AREA : 0; - rcRasterizeTriangle(Context, &v0.X, &v1.X, &v2.X, area, *Heightfield); - } - } - else - { - // Transform vertices from world space into the navmesh space - const Matrix worldToNavMesh = WorldToNavMesh; - for (int32 i0 = 0; i0 < ib.Count();) - { - auto v0 = Float3::Transform(vb[ib[i0++]], worldToNavMesh); - auto v1 = Float3::Transform(vb[ib[i0++]], worldToNavMesh); - auto v2 = Float3::Transform(vb[ib[i0++]], worldToNavMesh); + v0 = vbData[ibData[i0++]]; + v1 = vbData[ibData[i0++]]; + v2 = vbData[ibData[i0++]]; #if NAV_MESH_BUILD_DEBUG_DRAW_GEOMETRY DEBUG_DRAW_TRIANGLE(v0, v1, v2, Color::Orange.AlphaMultiplied(0.3f), 1.0f, true); #endif @@ -142,6 +126,29 @@ struct NavigationSceneRasterization rcRasterizeTriangle(Context, &v0.X, &v1.X, &v2.X, area, *Heightfield); } } + else + { + // Transform vertices from world space into the navmesh space + const Matrix worldToNavMesh = WorldToNavMesh; + for (int32 i0 = 0; i0 < ib.Count();) + { + Float3::Transform(vbData[ibData[i0++]], worldToNavMesh, v0); + Float3::Transform(vbData[ibData[i0++]], worldToNavMesh, v1); + Float3::Transform(vbData[ibData[i0++]], worldToNavMesh, v2); +#if NAV_MESH_BUILD_DEBUG_DRAW_GEOMETRY + DEBUG_DRAW_TRIANGLE(v0, v1, v2, Color::Orange.AlphaMultiplied(0.3f), 1.0f, true); +#endif + + auto n = Float3::Cross(v0 - v1, v0 - v2); + n.Normalize(); + const char area = n.Y > WalkableThreshold ? RC_WALKABLE_AREA : RC_NULL_AREA; + rcRasterizeTriangle(Context, &v0.X, &v1.X, &v2.X, area, *Heightfield); + } + } + + // Clear after use + vb.Clear(); + ib.Clear(); } static void TriangulateBox(Array& vb, Array& ib, const OrientedBoundingBox& box) @@ -215,88 +222,67 @@ struct NavigationSceneRasterization } } - static bool Walk(Actor* actor, NavigationSceneRasterization& e) + void Rasterize(Actor* actor) { - // Early out if object is not intersecting with the tile bounds or is not using navigation - if (!actor->GetIsActive() || !(actor->GetStaticFlags() & StaticFlags::Navigation)) - return true; - BoundingBox actorBoxNavMesh; - BoundingBox::Transform(actor->GetBox(), e.WorldToNavMesh, actorBoxNavMesh); - if (!actorBoxNavMesh.Intersects(e.TileBoundsNavMesh)) - return true; - - // Prepare buffers (for triangles) - auto& vb = e.VertexBuffer; - auto& ib = e.IndexBuffer; - vb.Clear(); - ib.Clear(); - - // Extract data from the actor if (const auto* boxCollider = dynamic_cast(actor)) { if (boxCollider->GetIsTrigger()) - return true; + return; PROFILE_CPU_NAMED("BoxCollider"); const OrientedBoundingBox box = boxCollider->GetOrientedBox(); - TriangulateBox(vb, ib, box); - - e.RasterizeTriangles(); + TriangulateBox(VertexBuffer, IndexBuffer, box); + RasterizeTriangles(); } else if (const auto* sphereCollider = dynamic_cast(actor)) { if (sphereCollider->GetIsTrigger()) - return true; + return; PROFILE_CPU_NAMED("SphereCollider"); const BoundingSphere sphere = sphereCollider->GetSphere(); - TriangulateSphere(vb, ib, sphere); - - e.RasterizeTriangles(); + TriangulateSphere(VertexBuffer, IndexBuffer, sphere); + RasterizeTriangles(); } else if (const auto* capsuleCollider = dynamic_cast(actor)) { if (capsuleCollider->GetIsTrigger()) - return true; + return; PROFILE_CPU_NAMED("CapsuleCollider"); const BoundingBox box = capsuleCollider->GetBox(); - TriangulateBox(vb, ib, box); - - e.RasterizeTriangles(); + TriangulateBox(VertexBuffer, IndexBuffer, box); + RasterizeTriangles(); } else if (const auto* meshCollider = dynamic_cast(actor)) { if (meshCollider->GetIsTrigger()) - return true; + return; PROFILE_CPU_NAMED("MeshCollider"); auto collisionData = meshCollider->CollisionData.Get(); if (!collisionData || collisionData->WaitForLoaded()) - return true; - - collisionData->ExtractGeometry(vb, ib); + return; + collisionData->ExtractGeometry(VertexBuffer, IndexBuffer); Matrix meshColliderToWorld; meshCollider->GetLocalToWorldMatrix(meshColliderToWorld); - for (auto& v : vb) + for (auto& v : VertexBuffer) Float3::Transform(v, meshColliderToWorld, v); - - e.RasterizeTriangles(); + RasterizeTriangles(); } else if (const auto* splineCollider = dynamic_cast(actor)) { if (splineCollider->GetIsTrigger()) - return true; + return; PROFILE_CPU_NAMED("SplineCollider"); auto collisionData = splineCollider->CollisionData.Get(); if (!collisionData || collisionData->WaitForLoaded()) - return true; + return; - splineCollider->ExtractGeometry(vb, ib); - - e.RasterizeTriangles(); + splineCollider->ExtractGeometry(VertexBuffer, IndexBuffer); + RasterizeTriangles(); } else if (const auto* terrain = dynamic_cast(actor)) { @@ -306,13 +292,13 @@ struct NavigationSceneRasterization { const auto patch = terrain->GetPatch(patchIndex); BoundingBox patchBoundsNavMesh; - BoundingBox::Transform(patch->GetBounds(), e.WorldToNavMesh, patchBoundsNavMesh); - if (!patchBoundsNavMesh.Intersects(e.TileBoundsNavMesh)) + BoundingBox::Transform(patch->GetBounds(), WorldToNavMesh, patchBoundsNavMesh); + if (!patchBoundsNavMesh.Intersects(TileBoundsNavMesh)) continue; - patch->ExtractCollisionGeometry(vb, ib); - - e.RasterizeTriangles(); + // TODO: get collision only from tile area + patch->ExtractCollisionGeometry(VertexBuffer, IndexBuffer); + RasterizeTriangles(); } } else if (const auto* navLink = dynamic_cast(actor)) @@ -321,44 +307,33 @@ struct NavigationSceneRasterization OffMeshLink link; link.Start = navLink->GetTransform().LocalToWorld(navLink->Start); - Float3::Transform(link.Start, e.WorldToNavMesh, link.Start); + Float3::Transform(link.Start, WorldToNavMesh, link.Start); link.End = navLink->GetTransform().LocalToWorld(navLink->End); - Float3::Transform(link.End, e.WorldToNavMesh, link.End); + Float3::Transform(link.End, WorldToNavMesh, link.End); link.Radius = navLink->Radius; link.BiDir = navLink->BiDirectional; link.Id = GetHash(navLink->GetID()); - e.OffMeshLinks->Add(link); + OffMeshLinks->Add(link); } else if (const auto* navModifierVolume = dynamic_cast(actor)) { - if (navModifierVolume->AgentsMask.IsNavMeshSupported(e.NavMesh->Properties)) + if (navModifierVolume->AgentsMask.IsNavMeshSupported(NavMesh->Properties)) { PROFILE_CPU_NAMED("NavModifierVolume"); Modifier modifier; OrientedBoundingBox bounds = navModifierVolume->GetOrientedBox(); - bounds.Transform(e.WorldToNavMesh); + bounds.Transform(WorldToNavMesh); bounds.GetBoundingBox(modifier.Bounds); modifier.NavArea = navModifierVolume->GetNavArea(); - e.Modifiers->Add(modifier); + Modifiers->Add(modifier); } } - - return true; } }; -void RasterizeGeometry(NavMesh* navMesh, const BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh, rcContext* context, rcConfig* config, rcHeightfield* heightfield, Array* offMeshLinks, Array* modifiers) -{ - PROFILE_CPU_NAMED("RasterizeGeometry"); - - NavigationSceneRasterization rasterization(navMesh, tileBoundsNavMesh, worldToNavMesh, context, config, heightfield, offMeshLinks, modifiers); - Function treeWalkFunction(NavigationSceneRasterization::Walk); - SceneQuery::TreeExecute(treeWalkFunction, rasterization); -} - // Builds navmesh tile bounds and check if there are any valid navmesh volumes at that tile location // Returns true if tile is intersecting with any navmesh bounds volume actor - which means tile is in use bool GetNavMeshTileBounds(Scene* scene, NavMesh* navMesh, int32 x, int32 y, float tileSize, BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh) @@ -455,11 +430,44 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B Array offMeshLinks; Array modifiers; - RasterizeGeometry(navMesh, tileBoundsNavMesh, worldToNavMesh, &context, &config, heightfield, &offMeshLinks, &modifiers); + { + PROFILE_CPU_NAMED("RasterizeGeometry"); + NavSceneRasterizer rasterizer(navMesh, tileBoundsNavMesh, worldToNavMesh, &context, &config, heightfield, &offMeshLinks, &modifiers); - rcFilterLowHangingWalkableObstacles(&context, config.walkableClimb, *heightfield); - rcFilterLedgeSpans(&context, config.walkableHeight, config.walkableClimb, *heightfield); - rcFilterWalkableLowHeightSpans(&context, config.walkableHeight, *heightfield); + // Collect actors to rasterize + Array actors; + { + PROFILE_CPU_NAMED("CollectActors"); + ScopeLock lock(Level::ScenesLock); + for (Scene* scene : Level::Scenes) + { + for (Actor* actor : scene->Navigation.Actors) + { + BoundingBox actorBoxNavMesh; + BoundingBox::Transform(actor->GetBox(), rasterizer.WorldToNavMesh, actorBoxNavMesh); + if (actorBoxNavMesh.Intersects(rasterizer.TileBoundsNavMesh) && + actor->IsActiveInHierarchy() && + EnumHasAllFlags(actor->GetStaticFlags(), StaticFlags::Navigation)) + { + actors.Add(actor); + } + } + } + } + + // Rasterize actors + for (Actor* actor : actors) + { + rasterizer.Rasterize(actor); + } + } + + { + PROFILE_CPU_NAMED("FilterHeightfield"); + rcFilterLowHangingWalkableObstacles(&context, config.walkableClimb, *heightfield); + rcFilterLedgeSpans(&context, config.walkableHeight, config.walkableClimb, *heightfield); + rcFilterWalkableLowHeightSpans(&context, config.walkableHeight, *heightfield); + } rcCompactHeightfield* compactHeightfield = rcAllocCompactHeightfield(); if (!compactHeightfield) @@ -467,39 +475,51 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B LOG(Warning, "Could not generate navmesh: Out of memory compact heightfield."); return true; } - if (!rcBuildCompactHeightfield(&context, config.walkableHeight, config.walkableClimb, *heightfield, *compactHeightfield)) { - LOG(Warning, "Could not generate navmesh: Could not build compact data."); - return true; + PROFILE_CPU_NAMED("CompactHeightfield"); + if (!rcBuildCompactHeightfield(&context, config.walkableHeight, config.walkableClimb, *heightfield, *compactHeightfield)) + { + LOG(Warning, "Could not generate navmesh: Could not build compact data."); + return true; + } } - rcFreeHeightField(heightfield); - - if (!rcErodeWalkableArea(&context, config.walkableRadius, *compactHeightfield)) { - LOG(Warning, "Could not generate navmesh: Could not erode."); - return true; + PROFILE_CPU_NAMED("ErodeWalkableArea"); + if (!rcErodeWalkableArea(&context, config.walkableRadius, *compactHeightfield)) + { + LOG(Warning, "Could not generate navmesh: Could not erode."); + return true; + } } // Mark areas - for (auto& modifier : modifiers) { - const unsigned char areaId = modifier.NavArea ? modifier.NavArea->Id : RC_NULL_AREA; - Float3 bMin = modifier.Bounds.Minimum; - Float3 bMax = modifier.Bounds.Maximum; - rcMarkBoxArea(&context, &bMin.X, &bMax.X, areaId, *compactHeightfield); + PROFILE_CPU_NAMED("MarkModifiers"); + for (auto& modifier : modifiers) + { + const unsigned char areaId = modifier.NavArea ? modifier.NavArea->Id : RC_NULL_AREA; + Float3 bMin = modifier.Bounds.Minimum; + Float3 bMax = modifier.Bounds.Maximum; + rcMarkBoxArea(&context, &bMin.X, &bMax.X, areaId, *compactHeightfield); + } } - if (!rcBuildDistanceField(&context, *compactHeightfield)) { - LOG(Warning, "Could not generate navmesh: Could not build distance field."); - return true; + PROFILE_CPU_NAMED("BuildDistanceField"); + if (!rcBuildDistanceField(&context, *compactHeightfield)) + { + LOG(Warning, "Could not generate navmesh: Could not build distance field."); + return true; + } } - - if (!rcBuildRegions(&context, *compactHeightfield, config.borderSize, config.minRegionArea, config.mergeRegionArea)) { - LOG(Warning, "Could not generate navmesh: Could not build regions."); - return true; + PROFILE_CPU_NAMED("BuildRegions"); + if (!rcBuildRegions(&context, *compactHeightfield, config.borderSize, config.minRegionArea, config.mergeRegionArea)) + { + LOG(Warning, "Could not generate navmesh: Could not build regions."); + return true; + } } rcContourSet* contourSet = rcAllocContourSet(); @@ -508,10 +528,13 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B LOG(Warning, "Could not generate navmesh: Out of memory for contour set."); return true; } - if (!rcBuildContours(&context, *compactHeightfield, config.maxSimplificationError, config.maxEdgeLen, *contourSet)) { - LOG(Warning, "Could not generate navmesh: Could not create contours."); - return true; + PROFILE_CPU_NAMED("BuildContours"); + if (!rcBuildContours(&context, *compactHeightfield, config.maxSimplificationError, config.maxEdgeLen, *contourSet)) + { + LOG(Warning, "Could not generate navmesh: Could not create contours."); + return true; + } } rcPolyMesh* polyMesh = rcAllocPolyMesh(); @@ -520,10 +543,13 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B LOG(Warning, "Could not generate navmesh: Out of memory for poly mesh."); return true; } - if (!rcBuildPolyMesh(&context, *contourSet, config.maxVertsPerPoly, *polyMesh)) { - LOG(Warning, "Could not generate navmesh: Could not triangulate contours."); - return true; + PROFILE_CPU_NAMED("BuildPolyMesh"); + if (!rcBuildPolyMesh(&context, *contourSet, config.maxVertsPerPoly, *polyMesh)) + { + LOG(Warning, "Could not generate navmesh: Could not triangulate contours."); + return true; + } } rcPolyMeshDetail* detailMesh = rcAllocPolyMeshDetail(); @@ -532,20 +558,20 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B LOG(Warning, "Could not generate navmesh: Out of memory for detail mesh."); return true; } - if (!rcBuildPolyMeshDetail(&context, *polyMesh, *compactHeightfield, config.detailSampleDist, config.detailSampleMaxError, *detailMesh)) { - LOG(Warning, "Could not generate navmesh: Could not build detail mesh."); - return true; + PROFILE_CPU_NAMED("BuildPolyMeshDetail"); + if (!rcBuildPolyMeshDetail(&context, *polyMesh, *compactHeightfield, config.detailSampleDist, config.detailSampleMaxError, *detailMesh)) + { + LOG(Warning, "Could not generate navmesh: Could not build detail mesh."); + return true; + } } rcFreeCompactHeightfield(compactHeightfield); rcFreeContourSet(contourSet); for (int i = 0; i < polyMesh->npolys; i++) - { polyMesh->flags[i] = polyMesh->areas[i] != RC_NULL_AREA ? 1 : 0; - } - if (polyMesh->nverts == 0) { // Empty tile @@ -623,15 +649,18 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B // Generate navmesh tile data unsigned char* navData = nullptr; int navDataSize = 0; - if (!dtCreateNavMeshData(¶ms, &navData, &navDataSize)) { - LOG(Warning, "Could not build Detour navmesh."); - return true; + PROFILE_CPU_NAMED("CreateNavMeshData"); + if (!dtCreateNavMeshData(¶ms, &navData, &navDataSize)) + { + LOG(Warning, "Could not build Detour navmesh."); + return true; + } } ASSERT_LOW_LAYER(navDataSize > 4 && *(uint32*)navData == DT_NAVMESH_MAGIC); // Sanity check for Detour header { - PROFILE_CPU_NAMED("Navigation.CreateTile"); + PROFILE_CPU_NAMED("CreateTiles"); ScopeLock lock(runtime->Locker); @@ -729,17 +758,13 @@ public: bool Run() override { PROFILE_CPU_NAMED("BuildNavMeshTile"); - const auto navMesh = NavMesh.Get(); if (!navMesh) - { return false; - } if (GenerateTile(NavMesh, Runtime, X, Y, TileBoundsNavMesh, WorldToNavMesh, TileSize, Config)) { LOG(Warning, "Failed to generate navmesh tile at {0}x{1}.", X, Y); } - return false; } @@ -776,7 +801,7 @@ void OnSceneUnloading(Scene* scene, const Guid& sceneId) { NavBuildTasksLocker.Unlock(); - // Cancel task but without locking queue from this thread to prevent dead-locks + // Cancel task but without locking queue from this thread to prevent deadlocks task->Cancel(); NavBuildTasksLocker.Lock(); @@ -815,7 +840,7 @@ float NavMeshBuilder::GetNavMeshBuildingProgress() return result; } -void BuildTileAsync(NavMesh* navMesh, int32 x, int32 y, rcConfig& config, const BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh, float tileSize) +void BuildTileAsync(NavMesh* navMesh, const int32 x, const int32 y, const rcConfig& config, const BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh, float tileSize) { NavMeshRuntime* runtime = navMesh->GetRuntime(); NavBuildTasksLocker.Lock(); @@ -1108,7 +1133,7 @@ void NavMeshBuilder::Build(Scene* scene, const BoundingBox& dirtyBounds, float t if (!scene) { LOG(Warning, "Could not generate navmesh without scene."); - return; + return; } // Early out if scene is not using navigation diff --git a/Source/Engine/Navigation/NavModifierVolume.cpp b/Source/Engine/Navigation/NavModifierVolume.cpp index 9644b4e75..f4e6f2702 100644 --- a/Source/Engine/Navigation/NavModifierVolume.cpp +++ b/Source/Engine/Navigation/NavModifierVolume.cpp @@ -47,6 +47,20 @@ void NavModifierVolume::Deserialize(DeserializeStream& stream, ISerializeModifie DESERIALIZE(AreaName); } +void NavModifierVolume::OnEnable() +{ + GetScene()->Navigation.Actors.Add(this); + + BoxVolume::OnEnable(); +} + +void NavModifierVolume::OnDisable() +{ + BoxVolume::OnDisable(); + + GetScene()->Navigation.Actors.Remove(this); +} + void NavModifierVolume::OnBoundsChanged(const BoundingBox& prevBounds) { #if COMPILE_WITH_NAV_MESH_BUILDER diff --git a/Source/Engine/Navigation/NavModifierVolume.h b/Source/Engine/Navigation/NavModifierVolume.h index f394afa7c..b5b2163a5 100644 --- a/Source/Engine/Navigation/NavModifierVolume.h +++ b/Source/Engine/Navigation/NavModifierVolume.h @@ -34,6 +34,8 @@ public: // [BoxVolume] void Serialize(SerializeStream& stream, const void* otherObj) override; void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; + void OnEnable() override; + void OnDisable() override; protected: // [BoxVolume] diff --git a/Source/Engine/Physics/Colliders/Collider.cpp b/Source/Engine/Physics/Colliders/Collider.cpp index d8541a222..6175c9058 100644 --- a/Source/Engine/Physics/Colliders/Collider.cpp +++ b/Source/Engine/Physics/Colliders/Collider.cpp @@ -2,6 +2,7 @@ #include "Collider.h" #include "Engine/Core/Log.h" +#include "Engine/Level/Scene/Scene.h" #if USE_EDITOR #include "Engine/Level/Scene/SceneRendering.h" #endif @@ -35,6 +36,13 @@ void Collider::SetIsTrigger(bool value) _isTrigger = value; if (_shape) PhysicsBackend::SetShapeState(_shape, IsActiveInHierarchy(), _isTrigger && CanBeTrigger()); + if (EnumHasAnyFlags(_staticFlags, StaticFlags::Navigation) && _isEnabled) + { + if (_isTrigger) + GetScene()->Navigation.Actors.Remove(this); + else + GetScene()->Navigation.Actors.Add(this); + } } void Collider::SetCenter(const Vector3& value) @@ -43,13 +51,9 @@ void Collider::SetCenter(const Vector3& value) return; _center = value; if (_staticActor) - { PhysicsBackend::SetShapeLocalPose(_shape, _center, Quaternion::Identity); - } else if (const RigidBody* rigidBody = GetAttachedRigidBody()) - { PhysicsBackend::SetShapeLocalPose(_shape, (_localTransform.Translation + _localTransform.Orientation * _center) * rigidBody->GetScale(), _localTransform.Orientation); - } UpdateBounds(); } @@ -134,25 +138,27 @@ RigidBody* Collider::GetAttachedRigidBody() const return nullptr; } -#if USE_EDITOR - void Collider::OnEnable() { + if (EnumHasAnyFlags(_staticFlags, StaticFlags::Navigation) && !_isTrigger) + GetScene()->Navigation.Actors.Add(this); +#if USE_EDITOR GetSceneRendering()->AddPhysicsDebug(this); +#endif - // Base - Actor::OnEnable(); + PhysicsColliderActor::OnEnable(); } void Collider::OnDisable() { - // Base - Actor::OnDisable(); + PhysicsColliderActor::OnDisable(); + if (EnumHasAnyFlags(_staticFlags, StaticFlags::Navigation) && !_isTrigger) + GetScene()->Navigation.Actors.Remove(this); +#if USE_EDITOR GetSceneRendering()->RemovePhysicsDebug(this); -} - #endif +} void Collider::Attach(RigidBody* rigidBody) { @@ -432,6 +438,19 @@ void Collider::OnLayerChanged() UpdateLayerBits(); } +void Collider::OnStaticFlagsChanged() +{ + PhysicsColliderActor::OnStaticFlagsChanged(); + + if (!_isTrigger && _isEnabled) + { + if (EnumHasAnyFlags(_staticFlags, StaticFlags::Navigation)) + GetScene()->Navigation.Actors.AddUnique(this); + else + GetScene()->Navigation.Actors.Remove(this); + } +} + void Collider::OnPhysicsSceneChanged(PhysicsScene* previous) { PhysicsColliderActor::OnPhysicsSceneChanged(previous); diff --git a/Source/Engine/Physics/Colliders/Collider.h b/Source/Engine/Physics/Colliders/Collider.h index 05f2a6eb2..a1660d531 100644 --- a/Source/Engine/Physics/Colliders/Collider.h +++ b/Source/Engine/Physics/Colliders/Collider.h @@ -171,15 +171,14 @@ public: protected: // [PhysicsColliderActor] -#if USE_EDITOR void OnEnable() override; void OnDisable() override; -#endif void BeginPlay(SceneBeginData* data) override; void EndPlay() override; void OnActiveInTreeChanged() override; void OnParentChanged() override; void OnTransformChanged() override; void OnLayerChanged() override; + void OnStaticFlagsChanged() override; void OnPhysicsSceneChanged(PhysicsScene* previous) override; }; diff --git a/Source/Engine/Terrain/Terrain.cpp b/Source/Engine/Terrain/Terrain.cpp index aa677cda9..66ecbd377 100644 --- a/Source/Engine/Terrain/Terrain.cpp +++ b/Source/Engine/Terrain/Terrain.cpp @@ -12,6 +12,7 @@ #include "Engine/Graphics/RenderView.h" #include "Engine/Graphics/RenderTask.h" #include "Engine/Graphics/Textures/GPUTexture.h" +#include "Engine/Level/Scene/Scene.h" #include "Engine/Physics/PhysicsScene.h" #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Renderer/GlobalSignDistanceFieldPass.h" @@ -804,6 +805,7 @@ RigidBody* Terrain::GetAttachedRigidBody() const void Terrain::OnEnable() { + GetScene()->Navigation.Actors.Add(this); GetSceneRendering()->AddActor(this, _sceneRenderingKey); #if TERRAIN_USE_PHYSICS_DEBUG GetSceneRendering()->AddPhysicsDebug(this); @@ -824,6 +826,7 @@ void Terrain::OnEnable() void Terrain::OnDisable() { + GetScene()->Navigation.Actors.Remove(this); GetSceneRendering()->RemoveActor(this, _sceneRenderingKey); #if TERRAIN_USE_PHYSICS_DEBUG GetSceneRendering()->RemovePhysicsDebug(this);