Optimize navmesh building and reduce scene lock time
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -23,6 +23,17 @@ public:
|
||||
/// </summary>
|
||||
Array<NavMesh*> Meshes;
|
||||
|
||||
/// <summary>
|
||||
/// The list of registered navigation-relevant actors (on the scene).
|
||||
/// </summary>
|
||||
Array<Actor*> Actors;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Clears this instance data.
|
||||
/// </summary>
|
||||
void Clear();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total navigation volumes bounds.
|
||||
/// </summary>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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 <ThirdParty/recastnavigation/Recast.h>
|
||||
#include <ThirdParty/recastnavigation/DetourNavMeshBuilder.h>
|
||||
#include <ThirdParty/recastnavigation/DetourNavMesh.h>
|
||||
@@ -68,7 +67,7 @@ struct Modifier
|
||||
NavAreaProperties* NavArea;
|
||||
};
|
||||
|
||||
struct NavigationSceneRasterization
|
||||
struct NavSceneRasterizer
|
||||
{
|
||||
NavMesh* NavMesh;
|
||||
BoundingBox TileBoundsNavMesh;
|
||||
@@ -83,7 +82,7 @@ struct NavigationSceneRasterization
|
||||
Array<Modifier>* Modifiers;
|
||||
const bool IsWorldToNavMeshIdentity;
|
||||
|
||||
NavigationSceneRasterization(::NavMesh* navMesh, const BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh, rcContext* context, rcConfig* config, rcHeightfield* heightfield, Array<OffMeshLink>* offMeshLinks, Array<Modifier>* modifiers)
|
||||
NavSceneRasterizer(::NavMesh* navMesh, const BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh, rcContext* context, rcConfig* config, rcHeightfield* heightfield, Array<OffMeshLink>* offMeshLinks, Array<Modifier>* 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<Float3>& vb, Array<int32>& 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<BoxCollider*>(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<SphereCollider*>(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<CapsuleCollider*>(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<MeshCollider*>(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<SplineCollider*>(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<Terrain*>(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<NavLink*>(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<NavModifierVolume*>(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<OffMeshLink>* offMeshLinks, Array<Modifier>* modifiers)
|
||||
{
|
||||
PROFILE_CPU_NAMED("RasterizeGeometry");
|
||||
|
||||
NavigationSceneRasterization rasterization(navMesh, tileBoundsNavMesh, worldToNavMesh, context, config, heightfield, offMeshLinks, modifiers);
|
||||
Function<bool(Actor*, NavigationSceneRasterization&)> treeWalkFunction(NavigationSceneRasterization::Walk);
|
||||
SceneQuery::TreeExecute<NavigationSceneRasterization&>(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<OffMeshLink> offMeshLinks;
|
||||
Array<Modifier> 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<Actor*> 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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<Collider, &Collider::DrawPhysicsDebug>(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<Collider, &Collider::DrawPhysicsDebug>(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);
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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<Terrain, &Terrain::DrawPhysicsDebug>(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<Terrain, &Terrain::DrawPhysicsDebug>(this);
|
||||
|
||||
Reference in New Issue
Block a user