1437 lines
50 KiB
C++
1437 lines
50 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
LightmassRender.cpp: lightmass rendering-related implementation.
|
|
=============================================================================*/
|
|
|
|
#include "Lightmass/LightmassRender.h"
|
|
#include "HAL/FileManager.h"
|
|
#include "Misc/FileHelper.h"
|
|
#include "Misc/Paths.h"
|
|
#include "Misc/Guid.h"
|
|
#include "RenderingThread.h"
|
|
#include "MaterialDomain.h"
|
|
#include "MaterialShared.h"
|
|
#include "Materials/Material.h"
|
|
#include "Materials/MaterialAttributeDefinitionMap.h"
|
|
#include "Materials/MaterialRenderProxy.h"
|
|
#include "CanvasItem.h"
|
|
#include "CanvasTypes.h"
|
|
#include "Engine/TextureRenderTarget2D.h"
|
|
#include "MaterialExport.h"
|
|
#include "Misc/ConfigCacheIni.h"
|
|
#include "LandscapeLight.h"
|
|
#include "Lightmass/Lightmass.h"
|
|
#include "MaterialCompiler.h"
|
|
#include "LightMap.h"
|
|
#include "Lightmass/LightmassLandscapeRender.h"
|
|
#include "LandscapeMaterialInstanceConstant.h"
|
|
#include "EngineModule.h"
|
|
#include "EngineUtils.h"
|
|
#include "TextureResource.h"
|
|
|
|
DEFINE_LOG_CATEGORY_STATIC(LogLightmassRender, Error, All);
|
|
|
|
// FLightmassMaterialCompiler - A proxy compiler that overrides various compiler functions for potential problem expressions.
|
|
struct FLightmassMaterialCompiler : public FProxyMaterialCompiler
|
|
{
|
|
FLightmassMaterialCompiler(FMaterialCompiler* InCompiler) :
|
|
FProxyMaterialCompiler(InCompiler)
|
|
{}
|
|
|
|
// gets value stored by SetMaterialProperty()
|
|
virtual EShaderFrequency GetCurrentShaderFrequency() const override
|
|
{
|
|
// not used by Lightmass
|
|
return SF_Pixel;
|
|
}
|
|
|
|
virtual FMaterialShadingModelField GetMaterialShadingModels() const override
|
|
{
|
|
// not used by Lightmass
|
|
return MSM_MAX;
|
|
}
|
|
|
|
virtual FMaterialShadingModelField GetCompiledShadingModels() const override
|
|
{
|
|
// not used by Lightmass
|
|
return MSM_MAX;
|
|
}
|
|
|
|
virtual EMaterialValueType GetParameterType(int32 Index) const override
|
|
{
|
|
return MCT_Unknown;
|
|
}
|
|
|
|
virtual FMaterialUniformExpression* GetParameterUniformExpression(int32 Index) const override
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
virtual int32 ParticleMacroUV() override
|
|
{
|
|
return Compiler->ParticleMacroUV();
|
|
}
|
|
|
|
virtual int32 ParticleRelativeTime() override
|
|
{
|
|
return Compiler->Constant(0.0f);
|
|
}
|
|
|
|
virtual int32 ParticleMotionBlurFade() override
|
|
{
|
|
return Compiler->Constant(1.0f);
|
|
}
|
|
|
|
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 WorldPosition(EWorldPositionIncludedOffsets WorldPositionIncludedOffsets) override
|
|
{
|
|
//UE_LOG(LogLightmassRender, Log, TEXT("Lightmass material compiler has encountered WorldPosition... Forcing constant (0.0f,0.0f,0.0f)."));
|
|
return Compiler->Constant3(0.0f,0.0f,0.0f);
|
|
}
|
|
|
|
virtual int32 ObjectWorldPosition(EPositionOrigin OriginType) override
|
|
{
|
|
//UE_LOG(LogLightmassRender, Log, TEXT("Lightmass material compiler has encountered ObjectWorldPosition... Forcing constant (0.0f,0.0f,0.0f)."));
|
|
return Compiler->Constant3(0.0f,0.0f,0.0f);
|
|
}
|
|
|
|
virtual int32 ObjectRadius() override
|
|
{
|
|
//UE_LOG(LogLightmassRender, Log, TEXT("Lightmass material compiler has encountered ObjectRadius... Forcing constant 500.0f."));
|
|
return Compiler->Constant(500);
|
|
}
|
|
|
|
virtual int32 ObjectBounds() override
|
|
{
|
|
//UE_LOG(LogLightmassRender, Log, TEXT("Lightmass material compiler has encountered ObjectBounds... Forcing constant (0,0,0)."));
|
|
return Compiler->Constant3(0,0,0);
|
|
}
|
|
|
|
virtual int32 PreSkinnedLocalBounds(int32 OutputIndex) override
|
|
{
|
|
return Compiler->Constant3(0, 0, 0);
|
|
}
|
|
|
|
virtual int32 DistanceCullFade() override
|
|
{
|
|
return Compiler->Constant(1.0f);
|
|
}
|
|
|
|
virtual int32 ActorWorldPosition(EPositionOrigin OriginType) override
|
|
{
|
|
return Compiler->Constant3(0.0f,0.0f,0.0f);
|
|
}
|
|
|
|
virtual int32 CameraVector() override
|
|
{
|
|
//UE_LOG(LogLightmassRender, Log, TEXT("Lightmass material compiler has encountered CameraVector... Forcing constant (0.0f,0.0f,1.0f)."));
|
|
return Compiler->Constant3(0.0f,0.0f,1.0f);
|
|
}
|
|
|
|
virtual int32 LightVector() override
|
|
{
|
|
//UE_LOG(LogLightmassRender, Log, TEXT("Lightmass material compiler has encountered LightVector... Forcing constant (1.0f,0.0f,0.0f)."));
|
|
return Compiler->Constant3(1.0f,0.0f,0.0f);
|
|
}
|
|
|
|
virtual int32 ReflectionVector() override
|
|
{
|
|
//UE_LOG(LogLightmassRender, Log, TEXT("Lightmass material compiler has encountered ReflectionVector... Forcing constant (0.0f,0.0f,-1.0f)."));
|
|
return Compiler->Constant3(0.0f,0.0f,-1.0f);
|
|
}
|
|
|
|
virtual int32 ReflectionAboutCustomWorldNormal(int32 CustomWorldNormal, int32 bNormalizeCustomWorldNormal) override
|
|
{
|
|
//UE_LOG(LogLightmassRender, Log, TEXT("Lightmass material compiler has encountered ReflectionAboutCustomNormalVector... Forcing constant (0.0f,0.0f,-1.0f)."));
|
|
return Compiler->Constant3(0.0f,0.0f,-1.0f);
|
|
}
|
|
|
|
virtual int32 TransformVector(EMaterialCommonBasis SourceCoordBasis, EMaterialCommonBasis DestCoordBasis, FTransformParameters& Parameters, int32 A) override
|
|
{
|
|
//UE_LOG(LogLightmassRender, Log, TEXT("Lightmass material compiler has encountered TransformVector... Passing thru source vector untouched."));
|
|
return A;
|
|
}
|
|
|
|
virtual int32 TransformPosition(EMaterialCommonBasis SourceCoordBasis, EMaterialCommonBasis DestCoordBasis, FTransformParameters& Parameters, int32 A) override
|
|
{
|
|
//UE_LOG(LogLightmassRender, Log, TEXT("Lightmass material compiler has encountered TransformPosition... Passing thru source vector untouched."));
|
|
return A;
|
|
}
|
|
|
|
virtual int32 VertexColor() override
|
|
{
|
|
//UE_LOG(LogLightmassRender, Log, TEXT("Lightmass material compiler has encountered VertexColor... Forcing constant (1.0f,1.0f,1.0f,1.0f)."));
|
|
return Compiler->Constant4(1.0f,1.0f,1.0f,1.0f);
|
|
}
|
|
|
|
virtual int32 PreSkinnedPosition() override
|
|
{
|
|
return Compiler->Constant3(0.f,0.f,0.f);
|
|
}
|
|
|
|
virtual int32 PreSkinnedNormal() override
|
|
{
|
|
return Compiler->Constant3(0.f,0.f,1.f);
|
|
}
|
|
|
|
virtual int32 VertexInterpolator(uint32 InterpolatorIndex) override
|
|
{
|
|
return Compiler->VertexInterpolator(InterpolatorIndex);
|
|
}
|
|
|
|
virtual int32 RealTime(bool bPeriodic, float Period) override
|
|
{
|
|
//UE_LOG(LogLightmassRender, Log, TEXT("Lightmass material compiler has encountered RealTime... Forcing constant 0.0f."));
|
|
return Compiler->Constant(0.0f);
|
|
}
|
|
|
|
virtual int32 GameTime(bool bPeriodic, float Period) override
|
|
{
|
|
//UE_LOG(LogLightmassRender, Log, TEXT("Lightmass material compiler has encountered GameTime... Forcing constant 0.0f."));
|
|
return Compiler->Constant(0.0f);
|
|
}
|
|
|
|
virtual int32 DecalColor() override
|
|
{
|
|
return Compiler->Constant4(1.0f, 1.0f, 1.0f, 1.0f);
|
|
}
|
|
|
|
virtual int32 DecalLifetimeOpacity() override
|
|
{
|
|
return Compiler->Constant(0.0f);
|
|
}
|
|
|
|
virtual int32 GIReplace(int32 Direct, int32 StaticIndirect, int32 DynamicIndirect) override { return StaticIndirect; }
|
|
|
|
virtual EMaterialCompilerType GetCompilerType() const override { return EMaterialCompilerType::Lightmass; }
|
|
|
|
#if WITH_EDITOR
|
|
virtual int32 MaterialBakingWorldPosition() override
|
|
{
|
|
return Compiler->MaterialBakingWorldPosition();
|
|
}
|
|
#endif
|
|
};
|
|
|
|
/**
|
|
* Class for rendering previews of material expressions in the material editor's linked object viewport.
|
|
*/
|
|
class FLightmassMaterialProxy : public FMaterial, public FMaterialRenderProxy
|
|
{
|
|
public:
|
|
FLightmassMaterialProxy(): FMaterial(), FMaterialRenderProxy(TEXT("FLightmassMaterialProxy"))
|
|
{
|
|
SetQualityLevelProperties(GMaxRHIFeatureLevel);
|
|
}
|
|
|
|
/** Initializes the material proxy and kicks off async shader compiling. */
|
|
void BeginCompiling(UMaterialInterface* InMaterialInterface, EMaterialProperty InPropertyToCompile, EMaterialShaderMapUsage::Type InUsage)
|
|
{
|
|
if (InMaterialInterface)
|
|
{
|
|
bSubstrateEnabled = Substrate::IsSubstrateEnabled();
|
|
MaterialInterface = InMaterialInterface;
|
|
Material = MaterialInterface ? MaterialInterface->GetMaterial() : NULL;
|
|
PropertyToCompile = InPropertyToCompile;
|
|
Usage = InUsage;
|
|
|
|
ReferencedTextures = MaterialInterface->GetReferencedTextures();
|
|
ReferencedTextureCollections = MaterialInterface->GetReferencedTextureCollections();
|
|
|
|
FMaterialResource* Resource = InMaterialInterface->GetMaterialResource(GMaxRHIFeatureLevel);
|
|
if (Resource)
|
|
{
|
|
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
|
|
ResourceId.Usage = GetShaderMapUsage();
|
|
CacheShaders(ResourceId, GMaxRHIShaderPlatform);
|
|
}
|
|
}
|
|
}
|
|
|
|
virtual TArrayView<const TObjectPtr<UObject>> GetReferencedTextures() const override
|
|
{
|
|
return ReferencedTextures;
|
|
}
|
|
|
|
virtual TConstArrayView<TObjectPtr<UTextureCollection>> GetReferencedTextureCollections() const override
|
|
{
|
|
return ReferencedTextureCollections;
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
{
|
|
if (VertexFactoryType == FindVertexFactoryType(FName(TEXT("FLocalVertexFactory"), FNAME_Find)))
|
|
{
|
|
// we only need the non-light-mapped, base pass, local vertex factory shaders for drawing an opaque Material Tile
|
|
// @todo: Added a FindShaderType by fname or something"
|
|
|
|
if(FCString::Stristr(ShaderType->GetName(), TEXT("BasePassVSFNoLightMapPolicy")))
|
|
{
|
|
return true;
|
|
}
|
|
else if(FCString::Stristr(ShaderType->GetName(), TEXT("Simple")))
|
|
{
|
|
return true;
|
|
}
|
|
else if(FCString::Stristr(ShaderType->GetName(), TEXT("BasePassPSFNoLightMapPolicy")))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
////////////////
|
|
// 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);
|
|
|
|
int32 Ret = CompilePropertyAndSetMaterialPropertyWithoutCast(Property, Compiler);
|
|
|
|
return Compiler->ForceCast(Ret, FMaterialAttributeDefinitionMap::GetValueType(Property));
|
|
}
|
|
|
|
/** helper for CompilePropertyAndSetMaterialProperty() */
|
|
int32 CompilePropertyAndSetMaterialPropertyWithoutCast(EMaterialProperty Property, FMaterialCompiler* Compiler) const
|
|
{
|
|
// MAKE SURE THIS MATCHES THE CHART IN WillFillData
|
|
// RETURNED VALUES (F16 'textures')
|
|
// BLEND MODE | DIFFUSE | SPECULAR | EMISSIVE | NORMAL | TRANSMISSIVE |
|
|
// ------------+-------------+--------------+-------------+-----------+---------------------------|
|
|
// Opaque | Diffuse | Spec,SpecPwr | Emissive | Normal | 0 (EMPTY) |
|
|
// Masked | Diffuse | Spec,SpecPwr | Emissive | Normal | Opacity Mask |
|
|
// Translucent | 0 (EMPTY) | 0 (EMPTY) | Emissive | 0 (EMPTY) | (Emsv | Diffuse)*Opacity |
|
|
// Additive | 0 (EMPTY) | 0 (EMPTY) | Emissive | 0 (EMPTY) | (Emsv | Diffuse)*Opacity |
|
|
// Modulative | 0 (EMPTY) | 0 (EMPTY) | Emissive | 0 (EMPTY) | Emsv | Diffuse |
|
|
// ------------+-------------+--------------+-------------+-----------+---------------------------|
|
|
|
|
const uint32 ForceCast_Exact_Replicate = MFCF_ForceCast | MFCF_ExactMatch | MFCF_ReplicateValue;
|
|
const EMaterialProperty DiffuseInput = MP_BaseColor;
|
|
|
|
if (bSubstrateEnabled)
|
|
{
|
|
uint8 BlendMode = static_cast<uint8>(MaterialInterface->GetBlendMode());
|
|
ESubstrateMaterialExportContext SubstrateMaterialExportContext = IsOpaqueOrMaskedBlendMode(*MaterialInterface) ? ESubstrateMaterialExportContext::SMEC_Opaque : ESubstrateMaterialExportContext::SMEC_Translucent;
|
|
|
|
if (Usage == EMaterialShaderMapUsage::LightmassExportDiffuse)
|
|
{
|
|
Compiler->SetSubstrateMaterialExportType(SME_BaseColorPostCoverage, SubstrateMaterialExportContext, BlendMode);
|
|
}
|
|
else if (Usage == EMaterialShaderMapUsage::LightmassExportNormal)
|
|
{
|
|
Compiler->SetSubstrateMaterialExportType(SME_Normal, SubstrateMaterialExportContext, BlendMode);
|
|
}
|
|
else if (Usage == EMaterialShaderMapUsage::LightmassExportOpacity)
|
|
{
|
|
Compiler->SetSubstrateMaterialExportType(SME_Transmittance, SubstrateMaterialExportContext, BlendMode);
|
|
}
|
|
else if (Usage == EMaterialShaderMapUsage::LightmassExportEmissive)
|
|
{
|
|
Compiler->SetSubstrateMaterialExportType(SME_Emissive, SubstrateMaterialExportContext, BlendMode);
|
|
}
|
|
}
|
|
|
|
if( Property == MP_EmissiveColor )
|
|
{
|
|
UMaterial* ProxyMaterial = MaterialInterface->GetMaterial();
|
|
bool bIsMaterialUnlit = MaterialInterface->GetShadingModels().IsUnlit();
|
|
const bool bIsOpaque = IsOpaqueBlendMode(*MaterialInterface);
|
|
const bool bIsMasked = IsMaskedBlendMode(*MaterialInterface);
|
|
const bool bIsModulate = IsModulateBlendMode(*MaterialInterface);
|
|
const bool bIsTranslucentOnly = IsTranslucentOnlyBlendMode(*MaterialInterface);
|
|
const bool bIsAlphaHoldout = IsAlphaHoldoutBlendMode(*MaterialInterface);
|
|
const bool bIsAdditive = IsAdditiveBlendMode(*MaterialInterface);
|
|
const bool bIsAlphaComposite = IsAlphaCompositeBlendMode(*MaterialInterface);
|
|
check(ProxyMaterial);
|
|
FLightmassMaterialCompiler ProxyCompiler(Compiler);
|
|
|
|
switch (PropertyToCompile)
|
|
{
|
|
case MP_EmissiveColor:
|
|
// Emissive is ALWAYS returned...
|
|
return Compiler->Max(MaterialInterface->CompileProperty(&ProxyCompiler,MP_EmissiveColor, ForceCast_Exact_Replicate), Compiler->Constant3(0, 0, 0));
|
|
case MP_DiffuseColor:
|
|
// Only return for Opaque and Masked...
|
|
if (bIsOpaque || bIsMasked)
|
|
{
|
|
return Compiler->Saturate(MaterialInterface->CompileProperty(&ProxyCompiler, DiffuseInput, ForceCast_Exact_Replicate));
|
|
}
|
|
break;
|
|
case MP_SpecularColor:
|
|
// Only return for Opaque and Masked...
|
|
if (bIsOpaque || bIsMasked)
|
|
{
|
|
return Compiler->AppendVector(
|
|
Compiler->Saturate(MaterialInterface->CompileProperty(&ProxyCompiler, MP_SpecularColor, ForceCast_Exact_Replicate)),
|
|
Compiler->Saturate(MaterialInterface->CompileProperty(&ProxyCompiler, MP_Roughness, MFCF_ForceCast)));
|
|
}
|
|
break;
|
|
case MP_Normal:
|
|
// Only return for Opaque and Masked...
|
|
if (bIsOpaque || bIsMasked)
|
|
{
|
|
return MaterialInterface->CompileProperty(&ProxyCompiler, MP_Normal, ForceCast_Exact_Replicate);
|
|
}
|
|
break;
|
|
|
|
case MP_Opacity:
|
|
if (bIsMasked)
|
|
{
|
|
return MaterialInterface->CompileProperty(&ProxyCompiler, MP_OpacityMask);
|
|
}
|
|
else if (IsTranslucentBlendMode(*MaterialInterface) && ProxyMaterial->GetCastShadowAsMasked())
|
|
{
|
|
return MaterialInterface->CompileProperty(&ProxyCompiler, MP_Opacity);
|
|
}
|
|
else if (bIsModulate)
|
|
{
|
|
if (bIsMaterialUnlit)
|
|
{
|
|
return MaterialInterface->CompileProperty(Compiler, MP_EmissiveColor, ForceCast_Exact_Replicate);
|
|
}
|
|
else
|
|
{
|
|
return Compiler->Saturate(MaterialInterface->CompileProperty(Compiler, DiffuseInput, ForceCast_Exact_Replicate));
|
|
}
|
|
}
|
|
else if (bIsTranslucentOnly || bIsAdditive || bIsAlphaComposite || bIsAlphaHoldout)
|
|
{
|
|
int32 ColoredOpacity = INDEX_NONE;
|
|
if (bIsMaterialUnlit)
|
|
{
|
|
ColoredOpacity = MaterialInterface->CompileProperty(Compiler, MP_EmissiveColor, ForceCast_Exact_Replicate);
|
|
}
|
|
else
|
|
{
|
|
ColoredOpacity = Compiler->Saturate(MaterialInterface->CompileProperty(Compiler, DiffuseInput, ForceCast_Exact_Replicate));
|
|
}
|
|
return Compiler->Lerp(Compiler->Constant3(1.0f, 1.0f, 1.0f), ColoredOpacity, Compiler->Saturate(MaterialInterface->CompileProperty(&ProxyCompiler,MP_Opacity,MFCF_ForceCast)));
|
|
}
|
|
break;
|
|
case MP_ShadingModel:
|
|
return MaterialInterface->CompileProperty(&ProxyCompiler, MP_ShadingModel);
|
|
case MP_FrontMaterial:
|
|
if (bSubstrateEnabled)
|
|
{
|
|
// 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_PixelDepthOffset || 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_ShadingModel)
|
|
{
|
|
return MaterialInterface->CompileProperty(Compiler, MP_ShadingModel); // useless with Substrate
|
|
}
|
|
// When using Substrate, we need to actually compile more root node inputs,
|
|
// and then we handle what needs to actually be exported from the shader code (see SUBSTRATE_MATERIAL_EXPORT_TYPE).
|
|
else if (Property == MP_OpacityMask)
|
|
{
|
|
return MaterialInterface->CompileProperty(Compiler, MP_OpacityMask);
|
|
}
|
|
else if (Property == MP_SurfaceThickness)
|
|
{
|
|
return MaterialInterface->CompileProperty(Compiler, MP_SurfaceThickness);
|
|
}
|
|
else if (Property == MP_FrontMaterial)
|
|
{
|
|
if (bSubstrateEnabled)
|
|
{
|
|
return MaterialInterface->CompileProperty(Compiler, MP_FrontMaterial);
|
|
}
|
|
else
|
|
{
|
|
return Compiler->SubstrateCreateAndRegisterNullMaterial();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
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.
|
|
* For example lightmass exports diffuse and emissive, each of which requires a material resource with the same base material.
|
|
*/
|
|
virtual EMaterialShaderMapUsage::Type GetShaderMapUsage() const override { return Usage; }
|
|
|
|
virtual FString GetMaterialUsageDescription() const override { return FString::Printf(TEXT("%s FLightmassMaterialRenderer"), MaterialInterface ? *MaterialInterface->GetName() : TEXT("NULL")); }
|
|
|
|
virtual EMaterialDomain GetMaterialDomain() const override
|
|
{
|
|
if (Material)
|
|
{
|
|
return Material->MaterialDomain;
|
|
}
|
|
return MD_Surface;
|
|
}
|
|
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
|
|
{
|
|
return Material && Material->MaterialDomain == MD_DeferredDecal;
|
|
}
|
|
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 false; }
|
|
virtual enum EBlendMode GetBlendMode() const override { return BLEND_Opaque; }
|
|
virtual enum ERefractionMode GetRefractionMode() const override { return Material ? (ERefractionMode)Material->RefractionMethod : RM_None; }
|
|
virtual bool GetRootNodeOverridesDefaultRefraction()const override { return Material ? Material->bRootNodeOverridesDefaultDistortion : 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("FLightmassMaterialRenderer %s"), MaterialInterface ? *MaterialInterface->GetName() : TEXT("NULL")); }
|
|
|
|
virtual bool IsShadingModelFromMaterialExpression() const override { return 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;
|
|
}
|
|
/**
|
|
* 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 FLightmassMaterialProxy::GetShaderMapUsage() allows this to work
|
|
return Material->StateId;
|
|
}
|
|
|
|
virtual UMaterialInterface* GetMaterialInterface() const override
|
|
{
|
|
return MaterialInterface;
|
|
}
|
|
|
|
friend FArchive& operator<< ( FArchive& Ar, FLightmassMaterialProxy& V )
|
|
{
|
|
return Ar << V.MaterialInterface;
|
|
}
|
|
|
|
bool IsMaterialInputConnected(UMaterial* InMaterial, EMaterialProperty MaterialInput)
|
|
{
|
|
bool bConnected = false;
|
|
UMaterialEditorOnlyData* MaterialEditorOnly = InMaterial->GetEditorOnlyData();
|
|
|
|
if (bSubstrateEnabled)
|
|
{
|
|
// Material attribute do not override the FrontMaterial input
|
|
bConnected = MaterialEditorOnly->FrontMaterial.Expression != nullptr;
|
|
}
|
|
else
|
|
{
|
|
switch (MaterialInput)
|
|
{
|
|
case MP_EmissiveColor:
|
|
bConnected = MaterialEditorOnly->EmissiveColor.Expression != nullptr;
|
|
break;
|
|
case MP_DiffuseColor:
|
|
bConnected = MaterialEditorOnly->BaseColor.Expression != nullptr;
|
|
break;
|
|
case MP_SpecularColor:
|
|
bConnected = MaterialEditorOnly->Specular.Expression != nullptr;
|
|
break;
|
|
case MP_Normal:
|
|
bConnected = MaterialEditorOnly->Normal.Expression != nullptr;
|
|
break;
|
|
case MP_Opacity:
|
|
bConnected = MaterialEditorOnly->Opacity.Expression != nullptr;
|
|
break;
|
|
case MP_OpacityMask:
|
|
bConnected = MaterialEditorOnly->OpacityMask.Expression != nullptr;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Note: only checking to see whether the entire material attributes connection exists.
|
|
// This means materials using the material attributes input will export more attributes than is necessary.
|
|
bConnected = InMaterial->bUseMaterialAttributes ? MaterialEditorOnly->MaterialAttributes.Expression != NULL : bConnected;
|
|
}
|
|
|
|
return bConnected;
|
|
}
|
|
|
|
/**
|
|
* Checks if the configuration of the material proxy will generate a uniform
|
|
* value across the sampling... (Ie, nothing is hooked to the property)
|
|
*
|
|
* @param OutUniformValue The value that will be returned.
|
|
*
|
|
* @return bool true if a single value would be generated.
|
|
* false if not.
|
|
*/
|
|
bool WillGenerateUniformData(FFloat16Color& OutUniformValue)
|
|
{
|
|
// Pre-fill the value...
|
|
OutUniformValue.R = 0.0f;
|
|
OutUniformValue.G = 0.0f;
|
|
OutUniformValue.B = 0.0f;
|
|
OutUniformValue.A = 0.0f;
|
|
|
|
check(Material);
|
|
bool bExpressionIsNULL = false;
|
|
|
|
if (bSubstrateEnabled)
|
|
{
|
|
const bool bIsOpaque = IsOpaqueBlendMode(*MaterialInterface);
|
|
const bool bIsMasked = IsMaskedBlendMode(*MaterialInterface);
|
|
|
|
switch (Usage)
|
|
{
|
|
case EMaterialShaderMapUsage::LightmassExportEmissive:
|
|
bExpressionIsNULL = !IsMaterialInputConnected(Material, MP_FrontMaterial);
|
|
break;
|
|
case EMaterialShaderMapUsage::LightmassExportDiffuse:
|
|
if (bIsOpaque || bIsMasked)
|
|
{
|
|
bExpressionIsNULL = !IsMaterialInputConnected(Material, MP_FrontMaterial);
|
|
}
|
|
break;
|
|
case EMaterialShaderMapUsage::LightmassExportOpacity:
|
|
if (!bIsOpaque)
|
|
{
|
|
bExpressionIsNULL = !IsMaterialInputConnected(Material, MP_FrontMaterial);
|
|
OutUniformValue.A = 15.0f;
|
|
}
|
|
break;
|
|
case EMaterialShaderMapUsage::LightmassExportNormal:
|
|
if (bIsOpaque || bIsMasked)
|
|
{
|
|
bExpressionIsNULL = !IsMaterialInputConnected(Material, MP_FrontMaterial);
|
|
OutUniformValue.B = 1.0f; // Default normal is (0,0,1)
|
|
}
|
|
break;
|
|
default:
|
|
UE_LOG(LogLightmassRender, Error, TEXT("WillGenerateUniformData - cannot export a requested property for %s"), *(Material->GetPathName()));
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const bool bIsOpaqueOrMasked = IsOpaqueOrMaskedBlendMode(*MaterialInterface);
|
|
bool bIsMaterialUnlit = MaterialInterface->GetShadingModels().IsUnlit();
|
|
EBlendMode BlendMode = MaterialInterface->GetBlendMode();
|
|
|
|
switch (PropertyToCompile)
|
|
{
|
|
case MP_EmissiveColor:
|
|
// Emissive is ALWAYS returned...
|
|
bExpressionIsNULL = !IsMaterialInputConnected(Material, PropertyToCompile);
|
|
break;
|
|
case MP_DiffuseColor:
|
|
// Only return for Opaque and Masked...
|
|
if (bIsOpaqueOrMasked)
|
|
{
|
|
bExpressionIsNULL = !IsMaterialInputConnected(Material, PropertyToCompile);
|
|
}
|
|
break;
|
|
case MP_SpecularColor:
|
|
// Only return for Opaque and Masked...
|
|
if (bIsOpaqueOrMasked)
|
|
{
|
|
bExpressionIsNULL = !IsMaterialInputConnected(Material, PropertyToCompile);
|
|
OutUniformValue.A = 15.0f;
|
|
}
|
|
break;
|
|
case MP_Normal:
|
|
// Only return for Opaque and Masked...
|
|
if (bIsOpaqueOrMasked)
|
|
{
|
|
bExpressionIsNULL = !IsMaterialInputConnected(Material, PropertyToCompile);
|
|
OutUniformValue.B = 1.0f; // Default normal is (0,0,1)
|
|
}
|
|
break;
|
|
case MP_Opacity:
|
|
if (BlendMode == BLEND_Masked)
|
|
{
|
|
bExpressionIsNULL = !IsMaterialInputConnected(Material, MP_OpacityMask);
|
|
OutUniformValue.R = 1.0f;
|
|
OutUniformValue.G = 1.0f;
|
|
OutUniformValue.B = 1.0f;
|
|
OutUniformValue.A = 1.0f;
|
|
}
|
|
else
|
|
if ((BlendMode == BLEND_Modulate) ||
|
|
(BlendMode == BLEND_Translucent) ||
|
|
(BlendMode == BLEND_Additive) ||
|
|
(BlendMode == BLEND_AlphaComposite) ||
|
|
(BlendMode == BLEND_AlphaHoldout))
|
|
{
|
|
bool bColorInputIsNULL = false;
|
|
if (bIsMaterialUnlit)
|
|
{
|
|
bColorInputIsNULL = !IsMaterialInputConnected(Material, MP_EmissiveColor);
|
|
}
|
|
else
|
|
{
|
|
bColorInputIsNULL = !IsMaterialInputConnected(Material, MP_DiffuseColor);
|
|
}
|
|
if (BlendMode == BLEND_Translucent
|
|
|| BlendMode == BLEND_Additive
|
|
|| BlendMode == BLEND_AlphaComposite
|
|
|| BlendMode == BLEND_AlphaHoldout)
|
|
{
|
|
bExpressionIsNULL = bColorInputIsNULL && !IsMaterialInputConnected(Material, PropertyToCompile);
|
|
}
|
|
else
|
|
{
|
|
bExpressionIsNULL = bColorInputIsNULL;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return bExpressionIsNULL;
|
|
}
|
|
|
|
/**
|
|
* Retrieves the desired render target format and size for the given property.
|
|
* This will allow for overriding the format and/or size based on the material and property of interest.
|
|
*
|
|
* @param InMaterialProperty The material property that is going to be captured in the render target.
|
|
* @param OutFormat The format the render target should use.
|
|
* @param OutSizeX The width to use for the render target.
|
|
* @param OutSizeY The height to use for the render target.
|
|
*
|
|
* @return bool true if data is good, false if not (do not create render target)
|
|
*/
|
|
bool GetRenderTargetFormatAndSize(EMaterialProperty InMaterialProperty, EPixelFormat& OutFormat, float SizeScale, int32& OutSizeX, int32& OutSizeY)
|
|
{
|
|
OutFormat = PF_FloatRGBA;
|
|
|
|
int32 GlobalSize = 0;
|
|
// For now, just look them up in the config file...
|
|
if (InMaterialProperty == MP_DiffuseColor)
|
|
{
|
|
verify(GConfig->GetInt(TEXT("DevOptions.StaticLightingMaterial"), TEXT("DiffuseSampleSize"), GlobalSize, GLightmassIni));
|
|
}
|
|
else
|
|
if (InMaterialProperty == MP_SpecularColor)
|
|
{
|
|
verify(GConfig->GetInt(TEXT("DevOptions.StaticLightingMaterial"), TEXT("SpecularSampleSize"), GlobalSize, GLightmassIni));
|
|
}
|
|
else
|
|
if (InMaterialProperty == MP_EmissiveColor)
|
|
{
|
|
verify(GConfig->GetInt(TEXT("DevOptions.StaticLightingMaterial"), TEXT("EmissiveSampleSize"), GlobalSize, GLightmassIni));
|
|
}
|
|
else
|
|
if (InMaterialProperty == MP_Normal)
|
|
{
|
|
verify(GConfig->GetInt(TEXT("DevOptions.StaticLightingMaterial"), TEXT("NormalSampleSize"), GlobalSize, GLightmassIni));
|
|
}
|
|
else
|
|
if (InMaterialProperty == MP_Opacity)
|
|
{
|
|
verify(GConfig->GetInt(TEXT("DevOptions.StaticLightingMaterial"), TEXT("TransmissionSampleSize"), GlobalSize, GLightmassIni));
|
|
}
|
|
else
|
|
{
|
|
OutSizeX = 0;
|
|
OutSizeY = 0;
|
|
return false;
|
|
}
|
|
OutSizeX = OutSizeY = FMath::TruncToInt(GlobalSize * SizeScale);
|
|
return true;
|
|
}
|
|
|
|
static bool WillFillData(EBlendMode InBlendMode, EMaterialProperty InMaterialProperty, bool bSubstrateEnabled)
|
|
{
|
|
// MAKE SURE THIS MATCHES THE CHART IN CompileProperty
|
|
// RETURNED VALUES (F16 'textures')
|
|
// BLEND MODE | DIFFUSE | SPECULAR | EMISSIVE | NORMAL | TRANSMISSIVE |
|
|
// ------------+-------------+--------------+-------------+-----------+---------------------------|
|
|
// Opaque | Diffuse | Spec,SpecPwr | Emissive | Normal | 0 (EMPTY) |
|
|
// Masked | Diffuse | Spec,SpecPwr | Emissive | Normal | Opacity Mask |
|
|
// Translucent | 0 (EMPTY) | 0 (EMPTY) | Emissive | 0 (EMPTY) | (Emsv | Diffuse)*Opacity |
|
|
// Additive | 0 (EMPTY) | 0 (EMPTY) | Emissive | 0 (EMPTY) | (Emsv | Diffuse)*Opacity |
|
|
// Modulative | 0 (EMPTY) | 0 (EMPTY) | Emissive | 0 (EMPTY) | Emsv | Diffuse |
|
|
// ------------+-------------+--------------+-------------+-----------+---------------------------|
|
|
|
|
// Emissive will always fill data.
|
|
if (InMaterialProperty == MP_EmissiveColor)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (bSubstrateEnabled)
|
|
{
|
|
switch (InMaterialProperty)
|
|
{
|
|
case MP_DiffuseColor:
|
|
{
|
|
return InBlendMode == BLEND_Opaque || InBlendMode == BLEND_Masked;
|
|
}
|
|
case MP_Normal:
|
|
{
|
|
return InBlendMode == BLEND_Opaque || InBlendMode == BLEND_Masked;
|
|
}
|
|
case MP_Opacity:
|
|
{
|
|
return InBlendMode != BLEND_Opaque;
|
|
}
|
|
default:
|
|
{
|
|
UE_LOG(LogLightmassRender, Error, TEXT("FLightmassMaterialProxy::WillFillData - cannot export a requested property for"));
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (InBlendMode)
|
|
{
|
|
case BLEND_Opaque:
|
|
{
|
|
switch (InMaterialProperty)
|
|
{
|
|
case MP_DiffuseColor: return true;
|
|
case MP_SpecularColor: return true;
|
|
case MP_Normal: return true;
|
|
case MP_Opacity: return false;
|
|
}
|
|
}
|
|
break;
|
|
case BLEND_Masked:
|
|
{
|
|
switch (InMaterialProperty)
|
|
{
|
|
case MP_DiffuseColor: return true;
|
|
case MP_SpecularColor: return true;
|
|
case MP_Normal: return true;
|
|
case MP_Opacity: return true;
|
|
}
|
|
}
|
|
break;
|
|
case BLEND_Translucent:
|
|
case BLEND_Additive:
|
|
case BLEND_AlphaComposite:
|
|
case BLEND_AlphaHoldout:
|
|
{
|
|
switch (InMaterialProperty)
|
|
{
|
|
case MP_DiffuseColor: return false;
|
|
case MP_SpecularColor: return false;
|
|
case MP_Normal: return false;
|
|
case MP_Opacity: return true;
|
|
}
|
|
}
|
|
break;
|
|
case BLEND_Modulate:
|
|
{
|
|
switch (InMaterialProperty)
|
|
{
|
|
case MP_DiffuseColor: return false;
|
|
case MP_SpecularColor: return false;
|
|
case MP_Normal: return false;
|
|
case MP_Opacity: return true;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
virtual void GatherExpressionsForCustomInterpolators(TArray<UMaterialExpression*>& OutExpressions) const override
|
|
{
|
|
if (Material)
|
|
{
|
|
Material->GetAllExpressionsForCustomInterpolators(OutExpressions);
|
|
}
|
|
}
|
|
|
|
virtual void GetStaticParameterSet(EShaderPlatform Platform, FStaticParameterSet& OutSet) const override
|
|
{
|
|
if (const FMaterialResource* Resource = MaterialInterface->GetMaterialResource(GMaxRHIFeatureLevel))
|
|
{
|
|
Resource->GetStaticParameterSet(Platform, OutSet);
|
|
}
|
|
}
|
|
|
|
virtual bool CheckInValidStateForCompilation(class FMaterialCompiler* Compiler) const override
|
|
{
|
|
return Material && Material->CheckInValidStateForCompilation(Compiler);
|
|
}
|
|
|
|
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;
|
|
/** If Substrate is enabled, we need to specify things differently since redirection cannot straiforwardly be used*/
|
|
bool bSubstrateEnabled;
|
|
};
|
|
|
|
FMaterialExportDataEntry::~FMaterialExportDataEntry()
|
|
{
|
|
FLightmassMaterialProxy* LocalDiffuseMaterialProxy = DiffuseMaterialProxy;
|
|
FLightmassMaterialProxy* LocalEmissiveMaterialProxy = EmissiveMaterialProxy;
|
|
FLightmassMaterialProxy* LocalOpacityMaterialProxy = OpacityMaterialProxy;
|
|
FLightmassMaterialProxy* LocalNormalMaterialProxy = NormalMaterialProxy;
|
|
|
|
if (LocalDiffuseMaterialProxy )
|
|
{
|
|
FMaterial::DeferredDelete(LocalDiffuseMaterialProxy);
|
|
}
|
|
|
|
if (LocalEmissiveMaterialProxy)
|
|
{
|
|
FMaterial::DeferredDelete(LocalEmissiveMaterialProxy);
|
|
}
|
|
|
|
if (LocalOpacityMaterialProxy)
|
|
{
|
|
FMaterial::DeferredDelete(LocalOpacityMaterialProxy);
|
|
}
|
|
|
|
if (LocalNormalMaterialProxy)
|
|
{
|
|
FMaterial::DeferredDelete(LocalNormalMaterialProxy);
|
|
}
|
|
}
|
|
|
|
//
|
|
// FLightmassMaterialRenderer
|
|
//
|
|
FLightmassMaterialRenderer::~FLightmassMaterialRenderer()
|
|
{
|
|
if (!GExitPurge && RenderTarget)
|
|
{
|
|
RenderTarget->RemoveFromRoot();
|
|
}
|
|
RenderTarget = NULL;
|
|
delete Canvas;
|
|
Canvas = NULL;
|
|
}
|
|
|
|
void FLightmassMaterialRenderer::BeginGenerateMaterialData(
|
|
UMaterialInterface* InMaterial,
|
|
bool bInWantNormals,
|
|
const FString& ChannelName,
|
|
TMap<UMaterialInterface*, FMaterialExportDataEntry>& MaterialExportData)
|
|
{
|
|
UMaterial* BaseMaterial = InMaterial->GetMaterial();
|
|
|
|
EBlendMode BlendMode = InMaterial->GetBlendMode();
|
|
|
|
const bool bIsLandscapeMaterial = InMaterial->IsA<ULandscapeMaterialInstanceConstant>();
|
|
|
|
if (BaseMaterial)
|
|
{
|
|
check(!MaterialExportData.Contains(InMaterial));
|
|
const bool bSubstrateEnabled = Substrate::IsSubstrateEnabled();
|
|
|
|
FMaterialExportDataEntry& MaterialData = MaterialExportData.Add(InMaterial, FMaterialExportDataEntry(ChannelName));
|
|
|
|
if (FLightmassMaterialProxy::WillFillData(BlendMode, MP_DiffuseColor, bSubstrateEnabled))
|
|
{
|
|
MaterialData.DiffuseMaterialProxy = new FLightmassMaterialProxy();
|
|
MaterialData.DiffuseMaterialProxy->BeginCompiling(InMaterial, MP_DiffuseColor, EMaterialShaderMapUsage::LightmassExportDiffuse);
|
|
}
|
|
|
|
if (FLightmassMaterialProxy::WillFillData(BlendMode, MP_EmissiveColor, bSubstrateEnabled))
|
|
{
|
|
MaterialData.EmissiveMaterialProxy = new FLightmassMaterialProxy();
|
|
MaterialData.EmissiveMaterialProxy->BeginCompiling(InMaterial, MP_EmissiveColor, EMaterialShaderMapUsage::LightmassExportEmissive);
|
|
}
|
|
|
|
if (FLightmassMaterialProxy::WillFillData(BlendMode, MP_Opacity, bSubstrateEnabled))
|
|
{
|
|
// Landscape opacity is generated from the hole mask, not the material
|
|
if (!bIsLandscapeMaterial)
|
|
{
|
|
MaterialData.OpacityMaterialProxy = new FLightmassMaterialProxy();
|
|
MaterialData.OpacityMaterialProxy->BeginCompiling(InMaterial, MP_Opacity, EMaterialShaderMapUsage::LightmassExportOpacity);
|
|
}
|
|
}
|
|
|
|
if (bInWantNormals && FLightmassMaterialProxy::WillFillData(BlendMode, MP_Normal, bSubstrateEnabled))
|
|
{
|
|
MaterialData.NormalMaterialProxy = new FLightmassMaterialProxy();
|
|
MaterialData.NormalMaterialProxy->BeginCompiling(InMaterial, MP_Normal, EMaterialShaderMapUsage::LightmassExportNormal);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate the required material data for the given material.
|
|
*
|
|
* @param Material The material of interest.
|
|
* @param bInWantNormals True if normals should be generated as well
|
|
* @param MaterialEmissive The emissive samples for the material.
|
|
* @param MaterialDiffuse The diffuse samples for the material.
|
|
* @param MaterialTransmission The transmission samples for the material.
|
|
*
|
|
* @return bool true if successful, false if not.
|
|
*/
|
|
bool FLightmassMaterialRenderer::GenerateMaterialData(
|
|
FSceneInterface* InSceneInterface,
|
|
UMaterialInterface& InMaterial,
|
|
const FLightmassMaterialExportSettings& InExportSettings,
|
|
Lightmass::FMaterialData& OutMaterialData,
|
|
FMaterialExportDataEntry& MaterialExportEntry,
|
|
TArray<FFloat16Color>& OutMaterialDiffuse,
|
|
TArray<FFloat16Color>& OutMaterialEmissive,
|
|
TArray<FFloat16Color>& OutMaterialTransmission,
|
|
TArray<FFloat16Color>& OutMaterialNormal)
|
|
{
|
|
bool bResult = true;
|
|
UMaterial* BaseMaterial = InMaterial.GetMaterial();
|
|
check(BaseMaterial);
|
|
|
|
EBlendMode BlendMode = InMaterial.GetBlendMode();
|
|
const bool bSubstrateEnabled = Substrate::IsSubstrateEnabled();
|
|
|
|
FMaterialShadingModelField ShadingModels = InMaterial.GetShadingModels();
|
|
if (!bSubstrateEnabled && // Shading models are irrelevant when using Substrate
|
|
!ShadingModels.HasShadingModel(MSM_DefaultLit) &&
|
|
!ShadingModels.HasShadingModel(MSM_Unlit) &&
|
|
!ShadingModels.HasShadingModel(MSM_Subsurface) &&
|
|
!ShadingModels.HasShadingModel(MSM_PreintegratedSkin) &&
|
|
!ShadingModels.HasShadingModel(MSM_SubsurfaceProfile))
|
|
{
|
|
UE_LOG(LogLightmassRender, Warning, TEXT("LIGHTMASS: Material has an unsupported shading model: %d on %s"),
|
|
(int32)ShadingModels.GetShadingModelField(),
|
|
*(InMaterial.GetPathName()));
|
|
}
|
|
|
|
// Set the blend mode
|
|
static_assert(EBlendMode::BLEND_MAX == (EBlendMode)Lightmass::BLEND_MAX, "Debug type sizes must match.");
|
|
OutMaterialData.BlendMode = (Lightmass::EBlendMode)((int32)BlendMode);
|
|
|
|
// Set the two-sided flag
|
|
OutMaterialData.bTwoSided = (uint32)InMaterial.IsTwoSided();
|
|
OutMaterialData.bIsThinSurface = (uint32)InMaterial.IsThinSurface();
|
|
OutMaterialData.OpacityMaskClipValue = InMaterial.GetOpacityMaskClipValue();
|
|
// Cast shadow as masked feature need to access transmission texture. Only allow
|
|
// if transmission/opacity data exists
|
|
OutMaterialData.bCastShadowAsMasked = MaterialExportEntry.OpacityMaterialProxy
|
|
&& InMaterial.GetCastShadowAsMasked();
|
|
OutMaterialData.bSurfaceDomain = BaseMaterial->MaterialDomain == MD_Surface;
|
|
|
|
const bool bIsLandscapeMaterial = InMaterial.IsA<ULandscapeMaterialInstanceConstant>();
|
|
|
|
// due to landscape using an expanded mesh, we have to mask out the edge data even on opaque components (sigh)
|
|
const bool bIsOpaque = OutMaterialData.BlendMode == Lightmass::BLEND_Opaque;
|
|
if (bIsLandscapeMaterial && bIsOpaque)
|
|
{
|
|
OutMaterialData.BlendMode = Lightmass::BLEND_Masked;
|
|
}
|
|
|
|
// Diffuse
|
|
if (MaterialExportEntry.DiffuseMaterialProxy)
|
|
{
|
|
if (!GenerateMaterialPropertyData(InSceneInterface, InMaterial, InExportSettings, MaterialExportEntry.DiffuseMaterialProxy, MP_DiffuseColor, OutMaterialData.DiffuseSize, OutMaterialData.DiffuseSize, OutMaterialDiffuse))
|
|
{
|
|
UE_LOG(LogLightmassRender, Warning, TEXT("Failed to generate diffuse material samples for %s: %s"),
|
|
*(InMaterial.GetLightingGuid().ToString()), *(InMaterial.GetPathName()));
|
|
bResult = false;
|
|
OutMaterialData.DiffuseSize = 0;
|
|
}
|
|
}
|
|
|
|
// Emissive
|
|
if (MaterialExportEntry.EmissiveMaterialProxy)
|
|
{
|
|
if (!GenerateMaterialPropertyData(InSceneInterface, InMaterial, InExportSettings, MaterialExportEntry.EmissiveMaterialProxy, MP_EmissiveColor, OutMaterialData.EmissiveSize, OutMaterialData.EmissiveSize, OutMaterialEmissive))
|
|
{
|
|
UE_LOG(LogLightmassRender, Warning, TEXT("Failed to generate emissive material samples for %s: %s"),
|
|
*(InMaterial.GetLightingGuid().ToString()), *(InMaterial.GetPathName()));
|
|
bResult = false;
|
|
OutMaterialData.EmissiveSize = 0;
|
|
}
|
|
}
|
|
|
|
// Transmission
|
|
// Landscape opacity is generated from the hole mask, not the material
|
|
if (MaterialExportEntry.OpacityMaterialProxy || bIsLandscapeMaterial)
|
|
{
|
|
if (!GenerateMaterialPropertyData(InSceneInterface, InMaterial, InExportSettings, MaterialExportEntry.OpacityMaterialProxy, MP_Opacity, OutMaterialData.TransmissionSize, OutMaterialData.TransmissionSize, OutMaterialTransmission))
|
|
{
|
|
UE_LOG(LogLightmassRender, Warning, TEXT("Failed to generate transmissive material samples for %s: %s"),
|
|
*(InMaterial.GetLightingGuid().ToString()), *(InMaterial.GetPathName()));
|
|
bResult = false;
|
|
OutMaterialData.TransmissionSize = 0;
|
|
}
|
|
}
|
|
|
|
// Normal
|
|
if (MaterialExportEntry.NormalMaterialProxy)
|
|
{
|
|
if (!GenerateMaterialPropertyData(InSceneInterface, InMaterial, InExportSettings, MaterialExportEntry.NormalMaterialProxy, MP_Normal, OutMaterialData.NormalSize, OutMaterialData.NormalSize, OutMaterialNormal))
|
|
{
|
|
UE_LOG(LogLightmassRender, Warning, TEXT("Failed to generate normal material samples for %s: %s"),
|
|
*(InMaterial.GetLightingGuid().ToString()), *(InMaterial.GetPathName()));
|
|
bResult = false;
|
|
OutMaterialData.NormalSize = 0;
|
|
}
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
void LightmassDebugExportMaterial(UMaterialInterface& InMaterial, EMaterialProperty InMaterialProperty, FFloat16Color* InMaterialSamples, int32 InSizeX, int32 InSizeY)
|
|
{
|
|
TArray<FColor> OutputBuffer;
|
|
OutputBuffer.Empty(InSizeX * InSizeY);
|
|
bool bSRGB = InMaterialProperty != MP_Normal;
|
|
for (int32 i = 0; i < InSizeX * InSizeY; ++i)
|
|
{
|
|
FLinearColor LinearColor(InMaterialSamples[i]);
|
|
OutputBuffer.Add(LinearColor.ToFColor(bSRGB));
|
|
}
|
|
|
|
// Create screenshot folder if not already present.
|
|
// Save the contents of the array to a bitmap file.
|
|
FString TempPath = FPaths::ScreenShotDir();
|
|
TempPath += TEXT("/Materials");
|
|
IFileManager::Get().MakeDirectory(*TempPath, true);
|
|
FString TempName = InMaterial.GetPathName();
|
|
TempName = TempName.Replace(TEXT("."), TEXT("_"));
|
|
TempName = TempName.Replace(TEXT(":"), TEXT("_"));
|
|
FString OutputName = TempPath / TempName;
|
|
OutputName += TEXT("_");
|
|
switch (InMaterialProperty)
|
|
{
|
|
case MP_DiffuseColor: OutputName += TEXT("Diffuse"); break;
|
|
case MP_EmissiveColor: OutputName += TEXT("Emissive"); break;
|
|
case MP_SpecularColor: OutputName += TEXT("Specular"); break;
|
|
case MP_Normal: OutputName += TEXT("Normal"); break;
|
|
case MP_Opacity: OutputName += TEXT("Transmissive"); break;
|
|
}
|
|
OutputName += TEXT(".BMP");
|
|
FFileHelper::CreateBitmap(*OutputName, InSizeX, InSizeY, OutputBuffer.GetData());
|
|
}
|
|
|
|
/**
|
|
* Generate the material data for the given material and it's given property.
|
|
*
|
|
* @param InMaterial The material of interest.
|
|
* @param InMaterialProperty The property to generate the samples for
|
|
* @param InOutSizeX The desired X size of the sample to capture (in), the resulting size (out)
|
|
* @param InOutSizeY The desired Y size of the sample to capture (in), the resulting size (out)
|
|
* @param OutMaterialSamples The samples for the material.
|
|
*
|
|
* @return bool true if successful, false if not.
|
|
*/
|
|
bool FLightmassMaterialRenderer::GenerateMaterialPropertyData(
|
|
FSceneInterface* InSceneInterface,
|
|
UMaterialInterface& InMaterial,
|
|
const FLightmassMaterialExportSettings& InExportSettings,
|
|
FLightmassMaterialProxy* MaterialProxy,
|
|
EMaterialProperty InMaterialProperty,
|
|
int32& InOutSizeX,
|
|
int32& InOutSizeY,
|
|
TArray<FFloat16Color>& OutMaterialSamples)
|
|
{
|
|
bool bResult = true;
|
|
|
|
FFloat16Color UniformValue;
|
|
|
|
const bool bIsLandscapeMaterial = InMaterial.IsA<ULandscapeMaterialInstanceConstant>();
|
|
|
|
// Landscape opacity needs to be handled specially because it needs to look at the neighbor components
|
|
// trying to actually use the neighbor materials is all but impossible so we read the data from the hole mask ourself
|
|
if (bIsLandscapeMaterial && InMaterialProperty == MP_Opacity)
|
|
{
|
|
auto* LandscapeMesh = static_cast<const FLandscapeStaticLightingMesh*>(InExportSettings.UnwrapMesh);
|
|
GetLandscapeOpacityData(LandscapeMesh, InOutSizeX, InOutSizeY, OutMaterialSamples);
|
|
|
|
if (GLightmassDebugOptions.bDebugMaterials == true)
|
|
{
|
|
LightmassDebugExportMaterial(InMaterial, InMaterialProperty, OutMaterialSamples.GetData(), InOutSizeX, InOutSizeY);
|
|
}
|
|
}
|
|
else if (MaterialProxy->WillGenerateUniformData(UniformValue))
|
|
{
|
|
// Single value... fill it in.
|
|
InOutSizeX = 1;
|
|
InOutSizeY = 1;
|
|
OutMaterialSamples.Empty(1);
|
|
OutMaterialSamples.AddZeroed(1);
|
|
OutMaterialSamples[0] = UniformValue;
|
|
}
|
|
else
|
|
{
|
|
// Verify that async compiling has completed for this material
|
|
// If the ShaderMap is NULL that's because it failed to compile, which is ok as the default material will be used for exporting
|
|
check(!MaterialProxy->GetGameThreadShaderMap() || MaterialProxy->GetGameThreadShaderMap()->IsValidForRendering());
|
|
|
|
//@todo The format may be determined by the material property...
|
|
// For example, if Diffuse doesn't need to be F16 it can create a standard RGBA8 target.
|
|
EPixelFormat Format = PF_FloatRGBA;
|
|
if (MaterialProxy->GetRenderTargetFormatAndSize(InMaterialProperty, Format, InMaterial.GetExportResolutionScale(), InOutSizeX, InOutSizeY))
|
|
{
|
|
if (CreateRenderTarget(Format, InOutSizeX, InOutSizeY) == false)
|
|
{
|
|
UE_LOG(LogLightmassRender, Warning, TEXT("Failed to create %4dx%4d render target!"), InOutSizeX, InOutSizeY);
|
|
bResult = false;
|
|
}
|
|
else
|
|
{
|
|
ENQUEUE_RENDER_COMMAND(InitializeSystemTextures)(
|
|
[](FRHICommandListImmediate& RHICmdList)
|
|
{
|
|
GetRendererModule().InitializeSystemTextures(RHICmdList);
|
|
});
|
|
|
|
// Prefetch all virtual textures so that we have content available
|
|
//todo[vt]: Move this to calling function to avoid multiple prefetches
|
|
if (UseVirtualTexturing(GMaxRHIShaderPlatform))
|
|
{
|
|
const ERHIFeatureLevel::Type FeatureLevel = GMaxRHIFeatureLevel;
|
|
const FVector2D ScreenSpaceSize(InOutSizeX, InOutSizeY);
|
|
|
|
UE::RenderCommandPipe::FSyncScope SyncScope;
|
|
|
|
ENQUEUE_RENDER_COMMAND(LoadTiles)(
|
|
[FeatureLevel, ScreenSpaceSize](FRHICommandListImmediate& RHICmdList)
|
|
{
|
|
GetRendererModule().RequestVirtualTextureTiles(ScreenSpaceSize, -1);
|
|
GetRendererModule().LoadPendingVirtualTextureTiles(RHICmdList, FeatureLevel);
|
|
});
|
|
|
|
FlushRenderingCommands();
|
|
}
|
|
|
|
if (bIsLandscapeMaterial)
|
|
{
|
|
// Landscape needs special handling because it uses multiple UVs, which isn't yet supported by lightmass's regular pipeline
|
|
auto* LandscapeMesh = static_cast<const FLandscapeStaticLightingMesh*>(InExportSettings.UnwrapMesh);
|
|
RenderLandscapeMaterialForLightmass(LandscapeMesh, MaterialProxy, RenderTarget->GameThread_GetRenderTargetResource());
|
|
}
|
|
else
|
|
{
|
|
// At this point, we can't just return false at failure since we have some clean-up to do...
|
|
Canvas->SetRenderTarget_GameThread(RenderTarget->GameThread_GetRenderTargetResource());
|
|
|
|
// Clear the render target to black
|
|
// This is necessary because the below DrawTile doesn't write to the first column and first row
|
|
//@todo - figure out and fix DrawTile issues when rendering a full render target quad
|
|
Canvas->Clear(FLinearColor(0, 0, 0, 0));
|
|
FCanvasTileItem TileItem(FVector2D(0.0f, 0.0f), MaterialProxy, FVector2D(InOutSizeX, InOutSizeY));
|
|
TileItem.bFreezeTime = true;
|
|
Canvas->DrawItem(TileItem);
|
|
Canvas->Flush_GameThread();
|
|
FlushRenderingCommands();
|
|
Canvas->SetRenderTarget_GameThread(NULL);
|
|
FlushRenderingCommands();
|
|
}
|
|
|
|
// Read in the data
|
|
//@todo UE4. Check the format! RenderTarget->Format
|
|
// If we are going to allow non-F16 formats, then the storage will have to be aware of it!
|
|
if (RenderTarget->GameThread_GetRenderTargetResource()->ReadFloat16Pixels(OutMaterialSamples) == false)
|
|
{
|
|
UE_LOG(LogLightmassRender, Warning, TEXT("Failed to read Float16Pixels for 0x%08x property of %s: %s"),
|
|
(uint32)InMaterialProperty, *(InMaterial.GetLightingGuid().ToString()), *(InMaterial.GetPathName()));
|
|
bResult = false;
|
|
}
|
|
|
|
if (GLightmassDebugOptions.bDebugMaterials == true)
|
|
{
|
|
LightmassDebugExportMaterial(InMaterial, InMaterialProperty, OutMaterialSamples.GetData(), InOutSizeX, InOutSizeY);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogLightmassRender, Warning, TEXT("Failed to get render target format and size for 0x%08x property of %s: %s"),
|
|
(uint32)InMaterialProperty, *(InMaterial.GetLightingGuid().ToString()), *(InMaterial.GetPathName()));
|
|
bResult = false;
|
|
}
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
/**
|
|
* Create the required render target.
|
|
*
|
|
* @param InFormat The format of the render target
|
|
* @param InSizeX The X resolution of the render target
|
|
* @param InSizeY The Y resolution of the render target
|
|
*
|
|
* @return bool true if it was successful, false if not
|
|
*/
|
|
bool FLightmassMaterialRenderer::CreateRenderTarget(EPixelFormat InFormat, int32 InSizeX, int32 InSizeY)
|
|
{
|
|
if (RenderTarget &&
|
|
((RenderTarget->OverrideFormat != InFormat) || (RenderTarget->SizeX != InSizeX) || (RenderTarget->SizeY != InSizeY))
|
|
)
|
|
{
|
|
RenderTarget->RemoveFromRoot();
|
|
RenderTarget = NULL;
|
|
delete Canvas;
|
|
Canvas = NULL;
|
|
}
|
|
|
|
if (RenderTarget == NULL)
|
|
{
|
|
RenderTarget = NewObject<UTextureRenderTarget2D>();
|
|
check(RenderTarget);
|
|
RenderTarget->AddToRoot();
|
|
RenderTarget->ClearColor = FLinearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
|
RenderTarget->InitCustomFormat(InSizeX, InSizeY, InFormat, false);
|
|
|
|
Canvas = new FCanvas(RenderTarget->GameThread_GetRenderTargetResource(), NULL, FGameTime(), GMaxRHIFeatureLevel);
|
|
check(Canvas);
|
|
}
|
|
|
|
return (RenderTarget != NULL);
|
|
}
|