From da784e98e5913b8bc616d63a1ad722a9e26efd92 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 8 Feb 2021 15:44:38 +0100 Subject: [PATCH] Add Deformable material domain --- .../MaterialTemplates/Deformable.shader | 375 ++++++++++++++++++ Source/Editor/Surface/Archetypes/Material.cs | 1 + .../Viewport/Previews/MaterialPreview.cs | 3 + Source/Engine/Content/Assets/Material.cpp | 5 +- .../Materials/DeformableMaterialShader.cpp | 195 +++++++++ .../Materials/DeformableMaterialShader.h | 63 +++ Source/Engine/Graphics/Materials/IMaterial.h | 8 + .../Engine/Graphics/Materials/MaterialInfo.h | 5 + .../Graphics/Materials/MaterialShader.cpp | 8 +- Source/Engine/Graphics/Models/Mesh.cpp | 2 +- Source/Engine/Graphics/Models/Mesh.h | 2 +- Source/Engine/Renderer/DrawCall.h | 11 + .../MaterialGenerator/MaterialGenerator.cpp | 16 +- 13 files changed, 685 insertions(+), 9 deletions(-) create mode 100644 Content/Editor/MaterialTemplates/Deformable.shader create mode 100644 Source/Engine/Graphics/Materials/DeformableMaterialShader.cpp create mode 100644 Source/Engine/Graphics/Materials/DeformableMaterialShader.h diff --git a/Content/Editor/MaterialTemplates/Deformable.shader b/Content/Editor/MaterialTemplates/Deformable.shader new file mode 100644 index 000000000..43afa566a --- /dev/null +++ b/Content/Editor/MaterialTemplates/Deformable.shader @@ -0,0 +1,375 @@ +// File generated by Flax Materials Editor +// Version: @0 + +#define MATERIAL 1 +@3 +#include "./Flax/Common.hlsl" +#include "./Flax/MaterialCommon.hlsl" +#include "./Flax/GBufferCommon.hlsl" +@7 +// Primary constant buffer (with additional material parameters) +META_CB_BEGIN(0, Data) +float4x4 ViewProjectionMatrix; +float4x4 WorldMatrix; +float4x4 LocalMatrix; +float4x4 ViewMatrix; +float3 ViewPos; +float ViewFar; +float3 ViewDir; +float TimeParam; +float4 ViewInfo; +float4 ScreenSize; +float3 WorldInvScale; +float WorldDeterminantSign; +float MeshMinZ; +float Segment; +float ChunksPerSegment; +float PerInstanceRandom; +float4 TemporalAAJitter; +float3 GeometrySize; +float MeshMaxZ; +@1META_CB_END + +// Shader resources +@2 +// The spline deformation buffer (stored as 4x3, 3 float4 behind each other) +Buffer SplineDeformation : register(t0); + +// Geometry data passed though the graphics rendering stages up to the pixel shader +struct GeometryData +{ + float3 WorldPosition : TEXCOORD0; + float2 TexCoord : TEXCOORD1; +#if USE_VERTEX_COLOR + half4 VertexColor : COLOR; +#endif + float3 WorldNormal : TEXCOORD2; + float4 WorldTangent : TEXCOORD3; +}; + +// Interpolants passed from the vertex shader +struct VertexOutput +{ + float4 Position : SV_Position; + GeometryData Geometry; +#if USE_CUSTOM_VERTEX_INTERPOLATORS + float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9; +#endif +#if USE_TESSELLATION + float TessellationMultiplier : TESS; +#endif +}; + +// Interpolants passed to the pixel shader +struct PixelInput +{ + float4 Position : SV_Position; + GeometryData Geometry; +#if USE_CUSTOM_VERTEX_INTERPOLATORS + float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9; +#endif + bool IsFrontFace : SV_IsFrontFace; +}; + +// Material properties generation input +struct MaterialInput +{ + float3 WorldPosition; + float TwoSidedSign; + float2 TexCoord; +#if USE_VERTEX_COLOR + half4 VertexColor; +#endif + float3x3 TBN; + float4 SvPosition; + float3 PreSkinnedPosition; + float3 PreSkinnedNormal; +#if USE_CUSTOM_VERTEX_INTERPOLATORS + float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT]; +#endif +}; + +// Extracts geometry data to the material input +MaterialInput GetGeometryMaterialInput(GeometryData geometry) +{ + MaterialInput output = (MaterialInput)0; + output.WorldPosition = geometry.WorldPosition; + output.TexCoord = geometry.TexCoord; +#if USE_VERTEX_COLOR + output.VertexColor = geometry.VertexColor; +#endif + output.TBN = CalcTangentBasis(geometry.WorldNormal, geometry.WorldTangent); + return output; +} + +#if USE_TESSELLATION + +// Interpolates the geometry positions data only (used by the tessallation when generating vertices) +#define InterpolateGeometryPositions(output, p0, w0, p1, w1, p2, w2, offset) output.WorldPosition = p0.WorldPosition * w0 + p1.WorldPosition * w1 + p2.WorldPosition * w2 + offset + +// Offsets the geometry positions data only (used by the tessallation when generating vertices) +#define OffsetGeometryPositions(geometry, offset) geometry.WorldPosition += offset + +// Applies the Phong tessallation to the geometry positions (used by the tessallation when doing Phong tess) +#define ApplyGeometryPositionsPhongTess(geometry, p0, p1, p2, U, V, W) \ + float3 posProjectedU = TessalationProjectOntoPlane(p0.WorldNormal, p0.WorldPosition, geometry.WorldPosition); \ + float3 posProjectedV = TessalationProjectOntoPlane(p1.WorldNormal, p1.WorldPosition, geometry.WorldPosition); \ + float3 posProjectedW = TessalationProjectOntoPlane(p2.WorldNormal, p2.WorldPosition, geometry.WorldPosition); \ + geometry.WorldPosition = U * posProjectedU + V * posProjectedV + W * posProjectedW + +// Interpolates the geometry data except positions (used by the tessallation when generating vertices) +GeometryData InterpolateGeometry(GeometryData p0, float w0, GeometryData p1, float w1, GeometryData p2, float w2) +{ + GeometryData output = (GeometryData)0; + output.TexCoord = p0.TexCoord * w0 + p1.TexCoord * w1 + p2.TexCoord * w2; +#if USE_VERTEX_COLOR + output.VertexColor = p0.VertexColor * w0 + p1.VertexColor * w1 + p2.VertexColor * w2; +#endif + output.WorldNormal = p0.WorldNormal * w0 + p1.WorldNormal * w1 + p2.WorldNormal * w2; + output.WorldNormal = normalize(output.WorldNormal); + output.WorldTangent = p0.WorldTangent * w0 + p1.WorldTangent * w1 + p2.WorldTangent * w2; + output.WorldTangent.xyz = normalize(output.WorldTangent.xyz); + return output; +} + +#endif + +MaterialInput GetMaterialInput(PixelInput input) +{ + MaterialInput output = GetGeometryMaterialInput(input.Geometry); + output.TwoSidedSign = WorldDeterminantSign * (input.IsFrontFace ? 1.0 : -1.0); + output.SvPosition = input.Position; +#if USE_CUSTOM_VERTEX_INTERPOLATORS + output.CustomVSToPS = input.CustomVSToPS; +#endif + return output; +} + +// Removes the scale vector from the local to world transformation matrix +float3x3 RemoveScaleFromLocalToWorld(float3x3 localToWorld) +{ + localToWorld[0] *= WorldInvScale.x; + localToWorld[1] *= WorldInvScale.y; + localToWorld[2] *= WorldInvScale.z; + return localToWorld; +} + +// Transforms a vector from tangent space to world space +float3 TransformTangentVectorToWorld(MaterialInput input, float3 tangentVector) +{ + return mul(tangentVector, input.TBN); +} + +// Transforms a vector from world space to tangent space +float3 TransformWorldVectorToTangent(MaterialInput input, float3 worldVector) +{ + return mul(input.TBN, worldVector); +} + +// Transforms a vector from world space to view space +float3 TransformWorldVectorToView(MaterialInput input, float3 worldVector) +{ + return mul(worldVector, (float3x3)ViewMatrix); +} + +// Transforms a vector from view space to world space +float3 TransformViewVectorToWorld(MaterialInput input, float3 viewVector) +{ + return mul((float3x3)ViewMatrix, viewVector); +} + +// Transforms a vector from local space to world space +float3 TransformLocalVectorToWorld(MaterialInput input, float3 localVector) +{ + float3x3 localToWorld = (float3x3)WorldMatrix; + //localToWorld = RemoveScaleFromLocalToWorld(localToWorld); + return mul(localVector, localToWorld); +} + +// Transforms a vector from local space to world space +float3 TransformWorldVectorToLocal(MaterialInput input, float3 worldVector) +{ + float3x3 localToWorld = (float3x3)WorldMatrix; + //localToWorld = RemoveScaleFromLocalToWorld(localToWorld); + return mul(localToWorld, worldVector); +} + +// Gets the current object position +float3 GetObjectPosition(MaterialInput input) +{ + return WorldMatrix[3].xyz; +} + +// Gets the current object size +float3 GetObjectSize(MaterialInput input) +{ + float4x4 world = WorldMatrix; + return GeometrySize * float3(world._m00, world._m11, world._m22); +} + +// Get the current object random value +float GetPerInstanceRandom(MaterialInput input) +{ + return PerInstanceRandom; +} + +// Get the current object LOD transition dither factor +float GetLODDitherFactor(MaterialInput input) +{ + return 0; +} + +// Gets the interpolated vertex color (in linear space) +float4 GetVertexColor(MaterialInput input) +{ +#if USE_VERTEX_COLOR + return input.VertexColor; +#else + return 1; +#endif +} + +float3 SampleLightmap(Material material, MaterialInput materialInput) +{ + return 0; +} + +@8 + +// Get material properties function (for vertex shader) +Material GetMaterialVS(MaterialInput input) +{ +@5 +} + +// Get material properties function (for domain shader) +Material GetMaterialDS(MaterialInput input) +{ +@6 +} + +// Get material properties function (for pixel shader) +Material GetMaterialPS(MaterialInput input) +{ +@4 +} + +// Calculates the transform matrix from mesh tangent space to local space +float3x3 CalcTangentToLocal(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; + return float3x3(tangent, bitangent, normal); +} + +// Vertex Shader function for GBuffer Pass and Depth Pass (with full vertex data) +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) +META_VS_IN_ELEMENT(COLOR, 0, R8G8B8A8_UNORM, 2, 0, PER_VERTEX, 0, USE_VERTEX_COLOR) +VertexOutput VS_SplineModel(ModelInput input) +{ + VertexOutput output; + + // Apply local transformation of the geometry before deformation + float3 position = mul(float4(input.Position, 1), LocalMatrix).xyz; + + // Apply spline curve deformation + float splineAlpha = saturate((position.z - MeshMinZ) / (MeshMaxZ - MeshMinZ)); + int splineIndex = (int)((Segment + splineAlpha) * ChunksPerSegment); + position.z = splineAlpha; + float3x4 splineMatrix = float3x4(SplineDeformation[splineIndex * 3], SplineDeformation[splineIndex * 3 + 1], SplineDeformation[splineIndex * 3 + 2]); + position = mul(splineMatrix, float4(position, 1)); + + // Compute world space vertex position + output.Geometry.WorldPosition = mul(float4(position, 1), WorldMatrix).xyz; + + // Compute clip space position + output.Position = mul(float4(output.Geometry.WorldPosition, 1), ViewProjectionMatrix); + + // Pass vertex attributes + output.Geometry.TexCoord = input.TexCoord; +#if USE_VERTEX_COLOR + output.Geometry.VertexColor = input.Color; +#endif + + // Calculate tanget space to world space transformation matrix for unit vectors + float3x3 tangentToLocal = CalcTangentToLocal(input); + float3x3 localToWorld = RemoveScaleFromLocalToWorld((float3x3)WorldMatrix); + float3x3 tangentToWorld = mul(tangentToLocal, localToWorld); + output.Geometry.WorldNormal = tangentToWorld[2]; + output.Geometry.WorldTangent.xyz = tangentToWorld[0]; + output.Geometry.WorldTangent.w = input.Tangent.w ? -1.0f : +1.0f; + + // Get material input params if need to evaluate any material property +#if USE_POSITION_OFFSET || USE_TESSELLATION || USE_CUSTOM_VERTEX_INTERPOLATORS + MaterialInput materialInput = GetGeometryMaterialInput(output.Geometry); + materialInput.TwoSidedSign = WorldDeterminantSign; + materialInput.SvPosition = output.Position; + materialInput.PreSkinnedPosition = input.Position.xyz; + materialInput.PreSkinnedNormal = tangentToLocal[2].xyz; + Material material = GetMaterialVS(materialInput); +#endif + + // Apply world position offset per-vertex +#if USE_POSITION_OFFSET + output.Geometry.WorldPosition += material.PositionOffset; + output.Position = mul(float4(output.Geometry.WorldPosition, 1), ViewProjectionMatrix); +#endif + + // Get tessalation multiplier (per vertex) +#if USE_TESSELLATION + output.TessellationMultiplier = material.TessellationMultiplier; +#endif + + // Copy interpolants for other shader stages +#if USE_CUSTOM_VERTEX_INTERPOLATORS + output.CustomVSToPS = material.CustomVSToPS; +#endif + + return output; +} + +// Vertex Shader function for Depth Pass +META_VS(true, FEATURE_LEVEL_ES2) +META_VS_IN_ELEMENT(POSITION, 0, R32G32B32_FLOAT, 0, 0, PER_VERTEX, 0, true) +float4 VS_Depth(ModelInput_PosOnly input) : SV_Position +{ + float3 worldPosition = mul(float4(input.Position.xyz, 1), WorldMatrix).xyz; + float4 position = mul(float4(worldPosition, 1), ViewProjectionMatrix); + return position; +} + +#if USE_DITHERED_LOD_TRANSITION + +void ClipLODTransition(PixelInput input) +{ +} + +#endif + +// Pixel Shader function for Depth Pass +META_PS(true, FEATURE_LEVEL_ES2) +void PS_Depth(PixelInput input) +{ +#if MATERIAL_MASKED || MATERIAL_BLEND != MATERIAL_BLEND_OPAQUE + // Get material parameters + MaterialInput materialInput = GetMaterialInput(input); + Material material = GetMaterialPS(materialInput); + + // Perform per pixel clipping +#if MATERIAL_MASKED + clip(material.Mask - MATERIAL_MASK_THRESHOLD); +#endif +#if MATERIAL_BLEND != MATERIAL_BLEND_OPAQUE + clip(material.Opacity - MATERIAL_OPACITY_THRESHOLD); +#endif +#endif +} + +@9 diff --git a/Source/Editor/Surface/Archetypes/Material.cs b/Source/Editor/Surface/Archetypes/Material.cs index 6e828fca4..9edbedcfb 100644 --- a/Source/Editor/Surface/Archetypes/Material.cs +++ b/Source/Editor/Surface/Archetypes/Material.cs @@ -156,6 +156,7 @@ namespace FlaxEditor.Surface.Archetypes case MaterialDomain.Surface: case MaterialDomain.Terrain: case MaterialDomain.Particle: + case MaterialDomain.Deformable: { bool isNotUnlit = info.ShadingModel != MaterialShadingModel.Unlit; bool isTransparent = info.BlendMode == MaterialBlendMode.Transparent; diff --git a/Source/Editor/Viewport/Previews/MaterialPreview.cs b/Source/Editor/Viewport/Previews/MaterialPreview.cs index 6ff695b69..41275ef9a 100644 --- a/Source/Editor/Viewport/Previews/MaterialPreview.cs +++ b/Source/Editor/Viewport/Previews/MaterialPreview.cs @@ -172,6 +172,9 @@ namespace FlaxEditor.Viewport.Previews usePreviewActor = false; particleMaterial = _material; break; + case MaterialDomain.Deformable: + // TODO: preview Deformable material (eg. by using Spline with Spline Model) + break; default: throw new ArgumentOutOfRangeException(); } } diff --git a/Source/Engine/Content/Assets/Material.cpp b/Source/Engine/Content/Assets/Material.cpp index 966253aba..2930aff24 100644 --- a/Source/Engine/Content/Assets/Material.cpp +++ b/Source/Engine/Content/Assets/Material.cpp @@ -392,12 +392,12 @@ void Material::InitCompilationOptions(ShaderCompilationOptions& options) auto& info = _shaderHeader.Material.Info; const bool isSurfaceOrTerrain = info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Terrain; const bool useCustomData = info.ShadingModel == MaterialShadingModel::Subsurface || info.ShadingModel == MaterialShadingModel::Foliage; - const bool useForward = (info.Domain == MaterialDomain::Surface && info.BlendMode != MaterialBlendMode::Opaque) || info.Domain == MaterialDomain::Particle; + const bool useForward = ((info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Deformable) && info.BlendMode != MaterialBlendMode::Opaque) || info.Domain == MaterialDomain::Particle; const bool useTess = info.TessellationMode != TessellationMethod::None && RenderTools::CanSupportTessellation(options.Profile) && isSurfaceOrTerrain; const bool useDistortion = - (info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Particle) && + (info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Deformable || info.Domain == MaterialDomain::Particle) && info.BlendMode != MaterialBlendMode::Opaque && (info.UsageFlags & MaterialUsageFlags::UseRefraction) != 0 && (info.FeaturesFlags & MaterialFeaturesFlags::DisableDistortion) == 0; @@ -455,6 +455,7 @@ void Material::InitCompilationOptions(ShaderCompilationOptions& options) options.Macros.Add({ "IS_DECAL", Numbers[info.Domain == MaterialDomain::Decal ? 1 : 0] }); options.Macros.Add({ "IS_TERRAIN", Numbers[info.Domain == MaterialDomain::Terrain ? 1 : 0] }); options.Macros.Add({ "IS_PARTICLE", Numbers[info.Domain == MaterialDomain::Particle ? 1 : 0] }); + options.Macros.Add({ "IS_DEFORMABLE", Numbers[info.Domain == MaterialDomain::Deformable ? 1 : 0] }); options.Macros.Add({ "USE_FORWARD", Numbers[useForward ? 1 : 0] }); options.Macros.Add({ "USE_DEFERRED", Numbers[isSurfaceOrTerrain && info.BlendMode == MaterialBlendMode::Opaque ? 1 : 0] }); options.Macros.Add({ "USE_DISTORTION", Numbers[useDistortion ? 1 : 0] }); diff --git a/Source/Engine/Graphics/Materials/DeformableMaterialShader.cpp b/Source/Engine/Graphics/Materials/DeformableMaterialShader.cpp new file mode 100644 index 000000000..5adb18470 --- /dev/null +++ b/Source/Engine/Graphics/Materials/DeformableMaterialShader.cpp @@ -0,0 +1,195 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#include "DeformableMaterialShader.h" +#include "MaterialShaderFeatures.h" +#include "MaterialParams.h" +#include "Engine/Graphics/RenderBuffers.h" +#include "Engine/Graphics/RenderView.h" +#include "Engine/Renderer/DrawCall.h" +#include "Engine/Renderer/RenderList.h" +#include "Engine/Graphics/GPUContext.h" +#include "Engine/Graphics/Shaders/GPUConstantBuffer.h" +#include "Engine/Graphics/GPUDevice.h" +#include "Engine/Graphics/Shaders/GPUShader.h" +#include "Engine/Graphics/GPULimits.h" +#include "Engine/Engine/Time.h" +#include "Engine/Graphics/RenderTask.h" + +PACK_STRUCT(struct DeformableMaterialShaderData { + Matrix ViewProjectionMatrix; + Matrix WorldMatrix; + Matrix LocalMatrix; + Matrix ViewMatrix; + Vector3 ViewPos; + float ViewFar; + Vector3 ViewDir; + float TimeParam; + Vector4 ViewInfo; + Vector4 ScreenSize; + Vector3 WorldInvScale; + float WorldDeterminantSign; + float MeshMinZ; + float Segment; + float ChunksPerSegment; + float PerInstanceRandom; + Vector4 TemporalAAJitter; + Vector3 GeometrySize; + float MeshMaxZ; + }); + +DrawPass DeformableMaterialShader::GetDrawModes() const +{ + return _drawModes; +} + +void DeformableMaterialShader::Bind(BindParameters& params) +{ + // Prepare + auto context = params.GPUContext; + auto& view = params.RenderContext.View; + auto& drawCall = *params.FirstDrawCall; + byte* cb = _cbData.Get(); + auto materialData = reinterpret_cast(cb); + cb += sizeof(DeformableMaterialShaderData); + int32 srv = 1; + + // Setup features + if (_info.BlendMode != MaterialBlendMode::Opaque) + ForwardShadingFeature::Bind(params, cb, srv); + + // Setup parameters + MaterialParameter::BindMeta bindMeta; + bindMeta.Context = context; + bindMeta.Constants = cb; + bindMeta.Input = nullptr; + bindMeta.Buffers = nullptr; + bindMeta.CanSampleDepth = false; + bindMeta.CanSampleGBuffer = false; + MaterialParams::Bind(params.ParamsLink, bindMeta); + + // Setup material constants + { + Matrix::Transpose(view.Frustum.GetMatrix(), materialData->ViewProjectionMatrix); + Matrix::Transpose(drawCall.World, materialData->WorldMatrix); + Matrix::Transpose(drawCall.Deformable.LocalMatrix, materialData->LocalMatrix); + Matrix::Transpose(view.View, materialData->ViewMatrix); + materialData->ViewPos = view.Position; + materialData->ViewFar = view.Far; + materialData->ViewDir = view.Direction; + materialData->TimeParam = Time::Draw.UnscaledTime.GetTotalSeconds(); + materialData->ViewInfo = view.ViewInfo; + materialData->ScreenSize = view.ScreenSize; + 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(); + materialData->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); + materialData->WorldDeterminantSign = drawCall.WorldDeterminantSign; + materialData->Segment = drawCall.Deformable.Segment; + materialData->ChunksPerSegment = drawCall.Deformable.ChunksPerSegment; + materialData->MeshMinZ = drawCall.Deformable.MeshMinZ; + materialData->MeshMaxZ = drawCall.Deformable.MeshMaxZ; + materialData->PerInstanceRandom = drawCall.PerInstanceRandom; + materialData->TemporalAAJitter = view.TemporalAAJitter; + materialData->GeometrySize = drawCall.Deformable.GeometrySize; + } + + // Bind spline deformation buffer + context->BindSR(0, drawCall.Deformable.SplineDeformation->View()); + + // Bind constants + if (_cb) + { + context->UpdateCB(_cb, _cbData.Get()); + context->BindCB(0, _cb); + } + + // Select pipeline state based on current pass and render mode + const bool wireframe = (_info.FeaturesFlags & MaterialFeaturesFlags::Wireframe) != 0 || view.Mode == ViewMode::Wireframe; + CullMode cullMode = view.Pass == DrawPass::Depth ? CullMode::TwoSided : _info.CullMode; + if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminantSign < 0) + { + // Invert culling when scale is negative + if (cullMode == CullMode::Normal) + cullMode = CullMode::Inverted; + else + cullMode = CullMode::Normal; + } + PipelineStateCache* psCache = _cache.GetPS(view.Pass); + ASSERT(psCache); + GPUPipelineState* state = psCache->GetPS(cullMode, wireframe); + + // Bind pipeline + context->SetState(state); +} + +void DeformableMaterialShader::Unload() +{ + // Base + MaterialShader::Unload(); + + _cache.Release(); +} + +bool DeformableMaterialShader::Load() +{ + _drawModes = DrawPass::Depth; + auto psDesc = GPUPipelineState::Description::Default; + psDesc.DepthTestEnable = (_info.FeaturesFlags & MaterialFeaturesFlags::DisableDepthTest) == 0; + psDesc.DepthWriteEnable = (_info.FeaturesFlags & MaterialFeaturesFlags::DisableDepthWrite) == 0; + + // Check if use tessellation (both material and runtime supports it) + const bool useTess = _info.TessellationMode != TessellationMethod::None && GPUDevice::Instance->Limits.HasTessellation; + if (useTess) + { + psDesc.HS = _shader->GetHS("HS"); + psDesc.DS = _shader->GetDS("DS"); + } + + if (_info.BlendMode == MaterialBlendMode::Opaque) + { + _drawModes = DrawPass::GBuffer; + + // GBuffer Pass + psDesc.VS = _shader->GetVS("VS_SplineModel"); + psDesc.PS = _shader->GetPS("PS_GBuffer"); + _cache.Default.Init(psDesc); + } + else + { + _drawModes = DrawPass::Forward; + + // Forward Pass + psDesc.VS = _shader->GetVS("VS_SplineModel"); + psDesc.PS = _shader->GetPS("PS_Forward"); + psDesc.DepthWriteEnable = false; + psDesc.BlendMode = BlendingMode::AlphaBlend; + switch (_info.BlendMode) + { + case MaterialBlendMode::Transparent: + psDesc.BlendMode = BlendingMode::AlphaBlend; + break; + case MaterialBlendMode::Additive: + psDesc.BlendMode = BlendingMode::Additive; + break; + case MaterialBlendMode::Multiply: + psDesc.BlendMode = BlendingMode::Multiply; + break; + } + _cache.Default.Init(psDesc); + } + + // Depth Pass + psDesc.CullMode = CullMode::TwoSided; + psDesc.DepthClipEnable = false; + psDesc.DepthWriteEnable = true; + psDesc.DepthTestEnable = true; + psDesc.DepthFunc = ComparisonFunc::Less; + psDesc.HS = nullptr; + psDesc.DS = nullptr; + _cache.Depth.Init(psDesc); + + return false; +} diff --git a/Source/Engine/Graphics/Materials/DeformableMaterialShader.h b/Source/Engine/Graphics/Materials/DeformableMaterialShader.h new file mode 100644 index 000000000..f2c796a23 --- /dev/null +++ b/Source/Engine/Graphics/Materials/DeformableMaterialShader.h @@ -0,0 +1,63 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#include "MaterialShader.h" + +/// +/// Represents material that can be used to render objects that can be deformed. +/// +class DeformableMaterialShader : public MaterialShader +{ +private: + + struct Cache + { + PipelineStateCache Default; + PipelineStateCache Depth; + + FORCE_INLINE PipelineStateCache* GetPS(const DrawPass pass) + { + switch (pass) + { + case DrawPass::Depth: + return &Depth; + case DrawPass::GBuffer: + case DrawPass::Forward: + return &Default; + default: + return nullptr; + } + } + + FORCE_INLINE void Release() + { + Default.Release(); + Depth.Release(); + } + }; + +private: + + Cache _cache; + DrawPass _drawModes = DrawPass::None; + +public: + + DeformableMaterialShader(const String& name) + : MaterialShader(name) + { + } + +public: + + // [MaterialShader] + DrawPass GetDrawModes() const override; + void Bind(BindParameters& params) override; + void Unload() override; + +protected: + + // [MaterialShader] + bool Load() override; +}; diff --git a/Source/Engine/Graphics/Materials/IMaterial.h b/Source/Engine/Graphics/Materials/IMaterial.h index e85328133..4d61656e8 100644 --- a/Source/Engine/Graphics/Materials/IMaterial.h +++ b/Source/Engine/Graphics/Materials/IMaterial.h @@ -74,6 +74,14 @@ public: return GetInfo().Domain == MaterialDomain::Particle; } + /// + /// Determines whether material is a deformable shader. + /// + FORCE_INLINE bool IsDeformable() const + { + return GetInfo().Domain == MaterialDomain::Deformable; + } + /// /// Returns true if material is ready for rendering. /// diff --git a/Source/Engine/Graphics/Materials/MaterialInfo.h b/Source/Engine/Graphics/Materials/MaterialInfo.h index 50b4c1e35..862f8f221 100644 --- a/Source/Engine/Graphics/Materials/MaterialInfo.h +++ b/Source/Engine/Graphics/Materials/MaterialInfo.h @@ -39,6 +39,11 @@ API_ENUM() enum class MaterialDomain : byte /// The particle shader. Can be used only with particles geometry (sprites, trails and ribbons). Supports reading particle data on a GPU. /// Particle = 5, + + /// + /// The deformable shader. Can be used only with objects that can be deformed (spline models). + /// + Deformable = 6, }; /// diff --git a/Source/Engine/Graphics/Materials/MaterialShader.cpp b/Source/Engine/Graphics/Materials/MaterialShader.cpp index 2d7998b8a..caac3b5f2 100644 --- a/Source/Engine/Graphics/Materials/MaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/MaterialShader.cpp @@ -14,7 +14,7 @@ #include "GUIMaterialShader.h" #include "TerrainMaterialShader.h" #include "ParticleMaterialShader.h" -//#include "DeformableMaterialShader.h" +#include "DeformableMaterialShader.h" GPUPipelineState* MaterialShader::PipelineStateCache::InitPS(CullMode mode, bool wireframe) { @@ -62,9 +62,9 @@ MaterialShader* MaterialShader::Create(const String& name, MemoryReadStream& sha case MaterialDomain::Particle: material = New(name); break; - /*case MaterialDomain::Deformable: - material = New(name); - break;*/ + case MaterialDomain::Deformable: + material = New(name); + break; default: LOG(Fatal, "Unknown material type."); return nullptr; diff --git a/Source/Engine/Graphics/Models/Mesh.cpp b/Source/Engine/Graphics/Models/Mesh.cpp index 99262cd51..e7007f4ec 100644 --- a/Source/Engine/Graphics/Models/Mesh.cpp +++ b/Source/Engine/Graphics/Models/Mesh.cpp @@ -350,7 +350,7 @@ bool Mesh::Intersects(const Ray& ray, const Matrix& world, float& distance, Vect #endif } -void Mesh::GetDrawCallGeometry(DrawCall& drawCall) +void Mesh::GetDrawCallGeometry(DrawCall& drawCall) const { drawCall.Geometry.IndexBuffer = _indexBuffer; drawCall.Geometry.VertexBuffers[0] = _vertexBuffers[0]; diff --git a/Source/Engine/Graphics/Models/Mesh.h b/Source/Engine/Graphics/Models/Mesh.h index 80d566fa3..94d4979a3 100644 --- a/Source/Engine/Graphics/Models/Mesh.h +++ b/Source/Engine/Graphics/Models/Mesh.h @@ -377,7 +377,7 @@ public: /// Gets the draw call geometry for this mesh. Sets the index and vertex buffers. /// /// The draw call. - void GetDrawCallGeometry(DrawCall& drawCall); + void GetDrawCallGeometry(DrawCall& drawCall) const; /// /// Model instance drawing packed data. diff --git a/Source/Engine/Renderer/DrawCall.h b/Source/Engine/Renderer/DrawCall.h index 0e91366e2..bb25c0a57 100644 --- a/Source/Engine/Renderer/DrawCall.h +++ b/Source/Engine/Renderer/DrawCall.h @@ -211,6 +211,17 @@ struct DrawCall } Ribbon; } Particle; + struct + { + GPUBuffer* SplineDeformation; + Matrix LocalMatrix; // Geometry transformation applied before deformation. + Vector3 GeometrySize; // Object geometry size in the world (unscaled). + float Segment; + float ChunksPerSegment; + float MeshMinZ; + float MeshMaxZ; + } Deformable; + struct { byte Raw[96]; diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp index 2f54efe82..a3a599529 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp @@ -206,6 +206,14 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo ADD_FEATURE(DistortionFeature); ADD_FEATURE(ForwardShadingFeature); break; + case MaterialDomain::Deformable: + if (materialInfo.TessellationMode != TessellationMethod::None) + ADD_FEATURE(TessellationFeature); + if (materialInfo.BlendMode == MaterialBlendMode::Opaque) + ADD_FEATURE(DeferredShadingFeature); + if (materialInfo.BlendMode != MaterialBlendMode::Opaque) + ADD_FEATURE(ForwardShadingFeature); + break; default: break; } @@ -228,7 +236,7 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo { materialVarPS = Value(VariantType::Void, baseLayer->GetVariableName(nullptr)); _writer.Write(TEXT("\tMaterial {0} = (Material)0;\n"), materialVarPS.Value); - if (baseLayer->Domain == MaterialDomain::Surface || baseLayer->Domain == MaterialDomain::Terrain || baseLayer->Domain == MaterialDomain::Particle) + if (baseLayer->Domain == MaterialDomain::Surface || baseLayer->Domain == MaterialDomain::Terrain || baseLayer->Domain == MaterialDomain::Particle || baseLayer->Domain == MaterialDomain::Deformable) { eatMaterialGraphBox(baseLayer, MaterialGraphBoxes::Emissive); eatMaterialGraphBox(baseLayer, MaterialGraphBoxes::Normal); @@ -410,6 +418,9 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo case MaterialDomain::Particle: srv = 2; // Particles data + Sorted indices/Ribbon segments break; + case MaterialDomain::Deformable: + srv = 1; // Mesh deformation buffer + break; } for (auto f : features) { @@ -489,6 +500,9 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo case MaterialDomain::Particle: path /= TEXT("Particle.shader"); break; + case MaterialDomain::Deformable: + path /= TEXT("Deformable.shader"); + break; default: LOG(Warning, "Unknown material domain."); return true;