Add NavMesh Modifier Volume actor type

This commit is contained in:
Wojtek Figat
2021-01-15 16:46:40 +01:00
parent 070faf12da
commit 01a30faa62
8 changed files with 151 additions and 8 deletions

View File

@@ -0,0 +1,20 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
using FlaxEngine;
namespace FlaxEditor.SceneGraph.Actors
{
/// <summary>
/// Actor node for <see cref="NavModifierVolume"/>.
/// </summary>
/// <seealso cref="BoxVolumeNode" />
[HideInEditor]
public sealed class NavModifierVolumeNode : BoxVolumeNode
{
/// <inheritdoc />
public NavModifierVolumeNode(Actor actor)
: base(actor)
{
}
}
}

View File

@@ -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));
}

View File

@@ -97,7 +97,8 @@ namespace FlaxEditor.Windows
new KeyValuePair<string, Type>("Audio Listener", typeof(AudioListener)),
new KeyValuePair<string, Type>("Scene Animation", typeof(SceneAnimationPlayer)),
new KeyValuePair<string, Type>("Nav Mesh Bounds Volume", typeof(NavMeshBoundsVolume)),
new KeyValuePair<string, Type>("Nav Mesh Link", typeof(NavLink)),
new KeyValuePair<string, Type>("Nav Link", typeof(NavLink)),
new KeyValuePair<string, Type>("Nav Modifier Volume", typeof(NavModifierVolume)),
}
},
new ActorsGroup

View File

@@ -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)));

View File

@@ -6,7 +6,7 @@
#include "NavigationTypes.h"
/// <summary>
/// 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.
/// </summary>
API_CLASS() class FLAXENGINE_API NavMeshBoundsVolume : public BoxVolume
{

View File

@@ -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<Vector3> VertexBuffer;
Array<int32> IndexBuffer;
Array<OffMeshLink>* OffMeshLinks;
Array<Modifier>* Modifiers;
const bool IsWorldToNavMeshIdentity;
NavigationSceneRasterization(const BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh, rcContext* context, rcConfig* config, rcHeightfield* heightfield, Array<OffMeshLink>* offMeshLinks)
NavigationSceneRasterization(::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())
{
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<NavModifierVolume*>(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<OffMeshLink>* offMeshLinks)
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(tileBoundsNavMesh, worldToNavMesh, context, config, heightfield, offMeshLinks);
NavigationSceneRasterization rasterization(navMesh, tileBoundsNavMesh, worldToNavMesh, context, config, heightfield, offMeshLinks, modifiers);
Function<bool(Actor*, NavigationSceneRasterization&)> treeWalkFunction(NavigationSceneRasterization::Walk);
SceneQuery::TreeExecute<NavigationSceneRasterization&>(treeWalkFunction, rasterization);
}
@@ -389,7 +413,8 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B
}
Array<OffMeshLink> offMeshLinks;
RasterizeGeometry(tileBoundsNavMesh, worldToNavMesh, &context, &config, heightfield, &offMeshLinks);
Array<Modifier> 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)

View File

@@ -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

View File

@@ -0,0 +1,35 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Level/Actors/BoxVolume.h"
#include "NavigationTypes.h"
/// <summary>
/// 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).
/// </summary>
API_CLASS() class FLAXENGINE_API NavModifierVolume : public BoxVolume
{
DECLARE_SCENE_OBJECT(NavModifierVolume);
public:
/// <summary>
/// The agent types used by this navmesh modifier volume (from navigation settings). Can be used to adjust navmesh for a certain set of agents.
/// </summary>
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
};