diff --git a/Source/Editor/SceneGraph/Actors/NavModifierVolumeNode.cs b/Source/Editor/SceneGraph/Actors/NavModifierVolumeNode.cs
new file mode 100644
index 000000000..38faba8ef
--- /dev/null
+++ b/Source/Editor/SceneGraph/Actors/NavModifierVolumeNode.cs
@@ -0,0 +1,20 @@
+// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
+
+using FlaxEngine;
+
+namespace FlaxEditor.SceneGraph.Actors
+{
+ ///
+ /// Actor node for .
+ ///
+ ///
+ [HideInEditor]
+ public sealed class NavModifierVolumeNode : BoxVolumeNode
+ {
+ ///
+ public NavModifierVolumeNode(Actor actor)
+ : base(actor)
+ {
+ }
+ }
+}
diff --git a/Source/Editor/SceneGraph/SceneGraphFactory.cs b/Source/Editor/SceneGraph/SceneGraphFactory.cs
index 68cb627f0..0076742b3 100644
--- a/Source/Editor/SceneGraph/SceneGraphFactory.cs
+++ b/Source/Editor/SceneGraph/SceneGraphFactory.cs
@@ -62,6 +62,7 @@ namespace FlaxEditor.SceneGraph
CustomNodesTypes.Add(typeof(NavMeshBoundsVolume), typeof(NavMeshBoundsVolumeNode));
CustomNodesTypes.Add(typeof(BoxVolume), typeof(BoxVolumeNode));
CustomNodesTypes.Add(typeof(NavLink), typeof(NavLinkNode));
+ CustomNodesTypes.Add(typeof(NavModifierVolume), typeof(NavModifierVolumeNode));
CustomNodesTypes.Add(typeof(ParticleEffect), typeof(ParticleEffectNode));
CustomNodesTypes.Add(typeof(SceneAnimationPlayer), typeof(SceneAnimationPlayerNode));
}
diff --git a/Source/Editor/Windows/SceneTreeWindow.Actors.cs b/Source/Editor/Windows/SceneTreeWindow.Actors.cs
index d3fff78d8..6fafe444f 100644
--- a/Source/Editor/Windows/SceneTreeWindow.Actors.cs
+++ b/Source/Editor/Windows/SceneTreeWindow.Actors.cs
@@ -97,7 +97,8 @@ namespace FlaxEditor.Windows
new KeyValuePair("Audio Listener", typeof(AudioListener)),
new KeyValuePair("Scene Animation", typeof(SceneAnimationPlayer)),
new KeyValuePair("Nav Mesh Bounds Volume", typeof(NavMeshBoundsVolume)),
- new KeyValuePair("Nav Mesh Link", typeof(NavLink)),
+ new KeyValuePair("Nav Link", typeof(NavLink)),
+ new KeyValuePair("Nav Modifier Volume", typeof(NavModifierVolume)),
}
},
new ActorsGroup
diff --git a/Source/Editor/Windows/ToolboxWindow.cs b/Source/Editor/Windows/ToolboxWindow.cs
index d10a111b6..8c30621b0 100644
--- a/Source/Editor/Windows/ToolboxWindow.cs
+++ b/Source/Editor/Windows/ToolboxWindow.cs
@@ -165,7 +165,8 @@ namespace FlaxEditor.Windows
groupOther.AddChild(CreateActorItem("Empty Actor", typeof(EmptyActor)));
groupOther.AddChild(CreateActorItem("Scene Animation", typeof(SceneAnimationPlayer)));
groupOther.AddChild(CreateActorItem("Nav Mesh Bounds Volume", typeof(NavMeshBoundsVolume)));
- groupOther.AddChild(CreateActorItem("Nav Mesh Link", typeof(NavLink)));
+ groupOther.AddChild(CreateActorItem("Nav Link", typeof(NavLink)));
+ groupOther.AddChild(CreateActorItem("Nav Modifier Volume", typeof(NavModifierVolume)));
var groupGui = CreateGroupWithList(actorGroups, "GUI");
groupGui.AddChild(CreateActorItem("UI Control", typeof(UIControl)));
diff --git a/Source/Engine/Navigation/NavMeshBoundsVolume.h b/Source/Engine/Navigation/NavMeshBoundsVolume.h
index 897a83666..674ba4877 100644
--- a/Source/Engine/Navigation/NavMeshBoundsVolume.h
+++ b/Source/Engine/Navigation/NavMeshBoundsVolume.h
@@ -6,7 +6,7 @@
#include "NavigationTypes.h"
///
-/// A special type of volume that defines the areas of the scene in which navigation meshes are generated.
+/// A special type of volume that defines the area of the scene in which navigation meshes are generated.
///
API_CLASS() class FLAXENGINE_API NavMeshBoundsVolume : public BoxVolume
{
diff --git a/Source/Engine/Navigation/NavMeshBuilder.cpp b/Source/Engine/Navigation/NavMeshBuilder.cpp
index 5cd5a52bd..8d0bbafeb 100644
--- a/Source/Engine/Navigation/NavMeshBuilder.cpp
+++ b/Source/Engine/Navigation/NavMeshBuilder.cpp
@@ -7,6 +7,7 @@
#include "NavigationSettings.h"
#include "NavMeshBoundsVolume.h"
#include "NavLink.h"
+#include "NavModifierVolume.h"
#include "NavMeshRuntime.h"
#include "Engine/Core/Math/BoundingBox.h"
#include "Engine/Core/Math/VectorInt.h"
@@ -55,8 +56,14 @@ struct OffMeshLink
int32 Id;
};
+struct Modifier
+{
+ BoundingBox Bounds;
+};
+
struct NavigationSceneRasterization
{
+ NavMesh* NavMesh;
BoundingBox TileBoundsNavMesh;
Matrix WorldToNavMesh;
rcContext* Context;
@@ -66,18 +73,21 @@ struct NavigationSceneRasterization
Array VertexBuffer;
Array IndexBuffer;
Array* OffMeshLinks;
+ Array* Modifiers;
const bool IsWorldToNavMeshIdentity;
- NavigationSceneRasterization(const BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh, rcContext* context, rcConfig* config, rcHeightfield* heightfield, Array* offMeshLinks)
+ NavigationSceneRasterization(::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())
{
+ NavMesh = navMesh;
Context = context;
Config = config;
Heightfield = heightfield;
WalkableThreshold = Math::Cos(config->walkableSlopeAngle * DegreesToRadians);
OffMeshLinks = offMeshLinks;
+ Modifiers = modifiers;
}
void RasterizeTriangles()
@@ -280,16 +290,30 @@ struct NavigationSceneRasterization
e.OffMeshLinks->Add(link);
}
+ else if (const auto* navModifierVolume = dynamic_cast(actor))
+ {
+ if (navModifierVolume->AgentsMask.IsNavMeshSupported(e.NavMesh->Properties))
+ {
+ PROFILE_CPU_NAMED("NavModifierVolume");
+
+ Modifier modifier;
+ OrientedBoundingBox bounds = navModifierVolume->GetOrientedBox();
+ bounds.Transform(e.WorldToNavMesh);
+ bounds.GetBoundingBox(modifier.Bounds);
+
+ e.Modifiers->Add(modifier);
+ }
+ }
return true;
}
};
-void RasterizeGeometry(const BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh, rcContext* context, rcConfig* config, rcHeightfield* heightfield, Array* offMeshLinks)
+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(tileBoundsNavMesh, worldToNavMesh, context, config, heightfield, offMeshLinks);
+ NavigationSceneRasterization rasterization(navMesh, tileBoundsNavMesh, worldToNavMesh, context, config, heightfield, offMeshLinks, modifiers);
Function treeWalkFunction(NavigationSceneRasterization::Walk);
SceneQuery::TreeExecute(treeWalkFunction, rasterization);
}
@@ -389,7 +413,8 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B
}
Array offMeshLinks;
- RasterizeGeometry(tileBoundsNavMesh, worldToNavMesh, &context, &config, heightfield, &offMeshLinks);
+ Array modifiers;
+ RasterizeGeometry(navMesh, tileBoundsNavMesh, worldToNavMesh, &context, &config, heightfield, &offMeshLinks, &modifiers);
rcFilterLowHangingWalkableObstacles(&context, config.walkableClimb, *heightfield);
rcFilterLedgeSpans(&context, config.walkableHeight, config.walkableClimb, *heightfield);
@@ -415,6 +440,12 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B
return true;
}
+ // Mark areas
+ for (auto& modifier : modifiers)
+ {
+ rcMarkBoxArea(&context, &modifier.Bounds.Minimum.X, &modifier.Bounds.Maximum.X, RC_NULL_AREA, *compactHeightfield);
+ }
+
if (!rcBuildDistanceField(&context, *compactHeightfield))
{
LOG(Warning, "Could not generate navmesh: Could not build distance field.");
@@ -468,7 +499,7 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B
for (int i = 0; i < polyMesh->npolys; i++)
{
- polyMesh->flags[i] = polyMesh->areas[i] == RC_WALKABLE_AREA ? 1 : 0;
+ polyMesh->flags[i] = polyMesh->areas[i] != RC_NULL_AREA ? 1 : 0;
}
if (polyMesh->nverts == 0)
diff --git a/Source/Engine/Navigation/NavModifierVolume.cpp b/Source/Engine/Navigation/NavModifierVolume.cpp
new file mode 100644
index 000000000..65325195a
--- /dev/null
+++ b/Source/Engine/Navigation/NavModifierVolume.cpp
@@ -0,0 +1,54 @@
+// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
+
+#include "NavModifierVolume.h"
+#include "Engine/Level/Scene/Scene.h"
+#include "Engine/Serialization/Serialization.h"
+#if USE_EDITOR
+#include "Editor/Editor.h"
+#include "Editor/Managed/ManagedEditor.h"
+#include "NavMeshBuilder.h"
+#endif
+
+NavModifierVolume::NavModifierVolume(const SpawnParams& params)
+ : BoxVolume(params)
+{
+ _size = 100.0f;
+}
+
+void NavModifierVolume::Serialize(SerializeStream& stream, const void* otherObj)
+{
+ // Base
+ BoxVolume::Serialize(stream, otherObj);
+
+ SERIALIZE_GET_OTHER_OBJ(NavModifierVolume);
+
+ SERIALIZE_MEMBER(AgentsMask, AgentsMask.Mask);
+}
+
+void NavModifierVolume::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
+{
+ // Base
+ BoxVolume::Deserialize(stream, modifier);
+
+ DESERIALIZE_MEMBER(AgentsMask, AgentsMask.Mask);
+}
+
+#if USE_EDITOR
+
+void NavModifierVolume::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);
+ }
+}
+
+Color NavModifierVolume::GetWiresColor()
+{
+ return Color::Red;
+}
+
+#endif
diff --git a/Source/Engine/Navigation/NavModifierVolume.h b/Source/Engine/Navigation/NavModifierVolume.h
new file mode 100644
index 000000000..3ca0d4b20
--- /dev/null
+++ b/Source/Engine/Navigation/NavModifierVolume.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
+
+#pragma once
+
+#include "Engine/Level/Actors/BoxVolume.h"
+#include "NavigationTypes.h"
+
+///
+/// A special type of volume that defines the area of the scene in which navigation is restricted (eg. higher traversal cost or dynamic obstacle block).
+///
+API_CLASS() class FLAXENGINE_API NavModifierVolume : public BoxVolume
+{
+DECLARE_SCENE_OBJECT(NavModifierVolume);
+public:
+
+ ///
+ /// The agent types used by this navmesh modifier volume (from navigation settings). Can be used to adjust navmesh for a certain set of agents.
+ ///
+ API_FIELD(Attributes="EditorDisplay(\"Box Volume\"), EditorOrder(10)")
+ NavAgentMask AgentsMask;
+
+public:
+
+ // [BoxVolume]
+ void Serialize(SerializeStream& stream, const void* otherObj) override;
+ void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
+
+protected:
+
+ // [BoxVolume]
+#if USE_EDITOR
+ void OnBoundsChanged(const BoundingBox& prevBounds) override;
+ Color GetWiresColor() override;
+#endif
+};