Add Render Layers to Camera and Render View for masking objects during rendering
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,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;
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user