You're breathtaking!

This commit is contained in:
Wojtek Figat
2020-12-07 23:40:54 +01:00
commit 6fb9eee74c
5143 changed files with 1153594 additions and 0 deletions

View File

@@ -0,0 +1,168 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#include "DecalMaterialShader.h"
#include "MaterialParams.h"
#include "Engine/Core/Math/OrientedBoundingBox.h"
#include "Engine/Engine/Time.h"
#include "Engine/Graphics/GPUDevice.h"
#include "Engine/Graphics/Shaders/GPUShader.h"
#include "Engine/Graphics/RenderBuffers.h"
#include "Engine/Graphics/RenderTask.h"
#include "Engine/Graphics/RenderView.h"
#include "Engine/Graphics/Shaders/GPUConstantBuffer.h"
#include "Engine/Renderer/DrawCall.h"
PACK_STRUCT(struct DecalMaterialShaderData {
Matrix ViewProjectionMatrix;
Matrix WorldMatrix;
Matrix ViewMatrix;
Matrix InvWorld;
Matrix SVPositionToWorld;
Vector3 ViewPos;
float ViewFar;
Vector3 ViewDir;
float TimeParam;
Vector4 ViewInfo;
Vector4 ScreenSize;
});
DrawPass DecalMaterialShader::GetDrawModes() const
{
return DrawPass::GBuffer;
}
void DecalMaterialShader::Bind(BindParameters& params)
{
// Prepare
auto context = params.GPUContext;
auto& view = params.RenderContext.View;
auto& drawCall = *params.FirstDrawCall;
const auto cb0 = _shader->GetCB(0);
const bool hasCb0 = cb0->GetSize() != 0;
const bool isCameraInside = OrientedBoundingBox(Vector3::Half, params.FirstDrawCall->World).Contains(view.Position) == ContainmentType::Contains;
// Setup parameters
MaterialParameter::BindMeta bindMeta;
bindMeta.Context = context;
bindMeta.Buffer0 = hasCb0 ? _cb0Data.Get() + sizeof(DecalMaterialShaderData) : nullptr;
bindMeta.Input = nullptr;
bindMeta.Buffers = nullptr;
bindMeta.CanSampleDepth = true;
bindMeta.CanSampleGBuffer = false;
MaterialParams::Bind(params.ParamsLink, bindMeta);
// Decals use depth buffer to draw on top of the objects
context->BindSR(0, GET_TEXTURE_VIEW_SAFE(params.RenderContext.Buffers->DepthBuffer));
// Setup material constants data
if (hasCb0)
{
const auto materialData = reinterpret_cast<DecalMaterialShaderData*>(_cb0Data.Get());
Matrix::Transpose(view.Frustum.GetMatrix(), materialData->ViewProjectionMatrix);
Matrix::Transpose(drawCall.World, materialData->WorldMatrix);
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;
// Matrix for transformation from world space to decal object space
Matrix invWorld;
Matrix::Invert(drawCall.World, invWorld);
Matrix::Transpose(invWorld, materialData->InvWorld);
// Matrix for transformation from SV Position space to world space
const Matrix offsetMatrix(
2.0f * view.ScreenSize.Z, 0, 0, 0,
0, -2.0f * view.ScreenSize.W, 0, 0,
0, 0, 1, 0,
-1.0f, 1.0f, 0, 1);
const Matrix svPositionToWorld = offsetMatrix * view.IVP;
Matrix::Transpose(svPositionToWorld, materialData->SVPositionToWorld);
}
// Bind constants
if (hasCb0)
{
context->UpdateCB(cb0, _cb0Data.Get());
context->BindCB(0, cb0);
}
// Bind pipeline
context->SetState(isCameraInside ? _cache.Inside : _cache.Outside);
}
void DecalMaterialShader::Unload()
{
// Base
MaterialShader::Unload();
_cache.Release();
}
bool DecalMaterialShader::Load()
{
GPUPipelineState::Description psDesc0 = GPUPipelineState::Description::DefaultNoDepth;
psDesc0.VS = _shader->GetVS("VS_Decal");
psDesc0.PS = _shader->GetPS("PS_Decal");
psDesc0.CullMode = CullMode::Normal;
switch (_info.DecalBlendingMode)
{
case MaterialDecalBlendingMode::Translucent:
{
psDesc0.BlendMode.BlendEnable = true;
psDesc0.BlendMode.SrcBlend = BlendingMode::Blend::SrcAlpha;
psDesc0.BlendMode.DestBlend = BlendingMode::Blend::InvSrcAlpha;
psDesc0.BlendMode.SrcBlendAlpha = BlendingMode::Blend::Zero;
psDesc0.BlendMode.DestBlendAlpha = BlendingMode::Blend::One;
psDesc0.BlendMode.RenderTargetWriteMask = BlendingMode::ColorWrite::RGB;
break;
}
case MaterialDecalBlendingMode::Stain:
{
psDesc0.BlendMode.BlendEnable = true;
psDesc0.BlendMode.SrcBlend = BlendingMode::Blend::DestColor;
psDesc0.BlendMode.DestBlend = BlendingMode::Blend::InvSrcAlpha;
psDesc0.BlendMode.SrcBlendAlpha = BlendingMode::Blend::Zero;
psDesc0.BlendMode.DestBlendAlpha = BlendingMode::Blend::One;
psDesc0.BlendMode.RenderTargetWriteMask = BlendingMode::ColorWrite::RGB;
break;
}
case MaterialDecalBlendingMode::Normal:
{
psDesc0.BlendMode.BlendEnable = true;
psDesc0.BlendMode.SrcBlend = BlendingMode::Blend::SrcAlpha;
psDesc0.BlendMode.DestBlend = BlendingMode::Blend::InvSrcAlpha;
psDesc0.BlendMode.SrcBlendAlpha = BlendingMode::Blend::One;
psDesc0.BlendMode.DestBlendAlpha = BlendingMode::Blend::One;
psDesc0.BlendMode.RenderTargetWriteMask = BlendingMode::ColorWrite::RGB;
break;
}
case MaterialDecalBlendingMode::Emissive:
{
psDesc0.BlendMode = BlendingMode::Additive;
break;
}
}
_cache.Outside = GPUDevice::Instance->CreatePipelineState();
if (_cache.Outside->Init(psDesc0))
{
LOG(Warning, "Failed to create decal material pipeline state.");
return true;
}
psDesc0.CullMode = CullMode::Inverted;
_cache.Inside = GPUDevice::Instance->CreatePipelineState();
if (_cache.Inside->Init(psDesc0))
{
LOG(Warning, "Failed to create decal material pipeline state.");
return true;
}
return false;
}

View File

@@ -0,0 +1,52 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "MaterialShader.h"
/// <summary>
/// Represents material that can be used to render decals.
/// </summary>
class DecalMaterialShader : public MaterialShader
{
private:
struct Cache
{
GPUPipelineState* Inside = nullptr;
GPUPipelineState* Outside = nullptr;
FORCE_INLINE void Release()
{
SAFE_DELETE_GPU_RESOURCE(Inside);
SAFE_DELETE_GPU_RESOURCE(Outside);
}
};
private:
Cache _cache;
public:
/// <summary>
/// Init
/// </summary>
/// <param name="name">Material resource name</param>
DecalMaterialShader(const String& name)
: MaterialShader(name)
{
}
public:
// [MaterialShader]
DrawPass GetDrawModes() const override;
void Bind(BindParameters& params) override;
void Unload() override;
protected:
// [MaterialShader]
bool Load() override;
};

View File

@@ -0,0 +1,268 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#include "DeferredMaterialShader.h"
#include "MaterialParams.h"
#include "Engine/Graphics/RenderBuffers.h"
#include "Engine/Graphics/RenderView.h"
#include "Engine/Renderer/DrawCall.h"
#include "Engine/Level/Scene/Lightmap.h"
#include "Engine/Graphics/GPUContext.h"
#include "Engine/Graphics/Shaders/GPUConstantBuffer.h"
#include "Engine/Graphics/Models/SkinnedMeshDrawData.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 DeferredMaterialShaderData {
Matrix ViewProjectionMatrix;
Matrix WorldMatrix;
Matrix ViewMatrix;
Matrix PrevViewProjectionMatrix;
Matrix PrevWorldMatrix;
Vector3 ViewPos;
float ViewFar;
Vector3 ViewDir;
float TimeParam;
Vector4 ViewInfo;
Vector4 ScreenSize;
Rectangle LightmapArea;
Vector3 WorldInvScale;
float WorldDeterminantSign;
Vector2 Dummy0;
float LODDitherFactor;
float PerInstanceRandom;
Vector4 TemporalAAJitter;
Vector3 GeometrySize;
float Dummy1;
});
DrawPass DeferredMaterialShader::GetDrawModes() const
{
return DrawPass::Depth | DrawPass::GBuffer | DrawPass::MotionVectors;
}
bool DeferredMaterialShader::CanUseLightmap() const
{
return true;
}
bool DeferredMaterialShader::CanUseInstancing() const
{
return true;
}
void DeferredMaterialShader::Bind(BindParameters& params)
{
// Prepare
auto context = params.GPUContext;
auto& view = params.RenderContext.View;
auto& drawCall = *params.FirstDrawCall;
const auto cb0 = _shader->GetCB(0);
const bool hasCb0 = cb0 && cb0->GetSize() != 0;
// Setup parameters
MaterialParameter::BindMeta bindMeta;
bindMeta.Context = context;
bindMeta.Buffer0 = hasCb0 ? _cb0Data.Get() + sizeof(DeferredMaterialShaderData) : nullptr;
bindMeta.Input = nullptr;
bindMeta.Buffers = nullptr;
bindMeta.CanSampleDepth = false;
bindMeta.CanSampleGBuffer = false;
MaterialParams::Bind(params.ParamsLink, bindMeta);
// Setup material constants data
auto materialData = reinterpret_cast<DeferredMaterialShaderData*>(_cb0Data.Get());
if (hasCb0)
{
Matrix::Transpose(view.Frustum.GetMatrix(), materialData->ViewProjectionMatrix);
Matrix::Transpose(drawCall.World, materialData->WorldMatrix);
Matrix::Transpose(view.View, materialData->ViewMatrix);
Matrix::Transpose(drawCall.PrevWorld, materialData->PrevWorldMatrix);
Matrix::Transpose(view.PrevViewProjection, materialData->PrevViewProjectionMatrix);
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;
// Extract per axis scales from LocalToWorld transform
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();
const Vector3 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->WorldInvScale = worldInvScale;
materialData->WorldDeterminantSign = drawCall.WorldDeterminantSign;
materialData->LODDitherFactor = drawCall.LODDitherFactor;
materialData->PerInstanceRandom = drawCall.PerInstanceRandom;
materialData->TemporalAAJitter = view.TemporalAAJitter;
materialData->GeometrySize = drawCall.GeometrySize;
}
const bool useLightmap = view.Flags & ViewFlags::GI
#if USE_EDITOR
&& EnableLightmapsUsage
#endif
&& drawCall.Lightmap != nullptr;
if (useLightmap)
{
// Bind lightmap textures
GPUTexture *lightmap0, *lightmap1, *lightmap2;
drawCall.Lightmap->GetTextures(&lightmap0, &lightmap1, &lightmap2);
context->BindSR(0, lightmap0);
context->BindSR(1, lightmap1);
context->BindSR(2, lightmap2);
// Set lightmap data
materialData->LightmapArea = drawCall.LightmapUVsArea;
}
// Check if is using mesh skinning
const bool useSkinning = drawCall.Skinning != nullptr;
bool perBoneMotionBlur = false;
if (useSkinning)
{
// Bind skinning buffer
ASSERT(drawCall.Skinning->IsReady());
context->BindSR(0, drawCall.Skinning->BoneMatrices->View());
if (drawCall.Skinning->PrevBoneMatrices && drawCall.Skinning->PrevBoneMatrices->IsAllocated())
{
context->BindSR(1, drawCall.Skinning->PrevBoneMatrices->View());
perBoneMotionBlur = true;
}
}
// Bind constants
if (hasCb0)
{
context->UpdateCB(cb0, _cb0Data.Get());
context->BindCB(0, cb0);
}
// 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 USE_EDITOR
if (IsRunningRadiancePass)
cullMode = CullMode::TwoSided;
#endif
if (cullMode != CullMode::TwoSided && drawCall.IsNegativeScale())
{
// Invert culling when scale is negative
if (cullMode == CullMode::Normal)
cullMode = CullMode::Inverted;
else
cullMode = CullMode::Normal;
}
ASSERT_LOW_LAYER(!(useSkinning && params.DrawCallsCount > 1)); // No support for instancing skinned meshes
const auto cache = params.DrawCallsCount == 1 ? &_cache : &_cacheInstanced;
PipelineStateCache* psCache = cache->GetPS(view.Pass, useLightmap, useSkinning, perBoneMotionBlur);
ASSERT(psCache);
GPUPipelineState* state = psCache->GetPS(cullMode, wireframe);
// Bind pipeline
context->SetState(state);
}
void DeferredMaterialShader::Unload()
{
// Base
MaterialShader::Unload();
_cache.Release();
_cacheInstanced.Release();
}
bool DeferredMaterialShader::Load()
{
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");
}
// GBuffer Pass
psDesc.VS = _shader->GetVS("VS");
psDesc.PS = _shader->GetPS("PS_GBuffer");
_cache.Default.Init(psDesc);
psDesc.VS = _shader->GetVS("VS", 1);
_cacheInstanced.Default.Init(psDesc);
// GBuffer Pass with lightmap (use pixel shader permutation for USE_LIGHTMAP=1)
psDesc.VS = _shader->GetVS("VS");
psDesc.PS = _shader->GetPS("PS_GBuffer", 1);
_cache.DefaultLightmap.Init(psDesc);
psDesc.VS = _shader->GetVS("VS", 1);
_cacheInstanced.DefaultLightmap.Init(psDesc);
// GBuffer Pass with skinning
psDesc.VS = _shader->GetVS("VS_Skinned");
psDesc.PS = _shader->GetPS("PS_GBuffer");
_cache.DefaultSkinned.Init(psDesc);
// Motion Vectors pass
psDesc.DepthWriteEnable = false;
psDesc.DepthTestEnable = true;
psDesc.DepthFunc = ComparisonFunc::LessEqual;
if (useTess)
{
psDesc.HS = _shader->GetHS("HS", 1);
psDesc.DS = _shader->GetDS("DS", 1);
}
psDesc.VS = _shader->GetVS("VS", 2);
psDesc.PS = _shader->GetPS("PS_MotionVectors");
_cache.MotionVectors.Init(psDesc);
// Motion Vectors pass with skinning
psDesc.VS = _shader->GetVS("VS_Skinned", 1);
_cache.MotionVectorsSkinned.Init(psDesc);
// Motion Vectors pass with skinning (with per-bone motion blur)
psDesc.VS = _shader->GetVS("VS_Skinned", 2);
_cache.MotionVectorsSkinnedPerBone.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;
GPUShaderProgramVS* instancedDepthPassVS;
if ((_info.UsageFlags & (MaterialUsageFlags::UseMask | MaterialUsageFlags::UsePositionOffset)) != 0)
{
// Materials with masking need full vertex buffer to get texcoord used to sample textures for per pixel masking.
// Materials with world pos offset need full VB to apply offset using texcoord etc.
psDesc.VS = _shader->GetVS("VS");
instancedDepthPassVS = _shader->GetVS("VS", 1);
psDesc.PS = _shader->GetPS("PS_Depth");
}
else
{
psDesc.VS = _shader->GetVS("VS_Depth");
instancedDepthPassVS = _shader->GetVS("VS_Depth", 1);
psDesc.PS = nullptr;
}
_cache.Depth.Init(psDesc);
psDesc.VS = instancedDepthPassVS;
_cacheInstanced.Depth.Init(psDesc);
// Depth Pass with skinning
psDesc.VS = _shader->GetVS("VS_Skinned");
_cache.DepthSkinned.Init(psDesc);
return false;
}

View File

@@ -0,0 +1,77 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "MaterialShader.h"
/// <summary>
/// Represents material that can be used to render objects to GBuffer.
/// </summary>
class DeferredMaterialShader : public MaterialShader
{
private:
struct Cache
{
PipelineStateCache Default;
PipelineStateCache DefaultSkinned;
PipelineStateCache DefaultLightmap;
PipelineStateCache Depth;
PipelineStateCache DepthSkinned;
PipelineStateCache MotionVectors;
PipelineStateCache MotionVectorsSkinned;
PipelineStateCache MotionVectorsSkinnedPerBone;
FORCE_INLINE PipelineStateCache* GetPS(const DrawPass pass, const bool useLightmap, const bool useSkinning, const bool perBoneMotionBlur)
{
switch (pass)
{
case DrawPass::Depth:
return useSkinning ? &DepthSkinned : &Depth;
case DrawPass::GBuffer:
return useLightmap ? &DefaultLightmap : (useSkinning ? &DefaultSkinned : &Default);
case DrawPass::MotionVectors:
return useSkinning ? (perBoneMotionBlur ? &MotionVectorsSkinnedPerBone : &MotionVectorsSkinned) : &MotionVectors;
default:
return nullptr;
}
}
FORCE_INLINE void Release()
{
Default.Release();
DefaultSkinned.Release();
DefaultLightmap.Release();
Depth.Release();
DepthSkinned.Release();
MotionVectors.Release();
MotionVectorsSkinned.Release();
}
};
private:
Cache _cache;
Cache _cacheInstanced;
public:
DeferredMaterialShader(const String& name)
: MaterialShader(name)
{
}
public:
// [MaterialShader]
DrawPass GetDrawModes() const override;
bool CanUseLightmap() const override;
bool CanUseInstancing() const override;
void Bind(BindParameters& params) override;
void Unload() override;
protected:
// [MaterialShader]
bool Load() override;
};

View File

@@ -0,0 +1,353 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#include "ForwardMaterialShader.h"
#include "MaterialParams.h"
#include "Engine/Engine/Time.h"
#include "Engine/Graphics/GPULimits.h"
#include "Engine/Graphics/Models/SkinnedMeshDrawData.h"
#include "Engine/Graphics/RenderView.h"
#include "Engine/Level/Actors/EnvironmentProbe.h"
#include "Engine/Renderer/DepthOfFieldPass.h"
#include "Engine/Renderer/DrawCall.h"
#include "Engine/Renderer/ShadowsPass.h"
#if USE_EDITOR
#include "Engine/Renderer/Lightmaps.h"
#endif
#define MAX_LOCAL_LIGHTS 4
PACK_STRUCT(struct ForwardMaterialShaderData {
Matrix ViewProjectionMatrix;
Matrix WorldMatrix;
Matrix ViewMatrix;
Matrix PrevViewProjectionMatrix;
Matrix PrevWorldMatrix;
Vector3 ViewPos;
float ViewFar;
Vector3 ViewDir;
float TimeParam;
Vector4 ViewInfo;
Vector4 ScreenSize;
Rectangle LightmapArea;
Vector3 WorldInvScale;
float WorldDeterminantSign;
Vector2 Dummy0;
float LODDitherFactor;
float PerInstanceRandom;
Vector3 GeometrySize;
float Dummy1;
});
PACK_STRUCT(struct ForwardMaterialShaderLightingData {
LightData DirectionalLight;
LightShadowData DirectionalLightShadow;
LightData SkyLight;
ProbeData EnvironmentProbe;
ExponentialHeightFogData ExponentialHeightFog;
Vector3 Dummy2;
uint32 LocalLightsCount;
LightData LocalLights[MAX_LOCAL_LIGHTS];
});
DrawPass ForwardMaterialShader::GetDrawModes() const
{
return _drawModes;
}
bool ForwardMaterialShader::CanUseInstancing() const
{
return true;
}
void ForwardMaterialShader::Bind(BindParameters& params)
{
// Prepare
auto context = params.GPUContext;
auto& view = params.RenderContext.View;
auto cache = params.RenderContext.List;
auto& drawCall = *params.FirstDrawCall;
const auto cb0 = _shader->GetCB(0);
const bool hasCb0 = cb0 && cb0->GetSize() != 0;
const auto cb1 = _shader->GetCB(1);
const bool hasCb1 = cb1 && cb1->GetSize() != 0;
// Setup parameters
MaterialParameter::BindMeta bindMeta;
bindMeta.Context = context;
bindMeta.Buffer0 = hasCb0 ? _cb0Data.Get() + sizeof(ForwardMaterialShaderData) : nullptr;
bindMeta.Input = nullptr; // forward pass materials cannot sample scene color for now
bindMeta.Buffers = params.RenderContext.Buffers;
bindMeta.CanSampleDepth = GPUDevice::Instance->Limits.HasReadOnlyDepth;
bindMeta.CanSampleGBuffer = true;
MaterialParams::Bind(params.ParamsLink, bindMeta);
// Check if is using mesh skinning
const bool useSkinning = drawCall.Skinning != nullptr;
if (useSkinning)
{
// Bind skinning buffer
ASSERT(drawCall.Skinning->IsReady());
context->BindSR(0, drawCall.Skinning->BoneMatrices->View());
}
// Setup material constants data
const auto materialData = reinterpret_cast<ForwardMaterialShaderData*>(_cb0Data.Get());
if (hasCb0)
{
Matrix::Transpose(view.Frustum.GetMatrix(), materialData->ViewProjectionMatrix);
Matrix::Transpose(drawCall.World, materialData->WorldMatrix);
Matrix::Transpose(view.View, materialData->ViewMatrix);
Matrix::Transpose(drawCall.PrevWorld, materialData->PrevWorldMatrix);
Matrix::Transpose(view.PrevViewProjection, materialData->PrevViewProjectionMatrix);
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;
// Extract per axis scales from LocalToWorld transform
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();
const Vector3 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->WorldInvScale = worldInvScale;
materialData->WorldDeterminantSign = drawCall.WorldDeterminantSign;
materialData->LODDitherFactor = drawCall.LODDitherFactor;
materialData->PerInstanceRandom = drawCall.PerInstanceRandom;
materialData->GeometrySize = drawCall.GeometrySize;
}
// Setup lighting constants data
if (hasCb1)
{
auto& lightingData = *reinterpret_cast<ForwardMaterialShaderLightingData*>(_cb1Data.Get());
const int32 envProbeShaderRegisterIndex = 0;
const int32 skyLightShaderRegisterIndex = 1;
const int32 dirLightShaderRegisterIndex = 2;
// Set fog input
if (cache->Fog)
{
cache->Fog->GetExponentialHeightFogData(view, lightingData.ExponentialHeightFog);
}
else
{
lightingData.ExponentialHeightFog.FogMinOpacity = 1.0f;
lightingData.ExponentialHeightFog.ApplyDirectionalInscattering = 0.0f;
}
// Set directional light input
if (cache->DirectionalLights.HasItems())
{
const auto& dirLight = cache->DirectionalLights.First();
const auto shadowPass = ShadowsPass::Instance();
const bool useShadow = shadowPass->LastDirLightIndex == 0;
if (useShadow)
{
lightingData.DirectionalLightShadow = shadowPass->LastDirLight;
context->BindSR(dirLightShaderRegisterIndex, shadowPass->LastDirLightShadowMap);
}
else
{
context->UnBindSR(dirLightShaderRegisterIndex);
}
dirLight.SetupLightData(&lightingData.DirectionalLight, view, useShadow);
}
else
{
lightingData.DirectionalLight.Color = Vector3::Zero;
lightingData.DirectionalLight.CastShadows = 0.0f;
context->UnBindSR(dirLightShaderRegisterIndex);
}
// Set sky light
if (cache->SkyLights.HasItems())
{
auto& skyLight = cache->SkyLights.First();
skyLight.SetupLightData(&lightingData.SkyLight, view, false);
const auto texture = skyLight.Image ? skyLight.Image->GetTexture() : nullptr;
context->BindSR(skyLightShaderRegisterIndex, GET_TEXTURE_VIEW_SAFE(texture));
}
else
{
Platform::MemoryClear(&lightingData.SkyLight, sizeof(lightingData.SkyLight));
context->UnBindSR(skyLightShaderRegisterIndex);
}
// Set reflection probe data
EnvironmentProbe* probe = nullptr;
// TODO: optimize env probe searching for a transparent material - use spatial cache for renderer to find it
for (int32 i = 0; i < cache->EnvironmentProbes.Count(); i++)
{
const auto p = cache->EnvironmentProbes[i];
if (p->GetSphere().Contains(drawCall.World.GetTranslation()) != ContainmentType::Disjoint)
{
probe = p;
break;
}
}
if (probe && probe->GetProbe())
{
probe->SetupProbeData(&lightingData.EnvironmentProbe);
const auto texture = probe->GetProbe()->GetTexture();
context->BindSR(envProbeShaderRegisterIndex, GET_TEXTURE_VIEW_SAFE(texture));
}
else
{
lightingData.EnvironmentProbe.Data1 = Vector4::Zero;
context->UnBindSR(envProbeShaderRegisterIndex);
}
// Set local lights
lightingData.LocalLightsCount = 0;
for (int32 i = 0; i < cache->PointLights.Count(); i++)
{
const auto& light = cache->PointLights[i];
if (BoundingSphere(light.Position, light.Radius).Contains(drawCall.World.GetTranslation()) != ContainmentType::Disjoint)
{
light.SetupLightData(&lightingData.LocalLights[lightingData.LocalLightsCount], view, false);
lightingData.LocalLightsCount++;
if (lightingData.LocalLightsCount == MAX_LOCAL_LIGHTS)
break;
}
}
for (int32 i = 0; i < cache->SpotLights.Count(); i++)
{
const auto& light = cache->SpotLights[i];
if (BoundingSphere(light.Position, light.Radius).Contains(drawCall.World.GetTranslation()) != ContainmentType::Disjoint)
{
light.SetupLightData(&lightingData.LocalLights[lightingData.LocalLightsCount], view, false);
lightingData.LocalLightsCount++;
if (lightingData.LocalLightsCount == MAX_LOCAL_LIGHTS)
break;
}
}
}
// Bind constants
if (hasCb0)
{
context->UpdateCB(cb0, _cb0Data.Get());
context->BindCB(0, cb0);
}
if (hasCb1)
{
context->UpdateCB(cb1, _cb1Data.Get());
context->BindCB(1, cb1);
}
// 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 USE_EDITOR
if (IsRunningRadiancePass)
cullMode = CullMode::TwoSided;
#endif
if (cullMode != CullMode::TwoSided && drawCall.IsNegativeScale())
{
// Invert culling when scale is negative
if (cullMode == CullMode::Normal)
cullMode = CullMode::Inverted;
else
cullMode = CullMode::Normal;
}
ASSERT_LOW_LAYER(!(useSkinning && params.DrawCallsCount > 1)); // No support for instancing skinned meshes
const auto cacheObj = params.DrawCallsCount == 1 ? &_cache : &_cacheInstanced;
PipelineStateCache* psCache = cacheObj->GetPS(view.Pass, useSkinning);
ASSERT(psCache);
GPUPipelineState* state = psCache->GetPS(cullMode, wireframe);
// Bind pipeline
context->SetState(state);
}
void ForwardMaterialShader::Unload()
{
// Base
MaterialShader::Unload();
_cache.Release();
_cacheInstanced.Release();
}
bool ForwardMaterialShader::Load()
{
_drawModes = DrawPass::Depth | DrawPass::Forward;
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");
}
// Check if use transparent distortion pass
if (_shader->HasShader("PS_Distortion"))
{
_drawModes |= DrawPass::Distortion;
// Accumulate Distortion Pass
psDesc.VS = _shader->GetVS("VS");
psDesc.PS = _shader->GetPS("PS_Distortion");
psDesc.BlendMode = BlendingMode::Add;
psDesc.DepthWriteEnable = false;
_cache.Distortion.Init(psDesc);
//psDesc.VS = _shader->GetVS("VS", 1);
//_cacheInstanced.Distortion.Init(psDesc);
psDesc.VS = _shader->GetVS("VS_Skinned");
_cache.DistortionSkinned.Init(psDesc);
}
// Forward Pass
psDesc.VS = _shader->GetVS("VS");
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);
//psDesc.VS = _shader->GetVS("VS", 1);
//_cacheInstanced.Default.Init(psDesc);
psDesc.VS = _shader->GetVS("VS_Skinned");
_cache.DefaultSkinned.Init(psDesc);
// Depth Pass
psDesc = GPUPipelineState::Description::Default;
psDesc.CullMode = CullMode::TwoSided;
psDesc.DepthClipEnable = false;
psDesc.DepthWriteEnable = true;
psDesc.DepthTestEnable = true;
psDesc.DepthFunc = ComparisonFunc::Less;
psDesc.HS = nullptr;
psDesc.DS = nullptr;
psDesc.VS = _shader->GetVS("VS");
psDesc.PS = _shader->GetPS("PS_Depth");
_cache.Depth.Init(psDesc);
psDesc.VS = _shader->GetVS("VS", 1);
_cacheInstanced.Depth.Init(psDesc);
psDesc.VS = _shader->GetVS("VS_Skinned");
_cache.DepthSkinned.Init(psDesc);
return false;
}

View File

@@ -0,0 +1,78 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "MaterialShader.h"
/// <summary>
/// Represents material that can be used to render objects with Forward Rendering.
/// </summary>
class ForwardMaterialShader : public MaterialShader
{
private:
struct Cache
{
PipelineStateCache Default;
PipelineStateCache DefaultSkinned;
PipelineStateCache Depth;
PipelineStateCache DepthSkinned;
PipelineStateCache Distortion;
PipelineStateCache DistortionSkinned;
FORCE_INLINE PipelineStateCache* GetPS(const DrawPass pass, const bool useSkinning)
{
switch (pass)
{
case DrawPass::Depth:
return useSkinning ? &DepthSkinned : &Depth;
case DrawPass::Distortion:
return useSkinning ? &DistortionSkinned : &Distortion;
case DrawPass::Forward:
return useSkinning ? &DefaultSkinned : &Default;
default:
return nullptr;
}
}
FORCE_INLINE void Release()
{
Default.Release();
DefaultSkinned.Release();
Depth.Release();
DepthSkinned.Release();
Distortion.Release();
DistortionSkinned.Release();
}
};
private:
Cache _cache;
Cache _cacheInstanced;
DrawPass _drawModes = DrawPass::None;
public:
/// <summary>
/// Init
/// </summary>
/// <param name="name">Material resource name</param>
ForwardMaterialShader(const String& name)
: MaterialShader(name)
{
}
public:
// [MaterialShader]
DrawPass GetDrawModes() const override;
bool CanUseInstancing() const override;
void Bind(BindParameters& params) override;
void Unload() override;
protected:
// [MaterialShader]
bool Load() override;
};

View File

@@ -0,0 +1,107 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#include "GUIMaterialShader.h"
#include "MaterialParams.h"
#include "Engine/Engine/Time.h"
#include "Engine/Core/Math/Viewport.h"
#include "Engine/Graphics/GPUDevice.h"
#include "Engine/Graphics/RenderTask.h"
#include "Engine/Graphics/Shaders/GPUShader.h"
#include "Engine/Graphics/RenderView.h"
#include "Engine/Graphics/Shaders/GPUConstantBuffer.h"
#include "Engine/Render2D/Render2D.h"
#include "Engine/Renderer/DrawCall.h"
PACK_STRUCT(struct GUIMaterialShaderData {
Matrix ViewProjectionMatrix;
Matrix WorldMatrix;
Matrix ViewMatrix;
Vector3 ViewPos;
float ViewFar;
Vector3 ViewDir;
float TimeParam;
Vector4 ViewInfo;
Vector4 ScreenSize;
});
void GUIMaterialShader::Bind(BindParameters& params)
{
// Prepare
auto context = params.GPUContext;
auto& view = params.RenderContext.View;
const auto cb0 = _shader->GetCB(0);
const bool hasCb0 = cb0->GetSize() != 0;
const auto ps = context->IsDepthBufferBinded() ? _cache.Depth : _cache.NoDepth;
// Setup parameters
MaterialParameter::BindMeta bindMeta;
bindMeta.Context = context;
bindMeta.Buffer0 = hasCb0 ? _cb0Data.Get() + sizeof(GUIMaterialShaderData) : nullptr;
bindMeta.Input = nullptr;
bindMeta.Buffers = nullptr;
bindMeta.CanSampleDepth = false;
bindMeta.CanSampleGBuffer = false;
MaterialParams::Bind(params.ParamsLink, bindMeta);
// Setup material constants data
if (hasCb0)
{
auto materialData = reinterpret_cast<GUIMaterialShaderData*>(_cb0Data.Get());
const auto viewProjectionMatrix = (Matrix*)params.CustomData;
Matrix::Transpose(*viewProjectionMatrix, materialData->ViewProjectionMatrix);
Matrix::Transpose(Matrix::Identity, materialData->WorldMatrix);
Matrix::Transpose(Matrix::Identity, materialData->ViewMatrix);
materialData->ViewPos = Vector3::Zero;
materialData->ViewFar = 0.0f;
materialData->ViewDir = Vector3::Forward;
materialData->TimeParam = Time::Draw.UnscaledTime.GetTotalSeconds();
materialData->ViewInfo = Vector4::Zero;
auto& viewport = Render2D::GetViewport();
materialData->ScreenSize = Vector4(viewport.Width, viewport.Height, 1.0f / viewport.Width, 1.0f / viewport.Height);
}
// Bind constants
if (hasCb0)
{
context->UpdateCB(cb0, _cb0Data.Get());
context->BindCB(0, cb0);
}
// Bind pipeline
context->SetState(ps);
}
void GUIMaterialShader::Unload()
{
// Base
MaterialShader::Unload();
_cache.Release();
}
bool GUIMaterialShader::Load()
{
GPUPipelineState::Description psDesc0 = GPUPipelineState::Description::DefaultFullscreenTriangle;
psDesc0.Wireframe = (_info.FeaturesFlags & MaterialFeaturesFlags::Wireframe) != 0;
psDesc0.VS = _shader->GetVS("VS_GUI");
psDesc0.PS = _shader->GetPS("PS_GUI");
psDesc0.BlendMode = BlendingMode::AlphaBlend;
psDesc0.DepthTestEnable = psDesc0.DepthWriteEnable = true;
_cache.Depth = GPUDevice::Instance->CreatePipelineState();
_cache.NoDepth = GPUDevice::Instance->CreatePipelineState();
bool failed = _cache.Depth->Init(psDesc0);
psDesc0.DepthTestEnable = psDesc0.DepthWriteEnable = false;
failed |= _cache.NoDepth->Init(psDesc0);
if (failed)
{
LOG(Warning, "Failed to create GUI material pipeline state.");
return true;
}
return false;
}

View File

@@ -0,0 +1,51 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "MaterialShader.h"
/// <summary>
/// Represents material that can be used to render GUI.
/// </summary>
class GUIMaterialShader : public MaterialShader
{
private:
struct Cache
{
GPUPipelineState* Depth = nullptr;
GPUPipelineState* NoDepth = nullptr;
FORCE_INLINE void Release()
{
SAFE_DELETE_GPU_RESOURCE(Depth);
SAFE_DELETE_GPU_RESOURCE(NoDepth);
}
};
private:
Cache _cache;
public:
/// <summary>
/// Init
/// </summary>
/// <param name="name">Material resource name</param>
GUIMaterialShader(const String& name)
: MaterialShader(name)
{
}
public:
// [MaterialShader]
void Bind(BindParameters& params) override;
void Unload() override;
protected:
// [MaterialShader]
bool Load() override;
};

View File

@@ -0,0 +1,184 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Threading/Task.h"
#include "MaterialInfo.h"
struct MaterialParamsLink;
class GPUContext;
class GPUTextureView;
class RenderBuffers;
class SceneRenderTask;
struct RenderView;
struct RenderContext;
struct DrawCall;
/// <summary>
/// Interface for material objects.
/// </summary>
class FLAXENGINE_API IMaterial
{
public:
/// <summary>
/// Gets the material info, structure which describes material surface.
/// </summary>
/// <returns>The constant reference to the material descriptor.</returns>
virtual const MaterialInfo& GetInfo() const = 0;
/// <summary>
/// Determines whether material is a surface shader.
/// </summary>
/// <returns><c>true</c> if material is surface shader; otherwise, <c>false</c>.</returns>
FORCE_INLINE bool IsSurface() const
{
return GetInfo().Domain == MaterialDomain::Surface;
}
/// <summary>
/// Determines whether material is a post fx.
/// </summary>
/// <returns><c>true</c> if material is post fx; otherwise, <c>false</c>.</returns>
FORCE_INLINE bool IsPostFx() const
{
return GetInfo().Domain == MaterialDomain::PostProcess;
}
/// <summary>
/// Determines whether material is a decal.
/// </summary>
/// <returns><c>true</c> if material is decal; otherwise, <c>false</c>.</returns>
FORCE_INLINE bool IsDecal() const
{
return GetInfo().Domain == MaterialDomain::Decal;
}
/// <summary>
/// Determines whether material is a GUI shader.
/// </summary>
/// <returns><c>true</c> if material is GUI shader; otherwise, <c>false</c>.</returns>
FORCE_INLINE bool IsGUI() const
{
return GetInfo().Domain == MaterialDomain::GUI;
}
/// <summary>
/// Determines whether material is a terrain shader.
/// </summary>
/// <returns><c>true</c> if material is terrain shader; otherwise, <c>false</c>.</returns>
FORCE_INLINE bool IsTerrain() const
{
return GetInfo().Domain == MaterialDomain::Terrain;
}
/// <summary>
/// Determines whether material is a particle shader.
/// </summary>
/// <returns><c>true</c> if material is particle shader; otherwise, <c>false</c>.</returns>
FORCE_INLINE bool IsParticle() const
{
return GetInfo().Domain == MaterialDomain::Particle;
}
/// <summary>
/// Checks if material needs vertex color vertex buffer data for rendering.
/// </summary>
/// <returns>True if mesh renderer have to provide vertex color buffer to render that material</returns>
FORCE_INLINE bool RequireVertexColor() const
{
return (GetInfo().UsageFlags & MaterialUsageFlags::UseVertexColor) != 0;
}
/// <summary>
/// Checks if material supports dithered LOD transitions.
/// </summary>
/// <returns>True if material supports dithered LOD transitions, otherwise false.</returns>
FORCE_INLINE bool IsDitheredLODTransition() const
{
return (GetInfo().FeaturesFlags & MaterialFeaturesFlags::DitheredLODTransition) != 0;
}
/// <summary>
/// Returns true if material is ready for rendering.
/// </summary>
/// <returns>True if can render that material</returns>
virtual bool IsReady() const = 0;
/// <summary>
/// Gets the mask of render passes supported by this material.
/// </summary>
/// <returns>The drw passes supported by this material.</returns>
virtual DrawPass GetDrawModes() const
{
return DrawPass::None;
}
/// <summary>
/// Returns true if material can use lightmaps (this includes lightmaps offline baking and sampling at runtime).
/// </summary>
/// <returns>True if can use lightmaps, otherwise false</returns>
virtual bool CanUseLightmap() const
{
return false;
}
/// <summary>
/// Returns true if material can use draw calls instancing.
/// </summary>
/// <returns>True if can use instancing, otherwise false.</returns>
virtual bool CanUseInstancing() const
{
return false;
}
public:
/// <summary>
/// Settings for the material binding to the graphics pipeline.
/// </summary>
struct BindParameters
{
GPUContext* GPUContext;
const RenderContext& RenderContext;
const DrawCall* FirstDrawCall;
int32 DrawCallsCount;
MaterialParamsLink* ParamsLink = nullptr;
void* CustomData = nullptr;
/// <summary>
/// The input scene color. It's optional and used in forward/postFx rendering.
/// </summary>
GPUTextureView* Input = nullptr;
BindParameters(::GPUContext* context, const ::RenderContext& renderContext)
: GPUContext(context)
, RenderContext(renderContext)
, FirstDrawCall(nullptr)
, DrawCallsCount(0)
{
}
BindParameters(::GPUContext* context, const ::RenderContext& renderContext, const DrawCall& drawCall)
: GPUContext(context)
, RenderContext(renderContext)
, FirstDrawCall(&drawCall)
, DrawCallsCount(1)
{
}
BindParameters(::GPUContext* context, const ::RenderContext& renderContext, const DrawCall* firstDrawCall, int32 drawCallsCount)
: GPUContext(context)
, RenderContext(renderContext)
, FirstDrawCall(firstDrawCall)
, DrawCallsCount(drawCallsCount)
{
}
};
/// <summary>
/// Binds the material state to the GPU pipeline. Should be called before the draw command.
/// </summary>
/// <param name="params">The material bind settings.</param>
virtual void Bind(BindParameters& params) = 0;
};

View File

@@ -0,0 +1,856 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "../Enums.h"
#include "Engine/Core/Math/Math.h"
/// <summary>
/// Material domain type. Material domain defines the target usage of the material shader.
/// </summary>
API_ENUM() enum class MaterialDomain : byte
{
/// <summary>
/// The surface material. Can be used to render the scene geometry including models and skinned models.
/// </summary>
Surface = 0,
/// <summary>
/// The post process material. Can be used to perform custom post-processing of the rendered frame.
/// </summary>
PostProcess = 1,
/// <summary>
/// The deferred decal material. Can be used to apply custom overlay or surface modifications to the object surfaces in the world.
/// </summary>
Decal = 2,
/// <summary>
/// The GUI shader. Can be used to draw custom control interface elements or to add custom effects to the GUI.
/// </summary>
GUI = 3,
/// <summary>
/// The terrain shader. Can be used only with landscape chunks geometry that use optimized vertex data and support multi-layered blending.
/// </summary>
Terrain = 4,
/// <summary>
/// The particle shader. Can be used only with particles geometry (sprites, trails and ribbons). Supports reading particle data on a GPU.
/// </summary>
Particle = 5,
};
/// <summary>
/// Material blending modes.
/// </summary>
API_ENUM() enum class MaterialBlendMode : byte
{
/// <summary>
/// The opaque material. Used during GBuffer pass rendering.
/// </summary>
Opaque = 0,
/// <summary>
/// The transparent material. Used during Forward pass rendering.
/// </summary>
Transparent = 1,
/// <summary>
/// The additive blend. Material color is used to add to color of the objects behind the surface. Used during Forward pass rendering.
/// </summary>
Additive = 2,
/// <summary>
/// The multiply blend. Material color is used to multiply color of the objects behind the surface. Used during Forward pass rendering.
/// </summary>
Multiply = 3,
};
// Old material blending mode used before introducing MaterialShadingModel
// [Deprecated on 10.09.2018, expires on 10.05.2019]
enum class OldMaterialBlendMode : byte
{
Opaque = 0,
Transparent = 1,
Unlit = 2,
};
/// <summary>
/// Material shading modes. Defines how material inputs and properties are combined to result the final surface color.
/// </summary>
API_ENUM() enum class MaterialShadingModel : byte
{
/// <summary>
/// The unlit material. Emissive channel is used as an output color. Can perform custom lighting operations or just glow. Won't be affected by the lighting pipeline.
/// </summary>
Unlit = 0,
/// <summary>
/// The default lit material. The most common choice for the material surfaces.
/// </summary>
Lit = 1,
/// <summary>
/// The subsurface material. Intended for materials like vax or skin that need light scattering to transport simulation through the object.
/// </summary>
Subsurface = 2,
/// <summary>
/// The foliage material. Intended for foliage materials like leaves and grass that need light scattering to transport simulation through the thin object.
/// </summary>
Foliage = 3,
};
/// <summary>
/// Material transparent lighting modes.
/// [Deprecated on 24.07.2019, expires on 10.05.2021]
/// </summary>
enum class MaterialTransparentLighting_Deprecated : byte
{
/// <summary>
/// Shading is disabled.
/// </summary>
None = 0,
/// <summary>
/// Shading is performed per pixel for single directional light.
/// </summary>
SingleDirectionalPerPixel = 1
};
/// <summary>
/// Material usage flags (deprecated).
/// [Deprecated on 24.07.2019, expires on 10.05.2021]
/// </summary>
enum class MaterialFlags_Deprecated : uint32
{
/// <summary>
/// The none.
/// </summary>
None = 0,
/// <summary>
/// Material is using mask to discard some pixels.
/// Masked materials are using full vertex buffer during shadow maps and depth pass rendering (need UVs).
/// </summary>
UseMask = 1 << 0,
/// <summary>
/// The two sided material. No triangle normal culling is performed.
/// </summary>
TwoSided = 1 << 1,
/// <summary>
/// The wireframe material.
/// </summary>
Wireframe = 1 << 2,
/// <summary>
/// The material is using emissive light.
/// </summary>
UseEmissive = 1 << 3,
/// <summary>
/// The transparent materials option. Disable depth test (material ignores depth).
/// </summary>
TransparentDisableDepthTest = 1 << 4,
/// <summary>
/// The transparent materials option. Disable fog.
/// </summary>
TransparentDisableFog = 1 << 5,
/// <summary>
/// The transparent materials option. Disable reflections.
/// </summary>
TransparentDisableReflections = 1 << 6,
/// <summary>
/// The transparent materials option. Disable depth buffer write (won't modify depth buffer value after rendering).
/// </summary>
DisableDepthWrite = 1 << 7,
/// <summary>
/// The transparent materials option. Disable distortion.
/// </summary>
TransparentDisableDistortion = 1 << 8,
/// <summary>
/// The material is using world position offset (it may be animated inside a shader).
/// </summary>
UsePositionOffset = 1 << 9,
/// <summary>
/// The material is using vertex colors. The render will try to feed the pipeline with a proper buffer so material can gather valid data.
/// </summary>
UseVertexColor = 1 << 10,
/// <summary>
/// The material is using per-pixel normal mapping.
/// </summary>
UseNormal = 1 << 11,
/// <summary>
/// The material is using position displacement (in domain shader).
/// </summary>
UseDisplacement = 1 << 12,
/// <summary>
/// The flag used to indicate that material input normal vector is defined in the world space rather than tangent space.
/// </summary>
InputWorldSpaceNormal = 1 << 13,
/// <summary>
/// The flag used to indicate that material uses dithered model LOD transition for smoother LODs switching.
/// </summary>
UseDitheredLODTransition = 1 << 14,
/// <summary>
/// The flag used to indicate that material uses refraction feature.
/// </summary>
UseRefraction = 1 << 15,
};
DECLARE_ENUM_OPERATORS(MaterialFlags_Deprecated);
/// <summary>
/// Material features flags.
/// </summary>
API_ENUM(Attributes="Flags") enum class MaterialFeaturesFlags : uint32
{
/// <summary>
/// No flags.
/// </summary>
None = 0,
/// <summary>
/// The wireframe material.
/// </summary>
Wireframe = 1 << 1,
/// <summary>
/// The depth test is disabled (material ignores depth).
/// </summary>
DisableDepthTest = 1 << 2,
/// <summary>
/// Disable depth buffer write (won't modify depth buffer value during rendering).
/// </summary>
DisableDepthWrite = 1 << 3,
/// <summary>
/// The flag used to indicate that material input normal vector is defined in the world space rather than tangent space.
/// </summary>
InputWorldSpaceNormal = 1 << 4,
/// <summary>
/// The flag used to indicate that material uses dithered model LOD transition for smoother LODs switching.
/// </summary>
DitheredLODTransition = 1 << 5,
/// <summary>
/// The flag used to disable fog. The Forward Pass materials option.
/// </summary>
DisableFog = 1 << 6,
/// <summary>
/// The flag used to disable reflections. The Forward Pass materials option.
/// </summary>
DisableReflections = 1 << 7,
/// <summary>
/// The flag used to disable distortion effect (light refraction). The Forward Pass materials option.
/// </summary>
DisableDistortion = 1 << 8,
/// <summary>
/// The flag used to enable refraction offset based on the difference between the per-pixel normal and the per-vertex normal. Useful for large water-like surfaces.
/// </summary>
PixelNormalOffsetRefraction = 1 << 9,
};
DECLARE_ENUM_OPERATORS(MaterialFeaturesFlags);
/// <summary>
/// Material features usage flags. Detected by the material generator to help graphics pipeline optimize rendering of material shaders.
/// </summary>
API_ENUM(Attributes="Flags") enum class MaterialUsageFlags : uint32
{
/// <summary>
/// No flags.
/// </summary>
None = 0,
/// <summary>
/// Material is using mask to discard some pixels. Masked materials are using full vertex buffer during shadow maps and depth pass rendering (need UVs).
/// </summary>
UseMask = 1 << 0,
/// <summary>
/// The material is using emissive light.
/// </summary>
UseEmissive = 1 << 1,
/// <summary>
/// The material is using world position offset (it may be animated inside a shader).
/// </summary>
UsePositionOffset = 1 << 2,
/// <summary>
/// The material is using vertex colors. The render will try to feed the pipeline with a proper buffer so material can gather valid data.
/// </summary>
UseVertexColor = 1 << 3,
/// <summary>
/// The material is using per-pixel normal mapping.
/// </summary>
UseNormal = 1 << 4,
/// <summary>
/// The material is using position displacement (in domain shader).
/// </summary>
UseDisplacement = 1 << 5,
/// <summary>
/// The flag used to indicate that material uses refraction feature.
/// </summary>
UseRefraction = 1 << 6,
};
DECLARE_ENUM_OPERATORS(MaterialUsageFlags);
/// <summary>
/// Decal material blending modes.
/// </summary>
API_ENUM() enum class MaterialDecalBlendingMode : byte
{
/// <summary>
/// Decal will be fully blended with the material surface.
/// </summary>
Translucent = 0,
/// <summary>
/// Decal color will be blended with the material surface color (using multiplication).
/// </summary>
Stain = 1,
/// <summary>
/// Decal will blend the normal vector only.
/// </summary>
Normal = 2,
/// <summary>
/// Decal will apply the emissive light only.
/// </summary>
Emissive = 3,
};
/// <summary>
/// Material input scene textures. Special inputs from the graphics pipeline.
/// </summary>
API_ENUM() enum class MaterialSceneTextures
{
/// <summary>
/// The scene color.
/// </summary>
SceneColor = 0,
/// <summary>
/// The scene depth.
/// </summary>
SceneDepth = 1,
/// <summary>
/// The material diffuse color.
/// </summary>
DiffuseColor = 2,
/// <summary>
/// The material specular color.
/// </summary>
SpecularColor = 3,
/// <summary>
/// The material world space normal.
/// </summary>
WorldNormal = 4,
/// <summary>
/// The ambient occlusion.
/// </summary>
AmbientOcclusion = 5,
/// <summary>
/// The material metalness value.
/// </summary>
Metalness = 6,
/// <summary>
/// The material roughness value.
/// </summary>
Roughness = 7,
/// <summary>
/// The material specular value.
/// </summary>
Specular = 8,
/// <summary>
/// The material color.
/// </summary>
BaseColor = 9,
/// <summary>
/// The material shading mode.
/// </summary>
ShadingModel = 10,
};
/// <summary>
/// Material info structure - version 1
/// [Deprecated on 10.09.2018, expires on 10.05.2019]
/// </summary>
struct MaterialInfo1
{
int32 Version;
MaterialDomain Domain;
OldMaterialBlendMode BlendMode;
MaterialFlags_Deprecated Flags;
/// <summary>
/// Compare structure with other one
/// </summary>
/// <param name="other">Other structure to compare</param>
/// <returns>True if both structures are equal</returns>
bool operator==(const MaterialInfo1& other) const
{
return Domain == other.Domain
&& BlendMode == other.BlendMode
&& Flags == other.Flags;
}
};
/// <summary>
/// Material info structure - version 2
/// [Deprecated on 10.09.2018, expires on 10.05.2019]
/// </summary>
struct MaterialInfo2
{
int32 Version;
MaterialDomain Domain;
OldMaterialBlendMode BlendMode;
MaterialFlags_Deprecated Flags;
MaterialTransparentLighting_Deprecated TransparentLighting;
/// <summary>
/// Compare structure with other one
/// </summary>
/// <param name="other">Other structure to compare</param>
/// <returns>True if both structures are equal</returns>
bool operator==(const MaterialInfo2& other) const
{
return Domain == other.Domain
&& BlendMode == other.BlendMode
&& TransparentLighting == other.TransparentLighting
&& Flags == other.Flags;
}
};
/// <summary>
/// Material info structure - version 3
/// [Deprecated on 10.09.2018, expires on 10.05.2019]
/// </summary>
struct MaterialInfo3
{
MaterialDomain Domain;
OldMaterialBlendMode BlendMode;
MaterialFlags_Deprecated Flags;
MaterialTransparentLighting_Deprecated TransparentLighting;
/// <summary>
/// Compare structure with other one
/// </summary>
/// <param name="other">Other structure to compare</param>
/// <returns>True if both structures are equal</returns>
bool operator==(const MaterialInfo3& other) const
{
return Domain == other.Domain
&& BlendMode == other.BlendMode
&& TransparentLighting == other.TransparentLighting
&& Flags == other.Flags;
}
};
/// <summary>
/// Material info structure - version 4
/// [Deprecated on 10.09.2018, expires on 10.05.2019]
/// </summary>
struct MaterialInfo4
{
MaterialDomain Domain;
OldMaterialBlendMode BlendMode;
MaterialFlags_Deprecated Flags;
MaterialTransparentLighting_Deprecated TransparentLighting;
MaterialPostFxLocation PostFxLocation;
/// <summary>
/// Compare structure with other one
/// </summary>
/// <param name="other">Other structure to compare</param>
/// <returns>True if both structures are equal</returns>
bool operator==(const MaterialInfo4& other) const
{
return Domain == other.Domain
&& BlendMode == other.BlendMode
&& TransparentLighting == other.TransparentLighting
&& PostFxLocation == other.PostFxLocation
&& Flags == other.Flags;
}
};
/// <summary>
/// Material info structure - version 5
/// [Deprecated on 10.09.2018, expires on 10.05.2019]
/// </summary>
struct MaterialInfo5
{
MaterialDomain Domain;
OldMaterialBlendMode BlendMode;
MaterialFlags_Deprecated Flags;
MaterialTransparentLighting_Deprecated TransparentLighting;
MaterialPostFxLocation PostFxLocation;
float MaskThreshold;
float OpacityThreshold;
/// <summary>
/// Compare structure with other one
/// </summary>
/// <param name="other">Other structure to compare</param>
/// <returns>True if both structures are equal</returns>
bool operator==(const MaterialInfo5& other) const
{
return Domain == other.Domain
&& BlendMode == other.BlendMode
&& TransparentLighting == other.TransparentLighting
&& PostFxLocation == other.PostFxLocation
&& Math::NearEqual(MaskThreshold, other.MaskThreshold)
&& Math::NearEqual(OpacityThreshold, other.OpacityThreshold)
&& Flags == other.Flags;
}
};
/// <summary>
/// Material info structure - version 6
/// [Deprecated on 10.09.2018, expires on 10.05.2019]
/// </summary>
struct MaterialInfo6
{
MaterialDomain Domain;
OldMaterialBlendMode BlendMode;
MaterialFlags_Deprecated Flags;
MaterialTransparentLighting_Deprecated TransparentLighting;
MaterialDecalBlendingMode DecalBlendingMode;
MaterialPostFxLocation PostFxLocation;
float MaskThreshold;
float OpacityThreshold;
MaterialInfo6()
{
}
MaterialInfo6(const MaterialInfo5& other)
{
Domain = other.Domain;
BlendMode = other.BlendMode;
Flags = other.Flags;
TransparentLighting = other.TransparentLighting;
DecalBlendingMode = MaterialDecalBlendingMode::Translucent;
PostFxLocation = other.PostFxLocation;
MaskThreshold = other.MaskThreshold;
OpacityThreshold = other.OpacityThreshold;
}
/// <summary>
/// Compare structure with other one
/// </summary>
/// <param name="other">Other structure to compare</param>
/// <returns>True if both structures are equal</returns>
bool operator==(const MaterialInfo6& other) const
{
return Domain == other.Domain
&& BlendMode == other.BlendMode
&& TransparentLighting == other.TransparentLighting
&& DecalBlendingMode == other.DecalBlendingMode
&& PostFxLocation == other.PostFxLocation
&& Math::NearEqual(MaskThreshold, other.MaskThreshold)
&& Math::NearEqual(OpacityThreshold, other.OpacityThreshold)
&& Flags == other.Flags;
}
};
/// <summary>
/// Material info structure - version 7
/// [Deprecated on 13.09.2018, expires on 13.12.2018]
/// </summary>
struct MaterialInfo7
{
MaterialDomain Domain;
OldMaterialBlendMode BlendMode;
MaterialFlags_Deprecated Flags;
MaterialTransparentLighting_Deprecated TransparentLighting;
MaterialDecalBlendingMode DecalBlendingMode;
MaterialPostFxLocation PostFxLocation;
float MaskThreshold;
float OpacityThreshold;
TessellationMethod TessellationMode;
int32 MaxTessellationFactor;
MaterialInfo7()
{
}
MaterialInfo7(const MaterialInfo6& other)
{
Domain = other.Domain;
BlendMode = other.BlendMode;
Flags = other.Flags;
TransparentLighting = other.TransparentLighting;
DecalBlendingMode = other.DecalBlendingMode;
PostFxLocation = other.PostFxLocation;
MaskThreshold = other.MaskThreshold;
OpacityThreshold = other.OpacityThreshold;
TessellationMode = TessellationMethod::None;
MaxTessellationFactor = 15;
}
/// <summary>
/// Compare structure with other one
/// </summary>
/// <param name="other">Other structure to compare</param>
/// <returns>True if both structures are equal</returns>
bool operator==(const MaterialInfo7& other) const
{
return Domain == other.Domain
&& BlendMode == other.BlendMode
&& TransparentLighting == other.TransparentLighting
&& DecalBlendingMode == other.DecalBlendingMode
&& PostFxLocation == other.PostFxLocation
&& Math::NearEqual(MaskThreshold, other.MaskThreshold)
&& Math::NearEqual(OpacityThreshold, other.OpacityThreshold)
&& Flags == other.Flags
&& TessellationMode == other.TessellationMode
&& MaxTessellationFactor == other.MaxTessellationFactor;
}
};
/// <summary>
/// Material info structure - version 8
/// [Deprecated on 24.07.2019, expires on 10.05.2021]
/// </summary>
struct MaterialInfo8
{
MaterialDomain Domain;
MaterialBlendMode BlendMode;
MaterialShadingModel ShadingModel;
MaterialFlags_Deprecated Flags;
MaterialTransparentLighting_Deprecated TransparentLighting;
MaterialDecalBlendingMode DecalBlendingMode;
MaterialPostFxLocation PostFxLocation;
float MaskThreshold;
float OpacityThreshold;
TessellationMethod TessellationMode;
int32 MaxTessellationFactor;
MaterialInfo8()
{
}
MaterialInfo8(const MaterialInfo7& other)
{
Domain = other.Domain;
switch (other.BlendMode)
{
case OldMaterialBlendMode::Opaque:
BlendMode = MaterialBlendMode::Opaque;
ShadingModel = MaterialShadingModel::Lit;
break;
case OldMaterialBlendMode::Transparent:
BlendMode = MaterialBlendMode::Transparent;
ShadingModel = MaterialShadingModel::Lit;
break;
case OldMaterialBlendMode::Unlit:
BlendMode = MaterialBlendMode::Opaque;
ShadingModel = MaterialShadingModel::Unlit;
break;
}
Flags = other.Flags;
TransparentLighting = other.TransparentLighting;
DecalBlendingMode = other.DecalBlendingMode;
PostFxLocation = other.PostFxLocation;
MaskThreshold = other.MaskThreshold;
OpacityThreshold = other.OpacityThreshold;
TessellationMode = other.TessellationMode;
MaxTessellationFactor = other.MaxTessellationFactor;
}
/// <summary>
/// Compare structure with other one
/// </summary>
/// <param name="other">Other structure to compare</param>
/// <returns>True if both structures are equal</returns>
bool operator==(const MaterialInfo8& other) const
{
return Domain == other.Domain
&& BlendMode == other.BlendMode
&& ShadingModel == other.ShadingModel
&& TransparentLighting == other.TransparentLighting
&& DecalBlendingMode == other.DecalBlendingMode
&& PostFxLocation == other.PostFxLocation
&& Math::NearEqual(MaskThreshold, other.MaskThreshold)
&& Math::NearEqual(OpacityThreshold, other.OpacityThreshold)
&& Flags == other.Flags
&& TessellationMode == other.TessellationMode
&& MaxTessellationFactor == other.MaxTessellationFactor;
}
};
/// <summary>
/// Structure with basic information about the material surface. It describes how material is reacting on light and which graphical features of it requires to render.
/// </summary>
API_STRUCT() struct FLAXENGINE_API MaterialInfo
{
DECLARE_SCRIPTING_TYPE_MINIMAL(MaterialInfo);
/// <summary>
/// The material shader domain.
/// </summary>
API_FIELD() MaterialDomain Domain;
/// <summary>
/// The blending mode for rendering.
/// </summary>
API_FIELD() MaterialBlendMode BlendMode;
/// <summary>
/// The shading mode for lighting.
/// </summary>
API_FIELD() MaterialShadingModel ShadingModel;
/// <summary>
/// The usage flags.
/// </summary>
API_FIELD() MaterialUsageFlags UsageFlags;
/// <summary>
/// The features usage flags.
/// </summary>
API_FIELD() MaterialFeaturesFlags FeaturesFlags;
/// <summary>
/// The decal material blending mode.
/// </summary>
API_FIELD() MaterialDecalBlendingMode DecalBlendingMode;
/// <summary>
/// The post fx material rendering location.
/// </summary>
API_FIELD() MaterialPostFxLocation PostFxLocation;
/// <summary>
/// The primitives culling mode.
/// </summary>
API_FIELD() CullMode CullMode;
/// <summary>
/// The mask threshold.
/// </summary>
API_FIELD() float MaskThreshold;
/// <summary>
/// The opacity threshold.
/// </summary>
API_FIELD() float OpacityThreshold;
/// <summary>
/// The tessellation mode.
/// </summary>
API_FIELD() TessellationMethod TessellationMode;
/// <summary>
/// The maximum tessellation factor (used only if material uses tessellation).
/// </summary>
API_FIELD() int32 MaxTessellationFactor;
MaterialInfo()
{
}
MaterialInfo(const MaterialInfo8& other)
{
Domain = other.Domain;
BlendMode = other.BlendMode;
ShadingModel = other.ShadingModel;
UsageFlags = MaterialUsageFlags::None;
if (other.Flags & MaterialFlags_Deprecated::UseMask)
UsageFlags |= MaterialUsageFlags::UseMask;
if (other.Flags & MaterialFlags_Deprecated::UseEmissive)
UsageFlags |= MaterialUsageFlags::UseEmissive;
if (other.Flags & MaterialFlags_Deprecated::UsePositionOffset)
UsageFlags |= MaterialUsageFlags::UsePositionOffset;
if (other.Flags & MaterialFlags_Deprecated::UseVertexColor)
UsageFlags |= MaterialUsageFlags::UseVertexColor;
if (other.Flags & MaterialFlags_Deprecated::UseNormal)
UsageFlags |= MaterialUsageFlags::UseNormal;
if (other.Flags & MaterialFlags_Deprecated::UseDisplacement)
UsageFlags |= MaterialUsageFlags::UseDisplacement;
if (other.Flags & MaterialFlags_Deprecated::UseRefraction)
UsageFlags |= MaterialUsageFlags::UseRefraction;
FeaturesFlags = MaterialFeaturesFlags::None;
if (other.Flags & MaterialFlags_Deprecated::Wireframe)
FeaturesFlags |= MaterialFeaturesFlags::Wireframe;
if (other.Flags & MaterialFlags_Deprecated::TransparentDisableDepthTest && BlendMode != MaterialBlendMode::Opaque)
FeaturesFlags |= MaterialFeaturesFlags::DisableDepthTest;
if (other.Flags & MaterialFlags_Deprecated::TransparentDisableFog && BlendMode != MaterialBlendMode::Opaque)
FeaturesFlags |= MaterialFeaturesFlags::DisableFog;
if (other.Flags & MaterialFlags_Deprecated::TransparentDisableReflections && BlendMode != MaterialBlendMode::Opaque)
FeaturesFlags |= MaterialFeaturesFlags::DisableReflections;
if (other.Flags & MaterialFlags_Deprecated::DisableDepthWrite)
FeaturesFlags |= MaterialFeaturesFlags::DisableDepthWrite;
if (other.Flags & MaterialFlags_Deprecated::TransparentDisableDistortion && BlendMode != MaterialBlendMode::Opaque)
FeaturesFlags |= MaterialFeaturesFlags::DisableDistortion;
if (other.Flags & MaterialFlags_Deprecated::InputWorldSpaceNormal)
FeaturesFlags |= MaterialFeaturesFlags::InputWorldSpaceNormal;
if (other.Flags & MaterialFlags_Deprecated::UseDitheredLODTransition)
FeaturesFlags |= MaterialFeaturesFlags::DitheredLODTransition;
if (other.BlendMode != MaterialBlendMode::Opaque && other.TransparentLighting == MaterialTransparentLighting_Deprecated::None)
ShadingModel = MaterialShadingModel::Unlit;
DecalBlendingMode = other.DecalBlendingMode;
PostFxLocation = other.PostFxLocation;
CullMode = other.Flags & MaterialFlags_Deprecated::TwoSided ? ::CullMode::TwoSided : ::CullMode::Normal;
MaskThreshold = other.MaskThreshold;
OpacityThreshold = other.OpacityThreshold;
TessellationMode = other.TessellationMode;
MaxTessellationFactor = other.MaxTessellationFactor;
}
bool operator==(const MaterialInfo& other) const
{
return Domain == other.Domain
&& BlendMode == other.BlendMode
&& ShadingModel == other.ShadingModel
&& UsageFlags == other.UsageFlags
&& FeaturesFlags == other.FeaturesFlags
&& DecalBlendingMode == other.DecalBlendingMode
&& PostFxLocation == other.PostFxLocation
&& CullMode == other.CullMode
&& Math::NearEqual(MaskThreshold, other.MaskThreshold)
&& Math::NearEqual(OpacityThreshold, other.OpacityThreshold)
&& TessellationMode == other.TessellationMode
&& MaxTessellationFactor == other.MaxTessellationFactor;
}
};
// The current material info descriptor version used by the material pipeline
typedef MaterialInfo MaterialInfo9;
#define MaterialInfo_Version 9

View File

@@ -0,0 +1,916 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#include "MaterialParams.h"
#include "MaterialInfo.h"
#include "Engine/Content/Content.h"
#include "Engine/Graphics/GPUContext.h"
#include "Engine/Engine/GameplayGlobals.h"
#include "Engine/Serialization/MemoryWriteStream.h"
#include "Engine/Graphics/RenderBuffers.h"
#include "Engine/Graphics/GPUDevice.h"
#include "Engine/Graphics/GPULimits.h"
const Char* ToString(MaterialParameterType value)
{
const Char* result;
switch (value)
{
case MaterialParameterType::Invalid:
result = TEXT("Invalid");
break;
case MaterialParameterType::Bool:
result = TEXT("Bool");
break;
case MaterialParameterType::Integer:
result = TEXT("Integer");
break;
case MaterialParameterType::Float:
result = TEXT("Float");
break;
case MaterialParameterType::Vector2:
result = TEXT("Vector2");
break;
case MaterialParameterType::Vector3:
result = TEXT("Vector3");
break;
case MaterialParameterType::Vector4:
result = TEXT("Vector4");
break;
case MaterialParameterType::Color:
result = TEXT("Color");
break;
case MaterialParameterType::Texture:
result = TEXT("Texture");
break;
case MaterialParameterType::CubeTexture:
result = TEXT("CubeTexture");
break;
case MaterialParameterType::NormalMap:
result = TEXT("NormalMap");
break;
case MaterialParameterType::SceneTexture:
result = TEXT("SceneTexture");
break;
case MaterialParameterType::GPUTexture:
result = TEXT("GPUTexture");
break;
case MaterialParameterType::Matrix:
result = TEXT("Matrix");
break;
case MaterialParameterType::GPUTextureArray:
result = TEXT("GPUTextureArray");
break;
case MaterialParameterType::GPUTextureVolume:
result = TEXT("GPUTextureVolume");
break;
case MaterialParameterType::GPUTextureCube:
result = TEXT("GPUTextureCube");
break;
case MaterialParameterType::ChannelMask:
result = TEXT("ChannelMask");
break;
case MaterialParameterType::GameplayGlobal:
result = TEXT("GameplayGlobal");
break;
default:
result = TEXT("");
break;
}
return result;
}
Variant MaterialParameter::GetValue() const
{
switch (_type)
{
case MaterialParameterType::Bool:
return _asBool;
case MaterialParameterType::Integer:
case MaterialParameterType::SceneTexture:
case MaterialParameterType::ChannelMask:
return _asInteger;
case MaterialParameterType::Float:
return _asFloat;
case MaterialParameterType::Vector2:
return _asVector2;
case MaterialParameterType::Vector3:
return _asVector3;
case MaterialParameterType::Vector4:
return _asVector4;
case MaterialParameterType::Color:
return _asColor;
case MaterialParameterType::Matrix:
return Variant(_asMatrix);
case MaterialParameterType::NormalMap:
case MaterialParameterType::Texture:
case MaterialParameterType::CubeTexture:
case MaterialParameterType::GameplayGlobal:
return _asAsset.Get();
case MaterialParameterType::GPUTextureVolume:
case MaterialParameterType::GPUTextureArray:
case MaterialParameterType::GPUTextureCube:
case MaterialParameterType::GPUTexture:
return _asGPUTexture.Get();
default:
CRASH;
return Variant::Zero;
}
}
void MaterialParameter::SetValue(const Variant& value)
{
bool invalidType = false;
switch (_type)
{
case MaterialParameterType::Bool:
_asBool = (bool)value;
break;
case MaterialParameterType::Integer:
case MaterialParameterType::SceneTexture:
case MaterialParameterType::ChannelMask:
_asInteger = (int32)value;
break;
case MaterialParameterType::Float:
_asFloat = (float)value;
break;
case MaterialParameterType::Vector2:
_asVector2 = (Vector2)value;
break;
case MaterialParameterType::Vector3:
_asVector3 = (Vector3)value;
break;
case MaterialParameterType::Vector4:
_asVector4 = (Vector4)value;
break;
case MaterialParameterType::Color:
_asColor = (Color)value;
break;
case MaterialParameterType::Matrix:
_asMatrix = (Matrix)value;
break;
case MaterialParameterType::NormalMap:
case MaterialParameterType::Texture:
case MaterialParameterType::CubeTexture:
switch (value.Type.Type)
{
case VariantType::Null:
_asAsset = nullptr;
break;
case VariantType::Guid:
_asAsset = Content::LoadAsync<TextureBase>(*(Guid*)value.AsData);
break;
case VariantType::Pointer:
_asAsset = (TextureBase*)value.AsPointer;
break;
case VariantType::Object:
_asAsset = Cast<TextureBase>(value.AsObject);
break;
case VariantType::Asset:
_asAsset = Cast<TextureBase>(value.AsAsset);
break;
default:
invalidType = true;
}
break;
case MaterialParameterType::GPUTextureVolume:
case MaterialParameterType::GPUTextureCube:
case MaterialParameterType::GPUTextureArray:
case MaterialParameterType::GPUTexture:
switch (value.Type.Type)
{
case VariantType::Null:
_asGPUTexture = nullptr;
break;
case VariantType::Guid:
_asGPUTexture = *(Guid*)value.AsData;
break;
case VariantType::Pointer:
_asGPUTexture = (GPUTexture*)value.AsPointer;
break;
case VariantType::Object:
_asGPUTexture = Cast<GPUTexture>(value.AsObject);
break;
default:
invalidType = true;
}
break;
case MaterialParameterType::GameplayGlobal:
switch (value.Type.Type)
{
case VariantType::Null:
_asAsset = nullptr;
break;
case VariantType::Guid:
_asAsset = Content::LoadAsync<GameplayGlobals>(*(Guid*)value.AsData);
break;
case VariantType::Pointer:
_asAsset = (GameplayGlobals*)value.AsPointer;
break;
case VariantType::Object:
_asAsset = Cast<GameplayGlobals>(value.AsObject);
break;
case VariantType::Asset:
_asAsset = Cast<GameplayGlobals>(value.AsAsset);
break;
default:
invalidType = true;
}
break;
default:
invalidType = true;
}
if (invalidType)
{
LOG(Error, "Invalid material parameter value type {0} to set (param type: {1})", value.Type, ::ToString(_type));
}
}
void MaterialParameter::Bind(BindMeta& meta) const
{
switch (_type)
{
case MaterialParameterType::Bool:
if (meta.Buffer0)
*((int32*)(meta.Buffer0 + _offset)) = _asBool;
break;
case MaterialParameterType::Integer:
if (meta.Buffer0)
*((int32*)(meta.Buffer0 + _offset)) = _asInteger;
break;
case MaterialParameterType::Float:
if (meta.Buffer0)
*((float*)(meta.Buffer0 + _offset)) = _asFloat;
break;
case MaterialParameterType::Vector2:
if (meta.Buffer0)
*((Vector2*)(meta.Buffer0 + _offset)) = _asVector2;
break;
case MaterialParameterType::Vector3:
if (meta.Buffer0)
*((Vector3*)(meta.Buffer0 + _offset)) = _asVector3;
break;
case MaterialParameterType::Vector4:
if (meta.Buffer0)
*((Vector4*)(meta.Buffer0 + _offset)) = _asVector4;
break;
case MaterialParameterType::Color:
if (meta.Buffer0)
*((Color*)(meta.Buffer0 + _offset)) = _asColor;
break;
case MaterialParameterType::Matrix:
if (meta.Buffer0)
Matrix::Transpose(_asMatrix, *(Matrix*)(meta.Buffer0 + _offset));
break;
case MaterialParameterType::NormalMap:
{
// If normal map texture is set but not loaded yet, use default engine normal map (reduces loading artifacts)
auto texture = _asAsset ? ((TextureBase*)_asAsset.Get())->GetTexture() : nullptr;
if (texture && texture->ResidentMipLevels() == 0)
texture = GPUDevice::Instance->GetDefaultNormalMap();
const auto view = GET_TEXTURE_VIEW_SAFE(texture);
meta.Context->BindSR(_registerIndex, view);
break;
}
case MaterialParameterType::Texture:
case MaterialParameterType::CubeTexture:
{
const auto texture = _asAsset ? ((TextureBase*)_asAsset.Get())->GetTexture() : nullptr;
const auto view = GET_TEXTURE_VIEW_SAFE(texture);
meta.Context->BindSR(_registerIndex, view);
break;
}
case MaterialParameterType::GPUTexture:
{
const auto texture = _asGPUTexture.Get();
const auto view = GET_TEXTURE_VIEW_SAFE(texture);
meta.Context->BindSR(_registerIndex, view);
break;
}
case MaterialParameterType::GPUTextureArray:
case MaterialParameterType::GPUTextureCube:
{
const auto view = _asGPUTexture ? _asGPUTexture->ViewArray() : nullptr;
meta.Context->BindSR(_registerIndex, view);
break;
}
case MaterialParameterType::GPUTextureVolume:
{
const auto view = _asGPUTexture ? _asGPUTexture->ViewVolume() : nullptr;
meta.Context->BindSR(_registerIndex, view);
break;
}
case MaterialParameterType::SceneTexture:
{
GPUTextureView* view = nullptr;
const auto type = (MaterialSceneTextures)_asInteger;
if (type == MaterialSceneTextures::SceneColor)
{
view = meta.Input;
}
else if (meta.Buffers)
{
switch (type)
{
case MaterialSceneTextures::SceneDepth:
view = meta.CanSampleDepth
? (GPUDevice::Instance->Limits.HasReadOnlyDepth ? meta.Buffers->DepthBuffer->ViewReadOnlyDepth() : meta.Buffers->DepthBuffer->View())
: GPUDevice::Instance->GetDefaultWhiteTexture()->View();
break;
case MaterialSceneTextures::AmbientOcclusion:
case MaterialSceneTextures::BaseColor:
view = meta.CanSampleGBuffer ? meta.Buffers->GBuffer0->View() : nullptr;
break;
case MaterialSceneTextures::WorldNormal:
case MaterialSceneTextures::ShadingModel:
view = meta.CanSampleGBuffer ? meta.Buffers->GBuffer1->View() : nullptr;
break;
case MaterialSceneTextures::Roughness:
case MaterialSceneTextures::Metalness:
case MaterialSceneTextures::Specular:
view = meta.CanSampleGBuffer ? meta.Buffers->GBuffer2->View() : nullptr;
break;
default: ;
}
}
meta.Context->BindSR(_registerIndex, view);
break;
}
case MaterialParameterType::ChannelMask:
if (meta.Buffer0)
*((Vector4*)(meta.Buffer0 + _offset)) = Vector4(_asInteger == 0, _asInteger == 1, _asInteger == 2, _asInteger == 3);
break;
case MaterialParameterType::GameplayGlobal:
if (meta.Buffer0 && _asAsset)
{
const auto e = _asAsset.As<GameplayGlobals>()->Variables.TryGet(_name);
if (e)
{
switch (e->Value.Type.Type)
{
case VariantType::Bool:
*((bool*)(meta.Buffer0 + _offset)) = e->Value.AsBool;
break;
case VariantType::Int:
*((int32*)(meta.Buffer0 + _offset)) = e->Value.AsInt;
break;
case VariantType::Uint:
*((uint32*)(meta.Buffer0 + _offset)) = e->Value.AsUint;
break;
case VariantType::Float:
*((float*)(meta.Buffer0 + _offset)) = e->Value.AsFloat;
break;
case VariantType::Vector2:
*((Vector2*)(meta.Buffer0 + _offset)) = e->Value.AsVector2();
break;
case VariantType::Vector3:
*((Vector3*)(meta.Buffer0 + _offset)) = e->Value.AsVector3();
break;
case VariantType::Vector4:
case VariantType::Color:
*((Vector4*)(meta.Buffer0 + _offset)) = e->Value.AsVector4();
break;
default: ;
}
}
}
break;
default:
break;
}
}
bool MaterialParameter::HasContentLoaded() const
{
return _asAsset == nullptr || _asAsset->IsLoaded();
}
void MaterialParameter::clone(const MaterialParameter* param)
{
// Clone data
_type = param->_type;
_isPublic = param->_isPublic;
_override = param->_override;
_registerIndex = param->_registerIndex;
_offset = param->_offset;
_name = param->_name;
_paramId = param->_paramId;
// Clone value
switch (_type)
{
case MaterialParameterType::Bool:
_asBool = param->_asBool;
break;
case MaterialParameterType::Integer:
case MaterialParameterType::SceneTexture:
_asInteger = param->_asInteger;
break;
case MaterialParameterType::Float:
_asFloat = param->_asFloat;
break;
case MaterialParameterType::Vector2:
_asVector2 = param->_asVector2;
break;
case MaterialParameterType::Vector3:
_asVector3 = param->_asVector3;
break;
case MaterialParameterType::Vector4:
_asVector4 = param->_asVector4;
break;
case MaterialParameterType::Color:
_asColor = param->_asColor;
break;
case MaterialParameterType::Matrix:
_asMatrix = param->_asMatrix;
break;
default:
break;
}
_asAsset = param->_asAsset;
_asGPUTexture = param->_asGPUTexture;
}
bool MaterialParameter::operator==(const MaterialParameter& other) const
{
return _paramId == other._paramId;
}
String MaterialParameter::ToString() const
{
return String::Format(TEXT("\'{0}\' ({1}:{2}:{3})"), _name, ::ToString(_type), _paramId, _isPublic);
}
void MaterialParams::Bind(MaterialParamsLink* link, MaterialParameter::BindMeta& meta)
{
ASSERT(link && link->This);
for (int32 i = 0; i < link->This->Count(); i++)
{
MaterialParamsLink* l = link;
while (l->Down && !l->This->At(i).IsOverride())
{
l = l->Down;
}
l->This->At(i).Bind(meta);
}
}
void MaterialParams::Clone(MaterialParams& result)
{
ASSERT(this != &result);
// Clone all parameters
result.Resize(Count(), false);
for (int32 i = 0; i < Count(); i++)
{
result.At(i).clone(&At(i));
}
result._versionHash = _versionHash;
}
void MaterialParams::Dispose()
{
Resize(0);
_versionHash = 0;
}
bool MaterialParams::Load(ReadStream* stream)
{
bool result = false;
// Release
Resize(0);
// Check for not empty params
if (stream != nullptr && stream->CanRead())
{
// Version
uint16 version;
stream->ReadUint16(&version);
switch (version)
{
case 1: // [Deprecated on 15.11.2019, expires on 15.11.2021]
{
// Size of the collection
uint16 paramsCount;
stream->ReadUint16(&paramsCount);
Resize(paramsCount, false);
// Read all parameters
Guid id;
for (int32 i = 0; i < paramsCount; i++)
{
auto param = &At(i);
// Read properties
param->_paramId = Guid::New();
param->_type = static_cast<MaterialParameterType>(stream->ReadByte());
param->_isPublic = stream->ReadBool();
param->_override = param->_isPublic;
stream->ReadString(&param->_name, 10421);
param->_registerIndex = stream->ReadByte();
stream->ReadUint16(&param->_offset);
// Read value
switch (param->_type)
{
case MaterialParameterType::Bool:
param->_asBool = stream->ReadBool();
break;
case MaterialParameterType::Integer:
case MaterialParameterType::SceneTexture:
case MaterialParameterType::ChannelMask:
stream->ReadInt32(&param->_asInteger);
break;
case MaterialParameterType::Float:
stream->ReadFloat(&param->_asFloat);
break;
case MaterialParameterType::Vector2:
stream->Read(&param->_asVector2);
break;
case MaterialParameterType::Vector3:
stream->Read(&param->_asVector3);
break;
case MaterialParameterType::Vector4:
stream->Read(&param->_asVector4);
break;
case MaterialParameterType::Color:
stream->Read(&param->_asColor);
break;
case MaterialParameterType::Matrix:
stream->Read(&param->_asMatrix);
break;
case MaterialParameterType::NormalMap:
case MaterialParameterType::Texture:
case MaterialParameterType::CubeTexture:
stream->Read(&id);
param->_asAsset = Content::LoadAsync<TextureBase>(id);
break;
case MaterialParameterType::GPUTextureVolume:
case MaterialParameterType::GPUTextureCube:
case MaterialParameterType::GPUTextureArray:
case MaterialParameterType::GPUTexture:
stream->Read(&id);
param->_asGPUTexture = id;
break;
case MaterialParameterType::GameplayGlobal:
stream->Read(&id);
param->_asAsset = Content::LoadAsync<GameplayGlobals>(id);
break;
default:
break;
}
}
}
break;
case 2: // [Deprecated on 15.11.2019, expires on 15.11.2021]
{
// Size of the collection
uint16 paramsCount;
stream->ReadUint16(&paramsCount);
Resize(paramsCount, false);
// Read all parameters
Guid id;
for (int32 i = 0; i < paramsCount; i++)
{
auto param = &At(i);
// Read properties
param->_type = static_cast<MaterialParameterType>(stream->ReadByte());
stream->Read(&param->_paramId);
param->_isPublic = stream->ReadBool();
param->_override = param->_isPublic;
stream->ReadString(&param->_name, 10421);
param->_registerIndex = stream->ReadByte();
stream->ReadUint16(&param->_offset);
// Read value
switch (param->_type)
{
case MaterialParameterType::Bool:
param->_asBool = stream->ReadBool();
break;
case MaterialParameterType::Integer:
case MaterialParameterType::SceneTexture:
stream->ReadInt32(&param->_asInteger);
break;
case MaterialParameterType::Float:
stream->ReadFloat(&param->_asFloat);
break;
case MaterialParameterType::Vector2:
stream->Read(&param->_asVector2);
break;
case MaterialParameterType::Vector3:
stream->Read(&param->_asVector3);
break;
case MaterialParameterType::Vector4:
stream->Read(&param->_asVector4);
break;
case MaterialParameterType::Color:
stream->Read(&param->_asColor);
break;
case MaterialParameterType::Matrix:
stream->Read(&param->_asMatrix);
break;
case MaterialParameterType::NormalMap:
case MaterialParameterType::Texture:
case MaterialParameterType::CubeTexture:
stream->Read(&id);
param->_asAsset = Content::LoadAsync<TextureBase>(id);
break;
case MaterialParameterType::GPUTextureVolume:
case MaterialParameterType::GPUTextureCube:
case MaterialParameterType::GPUTextureArray:
case MaterialParameterType::GPUTexture:
stream->Read(&id);
param->_asGPUTexture = id;
break;
case MaterialParameterType::GameplayGlobal:
stream->Read(&id);
param->_asAsset = Content::LoadAsync<GameplayGlobals>(id);
break;
default:
break;
}
}
}
break;
case 3:
{
// Size of the collection
uint16 paramsCount;
stream->ReadUint16(&paramsCount);
Resize(paramsCount, false);
// Read all parameters
Guid id;
for (int32 i = 0; i < paramsCount; i++)
{
auto param = &At(i);
// Read properties
param->_type = static_cast<MaterialParameterType>(stream->ReadByte());
stream->Read(&param->_paramId);
param->_isPublic = stream->ReadBool();
param->_override = stream->ReadBool();
stream->ReadString(&param->_name, 10421);
param->_registerIndex = stream->ReadByte();
stream->ReadUint16(&param->_offset);
// Read value
switch (param->_type)
{
case MaterialParameterType::Bool:
param->_asBool = stream->ReadBool();
break;
case MaterialParameterType::Integer:
case MaterialParameterType::SceneTexture:
case MaterialParameterType::ChannelMask:
stream->ReadInt32(&param->_asInteger);
break;
case MaterialParameterType::Float:
stream->ReadFloat(&param->_asFloat);
break;
case MaterialParameterType::Vector2:
stream->Read(&param->_asVector2);
break;
case MaterialParameterType::Vector3:
stream->Read(&param->_asVector3);
break;
case MaterialParameterType::Vector4:
stream->Read(&param->_asVector4);
break;
case MaterialParameterType::Color:
stream->Read(&param->_asColor);
break;
case MaterialParameterType::Matrix:
stream->Read(&param->_asMatrix);
break;
case MaterialParameterType::NormalMap:
case MaterialParameterType::Texture:
case MaterialParameterType::CubeTexture:
stream->Read(&id);
param->_asAsset = Content::LoadAsync<TextureBase>(id);
break;
case MaterialParameterType::GPUTextureVolume:
case MaterialParameterType::GPUTextureCube:
case MaterialParameterType::GPUTextureArray:
case MaterialParameterType::GPUTexture:
stream->Read(&id);
param->_asGPUTexture = id;
break;
case MaterialParameterType::GameplayGlobal:
stream->Read(&id);
param->_asAsset = Content::LoadAsync<GameplayGlobals>(id);
break;
default:
break;
}
}
}
break;
default:
result = true;
break;
}
}
UpdateHash();
return result;
}
void MaterialParams::Save(WriteStream* stream)
{
// Skip serialization if no parameters to save
if (IsEmpty())
return;
// Version
stream->WriteUint16(3);
// Size of the collection
stream->WriteUint16(Count());
// Write all parameters
for (int32 i = 0; i < Count(); i++)
{
// Cache
auto param = &At(i);
// Write properties
stream->WriteByte(static_cast<byte>(param->_type));
stream->Write(&param->_paramId);
stream->WriteBool(param->_isPublic);
stream->WriteBool(param->_override);
stream->WriteString(param->_name, 10421);
stream->WriteByte(param->_registerIndex);
stream->WriteUint16(param->_offset);
// Write value
Guid id;
switch (param->_type)
{
case MaterialParameterType::Bool:
stream->WriteBool(param->_asBool);
break;
case MaterialParameterType::Integer:
case MaterialParameterType::SceneTexture:
case MaterialParameterType::ChannelMask:
stream->WriteInt32(param->_asInteger);
break;
case MaterialParameterType::Float:
stream->WriteFloat(param->_asFloat);
break;
case MaterialParameterType::Vector2:
stream->Write(&param->_asVector2);
break;
case MaterialParameterType::Vector3:
stream->Write(&param->_asVector3);
break;
case MaterialParameterType::Vector4:
stream->Write(&param->_asVector4);
break;
case MaterialParameterType::Color:
stream->Write(&param->_asColor);
break;
case MaterialParameterType::Matrix:
stream->Write(&param->_asMatrix);
break;
case MaterialParameterType::NormalMap:
case MaterialParameterType::Texture:
case MaterialParameterType::CubeTexture:
case MaterialParameterType::GameplayGlobal:
id = param->_asAsset.GetID();
stream->Write(&id);
break;
case MaterialParameterType::GPUTextureVolume:
case MaterialParameterType::GPUTextureArray:
case MaterialParameterType::GPUTextureCube:
case MaterialParameterType::GPUTexture:
id = param->_asGPUTexture.GetID();
stream->Write(&id);
break;
default:
break;
}
}
}
void MaterialParams::Save(WriteStream* stream, const Array<SerializedMaterialParam>* params)
{
// Version
stream->WriteUint16(3);
// Size of the collection
stream->WriteUint16(params ? params->Count() : 0);
// Write all parameters
if (params)
{
for (int32 i = 0; i < params->Count(); i++)
{
// Cache
const SerializedMaterialParam& param = params->At(i);
// Write properties
stream->WriteByte(static_cast<byte>(param.Type));
stream->Write(&param.ID);
stream->WriteBool(param.IsPublic);
stream->WriteBool(param.Override);
stream->WriteString(param.Name, 10421);
stream->WriteByte(param.RegisterIndex);
stream->WriteUint16(param.Offset);
// Write value
switch (param.Type)
{
case MaterialParameterType::Bool:
stream->WriteBool(param.AsBool);
break;
case MaterialParameterType::SceneTexture:
case MaterialParameterType::ChannelMask:
case MaterialParameterType::Integer:
stream->WriteInt32(param.AsInteger);
break;
case MaterialParameterType::Float:
stream->WriteFloat(param.AsFloat);
break;
case MaterialParameterType::Vector2:
stream->Write(&param.AsVector2);
break;
case MaterialParameterType::Vector3:
stream->Write(&param.AsVector3);
break;
case MaterialParameterType::Vector4:
stream->Write(&param.AsVector4);
break;
case MaterialParameterType::Color:
stream->Write(&param.AsColor);
break;
case MaterialParameterType::Matrix:
stream->Write(&param.AsMatrix);
break;
case MaterialParameterType::NormalMap:
case MaterialParameterType::Texture:
case MaterialParameterType::CubeTexture:
case MaterialParameterType::GameplayGlobal:
case MaterialParameterType::GPUTextureVolume:
case MaterialParameterType::GPUTextureCube:
case MaterialParameterType::GPUTextureArray:
case MaterialParameterType::GPUTexture:
stream->Write(&param.AsGuid);
break;
default:
break;
}
}
}
}
void MaterialParams::Save(BytesContainer& data, const Array<SerializedMaterialParam>* params)
{
MemoryWriteStream stream(1024);
Save(&stream, params);
if (stream.GetPosition() > 0)
data.Copy(stream.GetHandle(), stream.GetPosition());
else
data.Release();
}
#if USE_EDITOR
void MaterialParams::GetReferences(Array<Guid>& output) const
{
for (int32 i = 0; i < Count(); i++)
{
if (At(i)._asAsset)
output.Add(At(i)._asAsset->GetID());
}
}
#endif
bool MaterialParams::HasContentLoaded() const
{
bool result = true;
for (int32 i = 0; i < Count(); i++)
{
if (!At(i).HasContentLoaded())
{
result = false;
break;
}
}
return result;
}
void MaterialParams::UpdateHash()
{
_versionHash = rand();
}

View File

@@ -0,0 +1,500 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Core/Math/Matrix.h"
#include "Engine/Core/Types/StringView.h"
#include "Engine/Core/Collections/Array.h"
#include "Engine/Scripting/ScriptingObjectReference.h"
#include "Engine/Content/Assets/Texture.h"
#include "Engine/Content/AssetReference.h"
class MaterialInstance;
class MaterialParams;
class GPUContext;
class RenderBuffers;
struct MaterialParamsLink
{
MaterialParams* This;
MaterialParamsLink* Up;
MaterialParamsLink* Down;
};
/// <summary>
/// The material parameter types.
/// </summary>
enum class MaterialParameterType : byte
{
/// <summary>
/// The invalid type.
/// </summary>
Invalid = 0,
/// <summary>
/// The bool.
/// </summary>
Bool = 1,
/// <summary>
/// The integer.
/// </summary>
Integer = 2,
/// <summary>
/// The float.
/// </summary>
Float = 3,
/// <summary>
/// The vector2
/// </summary>
Vector2 = 4,
/// <summary>
/// The vector3.
/// </summary>
Vector3 = 5,
/// <summary>
/// The vector4.
/// </summary>
Vector4 = 6,
/// <summary>
/// The color.
/// </summary>
Color = 7,
/// <summary>
/// The texture.
/// </summary>
Texture = 8,
/// <summary>
/// The cube texture.
/// </summary>
CubeTexture = 9,
/// <summary>
/// The normal map texture.
/// </summary>
NormalMap = 10,
/// <summary>
/// The scene texture.
/// </summary>
SceneTexture = 11,
/// <summary>
/// The GPU texture (created from code).
/// </summary>
GPUTexture = 12,
/// <summary>
/// The matrix.
/// </summary>
Matrix = 13,
/// <summary>
/// The GPU texture array (created from code).
/// </summary>
GPUTextureArray = 14,
/// <summary>
/// The GPU volume texture (created from code).
/// </summary>
GPUTextureVolume = 15,
/// <summary>
/// The GPU cube texture (created from code).
/// </summary>
GPUTextureCube = 16,
/// <summary>
/// The RGBA channel selection mask.
/// </summary>
ChannelMask = 17,
/// <summary>
/// The gameplay global.
/// </summary>
GameplayGlobal = 18,
};
const Char* ToString(MaterialParameterType value);
/// <summary>
/// Structure of serialized material parameter data.
/// </summary>
struct SerializedMaterialParam
{
MaterialParameterType Type;
Guid ID;
bool IsPublic;
bool Override;
String Name;
String ShaderName;
union
{
bool AsBool;
int32 AsInteger;
float AsFloat;
Vector2 AsVector2;
Vector3 AsVector3;
Vector4 AsVector4;
Color AsColor;
Guid AsGuid;
Matrix AsMatrix;
};
byte RegisterIndex;
uint16 Offset;
SerializedMaterialParam()
{
}
};
/// <summary>
/// Material variable object. Allows to modify material parameter value at runtime.
/// </summary>
API_CLASS(NoSpawn) class FLAXENGINE_API MaterialParameter : public PersistentScriptingObject
{
DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(MaterialParameter, PersistentScriptingObject);
friend MaterialParams;
friend MaterialInstance;
private:
Guid _paramId;
MaterialParameterType _type = MaterialParameterType::Invalid;
bool _isPublic;
bool _override;
byte _registerIndex;
uint16 _offset;
union
{
bool _asBool;
int32 _asInteger;
float _asFloat;
Vector2 _asVector2;
Vector3 _asVector3;
Vector4 _asVector4;
Color _asColor;
Matrix _asMatrix;
};
AssetReference<Asset> _asAsset;
ScriptingObjectReference<GPUTexture> _asGPUTexture;
String _name;
public:
MaterialParameter(const MaterialParameter& other)
: MaterialParameter()
{
#if !BUILD_RELEASE
CRASH; // Not used
#endif
}
MaterialParameter& operator=(const MaterialParameter& other)
{
#if !BUILD_RELEASE
CRASH; // Not used
#endif
return *this;
}
public:
/// <summary>
/// Gets the parameter ID (not the parameter instance Id but the original parameter ID).
/// </summary>
/// <returns>The ID.</returns>
API_PROPERTY() FORCE_INLINE Guid GetParameterID() const
{
return _paramId;
}
/// <summary>
/// Gets the parameter type.
/// </summary>
/// <returns>The type.</returns>
API_PROPERTY() FORCE_INLINE MaterialParameterType GetParameterType() const
{
return _type;
}
/// <summary>
/// Gets the parameter name.
/// </summary>
/// <returns>The name.</returns>
API_PROPERTY() FORCE_INLINE const String& GetName() const
{
return _name;
}
/// <summary>
/// Returns true is parameter is public visible.
/// </summary>
/// <returns>True if parameter has public access, otherwise false.</returns>
API_PROPERTY() FORCE_INLINE bool IsPublic() const
{
return _isPublic;
}
/// <summary>
/// Returns true is parameter is overriding the value.
/// </summary>
/// <returns>True if parameter is overriding the value, otherwise false.</returns>
API_PROPERTY() FORCE_INLINE bool IsOverride() const
{
return _override;
}
/// <summary>
/// Sets the value override mode.
/// </summary>
/// <param name="value">The value.</param>
API_PROPERTY() void SetIsOverride(bool value)
{
_override = value;
}
/// <summary>
/// Gets the parameter resource graphics pipeline binding register index.
/// </summary>
/// <returns>The binding register.</returns>
FORCE_INLINE byte GetRegister() const
{
return _registerIndex;
}
/// <summary>
/// Gets the parameter binding offset since the start of the constant buffer.
/// </summary>
/// <returns>The binding data offset (in bytes).</returns>
FORCE_INLINE uint16 GetBindOffset() const
{
return _offset;
}
public:
/// <summary>
/// Gets the value of the parameter.
/// </summary>
/// <returns>The value.</returns>
API_PROPERTY() Variant GetValue() const;
/// <summary>
/// Sets the value of the parameter.
/// </summary>
/// <param name="value">The value.</param>
API_PROPERTY() void SetValue(const Variant& value);
public:
/// <summary>
/// The material parameter binding metadata.
/// </summary>
struct BindMeta
{
/// <summary>
/// The GPU commands context.
/// </summary>
GPUContext* Context;
/// <summary>
/// The pointer to the first constants buffer in memory.
/// </summary>
byte* Buffer0;
/// <summary>
/// The input scene color. It's optional and used in forward/postFx rendering.
/// </summary>
GPUTextureView* Input;
/// <summary>
/// The scene buffers. It's optional and used in forward/postFx rendering.
/// </summary>
const RenderBuffers* Buffers;
/// <summary>
/// True if parameters can sample depth buffer.
/// </summary>
bool CanSampleDepth;
/// <summary>
/// True if parameters can sample GBuffer.
/// </summary>
bool CanSampleGBuffer;
};
/// <summary>
/// Binds the parameter to the pipeline.
/// </summary>
/// <param name="meta">The bind meta.</param>
void Bind(BindMeta& meta) const;
bool HasContentLoaded() const;
private:
void clone(const MaterialParameter* param);
public:
bool operator==(const MaterialParameter& other) const;
// [Object]
String ToString() const override;
};
/// <summary>
/// The collection of material parameters.
/// </summary>
class FLAXENGINE_API MaterialParams : public Array<MaterialParameter>
{
friend MaterialInstance;
private:
int32 _versionHash = 0;
public:
MaterialParameter* Get(const Guid& id)
{
MaterialParameter* result = nullptr;
for (int32 i = 0; i < Count(); i++)
{
if (At(i).GetParameterID() == id)
{
result = &At(i);
break;
}
}
return result;
}
MaterialParameter* Get(const StringView& name)
{
MaterialParameter* result = nullptr;
for (int32 i = 0; i < Count(); i++)
{
if (At(i).GetName() == name)
{
result = &At(i);
break;
}
}
return result;
}
int32 Find(const Guid& id)
{
int32 result = -1;
for (int32 i = 0; i < Count(); i++)
{
if (At(i).GetParameterID() == id)
{
result = i;
break;
}
}
return result;
}
int32 Find(const StringView& name)
{
int32 result = -1;
for (int32 i = 0; i < Count(); i++)
{
if (At(i).GetName() == name)
{
result = i;
break;
}
}
return result;
}
public:
/// <summary>
/// Gets the parameters version hash. Every time the parameters are modified (loaded, edited, etc.) the hash changes. Can be used to sync instanced parameters collection.
/// </summary>
/// <returns>The hash.</returns>
FORCE_INLINE int32 GetVersionHash() const
{
return _versionHash;
}
public:
/// <summary>
/// Binds the parameters to the pipeline.
/// </summary>
/// <param name="link">The parameters binding link. Used to support per-parameter override.</param>
/// <param name="meta">The bind meta.</param>
static void Bind(MaterialParamsLink* link, MaterialParameter::BindMeta& meta);
/// <summary>
/// Clones the parameters list.
/// </summary>
/// <param name="result">The result container.</param>
void Clone(MaterialParams& result);
/// <summary>
/// Releases the whole data.
/// </summary>
void Dispose();
/// <summary>
/// Loads material parameters from the stream.
/// </summary>
/// <param name="stream">The stream with data.</param>
/// <returns>True if cannot load parameters for the file.</returns>
bool Load(ReadStream* stream);
/// <summary>
/// Saves material parameters to the stream.
/// </summary>
/// <param name="stream">The stream with data.</param>
void Save(WriteStream* stream);
/// <summary>
/// Saves the material parameters to the stream.
/// </summary>
/// <param name="stream">The stream with data.</param>
/// <param name="params">The array of parameters.</param>
static void Save(WriteStream* stream, const Array<SerializedMaterialParam>* params);
/// <summary>
/// Saves the material parameters to the bytes container.
/// </summary>
/// <param name="data">The output data.</param>
/// <param name="params">The array of parameters.</param>
static void Save(BytesContainer& data, const Array<SerializedMaterialParam>* params);
public:
#if USE_EDITOR
/// <summary>
/// Gets the asset references (see Asset.GetReferences for more info).
/// </summary>
/// <param name="output">The output.</param>
void GetReferences(Array<Guid>& output) const;
#endif
bool HasContentLoaded() const;
private:
void UpdateHash();
};

View File

@@ -0,0 +1,168 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#include "MaterialShader.h"
#include "Engine/Core/Log.h"
#include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Renderer/RenderList.h"
#include "Engine/Graphics/GPUDevice.h"
#include "Engine/Graphics/Shaders/GPUConstantBuffer.h"
#include "Engine/Graphics/Shaders/GPUShader.h"
#include "DecalMaterialShader.h"
#include "PostFxMaterialShader.h"
#include "ForwardMaterialShader.h"
#include "DeferredMaterialShader.h"
#include "GUIMaterialShader.h"
#include "TerrainMaterialShader.h"
#include "ParticleMaterialShader.h"
GPUPipelineState* MaterialShader::PipelineStateCache::GetPS(CullMode mode, bool wireframe)
{
const int32 index = static_cast<int32>(mode) + (wireframe ? 3 : 0);
if (PS[index])
return PS[index];
Desc.CullMode = mode;
Desc.Wireframe = wireframe;
PS[index] = GPUDevice::Instance->CreatePipelineState();
PS[index]->Init(Desc);
return PS[index];
}
MaterialShader::MaterialShader(const String& name)
: _isLoaded(false)
, _shader(nullptr)
{
ASSERT(GPUDevice::Instance);
_shader = GPUDevice::Instance->CreateShader(name);
}
MaterialShader::~MaterialShader()
{
ASSERT(!_isLoaded);
SAFE_DELETE_GPU_RESOURCE(_shader);
}
MaterialShader* MaterialShader::Create(const String& name, MemoryReadStream& shaderCacheStream, const MaterialInfo& info)
{
MaterialShader* material;
switch (info.Domain)
{
case MaterialDomain::Surface:
material = info.BlendMode == MaterialBlendMode::Opaque ? (MaterialShader*)New<DeferredMaterialShader>(name) : (MaterialShader*)New<ForwardMaterialShader>(name);
break;
case MaterialDomain::PostProcess:
material = New<PostFxMaterialShader>(name);
break;
case MaterialDomain::Decal:
material = New<DecalMaterialShader>(name);
break;
case MaterialDomain::GUI:
material = New<GUIMaterialShader>(name);
break;
case MaterialDomain::Terrain:
material = New<TerrainMaterialShader>(name);
break;
case MaterialDomain::Particle:
material = New<ParticleMaterialShader>(name);
break;
default:
LOG(Fatal, "Unknown material type.");
return nullptr;
}
if (material->Load(shaderCacheStream, info))
{
Delete(material);
return nullptr;
}
return material;
}
class DummyMaterial : public MaterialShader
{
public:
DummyMaterial()
: MaterialShader(String::Empty)
{
}
public:
// [Material]
void Bind(BindParameters& params) override
{
}
protected:
// [Material]
bool Load() override
{
return false;
}
};
MaterialShader* MaterialShader::CreateDummy(MemoryReadStream& shaderCacheStream, const MaterialInfo& info)
{
MaterialShader* material = New<DummyMaterial>();
if (material->Load(shaderCacheStream, info))
{
Delete(material);
return nullptr;
}
return material;
}
const MaterialInfo& MaterialShader::GetInfo() const
{
return _info;
}
bool MaterialShader::IsReady() const
{
return _isLoaded;
}
bool MaterialShader::Load(MemoryReadStream& shaderCacheStream, const MaterialInfo& info)
{
ASSERT(!_isLoaded);
// Cache material info
_info = info;
// Create shader
if (_shader->Create(shaderCacheStream))
{
LOG(Warning, "Cannot load shader.");
return true;
}
// Init memory for a constant buffer
const auto cb0 = _shader->GetCB(0);
if (cb0)
{
_cb0Data.Resize(cb0->GetSize(), false);
}
const auto cb1 = _shader->GetCB(1);
if (cb1)
{
_cb1Data.Resize(cb1->GetSize(), false);
}
// Initialize the material based on type (create pipeline states and setup)
if (Load())
{
return true;
}
_isLoaded = true;
return false;
}
void MaterialShader::Unload()
{
_isLoaded = false;
_cb0Data.Resize(0, false);
_cb1Data.Resize(0, false);
_shader->ReleaseGPU();
}

View File

@@ -0,0 +1,110 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "IMaterial.h"
#include "Engine/Graphics/GPUPipelineState.h"
#include "Engine/Renderer/Config.h"
/// <summary>
/// Current materials shader version.
/// </summary>
#define MATERIAL_GRAPH_VERSION 146
class Material;
class GPUShader;
class MemoryReadStream;
/// <summary>
/// Represents material shader that can be used to render objects, visuals or effects. Contains a dedicated shader.
/// </summary>
class MaterialShader : public IMaterial
{
protected:
struct PipelineStateCache
{
GPUPipelineState* PS[6];
GPUPipelineState::Description Desc;
PipelineStateCache()
{
Platform::MemoryClear(PS, sizeof(PS));
}
void Init(GPUPipelineState::Description& desc)
{
Desc = desc;
}
GPUPipelineState* GetPS(CullMode mode, bool wireframe);
void Release()
{
for (int32 i = 0; i < ARRAY_COUNT(PS); i++)
{
SAFE_DELETE_GPU_RESOURCE(PS[i]);
}
}
};
protected:
bool _isLoaded;
GPUShader* _shader;
Array<byte> _cb0Data;
Array<byte> _cb1Data;
MaterialInfo _info;
protected:
/// <summary>
/// Init
/// </summary>
/// <param name="name">Material resource name</param>
MaterialShader(const String& name);
public:
/// <summary>
/// Finalizes an instance of the <see cref="MaterialShader"/> class.
/// </summary>
virtual ~MaterialShader();
public:
/// <summary>
/// Creates and loads the material from the data.
/// </summary>
/// <param name="name">Material resource name</param>
/// <param name="shaderCacheStream">Stream with compiled shader data</param>
/// <param name="info">Loaded material info structure</param>
/// <returns>The created and loaded material or null if failed.</returns>
static MaterialShader* Create(const String& name, MemoryReadStream& shaderCacheStream, const MaterialInfo& info);
/// <summary>
/// Creates the dummy material used by the Null rendering backend to mock object but not perform any rendering.
/// </summary>
/// <param name="shaderCacheStream">The shader cache stream.</param>
/// <param name="info">The material information.</param>
/// <returns>The created and loaded material or null if failed.</returns>
static MaterialShader* CreateDummy(MemoryReadStream& shaderCacheStream, const MaterialInfo& info);
public:
/// <summary>
/// Clear loaded data
/// </summary>
virtual void Unload();
protected:
bool Load(MemoryReadStream& shaderCacheStream, const MaterialInfo& info);
virtual bool Load() = 0;
public:
// [IMaterial]
const MaterialInfo& GetInfo() const override;
bool IsReady() const override;
};

View File

@@ -0,0 +1,400 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#include "ParticleMaterialShader.h"
#include "MaterialParams.h"
#include "Engine/Renderer/DrawCall.h"
#include "Engine/Renderer/ShadowsPass.h"
#include "Engine/Graphics/RenderView.h"
#include "Engine/Renderer/RenderList.h"
#include "Engine/Graphics/GPUContext.h"
#include "Engine/Graphics/Shaders/GPUConstantBuffer.h"
#include "Engine/Engine/Time.h"
#include "Engine/Graphics/GPUDevice.h"
#include "Engine/Graphics/Shaders/GPUShader.h"
#include "Engine/Graphics/GPULimits.h"
#include "Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.h"
#include "Engine/Content/Assets/CubeTexture.h"
#include "Engine/Level/Actors/EnvironmentProbe.h"
#define MAX_LOCAL_LIGHTS 4
PACK_STRUCT(struct ParticleMaterialShaderData {
Matrix ViewProjectionMatrix;
Matrix WorldMatrix;
Matrix ViewMatrix;
Vector3 ViewPos;
float ViewFar;
Vector3 ViewDir;
float TimeParam;
Vector4 ViewInfo;
Vector4 ScreenSize;
uint32 SortedIndicesOffset;
float PerInstanceRandom;
int32 ParticleStride;
int32 PositionOffset;
int32 SpriteSizeOffset;
int32 SpriteFacingModeOffset;
int32 SpriteFacingVectorOffset;
int32 VelocityOffset;
int32 RotationOffset;
int32 ScaleOffset;
int32 ModelFacingModeOffset;
float RibbonUVTilingDistance;
Vector2 RibbonUVScale;
Vector2 RibbonUVOffset;
int32 RibbonWidthOffset;
int32 RibbonTwistOffset;
int32 RibbonFacingVectorOffset;
uint32 RibbonSegmentCount;
Matrix WorldMatrixInverseTransposed;
});
PACK_STRUCT(struct ParticleMaterialShaderLightingData {
LightData DirectionalLight;
LightShadowData DirectionalLightShadow;
LightData SkyLight;
ProbeData EnvironmentProbe;
ExponentialHeightFogData ExponentialHeightFog;
Vector3 Dummy1;
uint32 LocalLightsCount;
LightData LocalLights[MAX_LOCAL_LIGHTS];
});
DrawPass ParticleMaterialShader::GetDrawModes() const
{
return _drawModes;
}
void ParticleMaterialShader::Bind(BindParameters& params)
{
// Prepare
auto context = params.GPUContext;
auto& view = params.RenderContext.View;
auto cache = params.RenderContext.List;
auto& drawCall = *params.FirstDrawCall;
const auto cb0 = _shader->GetCB(0);
const bool hasCb0 = cb0->GetSize() != 0;
const auto cb1 = _shader->GetCB(1);
const bool hasCb1 = cb1->GetSize() != 0;
const uint32 sortedIndicesOffset = drawCall.Module->SortedIndicesOffset;
// Setup parameters
MaterialParameter::BindMeta bindMeta;
bindMeta.Context = context;
bindMeta.Buffer0 = hasCb0 ? _cb0Data.Get() + sizeof(ParticleMaterialShaderData) : nullptr;
bindMeta.Input = nullptr;
bindMeta.Buffers = params.RenderContext.Buffers;
bindMeta.CanSampleDepth = GPUDevice::Instance->Limits.HasReadOnlyDepth;
bindMeta.CanSampleGBuffer = true;
MaterialParams::Bind(params.ParamsLink, bindMeta);
// Setup particles data and attributes binding info
{
context->BindSR(0, drawCall.Particles->GPU.Buffer->View());
if (drawCall.Particles->GPU.SortedIndices)
context->BindSR(1, drawCall.Particles->GPU.SortedIndices->View());
if (hasCb0)
{
const auto& p = *params.ParamsLink->This;
for (int32 i = 0; i < p.Count(); i++)
{
const auto& param = p.At(i);
if (param.GetParameterType() == MaterialParameterType::Integer && param.GetName().StartsWith(TEXT("Particle.")))
{
auto name = StringView(param.GetName().Get() + 9);
const int32 offset = drawCall.Particles->Layout->FindAttributeOffset(name);
*((int32*)(bindMeta.Buffer0 + param.GetBindOffset())) = offset;
}
}
}
}
// 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;
PipelineStateCache* psCache = nullptr;
switch (drawCall.Module->TypeID)
{
// Sprite Rendering
case 400:
{
psCache = (PipelineStateCache*)_cacheSprite.GetPS(view.Pass);
break;
}
// Model Rendering
case 403:
{
psCache = (PipelineStateCache*)_cacheModel.GetPS(view.Pass);
break;
}
// Ribbon Rendering
case 404:
{
psCache = (PipelineStateCache*)_cacheRibbon.GetPS(view.Pass);
static StringView ParticleRibbonWidth(TEXT("RibbonWidth"));
static StringView ParticleRibbonTwist(TEXT("RibbonTwist"));
static StringView ParticleRibbonFacingVector(TEXT("RibbonFacingVector"));
if (hasCb0)
{
const auto materialData = reinterpret_cast<ParticleMaterialShaderData*>(_cb0Data.Get());
materialData->RibbonWidthOffset = drawCall.Particles->Layout->FindAttributeOffset(ParticleRibbonWidth, ParticleAttribute::ValueTypes::Float, -1);
materialData->RibbonTwistOffset = drawCall.Particles->Layout->FindAttributeOffset(ParticleRibbonTwist, ParticleAttribute::ValueTypes::Float, -1);
materialData->RibbonFacingVectorOffset = drawCall.Particles->Layout->FindAttributeOffset(ParticleRibbonFacingVector, ParticleAttribute::ValueTypes::Vector3, -1);
materialData->RibbonUVTilingDistance = drawCall.Ribbon.UVTilingDistance;
materialData->RibbonUVScale.X = drawCall.Ribbon.UVScaleX;
materialData->RibbonUVScale.Y = drawCall.Ribbon.UVScaleY;
materialData->RibbonUVOffset.X = drawCall.Ribbon.UVOffsetX;
materialData->RibbonUVOffset.Y = drawCall.Ribbon.UVOffsetY;
materialData->RibbonSegmentCount = drawCall.Ribbon.SegmentCount;
}
if (drawCall.Ribbon.SegmentDistances)
context->BindSR(1, drawCall.Ribbon.SegmentDistances->View());
break;
}
}
ASSERT(psCache);
GPUPipelineState* state = psCache->GetPS(cullMode, wireframe);
// Setup material constants data
if (hasCb0)
{
const auto materialData = reinterpret_cast<ParticleMaterialShaderData*>(_cb0Data.Get());
static StringView ParticlePosition(TEXT("Position"));
static StringView ParticleSpriteSize(TEXT("SpriteSize"));
static StringView ParticleSpriteFacingMode(TEXT("SpriteFacingMode"));
static StringView ParticleSpriteFacingVector(TEXT("SpriteFacingVector"));
static StringView ParticleVelocityOffset(TEXT("Velocity"));
static StringView ParticleRotationOffset(TEXT("Rotation"));
static StringView ParticleScaleOffset(TEXT("Scale"));
static StringView ParticleModelFacingModeOffset(TEXT("ModelFacingMode"));
Matrix::Transpose(view.Frustum.GetMatrix(), materialData->ViewProjectionMatrix);
Matrix::Transpose(drawCall.World, materialData->WorldMatrix);
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;
materialData->SortedIndicesOffset = drawCall.Particles->GPU.SortedIndices && params.RenderContext.View.Pass != DrawPass::Depth ? sortedIndicesOffset : 0xFFFFFFFF;
materialData->PerInstanceRandom = drawCall.PerInstanceRandom;
materialData->ParticleStride = drawCall.Particles->Stride;
materialData->PositionOffset = drawCall.Particles->Layout->FindAttributeOffset(ParticlePosition, ParticleAttribute::ValueTypes::Vector3);
materialData->SpriteSizeOffset = drawCall.Particles->Layout->FindAttributeOffset(ParticleSpriteSize, ParticleAttribute::ValueTypes::Vector2);
materialData->SpriteFacingModeOffset = drawCall.Particles->Layout->FindAttributeOffset(ParticleSpriteFacingMode, ParticleAttribute::ValueTypes::Int, -1);
materialData->SpriteFacingVectorOffset = drawCall.Particles->Layout->FindAttributeOffset(ParticleSpriteFacingVector, ParticleAttribute::ValueTypes::Vector3);
materialData->VelocityOffset = drawCall.Particles->Layout->FindAttributeOffset(ParticleVelocityOffset, ParticleAttribute::ValueTypes::Vector3);
materialData->RotationOffset = drawCall.Particles->Layout->FindAttributeOffset(ParticleRotationOffset, ParticleAttribute::ValueTypes::Vector3, -1);
materialData->ScaleOffset = drawCall.Particles->Layout->FindAttributeOffset(ParticleScaleOffset, ParticleAttribute::ValueTypes::Vector3, -1);
materialData->ModelFacingModeOffset = drawCall.Particles->Layout->FindAttributeOffset(ParticleModelFacingModeOffset, ParticleAttribute::ValueTypes::Int, -1);
Matrix::Invert(drawCall.World, materialData->WorldMatrixInverseTransposed);
}
// Setup lighting constants data
if (hasCb1)
{
auto& lightingData = *reinterpret_cast<ParticleMaterialShaderLightingData*>(_cb1Data.Get());
const int32 envProbeShaderRegisterIndex = 2;
const int32 skyLightShaderRegisterIndex = 3;
const int32 dirLightShaderRegisterIndex = 4;
// Set fog input
if (cache->Fog)
{
cache->Fog->GetExponentialHeightFogData(view, lightingData.ExponentialHeightFog);
}
else
{
lightingData.ExponentialHeightFog.FogMinOpacity = 1.0f;
lightingData.ExponentialHeightFog.ApplyDirectionalInscattering = 0.0f;
}
// Set directional light input
if (cache->DirectionalLights.HasItems())
{
const auto& dirLight = cache->DirectionalLights.First();
const auto shadowPass = ShadowsPass::Instance();
const bool useShadow = shadowPass->LastDirLightIndex == 0;
if (useShadow)
{
lightingData.DirectionalLightShadow = shadowPass->LastDirLight;
context->BindSR(dirLightShaderRegisterIndex, shadowPass->LastDirLightShadowMap);
}
else
{
context->UnBindSR(dirLightShaderRegisterIndex);
}
dirLight.SetupLightData(&lightingData.DirectionalLight, view, useShadow);
}
else
{
lightingData.DirectionalLight.Color = Vector3::Zero;
lightingData.DirectionalLight.CastShadows = 0.0f;
context->UnBindSR(dirLightShaderRegisterIndex);
}
// Set sky light
if (cache->SkyLights.HasItems())
{
auto& skyLight = cache->SkyLights.Last();
skyLight.SetupLightData(&lightingData.SkyLight, view, false);
const auto texture = skyLight.Image ? skyLight.Image->GetTexture() : nullptr;
context->BindSR(skyLightShaderRegisterIndex, texture);
}
else
{
Platform::MemoryClear(&lightingData.SkyLight, sizeof(lightingData.SkyLight));
context->UnBindSR(skyLightShaderRegisterIndex);
}
// Set reflection probe data
EnvironmentProbe* probe = nullptr;
// TODO: optimize env probe searching for a transparent material - use spatial cache for renderer to find it
for (int32 i = 0; i < cache->EnvironmentProbes.Count(); i++)
{
const auto p = cache->EnvironmentProbes[i];
if (p->GetSphere().Contains(drawCall.World.GetTranslation()) != ContainmentType::Disjoint)
{
probe = p;
break;
}
}
if (probe && probe->GetProbe())
{
probe->SetupProbeData(&lightingData.EnvironmentProbe);
const auto texture = probe->GetProbe()->GetTexture();
context->BindSR(envProbeShaderRegisterIndex, texture);
}
else
{
lightingData.EnvironmentProbe.Data1 = Vector4::Zero;
context->UnBindSR(envProbeShaderRegisterIndex);
}
// Set local lights
lightingData.LocalLightsCount = 0;
for (int32 i = 0; i < cache->PointLights.Count(); i++)
{
const auto& light = cache->PointLights[i];
if (BoundingSphere(light.Position, light.Radius).Contains(drawCall.World.GetTranslation()) != ContainmentType::Disjoint)
{
light.SetupLightData(&lightingData.LocalLights[lightingData.LocalLightsCount], view, false);
lightingData.LocalLightsCount++;
if (lightingData.LocalLightsCount == MAX_LOCAL_LIGHTS)
break;
}
}
for (int32 i = 0; i < cache->SpotLights.Count(); i++)
{
const auto& light = cache->SpotLights[i];
if (BoundingSphere(light.Position, light.Radius).Contains(drawCall.World.GetTranslation()) != ContainmentType::Disjoint)
{
light.SetupLightData(&lightingData.LocalLights[lightingData.LocalLightsCount], view, false);
lightingData.LocalLightsCount++;
if (lightingData.LocalLightsCount == MAX_LOCAL_LIGHTS)
break;
}
}
}
// Bind constants
if (hasCb0)
{
context->UpdateCB(cb0, _cb0Data.Get());
context->BindCB(0, cb0);
}
if (hasCb1)
{
context->UpdateCB(cb1, _cb1Data.Get());
context->BindCB(1, cb1);
}
// Bind pipeline
context->SetState(state);
}
void ParticleMaterialShader::Unload()
{
// Base
MaterialShader::Unload();
_cacheSprite.Release();
_cacheModel.Release();
_cacheRibbon.Release();
}
bool ParticleMaterialShader::Load()
{
_drawModes = DrawPass::Depth | DrawPass::Forward;
GPUPipelineState::Description psDesc = GPUPipelineState::Description::Default;
psDesc.DepthTestEnable = (_info.FeaturesFlags & MaterialFeaturesFlags::DisableDepthTest) == 0;
psDesc.DepthWriteEnable = (_info.FeaturesFlags & MaterialFeaturesFlags::DisableDepthWrite) == 0;
auto vsSprite = _shader->GetVS("VS_Sprite");
auto vsMesh = _shader->GetVS("VS_Model");
auto vsRibbon = _shader->GetVS("VS_Ribbon");
// Check if use transparent distortion pass
if (_shader->HasShader("PS_Distortion"))
{
_drawModes |= DrawPass::Distortion;
// Accumulate Distortion Pass
psDesc.PS = _shader->GetPS("PS_Distortion");
psDesc.BlendMode = BlendingMode::Add;
psDesc.DepthWriteEnable = false;
psDesc.VS = vsSprite;
_cacheSprite.Distortion.Init(psDesc);
psDesc.VS = vsMesh;
_cacheModel.Distortion.Init(psDesc);
psDesc.VS = vsRibbon;
_cacheRibbon.Distortion.Init(psDesc);
}
// Forward Pass
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;
}
psDesc.VS = vsSprite;
_cacheSprite.Default.Init(psDesc);
psDesc.VS = vsMesh;
_cacheModel.Default.Init(psDesc);
psDesc.VS = vsRibbon;
_cacheRibbon.Default.Init(psDesc);
// Depth Pass
psDesc = GPUPipelineState::Description::Default;
psDesc.CullMode = CullMode::TwoSided;
psDesc.DepthClipEnable = false;
psDesc.PS = _shader->GetPS("PS_Depth");
psDesc.VS = vsSprite;
_cacheSprite.Depth.Init(psDesc);
psDesc.VS = vsMesh;
_cacheModel.Depth.Init(psDesc);
psDesc.VS = vsRibbon;
_cacheRibbon.Depth.Init(psDesc);
return false;
}

View File

@@ -0,0 +1,72 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "MaterialShader.h"
/// <summary>
/// Represents material that can be used to render particles.
/// </summary>
class ParticleMaterialShader : public MaterialShader
{
private:
struct Cache
{
PipelineStateCache Default;
PipelineStateCache Depth;
PipelineStateCache Distortion;
FORCE_INLINE PipelineStateCache* GetPS(const DrawPass pass)
{
switch (pass)
{
case DrawPass::Depth:
return &Depth;
case DrawPass::Distortion:
return &Distortion;
case DrawPass::Forward:
return &Default;
default:
return nullptr;
}
}
FORCE_INLINE void Release()
{
Default.Release();
Depth.Release();
Distortion.Release();
}
};
private:
Cache _cacheSprite;
Cache _cacheModel;
Cache _cacheRibbon;
DrawPass _drawModes = DrawPass::None;
public:
/// <summary>
/// Init
/// </summary>
/// <param name="name">Material resource name</param>
ParticleMaterialShader(const String& name)
: MaterialShader(name)
{
}
public:
// [MaterialShader]
DrawPass GetDrawModes() const override;
void Bind(BindParameters& params) override;
void Unload() override;
protected:
// [MaterialShader]
bool Load() override;
};

View File

@@ -0,0 +1,91 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#include "PostFxMaterialShader.h"
#include "MaterialParams.h"
#include "Engine/Engine/Time.h"
#include "Engine/Graphics/GPUDevice.h"
#include "Engine/Graphics/RenderTask.h"
#include "Engine/Graphics/Shaders/GPUShader.h"
#include "Engine/Graphics/RenderView.h"
#include "Engine/Graphics/Shaders/GPUConstantBuffer.h"
#include "Engine/Renderer/DrawCall.h"
PACK_STRUCT(struct PostFxMaterialShaderData {
Matrix ViewMatrix;
Vector3 ViewPos;
float ViewFar;
Vector3 ViewDir;
float TimeParam;
Vector4 ViewInfo;
Vector4 ScreenSize;
Vector4 TemporalAAJitter;
});
void PostFxMaterialShader::Bind(BindParameters& params)
{
// Prepare
auto context = params.GPUContext;
auto& view = params.RenderContext.View;
auto& drawCall = *params.FirstDrawCall;
const auto cb0 = _shader->GetCB(0);
const bool hasCb0 = cb0->GetSize() != 0;
// Setup parameters
MaterialParameter::BindMeta bindMeta;
bindMeta.Context = context;
bindMeta.Buffer0 = hasCb0 ? _cb0Data.Get() + sizeof(PostFxMaterialShaderData) : nullptr;
bindMeta.Input = params.Input;
bindMeta.Buffers = params.RenderContext.Buffers;
bindMeta.CanSampleDepth = true;
bindMeta.CanSampleGBuffer = true;
MaterialParams::Bind(params.ParamsLink, bindMeta);
// Setup material constants data
if (hasCb0)
{
const auto materialData = reinterpret_cast<PostFxMaterialShaderData*>(_cb0Data.Get());
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;
materialData->TemporalAAJitter = view.TemporalAAJitter;
}
// Bind constants
if (hasCb0)
{
context->UpdateCB(cb0, _cb0Data.Get());
context->BindCB(0, cb0);
}
// Bind pipeline
context->SetState(_cache.Default);
}
void PostFxMaterialShader::Unload()
{
// Base
MaterialShader::Unload();
_cache.Release();
}
bool PostFxMaterialShader::Load()
{
// PostFx material uses 'PS_PostFx' pixel shader and default simple shared quad vertex shader
GPUPipelineState::Description psDesc0 = GPUPipelineState::Description::DefaultFullscreenTriangle;
psDesc0.VS = GPUDevice::Instance->QuadShader->GetVS("VS_PostFx");
psDesc0.PS = _shader->GetPS("PS_PostFx");
_cache.Default = GPUDevice::Instance->CreatePipelineState();
if (_cache.Default->Init(psDesc0))
{
LOG(Warning, "Failed to create postFx material pipeline state.");
return true;
}
return false;
}

View File

@@ -0,0 +1,49 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "MaterialShader.h"
/// <summary>
/// Represents material that can be used to render post-process effects.
/// </summary>
class PostFxMaterialShader : public MaterialShader
{
private:
struct Cache
{
GPUPipelineState* Default = nullptr;
FORCE_INLINE void Release()
{
SAFE_DELETE_GPU_RESOURCE(Default);
}
};
private:
Cache _cache;
public:
/// <summary>
/// Init
/// </summary>
/// <param name="name">Material resource name</param>
PostFxMaterialShader(const String& name)
: MaterialShader(name)
{
}
public:
// [MaterialShader]
void Bind(BindParameters& params) override;
void Unload() override;
protected:
// [MaterialShader]
bool Load() override;
};

View File

@@ -0,0 +1,220 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#include "TerrainMaterialShader.h"
#include "MaterialParams.h"
#include "Engine/Engine/Time.h"
#include "Engine/Graphics/GPUContext.h"
#include "Engine/Graphics/GPULimits.h"
#include "Engine/Graphics/GPUDevice.h"
#include "Engine/Graphics/Shaders/GPUShader.h"
#include "Engine/Graphics/RenderBuffers.h"
#include "Engine/Graphics/RenderTask.h"
#include "Engine/Graphics/RenderView.h"
#include "Engine/Graphics/Shaders/GPUConstantBuffer.h"
#include "Engine/Level/Scene/Lightmap.h"
#include "Engine/Renderer/DrawCall.h"
#include "Engine/Terrain/TerrainPatch.h"
PACK_STRUCT(struct TerrainMaterialShaderData {
Matrix ViewProjectionMatrix;
Matrix WorldMatrix;
Matrix ViewMatrix;
Vector3 ViewPos;
float ViewFar;
Vector3 ViewDir;
float TimeParam;
Vector4 ViewInfo;
Vector4 ScreenSize;
Rectangle LightmapArea;
Vector3 WorldInvScale;
float WorldDeterminantSign;
float PerInstanceRandom;
float CurrentLOD; // Index of the current LOD
float ChunkSizeNextLOD; // ChunkSize for the next current LOD (after applying LOD down-scaling)
float TerrainChunkSizeLOD0; // Size of the terrain chunk in world units of the top-most LOD0
Vector4 HeightmapUVScaleBias; // xy-scale, zw-offset for chunk geometry UVs into heightmap UVs (as single MAD instruction)
Vector4 NeighborLOD; // Per component LOD index for chunk neighbors ordered: top, left, right, bottom
Vector2 OffsetUV; // Offset applied to the texture coordinates (used to implement seamless UVs based on chunk location relative to terrain root)
Vector2 Dummy0;
});
DrawPass TerrainMaterialShader::GetDrawModes() const
{
return DrawPass::Depth | DrawPass::GBuffer;
}
bool TerrainMaterialShader::CanUseLightmap() const
{
return true;
}
void TerrainMaterialShader::Bind(BindParameters& params)
{
// Prepare
auto context = params.GPUContext;
auto& view = params.RenderContext.View;
auto& drawCall = *params.FirstDrawCall;
const auto cb0 = _shader->GetCB(0);
const bool hasCb0 = cb0->GetSize() != 0;
// Setup parameters
MaterialParameter::BindMeta bindMeta;
bindMeta.Context = context;
bindMeta.Buffer0 = hasCb0 ? _cb0Data.Get() + sizeof(TerrainMaterialShaderData) : nullptr;
bindMeta.Input = nullptr;
bindMeta.Buffers = nullptr;
bindMeta.CanSampleDepth = false;
bindMeta.CanSampleGBuffer = false;
MaterialParams::Bind(params.ParamsLink, bindMeta);
// Setup material constants data
auto data = reinterpret_cast<TerrainMaterialShaderData*>(_cb0Data.Get());
if (hasCb0)
{
Matrix::Transpose(view.Frustum.GetMatrix(), data->ViewProjectionMatrix);
Matrix::Transpose(drawCall.World, data->WorldMatrix);
Matrix::Transpose(view.View, data->ViewMatrix);
data->ViewPos = view.Position;
data->ViewFar = view.Far;
data->ViewDir = view.Direction;
data->TimeParam = Time::Draw.UnscaledTime.GetTotalSeconds();
data->ViewInfo = view.ViewInfo;
data->ScreenSize = view.ScreenSize;
// Extract per axis scales from LocalToWorld transform
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);
data->WorldDeterminantSign = drawCall.WorldDeterminantSign;
data->PerInstanceRandom = drawCall.PerInstanceRandom;
data->CurrentLOD = drawCall.TerrainData.CurrentLOD;
data->ChunkSizeNextLOD = drawCall.TerrainData.ChunkSizeNextLOD;
data->TerrainChunkSizeLOD0 = drawCall.TerrainData.TerrainChunkSizeLOD0;
data->HeightmapUVScaleBias = drawCall.TerrainData.HeightmapUVScaleBias;
data->NeighborLOD = drawCall.TerrainData.NeighborLOD;
data->OffsetUV = drawCall.TerrainData.OffsetUV;
}
const bool useLightmap = view.Flags & ViewFlags::GI
#if USE_EDITOR
&& EnableLightmapsUsage
#endif
&& view.Pass == DrawPass::GBuffer
&& drawCall.Lightmap != nullptr;
if (useLightmap)
{
// Bind lightmap textures
GPUTexture *lightmap0, *lightmap1, *lightmap2;
drawCall.Lightmap->GetTextures(&lightmap0, &lightmap1, &lightmap2);
context->BindSR(0, lightmap0);
context->BindSR(1, lightmap1);
context->BindSR(2, lightmap2);
// Set lightmap data
data->LightmapArea = drawCall.LightmapUVsArea;
}
// Bind terrain textures
const auto heightmap = drawCall.TerrainData.Patch->Heightmap.Get()->GetTexture();
const auto splatmap0 = drawCall.TerrainData.Patch->Splatmap[0] ? drawCall.TerrainData.Patch->Splatmap[0]->GetTexture() : nullptr;
const auto splatmap1 = drawCall.TerrainData.Patch->Splatmap[1] ? drawCall.TerrainData.Patch->Splatmap[1]->GetTexture() : nullptr;
context->BindSR(3, heightmap);
context->BindSR(4, splatmap0);
context->BindSR(5, splatmap1);
// Bind constants
if (hasCb0)
{
context->UpdateCB(cb0, _cb0Data.Get());
context->BindCB(0, cb0);
}
// 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 USE_EDITOR
if (IsRunningRadiancePass)
cullMode = CullMode::TwoSided;
#endif
if (cullMode != CullMode::TwoSided && drawCall.IsNegativeScale())
{
// Invert culling when scale is negative
if (cullMode == CullMode::Normal)
cullMode = CullMode::Inverted;
else
cullMode = CullMode::Normal;
}
const PipelineStateCache* psCache = _cache.GetPS(view.Pass, useLightmap);
ASSERT(psCache);
GPUPipelineState* state = ((PipelineStateCache*)psCache)->GetPS(cullMode, wireframe);
// Bind pipeline
context->SetState(state);
}
void TerrainMaterialShader::Unload()
{
// Base
MaterialShader::Unload();
_cache.Release();
}
bool TerrainMaterialShader::Load()
{
GPUPipelineState::Description 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");
}
// Support blending but then use only emissive channel
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;
default: ;
}
// GBuffer Pass
psDesc.VS = _shader->GetVS("VS");
psDesc.PS = _shader->GetPS("PS_GBuffer");
_cache.Default.Init(psDesc);
// GBuffer Pass with lightmap (use pixel shader permutation for USE_LIGHTMAP=1)
psDesc.PS = _shader->GetPS("PS_GBuffer", 1);
_cache.DefaultLightmap.Init(psDesc);
// Depth Pass
psDesc.CullMode = CullMode::TwoSided;
psDesc.BlendMode = BlendingMode::Opaque;
psDesc.DepthClipEnable = false;
psDesc.DepthWriteEnable = true;
psDesc.DepthTestEnable = true;
psDesc.DepthFunc = ComparisonFunc::Less;
psDesc.HS = nullptr;
psDesc.DS = nullptr;
// TODO: masked terrain materials (depth pass should clip holes)
psDesc.PS = nullptr;
_cache.Depth.Init(psDesc);
return false;
}

View File

@@ -0,0 +1,68 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "MaterialShader.h"
/// <summary>
/// Represents material that can be used to render terrain.
/// </summary>
class TerrainMaterialShader : public MaterialShader
{
private:
struct Cache
{
PipelineStateCache Default;
PipelineStateCache DefaultLightmap;
PipelineStateCache Depth;
FORCE_INLINE PipelineStateCache* GetPS(const DrawPass pass, const bool useLightmap)
{
switch (pass)
{
case DrawPass::Depth:
return &Depth;
case DrawPass::GBuffer:
return useLightmap ? &DefaultLightmap : &Default;
default:
return nullptr;
}
}
FORCE_INLINE void Release()
{
Default.Release();
DefaultLightmap.Release();
Depth.Release();
}
};
private:
Cache _cache;
public:
/// <summary>
/// Init
/// </summary>
/// <param name="name">Material resource name</param>
TerrainMaterialShader(const String& name)
: MaterialShader(name)
{
}
public:
// [MaterialShader]
DrawPass GetDrawModes() const override;
bool CanUseLightmap() const override;
void Bind(BindParameters& params) override;
void Unload() override;
protected:
// [MaterialShader]
bool Load() override;
};