diff --git a/Content/Shaders/Editor/LODPreview.flax b/Content/Shaders/Editor/LODPreview.flax
new file mode 100644
index 000000000..13f4d05e1
--- /dev/null
+++ b/Content/Shaders/Editor/LODPreview.flax
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b320422e51839935dc81d80b83de54aa5200d27ba97a90f49f84e4c99fe8b97d
+size 3404
diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs
index bae4d31a8..fa35eda94 100644
--- a/Source/Editor/Viewport/EditorViewport.cs
+++ b/Source/Editor/Viewport/EditorViewport.cs
@@ -1397,6 +1397,7 @@ namespace FlaxEditor.Viewport
new ViewModeOptions(ViewMode.LightmapUVsDensity, "Lightmap UVs Density"),
new ViewModeOptions(ViewMode.VertexColors, "Vertex Colors"),
new ViewModeOptions(ViewMode.PhysicsColliders, "Physics Colliders"),
+ new ViewModeOptions(ViewMode.LODPreview, "LOD Preview"),
};
private void WidgetCamSpeedShowHide(Control cm)
diff --git a/Source/Engine/Graphics/Enums.h b/Source/Engine/Graphics/Enums.h
index af03678e9..6ab7abdf7 100644
--- a/Source/Engine/Graphics/Enums.h
+++ b/Source/Engine/Graphics/Enums.h
@@ -813,6 +813,11 @@ API_ENUM() enum class ViewMode
/// Draw physics colliders debug view
///
PhysicsColliders = 20,
+
+ ///
+ /// Draw Level Of Detail number as colors to debug LOD switches.
+ ///
+ LODPreview = 21,
};
///
diff --git a/Source/Engine/Renderer/Editor/LODPreview.cpp b/Source/Engine/Renderer/Editor/LODPreview.cpp
new file mode 100644
index 000000000..cdbbc4135
--- /dev/null
+++ b/Source/Engine/Renderer/Editor/LODPreview.cpp
@@ -0,0 +1,127 @@
+// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
+
+#if USE_EDITOR
+
+#include "LODPreview.h"
+#include "Engine/Content/Content.h"
+#include "Engine/Content/Assets/Model.h"
+#include "Engine/Graphics/GPUDevice.h"
+#include "Engine/Graphics/GPUPipelineState.h"
+#include "Engine/Graphics/Shaders/GPUShader.h"
+#include "Engine/Graphics/Shaders/GPUConstantBuffer.h"
+#include "Engine/Graphics/RenderTask.h"
+#include "Engine/Renderer/DrawCall.h"
+
+PACK_STRUCT(struct LODPreviewMaterialShaderData {
+ Matrix ViewProjectionMatrix;
+ Matrix WorldMatrix;
+ Color Color;
+ Vector3 WorldInvScale;
+ float LODDitherFactor;
+ });
+
+LODPreviewMaterialShader::LODPreviewMaterialShader()
+{
+ _ps = GPUDevice::Instance->CreatePipelineState();
+ _shader = Content::LoadAsyncInternal(TEXT("Shaders/Editor/LODPreview"));
+ if (!_shader)
+ return;
+#if COMPILE_WITH_DEV_ENV
+ _shader.Get()->OnReloading.Bind(this);
+#endif
+}
+
+#if COMPILE_WITH_DEV_ENV
+
+void LODPreviewMaterialShader::OnShaderReloading(Asset* obj)
+{
+ _ps->ReleaseGPU();
+}
+
+#endif
+
+const MaterialInfo& LODPreviewMaterialShader::GetInfo() const
+{
+ return _info;
+}
+
+bool LODPreviewMaterialShader::IsReady() const
+{
+ return _shader && _shader->IsLoaded();
+}
+
+DrawPass LODPreviewMaterialShader::GetDrawModes() const
+{
+ return DrawPass::GBuffer;
+}
+
+void LODPreviewMaterialShader::Bind(BindParameters& params)
+{
+ auto context = params.GPUContext;
+ auto& drawCall = *params.FirstDrawCall;
+ auto shader = _shader->GetShader();
+ auto cb = shader->GetCB(0);
+ if (!_ps->IsValid())
+ {
+ auto psDesc = GPUPipelineState::Description::Default;
+ psDesc.VS = shader->GetVS("VS");
+ psDesc.PS = shader->GetPS("PS");
+ _ps->Init(psDesc);
+ }
+
+ // Find the LOD that produced this draw call
+ int32 lodIndex = 0;
+ for (auto& e : Content::GetAssetsRaw())
+ {
+ auto model = ScriptingObject::Cast(e.Value);
+ if (!model)
+ continue;
+ bool found = false;
+ for (const auto& lod : model->LODs)
+ {
+ for (const auto& mesh : lod.Meshes)
+ {
+ if (mesh.GetIndexBuffer() == drawCall.Geometry.IndexBuffer)
+ {
+ lodIndex = mesh.GetLODIndex();
+ found = true;
+ break;
+ }
+ }
+ }
+ if (found)
+ break;
+ }
+
+ // Bind
+ if (cb && cb->GetSize())
+ {
+ ASSERT(cb->GetSize() == sizeof(LODPreviewMaterialShaderData));
+ LODPreviewMaterialShaderData data;
+ Matrix::Transpose(params.RenderContext.View.Frustum.GetMatrix(), data.ViewProjectionMatrix);
+ Matrix::Transpose(drawCall.World, data.WorldMatrix);
+ const float scaleX = Vector3(drawCall.World.M11, drawCall.World.M12, drawCall.World.M13).Length();
+ const float scaleY = Vector3(drawCall.World.M21, drawCall.World.M22, drawCall.World.M23).Length();
+ const float scaleZ = Vector3(drawCall.World.M31, drawCall.World.M32, drawCall.World.M33).Length();
+ data.WorldInvScale = Vector3(
+ scaleX > 0.00001f ? 1.0f / scaleX : 0.0f,
+ scaleY > 0.00001f ? 1.0f / scaleY : 0.0f,
+ scaleZ > 0.00001f ? 1.0f / scaleZ : 0.0f);
+ const Color colors[MODEL_MAX_LODS] = {
+ Color::White,
+ Color::Red,
+ Color::Orange,
+ Color::Yellow,
+ Color::Green,
+ Color::Blue,
+ };
+ ASSERT(lodIndex < MODEL_MAX_LODS);
+ data.Color = colors[lodIndex];
+ data.LODDitherFactor = drawCall.Surface.LODDitherFactor;
+ context->UpdateCB(cb, &data);
+ context->BindCB(0, cb);
+ }
+ context->SetState(_ps);
+}
+
+#endif
diff --git a/Source/Engine/Renderer/Editor/LODPreview.h b/Source/Engine/Renderer/Editor/LODPreview.h
new file mode 100644
index 000000000..c5ab05f1e
--- /dev/null
+++ b/Source/Engine/Renderer/Editor/LODPreview.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
+
+#pragma once
+
+#if USE_EDITOR
+
+#include "Engine/Content/AssetReference.h"
+#include "Engine/Content/Assets/Shader.h"
+#include "Engine/Content/Assets/Texture.h"
+#include "Engine/Graphics/Materials/IMaterial.h"
+
+class GPUPipelineState;
+
+///
+/// Rendering Level Of Detail number as colors to debug LOD switches in editor.
+///
+class LODPreviewMaterialShader : public IMaterial
+{
+private:
+
+ AssetReference _shader;
+ GPUPipelineState* _ps = nullptr;
+ MaterialInfo _info;
+
+public:
+
+ LODPreviewMaterialShader();
+ virtual ~LODPreviewMaterialShader()
+ {
+ }
+
+private:
+
+#if COMPILE_WITH_DEV_ENV
+ void OnShaderReloading(Asset* obj);
+#endif
+
+public:
+
+ // [IMaterial]
+ const MaterialInfo& GetInfo() const override;
+ bool IsReady() const override;
+ DrawPass GetDrawModes() const override;
+ void Bind(BindParameters& params) override;
+};
+
+#endif
diff --git a/Source/Engine/Renderer/GBufferPass.cpp b/Source/Engine/Renderer/GBufferPass.cpp
index 079ac9bbe..fdefb237f 100644
--- a/Source/Engine/Renderer/GBufferPass.cpp
+++ b/Source/Engine/Renderer/GBufferPass.cpp
@@ -2,8 +2,11 @@
#include "GBufferPass.h"
#include "RenderList.h"
+#if USE_EDITOR
#include "Engine/Renderer/Editor/VertexColors.h"
#include "Engine/Renderer/Editor/LightmapUVsDensity.h"
+#include "Engine/Renderer/Editor/LODPreview.h"
+#endif
#include "Engine/Core/Collections/Sorting.h"
#include "Engine/Graphics/GPUDevice.h"
#include "Engine/Graphics/GPUContext.h"
@@ -87,11 +90,37 @@ void GBufferPass::Dispose()
_skyModel = nullptr;
_boxModel = nullptr;
#if USE_EDITOR
- SAFE_DELETE(_lightmapUVsDensityMaterialShader);
- SAFE_DELETE(_vertexColorsMaterialShader);
+ SAFE_DELETE(_lightmapUVsDensity);
+ SAFE_DELETE(_vertexColors);
+ SAFE_DELETE(_lodPreview);
#endif
}
+#if USE_EDITOR
+
+void DebugOverrideDrawCallsMaterial(RenderContext& renderContext, IMaterial* material)
+{
+ if (material->IsReady())
+ {
+ auto& drawCallsList = renderContext.List->DrawCallsLists[(int32)DrawCallsListType::GBuffer];
+ for (int32 i : drawCallsList.Indices)
+ {
+ auto& drawCall = renderContext.List->DrawCalls[i];
+ if (drawCall.Material->IsSurface())
+ {
+ drawCall.Material = material;
+ }
+ }
+ IMaterial::InstancingHandler handler;
+ if (!material->CanUseInstancing(handler))
+ {
+ drawCallsList.CanUseInstancing = false;
+ }
+ }
+}
+
+#endif
+
void GBufferPass::Fill(RenderContext& renderContext, GPUTextureView* lightBuffer)
{
PROFILE_GPU_CPU("GBuffer");
@@ -133,47 +162,21 @@ void GBufferPass::Fill(RenderContext& renderContext, GPUTextureView* lightBuffer
// Override draw calls material to use material debug shader
if (renderContext.View.Mode == ViewMode::LightmapUVsDensity)
{
- if (!_lightmapUVsDensityMaterialShader)
- _lightmapUVsDensityMaterialShader = New();
- if (_lightmapUVsDensityMaterialShader->IsReady())
- {
- auto& drawCallsList = renderContext.List->DrawCallsLists[(int32)DrawCallsListType::GBuffer];
- for (int32 i : drawCallsList.Indices)
- {
- auto& drawCall = renderContext.List->DrawCalls[i];
- if (drawCall.Material->IsSurface())
- {
- drawCall.Material = _lightmapUVsDensityMaterialShader;
- }
- }
- IMaterial::InstancingHandler handler;
- if (!_lightmapUVsDensityMaterialShader->CanUseInstancing(handler))
- {
- drawCallsList.CanUseInstancing = false;
- }
- }
+ if (!_lightmapUVsDensity)
+ _lightmapUVsDensity = New();
+ DebugOverrideDrawCallsMaterial(renderContext, _lightmapUVsDensity);
}
else if (renderContext.View.Mode == ViewMode::VertexColors)
{
- if (!_vertexColorsMaterialShader)
- _vertexColorsMaterialShader = New();
- if (_vertexColorsMaterialShader->IsReady())
- {
- auto& drawCallsList = renderContext.List->DrawCallsLists[(int32)DrawCallsListType::GBuffer];
- for (int32 i : drawCallsList.Indices)
- {
- auto& drawCall = renderContext.List->DrawCalls[i];
- if (drawCall.Material->IsSurface())
- {
- drawCall.Material = _vertexColorsMaterialShader;
- }
- }
- IMaterial::InstancingHandler handler;
- if (!_vertexColorsMaterialShader->CanUseInstancing(handler))
- {
- drawCallsList.CanUseInstancing = false;
- }
- }
+ if (!_vertexColors)
+ _vertexColors = New();
+ DebugOverrideDrawCallsMaterial(renderContext, _vertexColors);
+ }
+ else if (renderContext.View.Mode == ViewMode::LODPreview)
+ {
+ if (!_lodPreview)
+ _lodPreview = New();
+ DebugOverrideDrawCallsMaterial(renderContext, _lodPreview);
}
if (renderContext.View.Mode == ViewMode::PhysicsColliders)
{
diff --git a/Source/Engine/Renderer/GBufferPass.h b/Source/Engine/Renderer/GBufferPass.h
index dce5d3ee7..7bb1c1eaf 100644
--- a/Source/Engine/Renderer/GBufferPass.h
+++ b/Source/Engine/Renderer/GBufferPass.h
@@ -16,8 +16,9 @@ private:
AssetReference _skyModel;
AssetReference _boxModel;
#if USE_EDITOR
- class LightmapUVsDensityMaterialShader* _lightmapUVsDensityMaterialShader = nullptr;
- class VertexColorsMaterialShader* _vertexColorsMaterialShader = nullptr;
+ class LightmapUVsDensityMaterialShader* _lightmapUVsDensity = nullptr;
+ class VertexColorsMaterialShader* _vertexColors = nullptr;
+ class LODPreviewMaterialShader* _lodPreview = nullptr;
#endif
public:
diff --git a/Source/Shaders/Editor/LODPreview.shader b/Source/Shaders/Editor/LODPreview.shader
new file mode 100644
index 000000000..26b4114bd
--- /dev/null
+++ b/Source/Shaders/Editor/LODPreview.shader
@@ -0,0 +1,89 @@
+// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
+
+#define MATERIAL 1
+
+#include "./Flax/Common.hlsl"
+#include "./Flax/MaterialCommon.hlsl"
+
+META_CB_BEGIN(0, Data)
+float4x4 ViewProjectionMatrix;
+float4x4 WorldMatrix;
+float4 Color;
+float3 WorldInvScale;
+float LODDitherFactor;
+META_CB_END
+
+struct VertexOutput
+{
+ float4 Position : SV_Position;
+ float3 WorldNormal : TEXCOORD0;
+};
+
+struct PixelInput
+{
+ float4 Position : SV_Position;
+ float3 WorldNormal : TEXCOORD0;
+};
+
+float3x3 RemoveScaleFromLocalToWorld(float3x3 localToWorld)
+{
+ localToWorld[0] *= WorldInvScale.x;
+ localToWorld[1] *= WorldInvScale.y;
+ localToWorld[2] *= WorldInvScale.z;
+ return localToWorld;
+}
+
+float3x3 CalcTangentToWorld(float4x4 world, float3x3 tangentToLocal)
+{
+ float3x3 localToWorld = RemoveScaleFromLocalToWorld((float3x3)world);
+ return mul(tangentToLocal, localToWorld);
+}
+
+float4 PerformFakeLighting(float3 n, float4 c)
+{
+ c.rgb *= saturate(abs(dot(n, float3(0, 1, 0))) + 0.5f);
+ return c;
+}
+
+void ClipLODTransition(float4 svPosition, float ditherFactor)
+{
+ if (abs(ditherFactor) > 0.001)
+ {
+ float randGrid = cos(dot(floor(svPosition.xy), float2(347.83452793, 3343.28371863)));
+ float randGridFrac = frac(randGrid * 1000.0);
+ half mask = (ditherFactor < 0.0) ? (ditherFactor + 1.0 > randGridFrac) : (ditherFactor < randGridFrac);
+ clip(mask - 0.001);
+ }
+}
+
+META_VS(true, FEATURE_LEVEL_ES2)
+META_VS_IN_ELEMENT(POSITION, 0, R32G32B32_FLOAT, 0, 0, PER_VERTEX, 0, true)
+META_VS_IN_ELEMENT(TEXCOORD, 0, R16G16_FLOAT, 1, 0, PER_VERTEX, 0, true)
+META_VS_IN_ELEMENT(NORMAL, 0, R10G10B10A2_UNORM, 1, ALIGN, PER_VERTEX, 0, true)
+META_VS_IN_ELEMENT(TANGENT, 0, R10G10B10A2_UNORM, 1, ALIGN, PER_VERTEX, 0, true)
+META_VS_IN_ELEMENT(TEXCOORD, 1, R16G16_FLOAT, 1, ALIGN, PER_VERTEX, 0, true)
+VertexOutput VS(ModelInput input)
+{
+ float bitangentSign = input.Tangent.w ? -1.0f : +1.0f;
+ float3 normal = input.Normal.xyz * 2.0 - 1.0;
+ float3 tangent = input.Tangent.xyz * 2.0 - 1.0;
+ float3 bitangent = cross(normal, tangent) * bitangentSign;
+ float3x3 tangentToLocal = float3x3(tangent, bitangent, normal);
+ float3x3 tangentToWorld = CalcTangentToWorld(WorldMatrix, tangentToLocal);
+ float3 worldPosition = mul(float4(input.Position.xyz, 1), WorldMatrix).xyz;
+
+ VertexOutput output;
+ output.Position = mul(float4(worldPosition, 1), ViewProjectionMatrix);
+ output.WorldNormal = tangentToWorld[2];
+ return output;
+}
+
+META_PS(true, FEATURE_LEVEL_ES2)
+void PS(in PixelInput input, out float4 Light : SV_Target0, out float4 RT0 : SV_Target1, out float4 RT1 : SV_Target2, out float4 RT2 : SV_Target3)
+{
+ ClipLODTransition(input.Position, LODDitherFactor);
+ Light = PerformFakeLighting(input.WorldNormal, Color);
+ RT0 = float4(0, 0, 0, 0);
+ RT1 = float4(input.WorldNormal * 0.5 + 0.5, SHADING_MODEL_LIT * (1.0 / 3.0));
+ RT2 = float4(0.4, 0, 0.5, 0);
+}