diff --git a/Source/Editor/CustomEditors/Dedicated/LayersMaskEditor.cs b/Source/Editor/CustomEditors/Dedicated/LayersMaskEditor.cs
new file mode 100644
index 000000000..93cc84e76
--- /dev/null
+++ b/Source/Editor/CustomEditors/Dedicated/LayersMaskEditor.cs
@@ -0,0 +1,89 @@
+// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
+
+using FlaxEditor.Content.Settings;
+using FlaxEngine;
+using FlaxEngine.GUI;
+
+namespace FlaxEditor.CustomEditors.Dedicated
+{
+ ///
+ /// Custom editor for .
+ ///
+ [CustomEditor(typeof(LayersMask)), DefaultEditor]
+ internal class LayersMaskEditor : CustomEditor
+ {
+ private CheckBox[] _checkBoxes;
+
+ ///
+ public override void Initialize(LayoutElementsContainer layout)
+ {
+ var layers = LayersAndTagsSettings.GetCurrentLayers();
+ if (layers == null || layers.Length == 0)
+ {
+ layout.Label("Missing layers and tags settings");
+ return;
+ }
+
+ _checkBoxes = new CheckBox[layers.Length];
+ for (int i = 0; i < layers.Length; i++)
+ {
+ var layer = layers[i];
+ var property = layout.AddPropertyItem(layer);
+ var checkbox = property.Checkbox().CheckBox;
+ UpdateCheckbox(checkbox, i);
+ checkbox.Tag = i;
+ checkbox.StateChanged += OnCheckboxStateChanged;
+ _checkBoxes[i] = checkbox;
+ }
+ }
+
+ ///
+ protected override void Deinitialize()
+ {
+ _checkBoxes = null;
+
+ base.Deinitialize();
+ }
+
+ ///
+ public override void Refresh()
+ {
+ if (_checkBoxes != null)
+ {
+ for (int i = 0; i < _checkBoxes.Length; i++)
+ {
+ UpdateCheckbox(_checkBoxes[i], i);
+ }
+ }
+
+ base.Refresh();
+ }
+
+ private void OnCheckboxStateChanged(CheckBox checkBox)
+ {
+ var i = (int)checkBox.Tag;
+ var value = (LayersMask)Values[0];
+ var mask = 1u << i;
+ value.Mask &= ~mask;
+ value.Mask |= checkBox.Checked ? mask : 0;
+ SetValue(value);
+ }
+
+ private void UpdateCheckbox(CheckBox checkbox, int i)
+ {
+ for (var j = 0; j < Values.Count; j++)
+ {
+ var value = (((LayersMask)Values[j]).Mask & (1 << i)) != 0;
+ if (j == 0)
+ {
+ checkbox.Checked = value;
+ }
+ else if (checkbox.State != CheckBoxState.Intermediate)
+ {
+ if (checkbox.Checked != value)
+ checkbox.State = CheckBoxState.Intermediate;
+ }
+ }
+ }
+ }
+}
diff --git a/Source/Engine/Core/Config/GameSettings.cpp b/Source/Engine/Core/Config/GameSettings.cpp
index 5ea356d61..d06f0616f 100644
--- a/Source/Engine/Core/Config/GameSettings.cpp
+++ b/Source/Engine/Core/Config/GameSettings.cpp
@@ -200,3 +200,44 @@ void GameSettings::Deserialize(DeserializeStream& stream, ISerializeModifier* mo
DESERIALIZE(XboxScarlettPlatform);
DESERIALIZE(AndroidPlatform);
}
+
+void LayersAndTagsSettings::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
+{
+ const auto tags = stream.FindMember("Tags");
+ if (tags != stream.MemberEnd())
+ {
+ auto& tagsArray = tags->value;
+ ASSERT(tagsArray.IsArray());
+ Tags.EnsureCapacity(tagsArray.Size());
+
+ // Note: we cannot remove tags at runtime so this should deserialize them in additive mode
+ // Tags are stored as tagIndex in actors so collection change would break the linkage
+
+ for (uint32 i = 0; i < tagsArray.Size(); i++)
+ {
+ auto& v = tagsArray[i];
+ if (v.IsString())
+ {
+ const String tag = v.GetText();
+ if (!Tags.Contains(tag))
+ Tags.Add(tag);
+ }
+ }
+ }
+
+ const auto layers = stream.FindMember("Layers");
+ if (layers != stream.MemberEnd())
+ {
+ auto& layersArray = layers->value;
+ ASSERT(layersArray.IsArray());
+
+ for (uint32 i = 0; i < layersArray.Size() && i < 32; i++)
+ {
+ auto& v = layersArray[i];
+ if (v.IsString())
+ Layers[i] = v.GetText();
+ else
+ Layers[i].Clear();
+ }
+ }
+}
diff --git a/Source/Engine/Core/Config/LayersMask.h b/Source/Engine/Core/Config/LayersMask.h
new file mode 100644
index 000000000..b2539e78a
--- /dev/null
+++ b/Source/Engine/Core/Config/LayersMask.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
+
+#pragma once
+
+#include "Engine/Core/Types/BaseTypes.h"
+#include "Engine/Serialization/SerializationFwd.h"
+
+///
+/// The objects layers selection mask (from layers and tags settings). Uses 1 bit per layer (up to 32 layers).
+///
+API_STRUCT() struct FLAXENGINE_API LayersMask
+{
+DECLARE_SCRIPTING_TYPE_MINIMAL(LayersMask);
+
+ ///
+ /// The layers selection mask.
+ ///
+ API_FIELD() uint32 Mask = MAX_uint32;
+
+ FORCE_INLINE bool HasLayer(int32 layerIndex) const
+ {
+ return (Mask & (1 << layerIndex)) != 0;
+ }
+
+ bool HasLayer(const StringView& layerName) const;
+
+ bool operator==(const LayersMask& other) const;
+ bool operator!=(const LayersMask& other) const;
+};
+
+// @formatter:off
+namespace Serialization
+{
+ inline bool ShouldSerialize(const LayersMask& v, const void* otherObj)
+ {
+ return !otherObj || v != *(LayersMask*)otherObj;
+ }
+ inline void Serialize(ISerializable::SerializeStream& stream, const LayersMask& v, const void* otherObj)
+ {
+ stream.Uint(v.Mask);
+ }
+ inline void Deserialize(ISerializable::DeserializeStream& stream, LayersMask& v, ISerializeModifier* modifier)
+ {
+ v.Mask = stream.GetUint();
+ }
+}
+// @formatter:on
diff --git a/Source/Engine/Core/Config/LayersTagsSettings.h b/Source/Engine/Core/Config/LayersTagsSettings.h
index 945a24fbf..92edf61c7 100644
--- a/Source/Engine/Core/Config/LayersTagsSettings.h
+++ b/Source/Engine/Core/Config/LayersTagsSettings.h
@@ -3,7 +3,6 @@
#pragma once
#include "Engine/Core/Config/Settings.h"
-#include "Engine/Serialization/Json.h"
///
/// Layers and objects tags settings.
@@ -14,12 +13,12 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(LayersAndTagsSettings);
public:
///
- /// The tags names.
+ /// The tag names.
///
Array Tags;
///
- /// The layers names.
+ /// The layer names.
///
String Layers[32];
@@ -31,44 +30,5 @@ public:
static LayersAndTagsSettings* Get();
// [SettingsBase]
- void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override
- {
- const auto tags = stream.FindMember("Tags");
- if (tags != stream.MemberEnd())
- {
- auto& tagsArray = tags->value;
- ASSERT(tagsArray.IsArray());
- Tags.EnsureCapacity(tagsArray.Size());
-
- // Note: we cannot remove tags at runtime so this should deserialize them in additive mode
- // Tags are stored as tagIndex in actors so collection change would break the linkage
-
- for (uint32 i = 0; i < tagsArray.Size(); i++)
- {
- auto& v = tagsArray[i];
- if (v.IsString())
- {
- const String tag = v.GetText();
- if (!Tags.Contains(tag))
- Tags.Add(tag);
- }
- }
- }
-
- const auto layers = stream.FindMember("Layers");
- if (layers != stream.MemberEnd())
- {
- auto& layersArray = layers->value;
- ASSERT(layersArray.IsArray());
-
- for (uint32 i = 0; i < layersArray.Size() && i < 32; i++)
- {
- auto& v = layersArray[i];
- if (v.IsString())
- Layers[i] = v.GetText();
- else
- Layers[i].Clear();
- }
- }
- }
+ void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override;
};
diff --git a/Source/Engine/Graphics/RenderView.cpp b/Source/Engine/Graphics/RenderView.cpp
index e7eb092d6..b270ba69a 100644
--- a/Source/Engine/Graphics/RenderView.cpp
+++ b/Source/Engine/Graphics/RenderView.cpp
@@ -115,6 +115,7 @@ void RenderView::CopyFrom(Camera* camera)
Matrix::Invert(Projection, IP);
Frustum.GetInvMatrix(IVP);
CullingFrustum = Frustum;
+ RenderLayersMask = camera->RenderLayersMask;
}
void RenderView::CopyFrom(Camera* camera, Viewport* viewport)
@@ -131,6 +132,7 @@ void RenderView::CopyFrom(Camera* camera, Viewport* viewport)
Matrix::Invert(Projection, IP);
Frustum.GetInvMatrix(IVP);
CullingFrustum = Frustum;
+ RenderLayersMask = camera->RenderLayersMask;
}
DrawPass RenderView::GetShadowsDrawPassMask(ShadowsCastingMode shadowsMode) const
diff --git a/Source/Engine/Graphics/RenderView.cs b/Source/Engine/Graphics/RenderView.cs
index 1b495cd54..d82ac7517 100644
--- a/Source/Engine/Graphics/RenderView.cs
+++ b/Source/Engine/Graphics/RenderView.cs
@@ -89,6 +89,7 @@ namespace FlaxEngine
Projection = camera.Projection;
NonJitteredProjection = Projection;
TemporalAAJitter = Vector4.Zero;
+ RenderLayersMask = camera.RenderLayersMask;
UpdateCachedData();
}
@@ -107,6 +108,7 @@ namespace FlaxEngine
camera.GetMatrices(out View, out Projection, ref customViewport);
NonJitteredProjection = Projection;
TemporalAAJitter = Vector4.Zero;
+ RenderLayersMask = camera.RenderLayersMask;
UpdateCachedData();
}
diff --git a/Source/Engine/Graphics/RenderView.h b/Source/Engine/Graphics/RenderView.h
index 36cd55ffa..f478576f2 100644
--- a/Source/Engine/Graphics/RenderView.h
+++ b/Source/Engine/Graphics/RenderView.h
@@ -5,6 +5,7 @@
#include "Engine/Core/Math/BoundingFrustum.h"
#include "Engine/Core/Math/Matrix.h"
#include "Engine/Core/Math/Vector3.h"
+#include "Engine/Core/Config/LayersMask.h"
#include "Engine/Level/Types.h"
#include "Enums.h"
@@ -141,6 +142,11 @@ public:
///
API_FIELD() int32 TaaFrameIndex = 0;
+ ///
+ /// The rendering mask for layers. Used to exclude objects from rendering.
+ ///
+ API_FIELD() LayersMask RenderLayersMask;
+
public:
///
diff --git a/Source/Engine/Level/Actor.cpp b/Source/Engine/Level/Actor.cpp
index 42e937976..41e929da0 100644
--- a/Source/Engine/Level/Actor.cpp
+++ b/Source/Engine/Level/Actor.cpp
@@ -406,7 +406,7 @@ const String& Actor::GetTag() const
void Actor::SetLayer(int32 layerIndex)
{
- layerIndex = Math::Min(layerIndex, 31);
+ layerIndex = Math::Clamp(layerIndex, 0, 31);
if (layerIndex == _layer)
return;
diff --git a/Source/Engine/Level/Actors/Camera.cpp b/Source/Engine/Level/Actors/Camera.cpp
index f0682ddb5..95eebefc1 100644
--- a/Source/Engine/Level/Actors/Camera.cpp
+++ b/Source/Engine/Level/Actors/Camera.cpp
@@ -330,6 +330,7 @@ void Camera::Serialize(SerializeStream& stream, const void* otherObj)
SERIALIZE_MEMBER(Near, _near);
SERIALIZE_MEMBER(Far, _far);
SERIALIZE_MEMBER(OrthoScale, _orthoScale);
+ SERIALIZE(RenderLayersMask);
}
void Camera::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
@@ -343,6 +344,7 @@ void Camera::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier
DESERIALIZE_MEMBER(Near, _near);
DESERIALIZE_MEMBER(Far, _far);
DESERIALIZE_MEMBER(OrthoScale, _orthoScale);
+ DESERIALIZE(RenderLayersMask);
}
void Camera::OnEnable()
diff --git a/Source/Engine/Level/Actors/Camera.h b/Source/Engine/Level/Actors/Camera.h
index edf94ad22..4d05b53fb 100644
--- a/Source/Engine/Level/Actors/Camera.h
+++ b/Source/Engine/Level/Actors/Camera.h
@@ -3,13 +3,16 @@
#pragma once
#include "../Actor.h"
-#include "Engine/Content/AssetReference.h"
#include "Engine/Core/Math/Matrix.h"
#include "Engine/Core/Math/BoundingFrustum.h"
#include "Engine/Core/Math/Viewport.h"
#include "Engine/Core/Math/Ray.h"
+#include "Engine/Core/Config/LayersMask.h"
+#if USE_EDITOR
+#include "Engine/Content/AssetReference.h"
#include "Engine/Graphics/Models/ModelInstanceEntry.h"
#include "Engine/Content/Assets/Model.h"
+#endif
///
/// Describes the camera projection and view. Provides information about how to render scene (viewport location and direction, etc.).
@@ -21,6 +24,7 @@ DECLARE_SCENE_OBJECT(Camera);
// List with all created cameras actors on the scene
static Array Cameras;
+ // The current cut-scene camera. Set by the Scene Animation Player to the current shot camera.
static Camera* CutSceneCamera;
// The overriden main camera.
@@ -161,6 +165,12 @@ public:
///
API_PROPERTY() void SetOrthographicScale(float value);
+ ///
+ /// The layers mask used for rendering using this camera. Can be used to include or exclude specific actor layers from the drawing.
+ ///
+ API_FIELD(Attributes="EditorOrder(100), EditorDisplay(\"Camera\")")
+ LayersMask RenderLayersMask;
+
public:
///
diff --git a/Source/Engine/Level/Level.cpp b/Source/Engine/Level/Level.cpp
index be07f56ec..134808962 100644
--- a/Source/Engine/Level/Level.cpp
+++ b/Source/Engine/Level/Level.cpp
@@ -37,6 +37,21 @@
#include "Engine/Engine/CommandLine.h"
#endif
+bool LayersMask::HasLayer(const StringView& layerName) const
+{
+ return HasLayer(Level::GetLayerIndex(layerName));
+}
+
+bool LayersMask::operator==(const LayersMask& other) const
+{
+ return Mask == other.Mask;
+}
+
+bool LayersMask::operator!=(const LayersMask& other) const
+{
+ return Mask != other.Mask;
+}
+
enum class SceneEventType
{
OnSceneSaving = 0,
@@ -674,6 +689,20 @@ int32 Level::GetNonEmptyLayerNamesCount()
return result + 1;
}
+int32 Level::GetLayerIndex(const StringView& layer)
+{
+ int32 result = -1;
+ for (int32 i = 0; i < 32; i++)
+ {
+ if (Layers[i] == layer)
+ {
+ result = i;
+ break;
+ }
+ }
+ return result;
+}
+
void Level::callActorEvent(ActorEventType eventType, Actor* a, Actor* b)
{
PROFILE_CPU();
diff --git a/Source/Engine/Level/Level.h b/Source/Engine/Level/Level.h
index 64bef8d27..8beed4a24 100644
--- a/Source/Engine/Level/Level.h
+++ b/Source/Engine/Level/Level.h
@@ -445,7 +445,7 @@ public:
/// The layers names.
///
static String Layers[32];
-
+
///
/// Gets or adds the tag (returns the tag index).
///
@@ -459,6 +459,11 @@ public:
/// The layers count.
static int32 GetNonEmptyLayerNamesCount();
+ ///
+ /// Gets the zero-based index of the layer.
+ ///
+ static int32 GetLayerIndex(const StringView& layer);
+
private:
// Actor API
diff --git a/Source/Engine/Level/Scene/SceneRendering.cpp b/Source/Engine/Level/Scene/SceneRendering.cpp
index 7a38178b0..0fb96b735 100644
--- a/Source/Engine/Level/Scene/SceneRendering.cpp
+++ b/Source/Engine/Level/Scene/SceneRendering.cpp
@@ -28,8 +28,8 @@ SceneRendering::SceneRendering(::Scene* scene)
void CullAndDraw(const BoundingFrustum& frustum, RenderContext& renderContext, const Array& actors)
{
-#if SCENE_RENDERING_USE_SIMD
auto& view = renderContext.View;
+#if SCENE_RENDERING_USE_SIMD
CullDataSIMD cullData;
{
// Near
@@ -126,7 +126,7 @@ void CullAndDraw(const BoundingFrustum& frustum, RenderContext& renderContext, c
for (int32 i = 0; i < actors.Count(); i++)
{
auto actor = actors[i];
- if (frustum.Intersects(actor->GetSphere()))
+ if (view.RenderLayersMask.HasLayer(actor->GetLayer()) && frustum.Intersects(actor->GetSphere()))
actor->Draw(renderContext);
}
#endif
@@ -134,8 +134,8 @@ void CullAndDraw(const BoundingFrustum& frustum, RenderContext& renderContext, c
void CullAndDrawOffline(const BoundingFrustum& frustum, RenderContext& renderContext, const Array& actors)
{
-#if SCENE_RENDERING_USE_SIMD
auto& view = renderContext.View;
+#if SCENE_RENDERING_USE_SIMD
CullDataSIMD cullData;
{
// Near
@@ -233,7 +233,7 @@ void CullAndDrawOffline(const BoundingFrustum& frustum, RenderContext& renderCon
for (int32 i = 0; i < actors.Count(); i++)
{
auto actor = actors[i];
- if (actor->GetStaticFlags() & renderContext.View.StaticFlagsMask && frustum.Intersects(actor->GetSphere()))
+ if (actor->GetStaticFlags() & view.StaticFlagsMask && view.RenderLayersMask.HasLayer(actor->GetLayer()) && frustum.Intersects(actor->GetSphere()))
actor->Draw(renderContext);
}
#endif
@@ -257,7 +257,7 @@ void SceneRendering::Draw(RenderContext& renderContext)
for (int32 i = 0; i < CommonNoCulling.Count(); i++)
{
auto actor = CommonNoCulling[i];
- if (actor->GetStaticFlags() & view.StaticFlagsMask)
+ if (actor->GetStaticFlags() & view.StaticFlagsMask && view.RenderLayersMask.HasLayer(actor->GetLayer()))
actor->Draw(renderContext);
}
}
@@ -271,7 +271,8 @@ void SceneRendering::Draw(RenderContext& renderContext)
for (int32 i = 0; i < CommonNoCulling.Count(); i++)
{
auto actor = CommonNoCulling[i];
- actor->Draw(renderContext);
+ if (view.RenderLayersMask.HasLayer(actor->GetLayer()))
+ actor->Draw(renderContext);
}
}
}