diff --git a/Source/Editor/Utilities/ViewportIconsRenderer.cpp b/Source/Editor/Utilities/ViewportIconsRenderer.cpp
index 44b3b4ea0..26a705970 100644
--- a/Source/Editor/Utilities/ViewportIconsRenderer.cpp
+++ b/Source/Editor/Utilities/ViewportIconsRenderer.cpp
@@ -89,6 +89,7 @@ void ViewportIconsRenderer::DrawIcons(RenderContext& renderContext, Actor* actor
draw.Flags = StaticFlags::Transform;
draw.DrawModes = DrawPass::Forward;
draw.PerInstanceRandom = 0;
+ draw.StencilValue = 0;
draw.LODBias = 0;
draw.ForcedLOD = -1;
draw.SortOrder = 0;
diff --git a/Source/Engine/Foliage/Foliage.cpp b/Source/Engine/Foliage/Foliage.cpp
index 8a0e75a6e..ddc3468f7 100644
--- a/Source/Engine/Foliage/Foliage.cpp
+++ b/Source/Engine/Foliage/Foliage.cpp
@@ -323,6 +323,7 @@ void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster,
draw.Bounds = sphere;
draw.PerInstanceRandom = instance.Random;
draw.DrawModes = type._drawModes;
+ draw.SetStencilValue(_layer);
type.Model->Draw(renderContext, draw);
//DebugDraw::DrawSphere(instance.Bounds, Color::YellowGreen);
@@ -1182,6 +1183,7 @@ void Foliage::Draw(RenderContext& renderContext)
draw.Bounds = instance.Bounds;
draw.PerInstanceRandom = instance.Random;
draw.DrawModes = type.DrawModes & view.Pass & view.GetShadowsDrawPassMask(type.ShadowsMode);
+ draw.SetStencilValue(_layer);
type.Model->Draw(renderContext, draw);
return;
}
diff --git a/Source/Engine/Graphics/Config.h b/Source/Engine/Graphics/Config.h
index 542669f03..43fb319bf 100644
--- a/Source/Engine/Graphics/Config.h
+++ b/Source/Engine/Graphics/Config.h
@@ -70,7 +70,7 @@
// Default depth buffer pixel format
#ifndef GPU_DEPTH_BUFFER_PIXEL_FORMAT
-#define GPU_DEPTH_BUFFER_PIXEL_FORMAT PixelFormat::D32_Float
+#define GPU_DEPTH_BUFFER_PIXEL_FORMAT PixelFormat::D24_UNorm_S8_UInt
#endif
// Enable/disable gpu resources naming
diff --git a/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp b/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp
index 5f0abad33..bbcdc8207 100644
--- a/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp
+++ b/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp
@@ -85,13 +85,10 @@ void DeferredMaterialShader::Bind(BindParameters& params)
if (IsRunningRadiancePass)
cullMode = CullMode::TwoSided;
#endif
- if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminantSign < 0)
+ if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminant)
{
// Invert culling when scale is negative
- if (cullMode == CullMode::Normal)
- cullMode = CullMode::Inverted;
- else
- cullMode = CullMode::Normal;
+ cullMode = cullMode == CullMode::Normal ? CullMode::Inverted : CullMode::Normal;
}
ASSERT_LOW_LAYER(!(useSkinning && params.Instanced)); // No support for instancing skinned meshes
const auto cache = params.Instanced ? &_cacheInstanced : &_cache;
@@ -101,6 +98,7 @@ void DeferredMaterialShader::Bind(BindParameters& params)
// Bind pipeline
context->SetState(state);
+ context->SetStencilRef(drawCall.StencilValue);
}
void DeferredMaterialShader::Unload()
@@ -137,6 +135,10 @@ bool DeferredMaterialShader::Load()
}
#endif
+ psDesc.StencilEnable = true;
+ psDesc.StencilReadMask = 0;
+ psDesc.StencilPassOp = StencilOperation::Replace;
+
// GBuffer Pass
psDesc.VS = _shader->GetVS("VS");
failed |= psDesc.VS == nullptr;
@@ -160,6 +162,9 @@ bool DeferredMaterialShader::Load()
psDesc.PS = _shader->GetPS("PS_GBuffer");
_cache.DefaultSkinned.Init(psDesc);
+ psDesc.StencilEnable = false;
+ psDesc.StencilPassOp = StencilOperation::Keep;
+
#if USE_EDITOR
if (_shader->HasShader("PS_QuadOverdraw"))
{
diff --git a/Source/Engine/Graphics/Materials/DeformableMaterialShader.cpp b/Source/Engine/Graphics/Materials/DeformableMaterialShader.cpp
index 3efd9a2bc..6260163f4 100644
--- a/Source/Engine/Graphics/Materials/DeformableMaterialShader.cpp
+++ b/Source/Engine/Graphics/Materials/DeformableMaterialShader.cpp
@@ -62,7 +62,7 @@ void DeformableMaterialShader::Bind(BindParameters& params)
{
Matrix::Transpose(drawCall.World, materialData->WorldMatrix);
Matrix::Transpose(drawCall.Deformable.LocalMatrix, materialData->LocalMatrix);
- materialData->WorldDeterminantSign = drawCall.WorldDeterminantSign;
+ materialData->WorldDeterminantSign = drawCall.WorldDeterminant ? -1.0f : 1.0f;
materialData->Segment = drawCall.Deformable.Segment;
materialData->ChunksPerSegment = drawCall.Deformable.ChunksPerSegment;
materialData->MeshMinZ = drawCall.Deformable.MeshMinZ;
@@ -84,13 +84,10 @@ void DeformableMaterialShader::Bind(BindParameters& params)
// Select pipeline state based on current pass and render mode
const bool wireframe = (_info.FeaturesFlags & MaterialFeaturesFlags::Wireframe) != MaterialFeaturesFlags::None || view.Mode == ViewMode::Wireframe;
CullMode cullMode = view.Pass == DrawPass::Depth ? CullMode::TwoSided : _info.CullMode;
- if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminantSign < 0)
+ if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminant)
{
// Invert culling when scale is negative
- if (cullMode == CullMode::Normal)
- cullMode = CullMode::Inverted;
- else
- cullMode = CullMode::Normal;
+ cullMode = cullMode == CullMode::Normal ? CullMode::Inverted : CullMode::Normal;
}
PipelineStateCache* psCache = _cache.GetPS(view.Pass);
ASSERT(psCache);
@@ -98,6 +95,7 @@ void DeformableMaterialShader::Bind(BindParameters& params)
// Bind pipeline
context->SetState(state);
+ context->SetStencilRef(drawCall.StencilValue);
}
void DeformableMaterialShader::Unload()
@@ -139,10 +137,17 @@ bool DeformableMaterialShader::Load()
{
_drawModes |= DrawPass::GBuffer | DrawPass::GlobalSurfaceAtlas;
+ psDesc.StencilEnable = true;
+ psDesc.StencilReadMask = 0;
+ psDesc.StencilPassOp = StencilOperation::Replace;
+
// GBuffer Pass
psDesc.VS = _shader->GetVS("VS_SplineModel");
psDesc.PS = _shader->GetPS("PS_GBuffer");
_cache.Default.Init(psDesc);
+
+ psDesc.StencilEnable = false;
+ psDesc.StencilPassOp = StencilOperation::Keep;
}
else
{
diff --git a/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp b/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp
index a966507d8..72ec3c7bd 100644
--- a/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp
+++ b/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp
@@ -80,13 +80,10 @@ void ForwardMaterialShader::Bind(BindParameters& params)
if (IsRunningRadiancePass)
cullMode = CullMode::TwoSided;
#endif
- if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminantSign < 0)
+ if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminant)
{
// Invert culling when scale is negative
- if (cullMode == CullMode::Normal)
- cullMode = CullMode::Inverted;
- else
- cullMode = CullMode::Normal;
+ cullMode = cullMode == CullMode::Normal ? CullMode::Inverted : CullMode::Normal;
}
ASSERT_LOW_LAYER(!(useSkinning && params.Instanced)); // No support for instancing skinned meshes
const auto cacheObj = params.Instanced ? &_cacheInstanced : &_cache;
diff --git a/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp b/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp
index d99e0f5d0..b6b455f20 100644
--- a/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp
+++ b/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp
@@ -76,7 +76,7 @@ void TerrainMaterialShader::Bind(BindParameters& params)
scaleX > 0.00001f ? 1.0f / scaleX : 0.0f,
scaleY > 0.00001f ? 1.0f / scaleY : 0.0f,
scaleZ > 0.00001f ? 1.0f / scaleZ : 0.0f);
- materialData->WorldDeterminantSign = drawCall.WorldDeterminantSign;
+ materialData->WorldDeterminantSign = drawCall.WorldDeterminant ? -1.0f : 1.0f;
materialData->PerInstanceRandom = drawCall.PerInstanceRandom;
materialData->CurrentLOD = drawCall.Terrain.CurrentLOD;
materialData->ChunkSizeNextLOD = drawCall.Terrain.ChunkSizeNextLOD;
@@ -109,13 +109,10 @@ void TerrainMaterialShader::Bind(BindParameters& params)
if (IsRunningRadiancePass)
cullMode = CullMode::TwoSided;
#endif
- if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminantSign < 0)
+ if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminant)
{
// Invert culling when scale is negative
- if (cullMode == CullMode::Normal)
- cullMode = CullMode::Inverted;
- else
- cullMode = CullMode::Normal;
+ cullMode = cullMode == CullMode::Normal ? CullMode::Inverted : CullMode::Normal;
}
const PipelineStateCache* psCache = _cache.GetPS(view.Pass, useLightmap);
ASSERT(psCache);
@@ -123,6 +120,7 @@ void TerrainMaterialShader::Bind(BindParameters& params)
// Bind pipeline
context->SetState(state);
+ context->SetStencilRef(drawCall.StencilValue);
}
void TerrainMaterialShader::Unload()
@@ -139,6 +137,10 @@ bool TerrainMaterialShader::Load()
psDesc.DepthEnable = (_info.FeaturesFlags & MaterialFeaturesFlags::DisableDepthTest) == MaterialFeaturesFlags::None;
psDesc.DepthWriteEnable = (_info.FeaturesFlags & MaterialFeaturesFlags::DisableDepthWrite) == MaterialFeaturesFlags::None;
+ psDesc.StencilEnable = true;
+ psDesc.StencilReadMask = 0;
+ psDesc.StencilPassOp = StencilOperation::Replace;
+
#if GPU_ALLOW_TESSELLATION_SHADERS
// Check if use tessellation (both material and runtime supports it)
const bool useTess = _info.TessellationMode != TessellationMethod::None && GPUDevice::Instance->Limits.HasTessellation;
diff --git a/Source/Engine/Graphics/Models/Mesh.cpp b/Source/Engine/Graphics/Models/Mesh.cpp
index 32df730e8..c2f340645 100644
--- a/Source/Engine/Graphics/Models/Mesh.cpp
+++ b/Source/Engine/Graphics/Models/Mesh.cpp
@@ -218,7 +218,7 @@ bool Mesh::Load(uint32 vertices, uint32 triangles, const void* vb0, const void*
return Init(vertices, triangles, vbData, ib, use16BitIndexBuffer, vbLayout);
}
-void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, const Matrix& world, StaticFlags flags, bool receiveDecals, DrawPass drawModes, float perInstanceRandom, int8 sortOrder) const
+void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, const Matrix& world, StaticFlags flags, bool receiveDecals, DrawPass drawModes, float perInstanceRandom, int8 sortOrder, uint8 stencilValue) const
{
if (!material || !material->IsSurface() || !IsInitialized())
return;
@@ -240,8 +240,8 @@ void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, cons
drawCall.ObjectRadius = (float)_sphere.Radius * drawCall.World.GetScaleVector().GetAbsolute().MaxValue();
drawCall.Surface.GeometrySize = _box.GetSize();
drawCall.Surface.PrevWorld = world;
- drawCall.WorldDeterminantSign = RenderTools::GetWorldDeterminantSign(drawCall.World);
drawCall.PerInstanceRandom = perInstanceRandom;
+ drawCall.StencilValue = stencilValue;
#if USE_EDITOR
const ViewMode viewMode = renderContext.View.Mode;
if (viewMode == ViewMode::LightmapUVsDensity || viewMode == ViewMode::LODPreview)
@@ -307,8 +307,8 @@ void Mesh::Draw(const RenderContext& renderContext, const DrawInfo& info, float
drawCall.Surface.Lightmap = (info.Flags & StaticFlags::Lightmap) != StaticFlags::None ? info.Lightmap : nullptr;
drawCall.Surface.LightmapUVsArea = info.LightmapUVs ? *info.LightmapUVs : Rectangle::Empty;
drawCall.Surface.LODDitherFactor = lodDitherFactor;
- drawCall.WorldDeterminantSign = RenderTools::GetWorldDeterminantSign(drawCall.World);
drawCall.PerInstanceRandom = info.PerInstanceRandom;
+ drawCall.StencilValue = info.StencilValue;
#if USE_EDITOR
const ViewMode viewMode = renderContext.View.Mode;
if (viewMode == ViewMode::LightmapUVsDensity || viewMode == ViewMode::LODPreview)
@@ -370,8 +370,8 @@ void Mesh::Draw(const RenderContextBatch& renderContextBatch, const DrawInfo& in
drawCall.Surface.Lightmap = (info.Flags & StaticFlags::Lightmap) != StaticFlags::None ? info.Lightmap : nullptr;
drawCall.Surface.LightmapUVsArea = info.LightmapUVs ? *info.LightmapUVs : Rectangle::Empty;
drawCall.Surface.LODDitherFactor = lodDitherFactor;
- drawCall.WorldDeterminantSign = RenderTools::GetWorldDeterminantSign(drawCall.World);
drawCall.PerInstanceRandom = info.PerInstanceRandom;
+ drawCall.StencilValue = info.StencilValue;
#if USE_EDITOR
const ViewMode viewMode = renderContextBatch.GetMainContext().View.Mode;
if (viewMode == ViewMode::LightmapUVsDensity || viewMode == ViewMode::LODPreview)
diff --git a/Source/Engine/Graphics/Models/Mesh.h b/Source/Engine/Graphics/Models/Mesh.h
index 63b069f9a..8e273a3d5 100644
--- a/Source/Engine/Graphics/Models/Mesh.h
+++ b/Source/Engine/Graphics/Models/Mesh.h
@@ -142,7 +142,8 @@ public:
/// The draw passes to use for rendering this object.
/// The random per-instance value (normalized to range 0-1).
/// Object sorting key.
- API_FUNCTION() void Draw(API_PARAM(Ref) const RenderContext& renderContext, MaterialBase* material, API_PARAM(Ref) const Matrix& world, StaticFlags flags = StaticFlags::None, bool receiveDecals = true, DrawPass drawModes = DrawPass::Default, float perInstanceRandom = 0.0f, int8 sortOrder = 0) const;
+ /// Object stencil value.
+ API_FUNCTION() void Draw(API_PARAM(Ref) const RenderContext& renderContext, MaterialBase* material, API_PARAM(Ref) const Matrix& world, StaticFlags flags = StaticFlags::None, bool receiveDecals = true, DrawPass drawModes = DrawPass::Default, float perInstanceRandom = 0.0f, int8 sortOrder = 0, uint8 stencilValue = 0) const;
///
/// Draws the mesh.
diff --git a/Source/Engine/Graphics/Models/MeshBase.h b/Source/Engine/Graphics/Models/MeshBase.h
index 6a61ec3a5..1c76df166 100644
--- a/Source/Engine/Graphics/Models/MeshBase.h
+++ b/Source/Engine/Graphics/Models/MeshBase.h
@@ -404,6 +404,11 @@ public:
///
float PerInstanceRandom;
+ ///
+ /// The 8-bit stencil value to write into Depth-Stencil Buffer.
+ ///
+ uint8 StencilValue;
+
///
/// The LOD bias value.
///
@@ -422,6 +427,12 @@ public:
#if USE_EDITOR
float LightmapScale = -1.0f;
#endif
+
+ // Packs object layer into the stencil bits.
+ FORCE_INLINE void SetStencilValue(int32 layer)
+ {
+ StencilValue = uint8(layer & 0x1f);
+ }
};
///
diff --git a/Source/Engine/Graphics/Models/SkinnedMesh.cpp b/Source/Engine/Graphics/Models/SkinnedMesh.cpp
index 8c1b98ebf..66b3e5701 100644
--- a/Source/Engine/Graphics/Models/SkinnedMesh.cpp
+++ b/Source/Engine/Graphics/Models/SkinnedMesh.cpp
@@ -314,8 +314,8 @@ void SkinnedMesh::Draw(const RenderContext& renderContext, const DrawInfo& info,
drawCall.Surface.PrevWorld = info.DrawState->PrevWorld;
drawCall.Surface.Skinning = info.Skinning;
drawCall.Surface.LODDitherFactor = lodDitherFactor;
- drawCall.WorldDeterminantSign = RenderTools::GetWorldDeterminantSign(drawCall.World);
drawCall.PerInstanceRandom = info.PerInstanceRandom;
+ drawCall.StencilValue = info.StencilValue;
// Push draw call to the render list
renderContext.List->AddDrawCall(renderContext, drawModes, StaticFlags::None, drawCall, entry.ReceiveDecals, info.SortOrder);
@@ -355,8 +355,8 @@ void SkinnedMesh::Draw(const RenderContextBatch& renderContextBatch, const DrawI
drawCall.Surface.PrevWorld = info.DrawState->PrevWorld;
drawCall.Surface.Skinning = info.Skinning;
drawCall.Surface.LODDitherFactor = lodDitherFactor;
- drawCall.WorldDeterminantSign = RenderTools::GetWorldDeterminantSign(drawCall.World);
drawCall.PerInstanceRandom = info.PerInstanceRandom;
+ drawCall.StencilValue = info.StencilValue;
// Push draw call to the render lists
const auto shadowsMode = entry.ShadowsMode & slot.ShadowsMode;
diff --git a/Source/Engine/Graphics/RenderBuffers.h b/Source/Engine/Graphics/RenderBuffers.h
index 647d28c4c..dadd06175 100644
--- a/Source/Engine/Graphics/RenderBuffers.h
+++ b/Source/Engine/Graphics/RenderBuffers.h
@@ -13,6 +13,17 @@
#define GBUFFER2_FORMAT PixelFormat::R8G8B8A8_UNorm
#define GBUFFER3_FORMAT PixelFormat::R8G8B8A8_UNorm
+// Stencil bits usage (must match GBuffer.hlsl)
+// [0] | Object Layer
+// [1] | Object Layer
+// [2] | Object Layer
+// [3] | Object Layer
+// [4] | Object Layer
+// [5] |
+// [6] |
+// [7] |
+#define STENCIL_BUFFER_OBJECT_LAYER(value) uint8(value & 0x1f)
+
///
/// The scene rendering buffers container.
///
diff --git a/Source/Engine/Graphics/Textures/GPUTexture.h b/Source/Engine/Graphics/Textures/GPUTexture.h
index 85ac4ae0b..184f50c89 100644
--- a/Source/Engine/Graphics/Textures/GPUTexture.h
+++ b/Source/Engine/Graphics/Textures/GPUTexture.h
@@ -437,6 +437,12 @@ public:
/// The view to the depth-stencil resource descriptor as read-only depth.
API_FUNCTION() virtual GPUTextureView* ViewReadOnlyDepth() const = 0;
+ ///
+ /// Gets the view to the texture as stencil buffer.
+ ///
+ /// The view to the stencil resource descriptor.
+ API_FUNCTION() virtual GPUTextureView* ViewStencil() const = 0;
+
///
/// Implicit conversion to the first surface (only for 2D textures).
///
diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp
index 2a50fbfd8..a0ec80bb1 100644
--- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp
+++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp
@@ -11,6 +11,7 @@
#include "GPUSamplerDX11.h"
#include "GPUVertexLayoutDX11.h"
#include "Engine/GraphicsDevice/DirectX/RenderToolsDX.h"
+#include "Engine/Graphics/PixelFormatExtensions.h"
#include "Engine/Core/Math/Viewport.h"
#include "Engine/Core/Math/Rectangle.h"
#include "Engine/Profiler/RenderStats.h"
@@ -216,7 +217,10 @@ void GPUContextDX11::ClearDepth(GPUTextureView* depthBuffer, float depthValue, u
if (depthBufferDX11)
{
ASSERT(depthBufferDX11->DSV());
- _context->ClearDepthStencilView(depthBufferDX11->DSV(), D3D11_CLEAR_DEPTH, depthValue, stencilValue);
+ UINT clearFlags = D3D11_CLEAR_DEPTH;
+ if (PixelFormatExtensions::HasStencil(depthBufferDX11->GetFormat()))
+ clearFlags |= D3D11_CLEAR_STENCIL;
+ _context->ClearDepthStencilView(depthBufferDX11->DSV(), clearFlags, depthValue, stencilValue);
}
}
diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTextureDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTextureDX11.cpp
index e858f7e3e..a05585696 100644
--- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTextureDX11.cpp
+++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTextureDX11.cpp
@@ -150,6 +150,7 @@ void GPUTextureDX11::OnReleaseGPU()
_handleArray.Release();
_handleVolume.Release();
_handleReadOnlyDepth.Release();
+ _handleStencil.Release();
DX_SAFE_RELEASE_CHECK(_resource, 0);
// Base
@@ -547,6 +548,41 @@ void GPUTextureDX11::initHandles()
}
_handleReadOnlyDepth.Init(this, rtView, srView, dsView, nullptr, format, msaa);
}
+
+ // Stencil view
+ if (useDSV && useSRV && PixelFormatExtensions::HasStencil(format))
+ {
+ PixelFormat stencilFormat;
+ switch (_dxgiFormatDSV)
+ {
+ case PixelFormat::D24_UNorm_S8_UInt:
+ srDesc.Format = DXGI_FORMAT_X24_TYPELESS_G8_UINT;
+ stencilFormat = PixelFormat::X24_Typeless_G8_UInt;
+ break;
+ case PixelFormat::D32_Float_S8X24_UInt:
+ srDesc.Format = DXGI_FORMAT_X32_TYPELESS_G8X24_UINT;
+ stencilFormat = PixelFormat::X32_Typeless_G8X24_UInt;
+ break;
+ }
+ if (isCubeMap)
+ {
+ srDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
+ srDesc.TextureCube.MostDetailedMip = 0;
+ srDesc.TextureCube.MipLevels = mipLevels;
+ }
+ else if (isMsaa)
+ {
+ srDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMS;
+ }
+ else
+ {
+ srDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
+ srDesc.Texture2D.MostDetailedMip = 0;
+ srDesc.Texture2D.MipLevels = mipLevels;
+ }
+ VALIDATE_DIRECTX_CALL(device->CreateShaderResourceView(_resource, &srDesc, &srView));
+ _handleStencil.Init(this, nullptr, srView, nullptr, nullptr, stencilFormat, msaa);
+ }
}
bool GPUTextureDX11::GetData(int32 arrayIndex, int32 mipMapIndex, TextureMipData& data, uint32 mipRowPitch)
diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTextureDX11.h b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTextureDX11.h
index d33c56158..35101e79b 100644
--- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTextureDX11.h
+++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTextureDX11.h
@@ -158,6 +158,7 @@ private:
GPUTextureViewDX11 _handleArray;
GPUTextureViewDX11 _handleVolume;
GPUTextureViewDX11 _handleReadOnlyDepth;
+ GPUTextureViewDX11 _handleStencil;
Array _handlesPerSlice; // [slice]
Array> _handlesPerMip; // [slice][mip]
@@ -225,6 +226,11 @@ public:
ASSERT(_desc.Flags & GPUTextureFlags::ReadOnlyDepthView);
return (GPUTextureView*)&_handleReadOnlyDepth;
}
+ GPUTextureView* ViewStencil() const override
+ {
+ ASSERT(_desc.Flags & GPUTextureFlags::DepthStencil);
+ return (GPUTextureView*)&_handleStencil;
+ }
void* GetNativePtr() const override
{
return static_cast(_resource);
diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp
index c68e7f262..64c384447 100644
--- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp
+++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp
@@ -36,6 +36,7 @@
#include "CommandSignatureDX12.h"
#include "Engine/Profiler/RenderStats.h"
#include "Engine/Graphics/GPUResourceAccess.h"
+#include "Engine/Graphics/PixelFormatExtensions.h"
#include "Engine/Graphics/Shaders/GPUShader.h"
#include "Engine/Threading/Threading.h"
@@ -823,7 +824,10 @@ void GPUContextDX12::ClearDepth(GPUTextureView* depthBuffer, float depthValue, u
SetResourceState(depthBufferDX12->GetResourceOwner(), D3D12_RESOURCE_STATE_DEPTH_WRITE, depthBufferDX12->SubresourceIndex);
flushRBs();
- _commandList->ClearDepthStencilView(depthBufferDX12->DSV(), D3D12_CLEAR_FLAG_DEPTH, depthValue, stencilValue, 0, nullptr);
+ D3D12_CLEAR_FLAGS clearFlags = D3D12_CLEAR_FLAG_DEPTH;
+ if (PixelFormatExtensions::HasStencil(depthBufferDX12->GetFormat()))
+ clearFlags |= D3D12_CLEAR_FLAG_STENCIL;
+ _commandList->ClearDepthStencilView(depthBufferDX12->DSV(), clearFlags, depthValue, stencilValue, 0, nullptr);
}
}
diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp
index 5913468ee..abecfd776 100644
--- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp
+++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp
@@ -227,6 +227,8 @@ void GPUTextureDX12::OnReleaseGPU()
_handlesPerSlice.Resize(0, false);
_handleArray.Release();
_handleVolume.Release();
+ _handleReadOnlyDepth.Release();
+ _handleStencil.Release();
_srv.Release();
_uav.Release();
releaseResource();
@@ -721,6 +723,45 @@ void GPUTextureDX12::initHandles()
_handleReadOnlyDepth.SetSRV(srDesc);
}
}
+
+ // Stencil view
+ if (useDSV && useSRV && PixelFormatExtensions::HasStencil(format))
+ {
+ PixelFormat stencilFormat;
+ switch (_dxgiFormatDSV)
+ {
+ case PixelFormat::D24_UNorm_S8_UInt:
+ srDesc.Format = DXGI_FORMAT_X24_TYPELESS_G8_UINT;
+ stencilFormat = PixelFormat::X24_Typeless_G8_UInt;
+ break;
+ case PixelFormat::D32_Float_S8X24_UInt:
+ srDesc.Format = DXGI_FORMAT_X32_TYPELESS_G8X24_UINT;
+ stencilFormat = PixelFormat::X32_Typeless_G8X24_UInt;
+ break;
+ }
+ if (isCubeMap)
+ {
+ srDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
+ srDesc.TextureCube.MostDetailedMip = 0;
+ srDesc.TextureCube.MipLevels = mipLevels;
+ srDesc.TextureCube.ResourceMinLODClamp = 0;
+ }
+ else if (isMsaa)
+ {
+ srDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DMS;
+ }
+ else
+ {
+ srDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
+ srDesc.Texture2D.MostDetailedMip = 0;
+ srDesc.Texture2D.MipLevels = mipLevels;
+ srDesc.Texture2D.ResourceMinLODClamp = 0;
+ srDesc.Texture2D.PlaneSlice = 1;
+ }
+ _handleStencil.Init(this, _device, this, stencilFormat, msaa);
+ _handleStencil.ReadOnlyDepthView = true;
+ _handleStencil.SetSRV(srDesc);
+ }
}
#endif
diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.h
index e6427ab6d..b513b5b4f 100644
--- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.h
+++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.h
@@ -91,7 +91,7 @@ public:
// [IShaderResourceDX12]
bool IsDepthStencilResource() const override
{
- return _dsv.IsValid();
+ return ReadOnlyDepthView || _dsv.IsValid();
}
D3D12_CPU_DESCRIPTOR_HANDLE SRV() const override
{
@@ -117,6 +117,7 @@ private:
GPUTextureViewDX12 _handleArray;
GPUTextureViewDX12 _handleVolume;
GPUTextureViewDX12 _handleReadOnlyDepth;
+ GPUTextureViewDX12 _handleStencil;
Array _handlesPerSlice; // [slice]
Array> _handlesPerMip; // [slice][mip]
@@ -165,6 +166,11 @@ public:
ASSERT(_desc.Flags & GPUTextureFlags::ReadOnlyDepthView);
return (GPUTextureView*)&_handleReadOnlyDepth;
}
+ GPUTextureView* ViewStencil() const override
+ {
+ ASSERT(_desc.Flags & GPUTextureFlags::DepthStencil);
+ return (GPUTextureView*)&_handleStencil;
+ }
void* GetNativePtr() const override
{
return (void*)_resource;
diff --git a/Source/Engine/GraphicsDevice/Null/GPUTextureNull.h b/Source/Engine/GraphicsDevice/Null/GPUTextureNull.h
index 25b204184..23e7d2b54 100644
--- a/Source/Engine/GraphicsDevice/Null/GPUTextureNull.h
+++ b/Source/Engine/GraphicsDevice/Null/GPUTextureNull.h
@@ -33,6 +33,10 @@ public:
{
return nullptr;
}
+ GPUTextureView* ViewStencil() const override
+ {
+ return nullptr;
+ }
void* GetNativePtr() const override
{
return nullptr;
diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.cpp
index 326c87e8d..52f347959 100644
--- a/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.cpp
+++ b/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.cpp
@@ -11,7 +11,7 @@
#include "Engine/Graphics/Textures/TextureData.h"
#include "Engine/Scripting/Enums.h"
-void GPUTextureViewVulkan::Init(GPUDeviceVulkan* device, ResourceOwnerVulkan* owner, VkImage image, int32 totalMipLevels, PixelFormat format, MSAALevel msaa, VkExtent3D extent, VkImageViewType viewType, int32 mipLevels, int32 firstMipIndex, int32 arraySize, int32 firstArraySlice, bool readOnlyDepth)
+void GPUTextureViewVulkan::Init(GPUDeviceVulkan* device, ResourceOwnerVulkan* owner, VkImage image, int32 totalMipLevels, PixelFormat format, MSAALevel msaa, VkExtent3D extent, VkImageViewType viewType, int32 mipLevels, int32 firstMipIndex, int32 arraySize, int32 firstArraySlice, bool readOnlyDepth, bool stencilView)
{
ASSERT(View == VK_NULL_HANDLE);
@@ -57,7 +57,13 @@ void GPUTextureViewVulkan::Init(GPUDeviceVulkan* device, ResourceOwnerVulkan* ow
SubresourceIndex = RenderTools::CalcSubresourceIndex(firstMipIndex, firstArraySlice, totalMipLevels);
}
- if (PixelFormatExtensions::IsDepthStencil(format))
+ if (stencilView)
+ {
+ range.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT;
+ LayoutRTV = readOnlyDepth ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+ LayoutSRV = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
+ }
+ else if (PixelFormatExtensions::IsDepthStencil(format))
{
range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
#if 0
@@ -157,13 +163,23 @@ void GPUTextureViewVulkan::DescriptorAsImage(GPUContextVulkan* context, VkImageV
imageView = View;
layout = LayoutSRV;
const VkImageAspectFlags aspectMask = Info.subresourceRange.aspectMask;
- if (aspectMask == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT))
+ if (aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT))
{
- // Transition depth-only when binding depth buffer with stencil
+ // Transition depth-only when binding depth buffer with stencil (or stencil-only without depth)
if (ViewSRV == VK_NULL_HANDLE)
{
VkImageViewCreateInfo createInfo = Info;
- createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
+ if (aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT)
+ {
+ // Stencil
+ createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT;
+ createInfo.components.g = VK_COMPONENT_SWIZZLE_R; // Map .g component in shader to .r of stencil plane
+ }
+ else
+ {
+ // Depth
+ createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
+ }
VALIDATE_VULKAN_RESULT(vkCreateImageView(Device->Device, &createInfo, nullptr, &ViewSRV));
}
imageView = ViewSRV;
@@ -434,6 +450,26 @@ void GPUTextureVulkan::initHandles()
{
_handleReadOnlyDepth.Init(_device, this, _image, mipLevels, format, msaa, extent, VK_IMAGE_VIEW_TYPE_2D, mipLevels, 0, 1, 0, true);
}
+
+ // Stencil view
+ if (IsDepthStencil() && IsShaderResource() && PixelFormatExtensions::HasStencil(format))
+ {
+ PixelFormat stencilFormat;
+ switch (format)
+ {
+ case PixelFormat::D24_UNorm_S8_UInt:
+ case PixelFormat::R24_UNorm_X8_Typeless:
+ case PixelFormat::R24G8_Typeless:
+ stencilFormat = PixelFormat::X24_Typeless_G8_UInt;
+ break;
+ case PixelFormat::D32_Float_S8X24_UInt:
+ case PixelFormat::R32_Float_X8X24_Typeless:
+ case PixelFormat::R32G8X24_Typeless:
+ stencilFormat = PixelFormat::X32_Typeless_G8X24_UInt;
+ break;
+ }
+ _handleStencil.Init(_device, this, _image, mipLevels, stencilFormat, msaa, extent, VK_IMAGE_VIEW_TYPE_2D, mipLevels, 0, 1, 0, true, true);
+ }
}
void GPUTextureVulkan::OnResidentMipsChanged()
@@ -457,6 +493,7 @@ void GPUTextureVulkan::OnReleaseGPU()
_handleVolume.Release();
_handleUAV.Release();
_handleReadOnlyDepth.Release();
+ _handleStencil.Release();
for (int32 i = 0; i < _handlesPerMip.Count(); i++)
{
for (int32 j = 0; j < _handlesPerMip[i].Count(); j++)
diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.h
index 999e0f602..178938b82 100644
--- a/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.h
+++ b/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.h
@@ -45,7 +45,7 @@ public:
#endif
public:
- void Init(GPUDeviceVulkan* device, ResourceOwnerVulkan* owner, VkImage image, int32 totalMipLevels, PixelFormat format, MSAALevel msaa, VkExtent3D extent, VkImageViewType viewType, int32 mipLevels = 1, int32 firstMipIndex = 0, int32 arraySize = 1, int32 firstArraySlice = 0, bool readOnlyDepth = false);
+ void Init(GPUDeviceVulkan* device, ResourceOwnerVulkan* owner, VkImage image, int32 totalMipLevels, PixelFormat format, MSAALevel msaa, VkExtent3D extent, VkImageViewType viewType, int32 mipLevels = 1, int32 firstMipIndex = 0, int32 arraySize = 1, int32 firstArraySlice = 0, bool readOnlyDepth = false, bool stencilView = false);
VkImageView GetFramebufferView();
@@ -79,6 +79,7 @@ private:
GPUTextureViewVulkan _handleVolume;
GPUTextureViewVulkan _handleUAV;
GPUTextureViewVulkan _handleReadOnlyDepth;
+ GPUTextureViewVulkan _handleStencil;
Array _handlesPerSlice; // [slice]
Array> _handlesPerMip; // [slice][mip]
@@ -140,6 +141,11 @@ public:
ASSERT(_desc.Flags & GPUTextureFlags::ReadOnlyDepthView);
return (GPUTextureView*)&_handleReadOnlyDepth;
}
+ GPUTextureView* ViewStencil() const override
+ {
+ ASSERT(_desc.Flags & GPUTextureFlags::DepthStencil);
+ return (GPUTextureView*)&_handleStencil;
+ }
void* GetNativePtr() const override
{
return (void*)_image;
diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp
index 18342dbbb..ee66c6c77 100644
--- a/Source/Engine/Level/Actors/AnimatedModel.cpp
+++ b/Source/Engine/Level/Actors/AnimatedModel.cpp
@@ -1027,6 +1027,7 @@ void AnimatedModel::Draw(RenderContext& renderContext)
draw.LODBias = LODBias;
draw.ForcedLOD = ForcedLOD;
draw.SortOrder = SortOrder;
+ draw.SetStencilValue(_layer);
SkinnedModel->Draw(renderContext, draw);
}
@@ -1068,6 +1069,7 @@ void AnimatedModel::Draw(RenderContextBatch& renderContextBatch)
draw.LODBias = LODBias;
draw.ForcedLOD = ForcedLOD;
draw.SortOrder = SortOrder;
+ draw.SetStencilValue(_layer);
PRAGMA_DISABLE_DEPRECATION_WARNINGS
if (ShadowsMode != ShadowsCastingMode::All)
diff --git a/Source/Engine/Level/Actors/Camera.cpp b/Source/Engine/Level/Actors/Camera.cpp
index 490f9d786..4a7d2aa75 100644
--- a/Source/Engine/Level/Actors/Camera.cpp
+++ b/Source/Engine/Level/Actors/Camera.cpp
@@ -374,6 +374,7 @@ void Camera::Draw(RenderContext& renderContext)
BoundingSphere::FromBox(_previewModelBox, draw.Bounds);
draw.Bounds.Center -= renderContext.View.Origin;
draw.PerInstanceRandom = GetPerInstanceRandom();
+ draw.StencilValue = 0;
draw.LODBias = 0;
draw.ForcedLOD = -1;
draw.SortOrder = 0;
diff --git a/Source/Engine/Level/Actors/Skybox.cpp b/Source/Engine/Level/Actors/Skybox.cpp
index e08d8a61a..dbda65d3f 100644
--- a/Source/Engine/Level/Actors/Skybox.cpp
+++ b/Source/Engine/Level/Actors/Skybox.cpp
@@ -107,7 +107,6 @@ void Skybox::ApplySky(GPUContext* context, RenderContext& renderContext, const M
drawCall.ObjectPosition = drawCall.World.GetTranslation();
drawCall.ObjectRadius = (float)_sphere.Radius;
drawCall.Surface.GeometrySize = _box.GetSize();
- drawCall.WorldDeterminantSign = RenderTools::GetWorldDeterminantSign(drawCall.World);
drawCall.PerInstanceRandom = GetPerInstanceRandom();
MaterialBase::BindParameters bindParams(context, renderContext, drawCall);
bindParams.BindViewData();
diff --git a/Source/Engine/Level/Actors/SplineModel.cpp b/Source/Engine/Level/Actors/SplineModel.cpp
index 8680fb8d5..42b2dc620 100644
--- a/Source/Engine/Level/Actors/SplineModel.cpp
+++ b/Source/Engine/Level/Actors/SplineModel.cpp
@@ -407,12 +407,12 @@ void SplineModel::Draw(RenderContext& renderContext)
drawCall.Deformable.MeshMaxZ = _meshMaxZ;
drawCall.Deformable.GeometrySize = _box.GetSize();
drawCall.PerInstanceRandom = GetPerInstanceRandom();
+ drawCall.SetStencilValue(_layer);
_preTransform.GetWorld(drawCall.Deformable.LocalMatrix);
const Transform splineTransform = GetTransform();
renderContext.View.GetWorldMatrix(splineTransform, drawCall.World);
drawCall.ObjectPosition = drawCall.World.GetTranslation() + drawCall.Deformable.LocalMatrix.GetTranslation();
drawCall.ObjectRadius = (float)_sphere.Radius; // TODO: use radius for the spline chunk rather than whole spline
- const float worldDeterminantSign = drawCall.World.RotDeterminant() * drawCall.Deformable.LocalMatrix.RotDeterminant();
for (int32 segment = 0; segment < _instances.Count(); segment++)
{
auto& instance = _instances[segment];
@@ -468,7 +468,6 @@ void SplineModel::Draw(RenderContext& renderContext)
// Submit draw call
mesh->GetDrawCallGeometry(drawCall);
drawCall.Material = material;
- drawCall.WorldDeterminantSign = Math::FloatSelect(worldDeterminantSign * instance.RotDeterminant, 1, -1);
renderContext.List->AddDrawCall(renderContext, drawModes, _staticFlags, drawCall, entry.ReceiveDecals);
}
}
diff --git a/Source/Engine/Level/Actors/StaticModel.cpp b/Source/Engine/Level/Actors/StaticModel.cpp
index 912009b3b..f41e4a805 100644
--- a/Source/Engine/Level/Actors/StaticModel.cpp
+++ b/Source/Engine/Level/Actors/StaticModel.cpp
@@ -361,6 +361,7 @@ void StaticModel::Draw(RenderContext& renderContext)
draw.ForcedLOD = _forcedLod;
draw.SortOrder = _sortOrder;
draw.VertexColors = _vertexColorsCount ? _vertexColorsBuffer : nullptr;
+ draw.SetStencilValue(_layer);
#if USE_EDITOR
if (HasStaticFlag(StaticFlags::Lightmap))
draw.LightmapScale = _scaleInLightmap;
@@ -397,6 +398,7 @@ void StaticModel::Draw(RenderContextBatch& renderContextBatch)
draw.ForcedLOD = _forcedLod;
draw.SortOrder = _sortOrder;
draw.VertexColors = _vertexColorsCount ? _vertexColorsBuffer : nullptr;
+ draw.SetStencilValue(_layer);
#if USE_EDITOR
if (HasStaticFlag(StaticFlags::Lightmap))
draw.LightmapScale = _scaleInLightmap;
diff --git a/Source/Engine/Particles/Particles.cpp b/Source/Engine/Particles/Particles.cpp
index 487bc4b03..03da3e324 100644
--- a/Source/Engine/Particles/Particles.cpp
+++ b/Source/Engine/Particles/Particles.cpp
@@ -1168,9 +1168,6 @@ void Particles::DrawParticles(RenderContextBatch& renderContextBatch, ParticleEf
Matrix worlds[2];
Matrix::Translation(-viewOrigin, worlds[0]); // World
renderContextBatch.GetMainContext().View.GetWorldMatrix(effect->GetTransform(), worlds[1]); // Local
- float worldDeterminantSigns[2];
- worldDeterminantSigns[0] = Math::FloatSelect(worlds[0].RotDeterminant(), 1, -1);
- worldDeterminantSigns[1] = Math::FloatSelect(worlds[1].RotDeterminant(), 1, -1);
const StaticFlags staticFlags = effect->GetStaticFlags();
const int8 sortOrder = effect->SortOrder;
@@ -1194,6 +1191,7 @@ void Particles::DrawParticles(RenderContextBatch& renderContextBatch, ParticleEf
// Setup a draw call common data
DrawCall drawCall;
drawCall.PerInstanceRandom = effect->GetPerInstanceRandom();
+ drawCall.SetStencilValue(effect->GetLayer());
drawCall.ObjectPosition = bounds.Center;
drawCall.ObjectRadius = (float)bounds.Radius;
@@ -1209,7 +1207,6 @@ void Particles::DrawParticles(RenderContextBatch& renderContextBatch, ParticleEf
continue;
drawCall.World = worlds[(int32)emitter->SimulationSpace];
- drawCall.WorldDeterminantSign = worldDeterminantSigns[(int32)emitter->SimulationSpace];
drawCall.Particle.Particles = buffer;
// Check if need to render any module
diff --git a/Source/Engine/Renderer/DrawCall.h b/Source/Engine/Renderer/DrawCall.h
index 1a8d23285..b71b64874 100644
--- a/Source/Engine/Renderer/DrawCall.h
+++ b/Source/Engine/Renderer/DrawCall.h
@@ -265,16 +265,23 @@ struct DrawCall
///
float ObjectRadius;
- ///
- /// The world matrix determinant sign (used for geometry that is two sided or has inverse scale - needs to flip normal vectors and change triangles culling).
- ///
- float WorldDeterminantSign;
-
///
/// The random per-instance value (normalized to range 0-1).
///
float PerInstanceRandom;
+ ///
+ /// The 8-bit stencil value to write into Depth-Stencil Buffer.
+ ///
+ uint8 StencilValue;
+
+ ///
+ /// The world matrix determinant sign (used for geometry that is two sided or has inverse scale - needs to flip normal vectors and change triangles culling).
+ /// 0 - sign is positive
+ /// 1 - sign is negative (flips object surfaces)
+ ///
+ uint8 WorldDeterminant : 1;
+
///
/// The sorting key for the draw call calculate by RenderList.
///
@@ -287,6 +294,12 @@ struct DrawCall
{
Platform::MemoryClear(this, sizeof(DrawCall));
}
+
+ // Packs object layer into the stencil bits.
+ FORCE_INLINE void SetStencilValue(int32 layer)
+ {
+ StencilValue = uint8(layer & 0x1f);
+ }
};
template<>
diff --git a/Source/Engine/Renderer/Editor/MaterialComplexity.cpp b/Source/Engine/Renderer/Editor/MaterialComplexity.cpp
index 41283f63e..eb4afc26c 100644
--- a/Source/Engine/Renderer/Editor/MaterialComplexity.cpp
+++ b/Source/Engine/Renderer/Editor/MaterialComplexity.cpp
@@ -129,8 +129,6 @@ void MaterialComplexityMaterialShader::Draw(RenderContext& renderContext, GPUCon
DrawCall drawCall;
MaterialBase::BindParameters bindParams(context, renderContext, drawCall);
bindParams.BindViewData();
- drawCall.WorldDeterminantSign = 1.0f;
- drawCall.PerInstanceRandom = 0.0f;
context->SetRenderTarget(lightBuffer);
for (int32 i = 0; i < decals.Count(); i++)
{
diff --git a/Source/Engine/Renderer/Editor/QuadOverdrawPass.cpp b/Source/Engine/Renderer/Editor/QuadOverdrawPass.cpp
index c6804f300..3c38d11a4 100644
--- a/Source/Engine/Renderer/Editor/QuadOverdrawPass.cpp
+++ b/Source/Engine/Renderer/Editor/QuadOverdrawPass.cpp
@@ -50,8 +50,6 @@ void QuadOverdrawPass::Render(RenderContext& renderContext, GPUContext* context,
context->BindUA(1, overdrawTexture->View());
context->BindUA(2, liveCountTexture->View());
DrawCall drawCall;
- drawCall.WorldDeterminantSign = 1.0f;
- drawCall.PerInstanceRandom = 0.0f;
MaterialBase::BindParameters bindParams(context, renderContext, drawCall);
bindParams.BindViewData();
renderContext.View.Pass = DrawPass::QuadOverdraw;
@@ -83,7 +81,6 @@ void QuadOverdrawPass::Render(RenderContext& renderContext, GPUContext* context,
m1 *= m2;
drawCall.World = m1;
drawCall.ObjectPosition = drawCall.World.GetTranslation();
- drawCall.WorldDeterminantSign = RenderTools::GetWorldDeterminantSign(drawCall.World);
skyMaterial->Bind(bindParams);
skyModel->Render(context);
}
diff --git a/Source/Engine/Renderer/RenderList.cpp b/Source/Engine/Renderer/RenderList.cpp
index 72b9a4e20..bb580aaf3 100644
--- a/Source/Engine/Renderer/RenderList.cpp
+++ b/Source/Engine/Renderer/RenderList.cpp
@@ -564,7 +564,8 @@ FORCE_INLINE void CalculateSortKey(const RenderContext& renderContext, DrawCall&
IMaterial::InstancingHandler handler;
if (drawCall.Material->CanUseInstancing(handler))
handler.GetHash(drawCall, batchKey);
- uint32 drawKey = (uint32)(471 * drawCall.WorldDeterminantSign);
+ batchKey = (batchKey * 397) ^ drawCall.StencilValue;
+ uint32 drawKey = (uint32)(471 * drawCall.WorldDeterminant);
drawKey = (drawKey * 397) ^ GetHash(drawCall.Geometry.VertexBuffers[0]);
drawKey = (drawKey * 397) ^ GetHash(drawCall.Geometry.VertexBuffers[1]);
drawKey = (drawKey * 397) ^ GetHash(drawCall.Geometry.VertexBuffers[2]);
@@ -586,8 +587,11 @@ void RenderList::AddDrawCall(const RenderContext& renderContext, DrawPass drawMo
ASSERT_LOW_LAYER(drawModes != DrawPass::None && ((uint32)drawModes & ~(uint32)materialDrawModes) == 0);
#endif
- // Append draw call data
+ // Finalize draw call initialization
+ drawCall.WorldDeterminant = drawCall.World.RotDeterminant() < 0 ? 1 : 0;
CalculateSortKey(renderContext, drawCall, sortOrder);
+
+ // Append draw call data
const int32 index = DrawCalls.Add(drawCall);
// Add draw call to proper draw lists
@@ -625,8 +629,11 @@ void RenderList::AddDrawCall(const RenderContextBatch& renderContextBatch, DrawP
#endif
const RenderContext& mainRenderContext = renderContextBatch.Contexts.Get()[0];
- // Append draw call data
+ // Finalize draw call initialization
+ drawCall.WorldDeterminant = drawCall.World.RotDeterminant() < 0 ? 1 : 0;
CalculateSortKey(mainRenderContext, drawCall, sortOrder);
+
+ // Append draw call data
const int32 index = DrawCalls.Add(drawCall);
// Add draw call to proper draw lists
@@ -777,7 +784,8 @@ void RenderList::SortDrawCalls(const RenderContext& renderContext, bool reverseD
other.InstanceCount != 0 &&
drawCallHandler.CanBatch == otherHandler.CanBatch &&
drawCallHandler.CanBatch(drawCall, other, pass) &&
- drawCall.WorldDeterminantSign * other.WorldDeterminantSign > 0;
+ drawCall.WorldDeterminant == other.WorldDeterminant &&
+ drawCall.StencilValue == other.StencilValue;
if (!canBatch)
break;
batchSize++;
diff --git a/Source/Engine/Renderer/RenderList.h b/Source/Engine/Renderer/RenderList.h
index 24262162d..fc11b26cc 100644
--- a/Source/Engine/Renderer/RenderList.h
+++ b/Source/Engine/Renderer/RenderList.h
@@ -651,13 +651,15 @@ GPU_CB_STRUCT(ShaderObjectData
FORCE_INLINE void Store(const DrawCall& drawCall)
{
- Store(drawCall.World, drawCall.Surface.PrevWorld, drawCall.Surface.LightmapUVsArea, drawCall.Surface.GeometrySize, drawCall.PerInstanceRandom, drawCall.WorldDeterminantSign, drawCall.Surface.LODDitherFactor);
+ Store(drawCall.World, drawCall.Surface.PrevWorld, drawCall.Surface.LightmapUVsArea, drawCall.Surface.GeometrySize, drawCall.PerInstanceRandom, drawCall.WorldDeterminant ? -1.0f : 1.0f, drawCall.Surface.LODDitherFactor);
}
FORCE_INLINE void Load(DrawCall& drawCall) const
{
- Load(drawCall.World, drawCall.Surface.PrevWorld, drawCall.Surface.LightmapUVsArea, drawCall.Surface.GeometrySize, drawCall.PerInstanceRandom, drawCall.WorldDeterminantSign, drawCall.Surface.LODDitherFactor);
+ float worldDeterminantSign;
+ Load(drawCall.World, drawCall.Surface.PrevWorld, drawCall.Surface.LightmapUVsArea, drawCall.Surface.GeometrySize, drawCall.PerInstanceRandom, worldDeterminantSign, drawCall.Surface.LODDitherFactor);
drawCall.ObjectPosition = drawCall.World.GetTranslation();
+ drawCall.WorldDeterminant = worldDeterminantSign < 0 ? 1 : 0;
}
});
diff --git a/Source/Engine/Terrain/TerrainChunk.cpp b/Source/Engine/Terrain/TerrainChunk.cpp
index 513f6af97..d1bcb7842 100644
--- a/Source/Engine/Terrain/TerrainChunk.cpp
+++ b/Source/Engine/Terrain/TerrainChunk.cpp
@@ -122,8 +122,8 @@ void TerrainChunk::Draw(const RenderContext& renderContext) const
drawCall.Terrain.Lightmap = nullptr;
drawCall.Terrain.LightmapUVsArea = Rectangle::Empty;
}
- drawCall.WorldDeterminantSign = RenderTools::GetWorldDeterminantSign(drawCall.World);
drawCall.PerInstanceRandom = _perInstanceRandom;
+ drawCall.SetStencilValue(_patch->_terrain->GetLayer());
#if USE_EDITOR
if (renderContext.View.Mode == ViewMode::LightmapUVsDensity)
drawCall.Surface.LODDitherFactor = 1.0f; // See LightmapUVsDensityMaterialShader
@@ -183,8 +183,8 @@ void TerrainChunk::Draw(const RenderContext& renderContext, MaterialBase* materi
drawCall.Terrain.Lightmap = nullptr;
drawCall.Terrain.LightmapUVsArea = Rectangle::Empty;
}
- drawCall.WorldDeterminantSign = RenderTools::GetWorldDeterminantSign(drawCall.World);
drawCall.PerInstanceRandom = _perInstanceRandom;
+ drawCall.SetStencilValue(_patch->_terrain->GetLayer());
#if USE_EDITOR
if (renderContext.View.Mode == ViewMode::LightmapUVsDensity)
drawCall.Surface.LODDitherFactor = 1.0f; // See LightmapUVsDensityMaterialShader
diff --git a/Source/Engine/UI/TextRender.cpp b/Source/Engine/UI/TextRender.cpp
index 52132b972..c2f6789c7 100644
--- a/Source/Engine/UI/TextRender.cpp
+++ b/Source/Engine/UI/TextRender.cpp
@@ -390,8 +390,8 @@ void TextRender::Draw(RenderContext& renderContext)
drawCall.ObjectRadius = (float)_sphere.Radius;
drawCall.Surface.GeometrySize = _localBox.GetSize();
drawCall.Surface.PrevWorld = _drawState.PrevWorld;
- drawCall.WorldDeterminantSign = RenderTools::GetWorldDeterminantSign(drawCall.World);
drawCall.PerInstanceRandom = GetPerInstanceRandom();
+ drawCall.SetStencilValue(_layer);
drawCall.Geometry.IndexBuffer = _ib.GetBuffer();
drawCall.Geometry.VertexBuffers[0] = _vb.GetBuffer();
drawCall.InstanceCount = 1;
diff --git a/Source/Shaders/Stencil.hlsl b/Source/Shaders/Stencil.hlsl
new file mode 100644
index 000000000..7cb72f024
--- /dev/null
+++ b/Source/Shaders/Stencil.hlsl
@@ -0,0 +1,23 @@
+// Copyright (c) Wojciech Figat. All rights reserved.
+
+#ifndef __STENCIL__
+#define __STENCIL__
+
+#include "./Flax/Common.hlsl"
+
+// Stencil bits usage (must match RenderBuffers.h)
+// [0] | Object Layer
+// [1] | Object Layer
+// [2] | Object Layer
+// [3] | Object Layer
+// [4] | Object Layer
+// [5] |
+// [6] |
+// [7] |
+#define STENCIL_BUFFER_OBJECT_LAYER(stencil) uint(stencil & 0x1f)
+#ifndef STENCIL_BUFFER_SWIZZLE
+#define STENCIL_BUFFER_SWIZZLE .g
+#endif
+#define STENCIL_BUFFER_LOAD(rt, pos) rt.Load(int3(pos, 0)) STENCIL_BUFFER_SWIZZLE
+
+#endif