Add Render Layers to Camera and Render View for masking objects during rendering

This commit is contained in:
2021-02-19 17:26:41 +01:00
parent d866526dd8
commit 09be8994e9
13 changed files with 246 additions and 52 deletions
@@ -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
{
/// <summary>
/// Custom editor for <see cref="LayersMask"/>.
/// </summary>
[CustomEditor(typeof(LayersMask)), DefaultEditor]
internal class LayersMaskEditor : CustomEditor
{
private CheckBox[] _checkBoxes;
/// <inheritdoc />
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;
}
}
/// <inheritdoc />
protected override void Deinitialize()
{
_checkBoxes = null;
base.Deinitialize();
}
/// <inheritdoc />
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;
}
}
}
}
}
@@ -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();
}
}
}
+47
View File
@@ -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"
/// <summary>
/// The objects layers selection mask (from layers and tags settings). Uses 1 bit per layer (up to 32 layers).
/// </summary>
API_STRUCT() struct FLAXENGINE_API LayersMask
{
DECLARE_SCRIPTING_TYPE_MINIMAL(LayersMask);
/// <summary>
/// The layers selection mask.
/// </summary>
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
+3 -43
View File
@@ -3,7 +3,6 @@
#pragma once
#include "Engine/Core/Config/Settings.h"
#include "Engine/Serialization/Json.h"
/// <summary>
/// Layers and objects tags settings.
@@ -14,12 +13,12 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(LayersAndTagsSettings);
public:
/// <summary>
/// The tags names.
/// The tag names.
/// </summary>
Array<String> Tags;
/// <summary>
/// The layers names.
/// The layer names.
/// </summary>
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;
};
+2
View File
@@ -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
+2
View File
@@ -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();
}
+6
View File
@@ -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:
/// </summary>
API_FIELD() int32 TaaFrameIndex = 0;
/// <summary>
/// The rendering mask for layers. Used to exclude objects from rendering.
/// </summary>
API_FIELD() LayersMask RenderLayersMask;
public:
/// <summary>
+1 -1
View File
@@ -406,7 +406,7 @@ const String& Actor::GetTag() const
void Actor::SetLayer(int32 layerIndex)
{
layerIndex = Math::Min<int32>(layerIndex, 31);
layerIndex = Math::Clamp(layerIndex, 0, 31);
if (layerIndex == _layer)
return;
+2
View File
@@ -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()
+11 -1
View File
@@ -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
/// <summary>
/// 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<Camera*> 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:
/// </summary>
API_PROPERTY() void SetOrthographicScale(float value);
/// <summary>
/// The layers mask used for rendering using this camera. Can be used to include or exclude specific actor layers from the drawing.
/// </summary>
API_FIELD(Attributes="EditorOrder(100), EditorDisplay(\"Camera\")")
LayersMask RenderLayersMask;
public:
/// <summary>
+29
View File
@@ -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();
+6 -1
View File
@@ -445,7 +445,7 @@ public:
/// The layers names.
/// </summary>
static String Layers[32];
/// <summary>
/// Gets or adds the tag (returns the tag index).
/// </summary>
@@ -459,6 +459,11 @@ public:
/// <returns>The layers count.</returns>
static int32 GetNonEmptyLayerNamesCount();
/// <summary>
/// Gets the zero-based index of the layer.
/// </summary>
static int32 GetLayerIndex(const StringView& layer);
private:
// Actor API
+7 -6
View File
@@ -28,8 +28,8 @@ SceneRendering::SceneRendering(::Scene* scene)
void CullAndDraw(const BoundingFrustum& frustum, RenderContext& renderContext, const Array<Actor*>& 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<Actor*>& 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);
}
}
}