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 +};