746 lines
27 KiB
C++
746 lines
27 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#pragma once
|
|
|
|
#include "MaterialDomain.h"
|
|
#include "MaterialShared.h"
|
|
#include "MaterialCompiler.h"
|
|
#include "TextureCompiler.h"
|
|
#include "Materials/MaterialParameterCollection.h"
|
|
|
|
#include "Engine/TextureLODSettings.h"
|
|
#include "Engine/Texture2D.h"
|
|
#include "Engine/Texture.h"
|
|
#include "Engine/TextureCube.h"
|
|
#include "Engine/Texture2DArray.h"
|
|
|
|
#include "DataDrivenShaderPlatformInfo.h"
|
|
#include "DeviceProfiles/DeviceProfileManager.h"
|
|
#include "DeviceProfiles/DeviceProfile.h"
|
|
#include "Materials/MaterialInterface.h"
|
|
#include "Materials/MaterialRenderProxy.h"
|
|
#include "SceneTypes.h"
|
|
#include "Materials/Material.h"
|
|
#include "Materials/MaterialAttributeDefinitionMap.h"
|
|
#include "Materials/MaterialExpressionCustomOutput.h"
|
|
|
|
#include "RenderUtils.h"
|
|
|
|
struct FExportMaterialCompiler : public FProxyMaterialCompiler
|
|
{
|
|
FExportMaterialCompiler(FMaterialCompiler* InCompiler) :
|
|
FProxyMaterialCompiler(InCompiler)
|
|
{}
|
|
|
|
// gets value stored by SetMaterialProperty()
|
|
virtual EShaderFrequency GetCurrentShaderFrequency() const override
|
|
{
|
|
return SF_Pixel;
|
|
}
|
|
|
|
virtual FMaterialShadingModelField GetMaterialShadingModels() const override
|
|
{
|
|
return Compiler->GetMaterialShadingModels();
|
|
}
|
|
|
|
virtual FMaterialShadingModelField GetCompiledShadingModels() const override
|
|
{
|
|
return Compiler->GetCompiledShadingModels();
|
|
}
|
|
|
|
virtual int32 WorldPosition(EWorldPositionIncludedOffsets WorldPositionIncludedOffsets) override
|
|
{
|
|
#if WITH_EDITOR
|
|
return MaterialBakingWorldPosition();
|
|
#else
|
|
return Compiler->WorldPosition(WorldPositionIncludedOffsets);
|
|
#endif
|
|
}
|
|
|
|
virtual int32 DistanceCullFade() override
|
|
{
|
|
return Compiler->Constant(1.0f);
|
|
}
|
|
|
|
virtual int32 ParticleRelativeTime() override
|
|
{
|
|
return Compiler->Constant(0.0f);
|
|
}
|
|
|
|
virtual int32 ParticleMotionBlurFade() override
|
|
{
|
|
return Compiler->Constant(1.0f);
|
|
}
|
|
|
|
virtual int32 PixelNormalWS() override
|
|
{
|
|
// Current returning vertex normal since pixel normal will contain incorrect data (normal calculated from uv data used as vertex positions to render out the material)
|
|
return Compiler->VertexNormal();
|
|
}
|
|
|
|
virtual int32 ParticleRandom() override
|
|
{
|
|
return Compiler->Constant(0.0f);
|
|
}
|
|
|
|
virtual int32 ParticleDirection() override
|
|
{
|
|
return Compiler->Constant3(0.0f, 0.0f, 0.0f);
|
|
}
|
|
|
|
virtual int32 ParticleSpeed() override
|
|
{
|
|
return Compiler->Constant(0.0f);
|
|
}
|
|
|
|
virtual int32 ParticleSize() override
|
|
{
|
|
return Compiler->Constant2(0.0f, 0.0f);
|
|
}
|
|
|
|
virtual int32 ParticleSpriteRotation() override
|
|
{
|
|
return Compiler->Constant2(0.0f, 0.0f);
|
|
}
|
|
|
|
virtual int32 CameraVector() override
|
|
{
|
|
// By returning vertex normal instead of a constant vector (like up), we ensure materials (with fresnel for example) are more correctly baked using custom mesh data.
|
|
return Compiler->VertexNormal();
|
|
}
|
|
|
|
virtual int32 ReflectionAboutCustomWorldNormal(int32 CustomWorldNormal, int32 bNormalizeCustomWorldNormal) override
|
|
{
|
|
if (CustomWorldNormal == INDEX_NONE)
|
|
{
|
|
return INDEX_NONE;
|
|
}
|
|
|
|
int32 N = CustomWorldNormal;
|
|
int32 C = CameraVector();
|
|
|
|
if (bNormalizeCustomWorldNormal)
|
|
{
|
|
// N = N / sqrt(dot(N, N))
|
|
N = Compiler->Div(N, Compiler->SquareRoot(Compiler->Dot(N, N)));
|
|
}
|
|
|
|
// return 2 * dot(N, C) * N - C
|
|
return Compiler->Sub(Compiler->Mul(Compiler->Constant(2.0f), Compiler->Mul(Compiler->Dot(N, C), N)), C);
|
|
}
|
|
|
|
virtual int32 PreSkinnedPosition() override
|
|
{
|
|
return Compiler->PreSkinnedPosition();
|
|
}
|
|
|
|
virtual int32 PreSkinnedNormal() override
|
|
{
|
|
return Compiler->PreSkinnedNormal();
|
|
}
|
|
|
|
virtual int32 VertexInterpolator(uint32 InterpolatorIndex) override
|
|
{
|
|
return Compiler->VertexInterpolator(InterpolatorIndex);
|
|
}
|
|
|
|
virtual int32 ReflectionVector() override
|
|
{
|
|
// Because camera vector is identical to normal vector we can work out that reflection vector will also be the same
|
|
return Compiler->VertexNormal();
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
virtual int32 MaterialBakingWorldPosition() override
|
|
{
|
|
// Depending on how the mesh data was retrieved, baking position may only be in local-space
|
|
const int32 BakingPosition = Compiler->MaterialBakingWorldPosition();
|
|
return Compiler->TransformPosition(MCB_Local, MCB_World, BakingPosition);
|
|
}
|
|
#endif
|
|
|
|
virtual int32 AccessCollectionParameter(UMaterialParameterCollection* ParameterCollection, int32 ParameterIndex, int32 ComponentIndex) override
|
|
{
|
|
if (!ParameterCollection || ParameterIndex == -1)
|
|
{
|
|
return INDEX_NONE;
|
|
}
|
|
|
|
// Collect names of all parameters
|
|
TArray<FName> ParameterNames;
|
|
ParameterCollection->GetParameterNames(ParameterNames, /*bVectorParameters=*/ false);
|
|
int32 NumScalarParameters = ParameterNames.Num();
|
|
ParameterCollection->GetParameterNames(ParameterNames, /*bVectorParameters=*/ true);
|
|
|
|
// Find a parameter corresponding to ParameterIndex/ComponentIndex pair
|
|
int32 Index;
|
|
for (Index = 0; Index < ParameterNames.Num(); Index++)
|
|
{
|
|
FGuid ParameterId = ParameterCollection->GetParameterId(ParameterNames[Index]);
|
|
int32 CheckParameterIndex, CheckComponentIndex;
|
|
ParameterCollection->GetParameterIndex(ParameterId, CheckParameterIndex, CheckComponentIndex);
|
|
if (CheckParameterIndex == ParameterIndex && CheckComponentIndex == ComponentIndex)
|
|
{
|
|
// Found
|
|
break;
|
|
}
|
|
}
|
|
if (Index >= ParameterNames.Num())
|
|
{
|
|
// Not found, should not happen
|
|
return INDEX_NONE;
|
|
}
|
|
|
|
// Create code for parameter
|
|
if (Index < NumScalarParameters)
|
|
{
|
|
const FCollectionScalarParameter* ScalarParameter = ParameterCollection->GetScalarParameterByName(ParameterNames[Index]);
|
|
check(ScalarParameter);
|
|
return Constant(ScalarParameter->DefaultValue);
|
|
}
|
|
else
|
|
{
|
|
const FCollectionVectorParameter* VectorParameter = ParameterCollection->GetVectorParameterByName(ParameterNames[Index]);
|
|
check(VectorParameter);
|
|
const FLinearColor& Color = VectorParameter->DefaultValue;
|
|
return Constant4(Color.R, Color.G, Color.B, Color.A);
|
|
}
|
|
}
|
|
|
|
virtual EMaterialCompilerType GetCompilerType() const override { return EMaterialCompilerType::MaterialProxy; }
|
|
};
|
|
|
|
class FExportMaterialProxy : public FMaterial, public FMaterialRenderProxy
|
|
{
|
|
public:
|
|
FExportMaterialProxy(UMaterialInterface* InMaterialInterface, EMaterialProperty InPropertyToCompile, const FString& InCustomOutputToCompile = TEXT(""), bool bInSynchronousCompilation = true, bool bTangentSpaceNormal = false, EBlendMode ProxyBlendMode = BLEND_Opaque, bool bAllowPixelDepthOffset = true)
|
|
: FMaterial()
|
|
, FMaterialRenderProxy(GetPathNameSafe(InMaterialInterface->GetMaterial()))
|
|
, MaterialInterface(InMaterialInterface)
|
|
, PropertyToCompile(InPropertyToCompile)
|
|
, CustomOutputToCompile(InCustomOutputToCompile)
|
|
, bSynchronousCompilation(bInSynchronousCompilation)
|
|
, bTangentSpaceNormal(bTangentSpaceNormal)
|
|
, ProxyBlendMode(ProxyBlendMode)
|
|
{
|
|
SetQualityLevelProperties(GMaxRHIFeatureLevel);
|
|
Material = InMaterialInterface->GetMaterial();
|
|
ReferencedTextures = InMaterialInterface->GetReferencedTextures();
|
|
ReferencedTextureCollections = InMaterialInterface->GetReferencedTextureCollections();
|
|
|
|
const FMaterialResource* Resource = InMaterialInterface->GetMaterialResource(GMaxRHIFeatureLevel);
|
|
|
|
FMaterialShaderMapId ResourceId;
|
|
Resource->GetShaderMapId(GMaxRHIShaderPlatform, nullptr, ResourceId);
|
|
|
|
{
|
|
TArray<FShaderType*> ShaderTypes;
|
|
TArray<FVertexFactoryType*> VFTypes;
|
|
TArray<const FShaderPipelineType*> ShaderPipelineTypes;
|
|
GetDependentShaderAndVFTypes(GMaxRHIShaderPlatform, ResourceId.LayoutParams, ShaderTypes, ShaderPipelineTypes, VFTypes);
|
|
|
|
// Overwrite the shader map Id's dependencies with ones that came from the FMaterial actually being compiled (this)
|
|
// This is necessary as we change FMaterial attributes like GetShadingModels(), which factor into the ShouldCache functions that determine dependent shader types
|
|
ResourceId.SetShaderDependencies(ShaderTypes, ShaderPipelineTypes, VFTypes, GMaxRHIShaderPlatform);
|
|
}
|
|
|
|
// Override with a special usage so we won't re-use the shader map used by the material for rendering
|
|
switch (InPropertyToCompile)
|
|
{
|
|
case MP_BaseColor: ResourceId.Usage = EMaterialShaderMapUsage::MaterialExportBaseColor; break;
|
|
case MP_Specular: ResourceId.Usage = EMaterialShaderMapUsage::MaterialExportSpecular; break;
|
|
case MP_Normal: ResourceId.Usage = EMaterialShaderMapUsage::MaterialExportNormal; break;
|
|
case MP_Tangent: ResourceId.Usage = EMaterialShaderMapUsage::MaterialExportTangent; break;
|
|
case MP_Metallic: ResourceId.Usage = EMaterialShaderMapUsage::MaterialExportMetallic; break;
|
|
case MP_Roughness: ResourceId.Usage = EMaterialShaderMapUsage::MaterialExportRoughness; break;
|
|
case MP_Anisotropy: ResourceId.Usage = EMaterialShaderMapUsage::MaterialExportAnisotropy; break;
|
|
case MP_AmbientOcclusion: ResourceId.Usage = EMaterialShaderMapUsage::MaterialExportAO; break;
|
|
case MP_EmissiveColor: ResourceId.Usage = EMaterialShaderMapUsage::MaterialExportEmissive; break;
|
|
case MP_Opacity: ResourceId.Usage = EMaterialShaderMapUsage::MaterialExportOpacity; break;
|
|
case MP_Refraction: ResourceId.Usage = EMaterialShaderMapUsage::MaterialExportRefraction; break;
|
|
case MP_OpacityMask: ResourceId.Usage = EMaterialShaderMapUsage::MaterialExportOpacityMask; break;
|
|
case MP_SubsurfaceColor: ResourceId.Usage = EMaterialShaderMapUsage::MaterialExportSubSurfaceColor; break;
|
|
case MP_ShadingModel: ResourceId.Usage = EMaterialShaderMapUsage::MaterialExportShadingModel; break;
|
|
case MP_CustomData0: ResourceId.Usage = EMaterialShaderMapUsage::MaterialExportClearCoat; break;
|
|
case MP_CustomData1: ResourceId.Usage = EMaterialShaderMapUsage::MaterialExportClearCoatRoughness; break;
|
|
case MP_CustomOutput:
|
|
ResourceId.Usage = EMaterialShaderMapUsage::MaterialExportCustomOutput;
|
|
ResourceId.UsageCustomOutput = InCustomOutputToCompile;
|
|
break;
|
|
default:
|
|
ensureMsgf(false, TEXT("ExportMaterial has no usage for property %i. Will likely reuse the normal rendering shader and crash later with a parameter mismatch"), (int32)InPropertyToCompile);
|
|
break;
|
|
};
|
|
|
|
Usage = ResourceId.Usage;
|
|
ResourceId.BaseMaterialId = Material->StateId;
|
|
|
|
SetAllowPixelDepthOffset(bAllowPixelDepthOffset);
|
|
|
|
CacheShaders(ResourceId, GMaxRHIShaderPlatform);
|
|
}
|
|
|
|
/** This override is required otherwise the shaders aren't ready for use when the surface is rendered resulting in a blank image */
|
|
virtual bool RequiresSynchronousCompilation() const override { return bSynchronousCompilation; };
|
|
|
|
/**
|
|
* Should the shader for this material with the given platform, shader type and vertex
|
|
* factory type combination be compiled
|
|
*
|
|
* @param Platform The platform currently being compiled for
|
|
* @param ShaderType Which shader is being compiled
|
|
* @param VertexFactory Which vertex factory is being compiled (can be NULL)
|
|
*
|
|
* @return true if the shader should be compiled
|
|
*/
|
|
virtual bool ShouldCache(EShaderPlatform Platform, const FShaderType* ShaderType, const FVertexFactoryType* VertexFactoryType) const override
|
|
{
|
|
const bool bCorrectVertexFactory = VertexFactoryType == FindVertexFactoryType(FName(TEXT("FLocalVertexFactory"), FNAME_Find));
|
|
const bool bPCPlatform = !IsConsolePlatform(Platform);
|
|
const bool bCorrectFrequency = ShaderType->GetFrequency() == SF_Vertex || ShaderType->GetFrequency() == SF_Pixel;
|
|
return bCorrectVertexFactory && bPCPlatform && bCorrectFrequency;
|
|
}
|
|
|
|
virtual TArrayView<const TObjectPtr<UObject>> GetReferencedTextures() const override
|
|
{
|
|
return ReferencedTextures;
|
|
}
|
|
|
|
virtual TConstArrayView<TObjectPtr<UTextureCollection>> GetReferencedTextureCollections() const override
|
|
{
|
|
return ReferencedTextureCollections;
|
|
}
|
|
|
|
virtual void GetStaticParameterSet(EShaderPlatform Platform, FStaticParameterSet& OutSet) const override
|
|
{
|
|
if (const FMaterialResource* Resource = MaterialInterface->GetMaterialResource(GMaxRHIFeatureLevel))
|
|
{
|
|
Resource->GetStaticParameterSet(Platform, OutSet);
|
|
}
|
|
}
|
|
|
|
////////////////
|
|
// FMaterialRenderProxy interface.
|
|
virtual const FMaterial* GetMaterialNoFallback(ERHIFeatureLevel::Type InFeatureLevel) const override
|
|
{
|
|
if (GetRenderingThreadShaderMap())
|
|
{
|
|
return this;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
virtual const FMaterialRenderProxy* GetFallback(ERHIFeatureLevel::Type InFeatureLevel) const override
|
|
{
|
|
return UMaterial::GetDefaultMaterial(MD_Surface)->GetRenderProxy();
|
|
}
|
|
|
|
virtual bool GetParameterValue(EMaterialParameterType Type, const FHashedMaterialParameterInfo& ParameterInfo, FMaterialParameterValue& OutValue, const FMaterialRenderContext& Context) const override
|
|
{
|
|
return MaterialInterface->GetRenderProxy()->GetParameterValue(Type, ParameterInfo, OutValue, Context);
|
|
}
|
|
|
|
// Material properties.
|
|
/** Entry point for compiling a specific material property. This must call SetMaterialProperty. */
|
|
virtual int32 CompilePropertyAndSetMaterialProperty(EMaterialProperty Property, FMaterialCompiler* Compiler, EShaderFrequency OverrideShaderFrequency, bool bUsePreviousFrameTime) const override
|
|
{
|
|
// needs to be called in this function!!
|
|
Compiler->SetMaterialProperty(Property, OverrideShaderFrequency, bUsePreviousFrameTime);
|
|
const int32 Ret = CompilePropertyAndSetMaterialPropertyWithoutCast(Property, Compiler);
|
|
return Compiler->ForceCast(Ret, FMaterialAttributeDefinitionMap::GetValueType(Property), MFCF_ExactMatch | MFCF_ReplicateValue);
|
|
}
|
|
|
|
/** helper for CompilePropertyAndSetMaterialProperty() */
|
|
int32 CompilePropertyAndSetMaterialPropertyWithoutCast(EMaterialProperty Property, FMaterialCompiler* Compiler) const
|
|
{
|
|
if (Property == MP_EmissiveColor)
|
|
{
|
|
FExportMaterialCompiler ProxyCompiler(Compiler);
|
|
const uint32 ForceCast_Exact_Replicate = MFCF_ForceCast | MFCF_ExactMatch | MFCF_ReplicateValue;
|
|
|
|
switch (PropertyToCompile)
|
|
{
|
|
case MP_EmissiveColor:
|
|
Compiler->SetSubstrateMaterialExportType(SME_Emissive, ESubstrateMaterialExportContext::SMEC_Opaque, BLEND_Opaque);
|
|
break;
|
|
case MP_BaseColor:
|
|
Compiler->SetSubstrateMaterialExportType(SME_BaseColor, ESubstrateMaterialExportContext::SMEC_Opaque, BLEND_Opaque);
|
|
break;
|
|
case MP_Specular:
|
|
Compiler->SetSubstrateMaterialExportType(SME_Specular, ESubstrateMaterialExportContext::SMEC_Opaque, BLEND_Opaque);
|
|
break;
|
|
case MP_Roughness:
|
|
Compiler->SetSubstrateMaterialExportType(SME_Roughness, ESubstrateMaterialExportContext::SMEC_Opaque, BLEND_Opaque);
|
|
break;
|
|
case MP_Anisotropy:
|
|
Compiler->SetSubstrateMaterialExportType(SME_Anisotropy, ESubstrateMaterialExportContext::SMEC_Opaque, BLEND_Opaque);
|
|
break;
|
|
case MP_Metallic:
|
|
Compiler->SetSubstrateMaterialExportType(SME_Metallic, ESubstrateMaterialExportContext::SMEC_Opaque, BLEND_Opaque);
|
|
break;
|
|
case MP_Opacity:
|
|
Compiler->SetSubstrateMaterialExportType(SME_Opacity, ESubstrateMaterialExportContext::SMEC_Opaque, BLEND_Opaque);
|
|
break;
|
|
case MP_OpacityMask:
|
|
Compiler->SetSubstrateMaterialExportType(SME_OpacityMask, ESubstrateMaterialExportContext::SMEC_Opaque, BLEND_Opaque);
|
|
break;
|
|
case MP_CustomData0:
|
|
Compiler->SetSubstrateMaterialExportType(SME_CustomData0, ESubstrateMaterialExportContext::SMEC_Opaque, BLEND_Opaque);
|
|
break;
|
|
case MP_CustomData1:
|
|
Compiler->SetSubstrateMaterialExportType(SME_CustomData1, ESubstrateMaterialExportContext::SMEC_Opaque, BLEND_Opaque);
|
|
break;
|
|
case MP_SubsurfaceColor:
|
|
Compiler->SetSubstrateMaterialExportType(SME_SubsurfaceColor, ESubstrateMaterialExportContext::SMEC_Opaque, BLEND_Opaque);
|
|
break;
|
|
case MP_Normal:
|
|
Compiler->SetSubstrateMaterialExportType(SME_Normal, ESubstrateMaterialExportContext::SMEC_Opaque, BLEND_Opaque);
|
|
break;
|
|
case MP_Tangent:
|
|
Compiler->SetSubstrateMaterialExportType(SME_Tangent, ESubstrateMaterialExportContext::SMEC_Opaque, BLEND_Opaque);
|
|
break;
|
|
case MP_ShadingModel:
|
|
Compiler->SetSubstrateMaterialExportType(SME_ShadingModel, ESubstrateMaterialExportContext::SMEC_Opaque, BLEND_Opaque);
|
|
break;
|
|
}
|
|
|
|
|
|
switch (PropertyToCompile)
|
|
{
|
|
case MP_EmissiveColor:
|
|
case MP_BaseColor:
|
|
case MP_Specular:
|
|
case MP_Roughness:
|
|
case MP_Anisotropy:
|
|
case MP_Metallic:
|
|
case MP_AmbientOcclusion:
|
|
case MP_Opacity:
|
|
case MP_OpacityMask:
|
|
case MP_CustomData0:
|
|
case MP_CustomData1:
|
|
case MP_SubsurfaceColor:
|
|
return MaterialInterface->CompileProperty(&ProxyCompiler, PropertyToCompile, ForceCast_Exact_Replicate);
|
|
case MP_Normal:
|
|
case MP_Tangent:
|
|
return CompileNormalEncoding(
|
|
Compiler,
|
|
CompileNormalTransform(&ProxyCompiler, MaterialInterface->CompileProperty(&ProxyCompiler, PropertyToCompile, ForceCast_Exact_Replicate)));
|
|
case MP_Refraction:
|
|
// Only index of refraction can be supported because other methods don't have values within a suitable range for encoding into 8-bit baked textures
|
|
if (Material->RefractionMethod == RM_IndexOfRefraction)
|
|
{
|
|
return CompileRefractionEncoding(
|
|
Compiler,
|
|
MaterialInterface->CompileProperty(&ProxyCompiler, MP_Refraction, ForceCast_Exact_Replicate));
|
|
}
|
|
break;
|
|
case MP_ShadingModel:
|
|
return CompileShadingModelEncoding(Compiler, MaterialInterface->CompileProperty(&ProxyCompiler, MP_ShadingModel));
|
|
case MP_CustomOutput:
|
|
if (const FMaterialCustomOutputAttributeDefintion* CustomAttribute = FMaterialAttributeDefinitionMap::GetCustomAttribute(CustomOutputToCompile))
|
|
{
|
|
constexpr int32 InputIndex = 0; // Assume input index is always 0, which it is for all custom outputs that are registered as material attributes
|
|
return CompileInputForCustomOutput(&ProxyCompiler, CustomAttribute, InputIndex, ForceCast_Exact_Replicate);
|
|
}
|
|
break;
|
|
case MP_FrontMaterial:
|
|
if (Substrate::IsSubstrateEnabled())
|
|
{
|
|
// When using Substrate, material property always compile from material.
|
|
// We cannot use rediction so instead we instruct the compiler the type of data export we are looking for.
|
|
return MaterialInterface->CompileProperty(&ProxyCompiler, MP_FrontMaterial);
|
|
}
|
|
else
|
|
{
|
|
return ProxyCompiler.SubstrateCreateAndRegisterNullMaterial();
|
|
}
|
|
default:
|
|
return Compiler->Constant(1.0f);
|
|
}
|
|
|
|
return Compiler->Constant(0.0f);
|
|
}
|
|
else if (Property == MP_WorldPositionOffset || Property == MP_Displacement)
|
|
{
|
|
//This property MUST return 0 as a default or during the process of rendering textures out for lightmass to use, pixels will be off by 1.
|
|
return Compiler->Constant(0.0f);
|
|
}
|
|
else if (Property >= MP_CustomizedUVs0 && Property <= MP_CustomizedUVs7)
|
|
{
|
|
// Pass through customized UVs
|
|
return MaterialInterface->CompileProperty(Compiler, Property);
|
|
}
|
|
else if (Property == MP_OpacityMask)
|
|
{
|
|
return MaterialInterface->CompileProperty(Compiler, MP_OpacityMask);
|
|
}
|
|
else if (Property == MP_ShadingModel)
|
|
{
|
|
return MaterialInterface->CompileProperty(Compiler, MP_ShadingModel);
|
|
}
|
|
else if (Property == MP_SurfaceThickness)
|
|
{
|
|
return MaterialInterface->CompileProperty(Compiler, MP_SurfaceThickness);
|
|
}
|
|
else if (Property == MP_FrontMaterial)
|
|
{
|
|
if (Substrate::IsSubstrateEnabled())
|
|
{
|
|
return MaterialInterface->CompileProperty(Compiler, MP_FrontMaterial);
|
|
}
|
|
else
|
|
{
|
|
return Compiler->SubstrateCreateAndRegisterNullMaterial();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (Substrate::IsSubstrateEnabled() && Property == MP_Roughness)
|
|
{
|
|
// In this case return non 0.5 to not trigger the fully rough path from the Translator.
|
|
// This is because we do not only compile EmissiveColor only anymore since we use the specified shading model (see GetShadingModels below).
|
|
return Compiler->Constant(0.5f);
|
|
}
|
|
return Compiler->Constant(1.0f);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the shader map usage of the material, which will be included in the DDC key.
|
|
* This mechanism allows derived material classes to create different DDC keys with the same base material.
|
|
*/
|
|
virtual EMaterialShaderMapUsage::Type GetShaderMapUsage() const override { return Usage; }
|
|
|
|
virtual FString GetMaterialUsageDescription() const override
|
|
{
|
|
return FString::Printf(TEXT("MaterialBaking_%s"), MaterialInterface ? *MaterialInterface->GetName() : TEXT("NULL"));
|
|
}
|
|
virtual EMaterialDomain GetMaterialDomain() const override
|
|
{
|
|
// Because the baking module applies the material to a plane (or mesh),
|
|
// it needs to be a surface material.
|
|
return MD_Surface;
|
|
}
|
|
virtual bool IsTangentSpaceNormal() const override
|
|
{
|
|
if (const FMaterialResource* Resource = MaterialInterface->GetMaterialResource(GMaxRHIFeatureLevel))
|
|
{
|
|
return Resource->IsTangentSpaceNormal();
|
|
}
|
|
return false;
|
|
}
|
|
virtual bool IsTwoSided() const override
|
|
{
|
|
if (MaterialInterface)
|
|
{
|
|
return MaterialInterface->IsTwoSided();
|
|
}
|
|
return false;
|
|
}
|
|
virtual bool IsThinSurface() const override
|
|
{
|
|
if (MaterialInterface)
|
|
{
|
|
return MaterialInterface->IsThinSurface();
|
|
}
|
|
return false;
|
|
}
|
|
virtual bool IsDitheredLODTransition() const override
|
|
{
|
|
if (MaterialInterface)
|
|
{
|
|
return MaterialInterface->IsDitheredLODTransition();
|
|
}
|
|
return false;
|
|
}
|
|
virtual bool IsLightFunction() const override
|
|
{
|
|
if (Material)
|
|
{
|
|
return (Material->MaterialDomain == MD_LightFunction);
|
|
}
|
|
return false;
|
|
}
|
|
virtual bool IsDeferredDecal() const override
|
|
{
|
|
// Decals are tricky. Since they mix with the underlying material
|
|
// and can't be applied to meshes, they can't really be baked 1:1.
|
|
// Instead we'll just bake them as surface materials.
|
|
return false;
|
|
}
|
|
virtual bool IsUIMaterial() const override
|
|
{
|
|
return Material && Material->MaterialDomain == MD_UI;
|
|
}
|
|
virtual bool IsVolumetricPrimitive() const override
|
|
{
|
|
return Material && Material->MaterialDomain == MD_Volume;
|
|
}
|
|
virtual bool IsSpecialEngineMaterial() const override
|
|
{
|
|
if (Material)
|
|
{
|
|
return (Material->bUsedAsSpecialEngineMaterial == 1);
|
|
}
|
|
return false;
|
|
}
|
|
virtual bool IsWireframe() const override
|
|
{
|
|
if (Material)
|
|
{
|
|
return (Material->Wireframe == 1);
|
|
}
|
|
return false;
|
|
}
|
|
virtual bool IsMasked() const override { return ProxyBlendMode == BLEND_Masked; }
|
|
virtual enum EBlendMode GetBlendMode() const override { return ProxyBlendMode; }
|
|
virtual enum ERefractionMode GetRefractionMode() const override { return Material ? (ERefractionMode)Material->RefractionMethod : RM_None; }
|
|
virtual bool GetRootNodeOverridesDefaultRefraction()const override { return Material ? Material->bRootNodeOverridesDefaultDistortion : false; }
|
|
|
|
virtual FMaterialShadingModelField GetShadingModels() const override
|
|
{
|
|
// Substrate needs the real material shading model since the expressions access GetMaterialShadingModels() through the compiler to generate the substrate operators,
|
|
// and we do not want unlit materials when it is a Slab or a ShadingModel node.
|
|
return Substrate::IsSubstrateEnabled() ? Material->GetShadingModels() : MSM_Unlit;
|
|
}
|
|
|
|
virtual bool IsShadingModelFromMaterialExpression() const override { return false; }
|
|
virtual float GetOpacityMaskClipValue() const override { return 0.5f; }
|
|
virtual bool GetCastDynamicShadowAsMasked() const override { return false; }
|
|
virtual FString GetFriendlyName() const override { return FString::Printf(TEXT("FExportMaterialRenderer %s"), MaterialInterface ? *MaterialInterface->GetName() : TEXT("NULL")); }
|
|
/**
|
|
* Should shaders compiled for this material be saved to disk?
|
|
*/
|
|
virtual bool IsPersistent() const override { return true; }
|
|
|
|
virtual FGuid GetMaterialId() const override
|
|
{
|
|
// Reuse the base material's Id
|
|
// Normally this would cause a bug as the shader map would try to be shared by both,
|
|
// But FExportMaterialProxy::GetShaderMapUsage() allows this to work
|
|
return Material->StateId;
|
|
}
|
|
|
|
virtual UMaterialInterface* GetMaterialInterface() const override
|
|
{
|
|
return MaterialInterface;
|
|
}
|
|
|
|
friend FArchive& operator<< (FArchive& Ar, FExportMaterialProxy& V)
|
|
{
|
|
return Ar << V.MaterialInterface;
|
|
}
|
|
|
|
virtual bool IsUsedWithStaticLighting() const override
|
|
{
|
|
return true;
|
|
}
|
|
|
|
virtual void GatherExpressionsForCustomInterpolators(TArray<UMaterialExpression*>& OutExpressions) const override
|
|
{
|
|
if(Material)
|
|
{
|
|
Material->GetAllExpressionsForCustomInterpolators(OutExpressions);
|
|
}
|
|
}
|
|
|
|
virtual bool CheckInValidStateForCompilation(FMaterialCompiler* Compiler) const override
|
|
{
|
|
return Material && Material->CheckInValidStateForCompilation(Compiler);
|
|
}
|
|
|
|
private:
|
|
int32 CompileInputForCustomOutput(FMaterialCompiler* Compiler, const FMaterialCustomOutputAttributeDefintion* CustomAttribute, int32 InputIndex, uint32 ForceCastFlags) const
|
|
{
|
|
check(CustomAttribute);
|
|
|
|
UMaterialExpressionCustomOutput* Expression = GetCustomOutputExpression(CustomAttribute->FunctionName);
|
|
FExpressionInput* ExpressionInput = Expression ? Expression->GetInput(InputIndex) : nullptr;
|
|
int32 Result;
|
|
|
|
if (ExpressionInput)
|
|
{
|
|
Result = ExpressionInput->Compile(Compiler);
|
|
}
|
|
else
|
|
{
|
|
Result = CustomAttribute->CompileDefaultValue(Compiler);
|
|
}
|
|
|
|
if (CustomOutputToCompile == TEXT("ClearCoatBottomNormal"))
|
|
{
|
|
Result = CompileNormalEncoding(Compiler, CompileNormalTransform(Compiler, Result));
|
|
}
|
|
|
|
if (ForceCastFlags & MFCF_ForceCast)
|
|
{
|
|
Result = Compiler->ForceCast(Result, CustomAttribute->ValueType, ForceCastFlags);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
UMaterialExpressionCustomOutput* GetCustomOutputExpression(const FString& FunctionName) const
|
|
{
|
|
for (UMaterialExpression* Expression : Material->GetExpressions())
|
|
{
|
|
UMaterialExpressionCustomOutput* CustomOutputExpression = Cast<UMaterialExpressionCustomOutput>(Expression);
|
|
if (CustomOutputExpression && CustomOutputExpression->GetFunctionName() == FunctionName)
|
|
{
|
|
return CustomOutputExpression;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
int32 CompileNormalTransform(FMaterialCompiler* Compiler, int32 NormalInput) const
|
|
{
|
|
return bTangentSpaceNormal && !Material->bTangentSpaceNormal
|
|
? Compiler->TransformVector(MCB_World, MCB_Tangent, NormalInput) : NormalInput;
|
|
}
|
|
|
|
static int32 CompileNormalEncoding(FMaterialCompiler* Compiler, int32 NormalInput)
|
|
{
|
|
return Compiler->Add(
|
|
Compiler->Mul(NormalInput, Compiler->Constant(0.5f)), // [-1,1] * 0.5
|
|
Compiler->Constant(0.5f)); // [-0.5,0.5] + 0.5
|
|
}
|
|
|
|
static int32 CompileRefractionEncoding(FMaterialCompiler* Compiler, int32 RefractionInput)
|
|
{
|
|
// [1,Infinity] -> [1,0]
|
|
return Compiler->Div(
|
|
Compiler->Constant(1.0f),
|
|
Compiler->Max(Compiler->Constant(1.0f), RefractionInput));
|
|
}
|
|
|
|
static int32 CompileShadingModelEncoding(FMaterialCompiler* Compiler, int32 ShadingModelInput)
|
|
{
|
|
// [0,MSM_NUM] -> [0,1]
|
|
return Compiler->Div(
|
|
Compiler->CastShadingModelToFloat(ShadingModelInput),
|
|
Compiler->Constant(MSM_NUM));
|
|
}
|
|
|
|
private:
|
|
/** The material interface for this proxy */
|
|
UMaterialInterface* MaterialInterface;
|
|
UMaterial* Material;
|
|
TArray<TObjectPtr<UObject>> ReferencedTextures;
|
|
TArray<TObjectPtr<UTextureCollection>> ReferencedTextureCollections;
|
|
/** The property to compile for rendering the sample */
|
|
EMaterialProperty PropertyToCompile;
|
|
/** Stores which exported attribute this proxy is compiling for. */
|
|
EMaterialShaderMapUsage::Type Usage;
|
|
/** The name of the specific custom output to compile for rendering the sample. Only used if PropertyToCompile is MP_CustomOutput */
|
|
FString CustomOutputToCompile;
|
|
bool bSynchronousCompilation;
|
|
|
|
public:
|
|
/** Whether to transform normals from world-space to tangent-space (does nothing if material already uses tangent-space normals) */
|
|
bool bTangentSpaceNormal;
|
|
/** The blend mode used when baking the proxy material */
|
|
EBlendMode ProxyBlendMode;
|
|
};
|