Files
UnrealEngineUWP/Engine/Source/Editor/UnrealEd/Private/PreviewMaterial.cpp
ben ingram 0bb0a923a0 Merging Dev-LWCRendering into Main, this includes initial work to support rendering with LWC-scale position
Basic approach is to add HLSL types FLWCScalar, FLWCMatrix, FLWCVector, etc.  Inside shaders, absolute world space position values should be represented as FLWCVector3.  Matrices that transform *into* absolute world space become FLWCMatrix.  Matrices that transform *from* world space become FLWCInverseMatrix.  Generally LWC values work by extending the regular 'float' value with an additional tile coordinate.  Final tile size will be a trade-off between scale/accuracy; I'm using 256k for now, but may need to be adjusted.  Value represented by a FLWCVector thus becomes V.Tile * TileSize + V.Offset.  Most operations can be performed directly on LWC values.  There are HLSL functions like LWCAdd, LWCSub, LWCMultiply, LWCDivide (operator overloading would be really nice here).  The goal is to stay with LWC values for as long as needed, then convert to regular float values when possible.  One thing that comes up a lot is working in translated (rather than absolute) world space.  WorldSpace + View.PrevPreViewTranslation = TranslatedWorldspace.  Except 'View.PrevPreViewTranslation' is now a FLWCVector3, and WorldSpace quantities should be as well.  So that becomes LWCAdd(WorldSpace, View.PrevPreViewTranslation) = TranslatedWorldspace.  Assuming that we're talking about a position that's "reasonably close" to the camera, it should be safe to convert the translated WS value to float.  The 'tile' coordinate of the 2 LWC values should cancel out when added together in this case.  I've done some work throughout the shader code to do this.  Materials are fully supporting LWC-values as well.  Projective texturing and vertex animation materials that I've tested work correctly even when positioned "far away" from the origin.

Lots of work remains to fully convert all of our shader code.  There's a function LWCHackToFloat(), which is a simple wrapper for LWCToFloat().  The idea of HackToFloat is to mark places that need further attention, where I'm simply converting absolute WS positions to float, to get shaders to compile.  Shaders converted in this way should continue to work for all existing content (without LWC-scale values), but they will break if positions get too large.

General overview of changed files:
LargeWorldCoordinates.ush - This defines the FLWC types and operations
GPUScene.cpp, SceneData.ush - Primitives add an extra 'float3' tile coordinate.  Instance data is unchanged, so instances need to stay within single-precision range of the primitive origin.  Could potentially split instances behind the scenes (I think) if we don't want this limitation
HLSLMaterialDerivativeAutogen.cpp, HLSLMaterialTranslator.cpp, Preshader.cpp - Translated materials to use LWC values
SceneView.cpp, SceneRelativeViewMatrices.cpp, ShaderCompiler.cpp, InstancedStereo.ush - View uniform buffer includes LWC values where appropriate
#jira UE-117101
#rb arne.schober, Michael.Galetzka

#ROBOMERGE-AUTHOR: ben.ingram
#ROBOMERGE-SOURCE: CL 17787435 in //UE5/Main/...
#ROBOMERGE-BOT: STARSHIP (Main -> Release-Engine-Test) (v881-17767770)

[CL 17787478 by ben ingram in ue5-release-engine-test branch]
2021-10-12 13:31:00 -04:00

1025 lines
35 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "MaterialEditor/PreviewMaterial.h"
#include "Modules/ModuleManager.h"
#include "MaterialEditor/DEditorParameterValue.h"
#include "MaterialEditor/DEditorFontParameterValue.h"
#include "MaterialEditor/DEditorMaterialLayersParameterValue.h"
#include "MaterialEditor/DEditorRuntimeVirtualTextureParameterValue.h"
#include "MaterialEditor/DEditorScalarParameterValue.h"
#include "MaterialEditor/DEditorStaticComponentMaskParameterValue.h"
#include "MaterialEditor/DEditorStaticSwitchParameterValue.h"
#include "MaterialEditor/DEditorTextureParameterValue.h"
#include "MaterialEditor/DEditorVectorParameterValue.h"
#include "MaterialEditor/DEditorDoubleVectorParameterValue.h"
#include "AI/NavigationSystemBase.h"
#include "MaterialEditor/MaterialEditorInstanceConstant.h"
#include "MaterialEditor/MaterialEditorPreviewParameters.h"
#include "MaterialEditor/MaterialEditorMeshComponent.h"
#include "MaterialEditorModule.h"
#include "Materials/MaterialInstance.h"
#include "Materials/MaterialInstanceConstant.h"
#include "Materials/MaterialFunctionInstance.h"
#include "Materials/MaterialExpressionScalarParameter.h"
#include "Materials/MaterialExpressionVectorParameter.h"
#include "Materials/MaterialExpressionTextureSampleParameter.h"
#include "Materials/MaterialExpressionRuntimeVirtualTextureSampleParameter.h"
#include "Materials/MaterialExpressionFontSampleParameter.h"
#include "Materials/MaterialExpressionMaterialAttributeLayers.h"
#include "Materials/MaterialExpressionStaticBoolParameter.h"
#include "Materials/MaterialExpressionStaticComponentMaskParameter.h"
#include "UObject/UObjectIterator.h"
#include "PropertyEditorDelegates.h"
#include "IDetailsView.h"
#include "MaterialEditingLibrary.h"
#include "MaterialPropertyHelpers.h"
#include "MaterialStatsCommon.h"
/**
* Class for rendering the material on the preview mesh in the Material Editor
*/
class FPreviewMaterial : public FMaterialResource
{
public:
virtual ~FPreviewMaterial()
{
}
/**
* 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
{
// only generate the needed shaders (which should be very restrictive for fast recompiling during editing)
// @todo: Add a FindShaderType by fname or something
if( Material->IsUIMaterial() )
{
if (FCString::Stristr(ShaderType->GetName(), TEXT("TSlateMaterialShaderPS")) ||
FCString::Stristr(ShaderType->GetName(), TEXT("TSlateMaterialShaderVS")))
{
return true;
}
}
if (Material->IsPostProcessMaterial())
{
if (FCString::Stristr(ShaderType->GetName(), TEXT("PostProcess")))
{
return true;
}
}
{
bool bEditorStatsMaterial = Material->bIsMaterialEditorStatsMaterial;
// Always allow HitProxy shaders.
if (FCString::Stristr(ShaderType->GetName(), TEXT("HitProxy")))
{
return true;
}
// we only need local vertex factory for the preview static mesh
if (VertexFactoryType != FindVertexFactoryType(FName(TEXT("FLocalVertexFactory"), FNAME_Find)))
{
//cache for gpu skinned vertex factory if the material allows it
//this way we can have a preview skeletal mesh
if (bEditorStatsMaterial ||
!IsUsedWithSkeletalMesh())
{
return false;
}
extern ENGINE_API bool IsGPUSkinCacheAvailable(EShaderPlatform Platform);
bool bSkinCache = IsGPUSkinCacheAvailable(Platform) && (VertexFactoryType == FindVertexFactoryType(FName(TEXT("FGPUSkinPassthroughVertexFactory"), FNAME_Find)));
if (
VertexFactoryType != FindVertexFactoryType(FName(TEXT("TGPUSkinVertexFactoryDefault"), FNAME_Find)) &&
VertexFactoryType != FindVertexFactoryType(FName(TEXT("TGPUSkinVertexFactoryUnlimited"), FNAME_Find)) &&
!bSkinCache
)
{
return false;
}
}
// Only allow shaders that are used in the stats.
if (bEditorStatsMaterial)
{
TMap<FName, TArray<FMaterialStatsUtils::FRepresentativeShaderInfo>> ShaderTypeNamesAndDescriptions;
FMaterialStatsUtils::GetRepresentativeShaderTypesAndDescriptions(ShaderTypeNamesAndDescriptions, this);
for (auto DescriptionPair : ShaderTypeNamesAndDescriptions)
{
auto &DescriptionArray = DescriptionPair.Value;
if (DescriptionArray.FindByPredicate([ShaderType = ShaderType](auto& Info) { return Info.ShaderName == ShaderType->GetFName(); }))
{
return true;
}
}
return false;
}
// look for any of the needed type
bool bShaderTypeMatches = false;
// For FMaterialResource::GetRepresentativeInstructionCounts
if (FCString::Stristr(ShaderType->GetName(), TEXT("MaterialCHSFNoLightMapPolicy")))
{
bShaderTypeMatches = true;
}
else if (FCString::Stristr(ShaderType->GetName(), TEXT("MobileDirectionalLight")))
{
bShaderTypeMatches = true;
}
else if (FCString::Stristr(ShaderType->GetName(), TEXT("MobileMovableDirectionalLight")))
{
bShaderTypeMatches = true;
}
else if (FCString::Stristr(ShaderType->GetName(), TEXT("BasePassPSTDistanceFieldShadowsAndLightMapPolicyHQ")))
{
bShaderTypeMatches = true;
}
else if (FCString::Stristr(ShaderType->GetName(), TEXT("Simple")))
{
bShaderTypeMatches = true;
}
else if (FCString::Stristr(ShaderType->GetName(), TEXT("BasePassPSFNoLightMapPolicy")))
{
bShaderTypeMatches = true;
}
else if (FCString::Stristr(ShaderType->GetName(), TEXT("CachedPointIndirectLightingPolicy")))
{
bShaderTypeMatches = true;
}
else if (FCString::Stristr(ShaderType->GetName(), TEXT("PrecomputedVolumetricLightmapLightingPolicy")))
{
bShaderTypeMatches = true;
}
else if (FCString::Stristr(ShaderType->GetName(), TEXT("BasePassPSFSelfShadowedTranslucencyPolicy")))
{
bShaderTypeMatches = true;
}
// Pick tessellation shader based on material settings
else if(FCString::Stristr(ShaderType->GetName(), TEXT("BasePassVSFNoLightMapPolicy")) ||
FCString::Stristr(ShaderType->GetName(), TEXT("BasePassHSFNoLightMapPolicy")) ||
FCString::Stristr(ShaderType->GetName(), TEXT("BasePassDSFNoLightMapPolicy")))
{
bShaderTypeMatches = true;
}
else if (FCString::Stristr(ShaderType->GetName(), TEXT("DepthOnly")))
{
bShaderTypeMatches = true;
}
else if (FCString::Stristr(ShaderType->GetName(), TEXT("ShadowDepth")))
{
bShaderTypeMatches = true;
}
else if (FCString::Stristr(ShaderType->GetName(), TEXT("Distortion")))
{
bShaderTypeMatches = true;
}
else if (FCString::Stristr(ShaderType->GetName(), TEXT("MeshDecal")))
{
bShaderTypeMatches = true;
}
else if (FCString::Stristr(ShaderType->GetName(), TEXT("TBasePassForForwardShading")))
{
bShaderTypeMatches = true;
}
else if (FCString::Stristr(ShaderType->GetName(), TEXT("FDebugViewModeVS")))
{
bShaderTypeMatches = true;
}
else if (FCString::Stristr(ShaderType->GetName(), TEXT("FVelocity")))
{
bShaderTypeMatches = true;
}
else if (FCString::Stristr(ShaderType->GetName(), TEXT("FAnisotropy")))
{
bShaderTypeMatches = true;
}
else if (FCString::Stristr(ShaderType->GetName(), TEXT("RayTracingDynamicGeometryConverter")))
{
bShaderTypeMatches = true;
}
else if (FCString::Stristr(ShaderType->GetName(), TEXT("FLumenCard")))
{
bShaderTypeMatches = true;
}
return bShaderTypeMatches;
}
}
/**
* Should shaders compiled for this material be saved to disk?
*/
virtual bool IsPersistent() const override { return false; }
virtual FString GetAssetName() const override { return FString::Printf(TEXT("Preview:%s"), *FMaterialResource::GetAssetName()); }
};
/** Implementation of Preview Material functions*/
UPreviewMaterial::UPreviewMaterial(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
FMaterialResource* UPreviewMaterial::AllocateResource()
{
return new FPreviewMaterial();
}
void UMaterialEditorPreviewParameters::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
if (PreviewMaterial && PropertyChangedEvent.ChangeType != EPropertyChangeType::Interactive)
{
FProperty* PropertyThatChanged = PropertyChangedEvent.Property;
if (OriginalFunction == nullptr)
{
CopyToSourceInstance();
PreviewMaterial->PostEditChangeProperty(PropertyChangedEvent);
}
else
{
ApplySourceFunctionChanges();
if (OriginalFunction->PreviewMaterial)
{
OriginalFunction->PreviewMaterial->PostEditChangeProperty(PropertyChangedEvent);
}
}
}
}
void UMaterialEditorPreviewParameters::AssignParameterToGroup(UDEditorParameterValue* ParameterValue, const FName& InParameterGroupName)
{
check(ParameterValue);
FName ParameterGroupName = InParameterGroupName;
if (ParameterGroupName == TEXT("") || ParameterGroupName == TEXT("None"))
{
ParameterGroupName = TEXT("None");
}
IMaterialEditorModule* MaterialEditorModule = &FModuleManager::LoadModuleChecked<IMaterialEditorModule>("MaterialEditor");
// Material layers
UDEditorMaterialLayersParameterValue* MaterialLayerParam = Cast<UDEditorMaterialLayersParameterValue>(ParameterValue);
if (ParameterValue->ParameterInfo.Association == EMaterialParameterAssociation::GlobalParameter)
{
if (MaterialLayerParam)
{
ParameterGroupName = FMaterialPropertyHelpers::LayerParamName;
}
else
{
FString AppendedGroupName = GlobalGroupPrefix.ToString();
if (ParameterGroupName != TEXT("None"))
{
ParameterGroupName.AppendString(AppendedGroupName);
ParameterGroupName = FName(*AppendedGroupName);
}
else
{
ParameterGroupName = TEXT("Global");
}
}
}
FEditorParameterGroup& CurrentGroup = FMaterialPropertyHelpers::GetParameterGroup(PreviewMaterial, ParameterGroupName, ParameterGroups);
CurrentGroup.GroupAssociation = ParameterValue->ParameterInfo.Association;
ParameterValue->SetFlags(RF_Transactional);
CurrentGroup.Parameters.Add(ParameterValue);
}
void UMaterialEditorPreviewParameters::RegenerateArrays()
{
ParameterGroups.Empty();
if (PreviewMaterial)
{
// Only operate on base materials
UMaterial* ParentMaterial = PreviewMaterial;
// This can run before UMaterial::PostEditChangeProperty has a chance to run, so explicitly call UpdateCachedExpressionData here
PreviewMaterial->UpdateCachedExpressionData();
// Loop through all types of parameters for this material and add them to the parameter arrays.
TMap<FMaterialParameterInfo, FMaterialParameterMetadata> ParameterValues;
for (int32 TypeIndex = 0; TypeIndex < NumMaterialParameterTypes; ++TypeIndex)
{
const EMaterialParameterType ParameterType = (EMaterialParameterType)TypeIndex;
ParentMaterial->GetAllParametersOfType(ParameterType, ParameterValues);
for (const auto& It : ParameterValues)
{
UDEditorParameterValue* Parameter = UDEditorParameterValue::Create(this, ParameterType, It.Key, It.Value);
Parameter->bOverride = true;
AssignParameterToGroup(Parameter, It.Value.Group);
}
}
// Static Material Layers Parameters
TArray<FMaterialParameterInfo> ParameterInfo;
TArray<FGuid> Guids;
ParentMaterial->GetAllMaterialLayersParameterInfo(ParameterInfo, Guids);
for (int32 ParameterIdx = 0; ParameterIdx < ParameterInfo.Num(); ParameterIdx++)
{
UDEditorMaterialLayersParameterValue* ParameterValue = NewObject<UDEditorMaterialLayersParameterValue>(this);
ParameterValue->bOverride = true;
ParameterValue->ParameterInfo = ParameterInfo[ParameterIdx];
ParameterValue->ExpressionId = Guids[ParameterIdx];
//get the settings from the parent in the MIC chain
FMaterialLayersFunctions Value;
if (PreviewMaterial->GetMaterialLayersParameterValue(ParameterValue->ParameterInfo, Value, ParameterValue->ExpressionId))
{
ParameterValue->ParameterValue = Value;
}
AssignParameterToGroup(ParameterValue, FName());
}
}
// sort contents of groups
for (int32 ParameterIdx = 0; ParameterIdx < ParameterGroups.Num(); ParameterIdx++)
{
FEditorParameterGroup & ParamGroup = ParameterGroups[ParameterIdx];
struct FCompareUDEditorParameterValueByParameterName
{
FORCEINLINE bool operator()(const UDEditorParameterValue& A, const UDEditorParameterValue& B) const
{
FString AName = A.ParameterInfo.Name.ToString();
FString BName = B.ParameterInfo.Name.ToString();
return A.SortPriority != B.SortPriority ? A.SortPriority < B.SortPriority : AName < BName;
}
};
ParamGroup.Parameters.Sort(FCompareUDEditorParameterValueByParameterName());
}
// sort groups itself pushing defaults to end
struct FCompareFEditorParameterGroupByName
{
FORCEINLINE bool operator()(const FEditorParameterGroup& A, const FEditorParameterGroup& B) const
{
FString AName = A.GroupName.ToString();
FString BName = B.GroupName.ToString();
if (AName == TEXT("none"))
{
return false;
}
if (BName == TEXT("none"))
{
return false;
}
return A.GroupSortPriority != B.GroupSortPriority ? A.GroupSortPriority < B.GroupSortPriority : AName < BName;
}
};
ParameterGroups.Sort(FCompareFEditorParameterGroupByName());
TArray<struct FEditorParameterGroup> ParameterDefaultGroups;
for (int32 ParameterIdx = 0; ParameterIdx < ParameterGroups.Num(); ParameterIdx++)
{
FEditorParameterGroup & ParamGroup = ParameterGroups[ParameterIdx];
if (ParamGroup.GroupName == TEXT("None"))
{
ParameterDefaultGroups.Add(ParamGroup);
ParameterGroups.RemoveAt(ParameterIdx);
break;
}
}
if (ParameterDefaultGroups.Num() > 0)
{
ParameterGroups.Append(ParameterDefaultGroups);
}
}
void UMaterialEditorPreviewParameters::CopyToSourceInstance()
{
if (PreviewMaterial->IsTemplate(RF_ClassDefaultObject) == false && OriginalMaterial != nullptr)
{
OriginalMaterial->MarkPackageDirty();
// Scalar Parameters
for (int32 GroupIdx = 0; GroupIdx < ParameterGroups.Num(); GroupIdx++)
{
FEditorParameterGroup & Group = ParameterGroups[GroupIdx];
for (int32 ParameterIdx = 0; ParameterIdx < Group.Parameters.Num(); ParameterIdx++)
{
UDEditorParameterValue* Parameter = Group.Parameters[ParameterIdx];
if (Parameter)
{
FMaterialParameterMetadata EditorValue;
if (Parameter->GetValue(EditorValue))
{
PreviewMaterial->SetParameterValueEditorOnly(Parameter->ParameterInfo.Name, EditorValue);
}
}
}
}
}
}
FName UMaterialEditorPreviewParameters::GlobalGroupPrefix = FName("Global ");
void UMaterialEditorPreviewParameters::ApplySourceFunctionChanges()
{
if (OriginalFunction != nullptr)
{
CopyToSourceInstance();
OriginalFunction->MarkPackageDirty();
// Scalar Parameters
for (int32 GroupIdx = 0; GroupIdx < ParameterGroups.Num(); GroupIdx++)
{
FEditorParameterGroup & Group = ParameterGroups[GroupIdx];
for (int32 ParameterIdx = 0; ParameterIdx < Group.Parameters.Num(); ParameterIdx++)
{
UDEditorParameterValue* Parameter = Group.Parameters[ParameterIdx];
if (Parameter)
{
FMaterialParameterMetadata EditorValue;
if (Parameter->GetValue(EditorValue))
{
OriginalFunction->SetParameterValueEditorOnly(Parameter->ParameterInfo.Name, EditorValue);
}
}
}
}
UMaterialEditingLibrary::UpdateMaterialFunction(OriginalFunction, PreviewMaterial);
}
}
#if WITH_EDITOR
void UMaterialEditorPreviewParameters::PostEditUndo()
{
Super::PostEditUndo();
}
#endif
FName UMaterialEditorInstanceConstant::GlobalGroupPrefix = FName("Global ");
UMaterialEditorInstanceConstant::UMaterialEditorInstanceConstant(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
bIsFunctionPreviewMaterial = false;
bShowOnlyOverrides = false;
}
void UMaterialEditorInstanceConstant::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
if (SourceInstance)
{
FProperty* PropertyThatChanged = PropertyChangedEvent.Property;
bool bLayersParameterChanged = false;
FNavigationLockContext NavUpdateLock(ENavigationLockReason::MaterialUpdate);
if(PropertyThatChanged && PropertyThatChanged->GetName()==TEXT("Parent") )
{
if(bIsFunctionPreviewMaterial)
{
bIsFunctionInstanceDirty = true;
ApplySourceFunctionChanges();
}
else
{
FMaterialUpdateContext Context;
UpdateSourceInstanceParent();
Context.AddMaterialInstance(SourceInstance);
// Fully update static parameters before recreating render state for all components
SetSourceInstance(SourceInstance);
}
}
else if (!bIsFunctionPreviewMaterial)
{
// If a material layers parameter changed we need to update it on the source instance
// immediately so parameters contained within the new functions can be collected
for (FEditorParameterGroup& Group : ParameterGroups)
{
for (UDEditorParameterValue* Parameter : Group.Parameters)
{
if (UDEditorMaterialLayersParameterValue* LayersParam = Cast<UDEditorMaterialLayersParameterValue>(Parameter))
{
if (SourceInstance->UpdateMaterialLayersParameterValue(LayersParam->ParameterInfo, LayersParam->ParameterValue, LayersParam->bOverride, LayersParam->ExpressionId))
{
bLayersParameterChanged = true;
}
}
}
}
if (bLayersParameterChanged)
{
RegenerateArrays();
}
}
CopyToSourceInstance(bLayersParameterChanged);
// Tell our source instance to update itself so the preview updates.
SourceInstance->PostEditChangeProperty(PropertyChangedEvent);
// Invalidate the streaming data so that it gets rebuilt.
SourceInstance->TextureStreamingData.Empty();
}
}
void UMaterialEditorInstanceConstant::AssignParameterToGroup(UDEditorParameterValue* ParameterValue, const FName& InParameterGroupName)
{
check(ParameterValue);
FName ParameterGroupName = InParameterGroupName;
if (ParameterGroupName == TEXT("") || ParameterGroupName == TEXT("None"))
{
if (bUseOldStyleMICEditorGroups == true)
{
ParameterGroupName = ParameterValue->GetDefaultGroupName();
}
else
{
ParameterGroupName = TEXT("None");
}
IMaterialEditorModule* MaterialEditorModule = &FModuleManager::LoadModuleChecked<IMaterialEditorModule>("MaterialEditor");
// Material layers
if (ParameterValue->ParameterInfo.Association == EMaterialParameterAssociation::GlobalParameter)
{
FString AppendedGroupName = GlobalGroupPrefix.ToString();
if (ParameterGroupName != TEXT("None"))
{
ParameterGroupName.AppendString(AppendedGroupName);
ParameterGroupName = FName(*AppendedGroupName);
}
else
{
ParameterGroupName = TEXT("Global");
}
}
}
FEditorParameterGroup& CurrentGroup = FMaterialPropertyHelpers::GetParameterGroup(Parent->GetMaterial(), ParameterGroupName, ParameterGroups);
CurrentGroup.GroupAssociation = ParameterValue->ParameterInfo.Association;
ParameterValue->SetFlags(RF_Transactional);
CurrentGroup.Parameters.Add(ParameterValue);
}
void UMaterialEditorInstanceConstant::RegenerateArrays()
{
VisibleExpressions.Empty();
ParameterGroups.Empty();
if (Parent)
{
// Only operate on base materials
UMaterial* ParentMaterial = Parent->GetMaterial();
SourceInstance->UpdateParameterNames(); // Update any parameter names that may have changed.
SourceInstance->UpdateCachedLayerParameters();
// Need to get layer info first as other params are collected from layers
{
TArray<FMaterialParameterInfo> OutParameterInfo;
TArray<FGuid> Guids;
SourceInstance->GetAllMaterialLayersParameterInfo(OutParameterInfo, Guids);
// Copy Static Material Layers Parameters
for (int32 ParameterIdx = 0; ParameterIdx < OutParameterInfo.Num(); ParameterIdx++)
{
FMaterialParameterInfo& ParameterInfo = OutParameterInfo[ParameterIdx];
FGuid ExpressionId = Guids[ParameterIdx];
UDEditorMaterialLayersParameterValue& ParameterValue = *(NewObject<UDEditorMaterialLayersParameterValue>(this));
ParameterValue.bOverride = true;
ParameterValue.ParameterInfo = ParameterInfo;
ParameterValue.ExpressionId = ExpressionId;
Parent->GetMaterialLayersParameterValue(ParameterInfo, ParameterValue.ParameterValue, ExpressionId);
ParameterValue.ParameterValue.LinkAllLayersToParent(); // Set parent guids for layers from parent material
// If the SourceInstance is overriding this parameter, use its settings
for (const FStaticMaterialLayersParameter& LayersParam : SourceInstance->GetStaticParameters().MaterialLayersParameters)
{
if (ParameterInfo == LayersParam.ParameterInfo)
{
ParameterValue.bOverride = LayersParam.bOverride;
if (LayersParam.bOverride)
{
ParameterValue.ParameterValue = LayersParam.Value;
}
}
}
AssignParameterToGroup(&ParameterValue, FName());
}
}
TMap<FMaterialParameterInfo, FMaterialParameterMetadata> ParameterValues;
for (int32 TypeIndex = 0; TypeIndex < NumMaterialParameterTypes; ++TypeIndex)
{
const EMaterialParameterType ParameterType = (EMaterialParameterType)TypeIndex;
SourceInstance->GetAllParametersOfType(ParameterType, ParameterValues);
for (const auto& It : ParameterValues)
{
UDEditorParameterValue* Parameter = UDEditorParameterValue::Create(this, ParameterType, It.Key, It.Value);
AssignParameterToGroup(Parameter, It.Value.Group);
}
}
IMaterialEditorModule* MaterialEditorModule = &FModuleManager::LoadModuleChecked<IMaterialEditorModule>( "MaterialEditor" );
MaterialEditorModule->GetVisibleMaterialParameters(ParentMaterial, SourceInstance, VisibleExpressions);
}
// sort contents of groups
for(int32 ParameterIdx = 0; ParameterIdx < ParameterGroups.Num(); ParameterIdx++)
{
FEditorParameterGroup & ParamGroup = ParameterGroups[ParameterIdx];
struct FCompareUDEditorParameterValueByParameterName
{
FORCEINLINE bool operator()(const UDEditorParameterValue& A, const UDEditorParameterValue& B) const
{
FString AName = A.ParameterInfo.Name.ToString();
FString BName = B.ParameterInfo.Name.ToString();
return A.SortPriority != B.SortPriority ? A.SortPriority < B.SortPriority : AName < BName;
}
};
ParamGroup.Parameters.Sort( FCompareUDEditorParameterValueByParameterName() );
}
// sort groups itself pushing defaults to end
struct FCompareFEditorParameterGroupByName
{
FORCEINLINE bool operator()(const FEditorParameterGroup& A, const FEditorParameterGroup& B) const
{
FString AName = A.GroupName.ToString();
FString BName = B.GroupName.ToString();
if (AName == TEXT("none"))
{
return false;
}
if (BName == TEXT("none"))
{
return false;
}
return A.GroupSortPriority != B.GroupSortPriority ? A.GroupSortPriority < B.GroupSortPriority : AName < BName;
}
};
ParameterGroups.Sort( FCompareFEditorParameterGroupByName() );
TArray<struct FEditorParameterGroup> ParameterDefaultGroups;
for(int32 ParameterIdx=0; ParameterIdx<ParameterGroups.Num(); ParameterIdx++)
{
FEditorParameterGroup & ParamGroup = ParameterGroups[ParameterIdx];
if (bUseOldStyleMICEditorGroups == false)
{
if (ParamGroup.GroupName == TEXT("None"))
{
ParameterDefaultGroups.Add(ParamGroup);
ParameterGroups.RemoveAt(ParameterIdx);
break;
}
}
else
{
if (ParamGroup.GroupName == TEXT("Vector Parameter Values") ||
ParamGroup.GroupName == TEXT("Scalar Parameter Values") ||
ParamGroup.GroupName == TEXT("Texture Parameter Values") ||
ParamGroup.GroupName == TEXT("Static Switch Parameter Values") ||
ParamGroup.GroupName == TEXT("Static Component Mask Parameter Values") ||
ParamGroup.GroupName == TEXT("Font Parameter Values") ||
ParamGroup.GroupName == TEXT("Material Layers Parameter Values"))
{
ParameterDefaultGroups.Add(ParamGroup);
ParameterGroups.RemoveAt(ParameterIdx);
}
}
}
if (ParameterDefaultGroups.Num() >0)
{
ParameterGroups.Append(ParameterDefaultGroups);
}
if (DetailsView.IsValid())
{
// Tell our source instance to update itself so the preview updates.
DetailsView.Pin()->ForceRefresh();
}
}
#if WITH_EDITOR
void UMaterialEditorInstanceConstant::CleanParameterStack(int32 Index, EMaterialParameterAssociation MaterialType)
{
check(GIsEditor);
TArray<FEditorParameterGroup> CleanedGroups;
for (FEditorParameterGroup Group : ParameterGroups)
{
FEditorParameterGroup DuplicatedGroup = FEditorParameterGroup();
DuplicatedGroup.GroupAssociation = Group.GroupAssociation;
DuplicatedGroup.GroupName = Group.GroupName;
DuplicatedGroup.GroupSortPriority = Group.GroupSortPriority;
for (UDEditorParameterValue* Parameter : Group.Parameters)
{
if (Parameter->ParameterInfo.Association != MaterialType
|| Parameter->ParameterInfo.Index != Index)
{
DuplicatedGroup.Parameters.Add(Parameter);
}
}
CleanedGroups.Add(DuplicatedGroup);
}
ParameterGroups = CleanedGroups;
CopyToSourceInstance(true);
}
void UMaterialEditorInstanceConstant::ResetOverrides(int32 Index, EMaterialParameterAssociation MaterialType)
{
check(GIsEditor);
for (const FEditorParameterGroup& Group : ParameterGroups)
{
for (UDEditorParameterValue* Parameter : Group.Parameters)
{
if (Parameter->ParameterInfo.Association == MaterialType
&& Parameter->ParameterInfo.Index == Index)
{
const EMaterialParameterType ParameterType = Parameter->GetParameterType();
if (ParameterType != EMaterialParameterType::None)
{
FMaterialParameterMetadata SourceValue;
if (SourceInstance->GetParameterValue(ParameterType, Parameter->ParameterInfo, SourceValue, EMaterialGetParameterValueFlags::CheckInstanceOverrides))
{
Parameter->bOverride = SourceValue.bOverride;
}
}
}
}
}
CopyToSourceInstance(true);
}
#endif
void UMaterialEditorInstanceConstant::CopyToSourceInstance(const bool bForceStaticPermutationUpdate)
{
if (SourceInstance && !SourceInstance->IsTemplate(RF_ClassDefaultObject))
{
if (bIsFunctionPreviewMaterial)
{
bIsFunctionInstanceDirty = true;
}
else
{
SourceInstance->MarkPackageDirty();
}
{
FMaterialInstanceParameterUpdateContext UpdateContext(SourceInstance, EMaterialInstanceClearParameterFlag::All);
UpdateContext.SetBasePropertyOverrides(BasePropertyOverrides);
UpdateContext.SetForceStaticPermutationUpdate(bForceStaticPermutationUpdate);
for (int32 GroupIdx = 0; GroupIdx < ParameterGroups.Num(); GroupIdx++)
{
FEditorParameterGroup& Group = ParameterGroups[GroupIdx];
for (int32 ParameterIdx = 0; ParameterIdx < Group.Parameters.Num(); ParameterIdx++)
{
UDEditorParameterValue* Parameter = Group.Parameters[ParameterIdx];
if (Parameter && Parameter->bOverride)
{
FMaterialParameterMetadata EditorValue;
if (Parameter->GetValue(EditorValue))
{
UpdateContext.SetParameterValueEditorOnly(Parameter->ParameterInfo, EditorValue, EMaterialSetParameterValueFlags::SetCurveAtlas);
}
else
{
// Material layers param
UDEditorMaterialLayersParameterValue* MaterialLayersParameterValue = Cast<UDEditorMaterialLayersParameterValue>(Parameter);
if (MaterialLayersParameterValue)
{
const FMaterialLayersFunctions& MaterialLayers = MaterialLayersParameterValue->ParameterValue;
FGuid ExpressionIdValue = MaterialLayersParameterValue->ExpressionId;
FStaticMaterialLayersParameter* NewParameter = new(UpdateContext.GetStaticParameters().MaterialLayersParameters)
FStaticMaterialLayersParameter(MaterialLayersParameterValue->ParameterInfo, MaterialLayers, MaterialLayersParameterValue->bOverride, ExpressionIdValue);
}
}
}
}
}
}
// Copy phys material back to source instance
SourceInstance->PhysMaterial = PhysMaterial;
// Copy the Lightmass settings...
SourceInstance->SetOverrideCastShadowAsMasked(LightmassSettings.CastShadowAsMasked.bOverride);
SourceInstance->SetCastShadowAsMasked(LightmassSettings.CastShadowAsMasked.ParameterValue);
SourceInstance->SetOverrideEmissiveBoost(LightmassSettings.EmissiveBoost.bOverride);
SourceInstance->SetEmissiveBoost(LightmassSettings.EmissiveBoost.ParameterValue);
SourceInstance->SetOverrideDiffuseBoost(LightmassSettings.DiffuseBoost.bOverride);
SourceInstance->SetDiffuseBoost(LightmassSettings.DiffuseBoost.ParameterValue);
SourceInstance->SetOverrideExportResolutionScale(LightmassSettings.ExportResolutionScale.bOverride);
SourceInstance->SetExportResolutionScale(LightmassSettings.ExportResolutionScale.ParameterValue);
// Copy Refraction bias setting
FMaterialParameterInfo RefractionInfo(TEXT("RefractionDepthBias"));
SourceInstance->SetScalarParameterValueEditorOnly(RefractionInfo, RefractionDepthBias);
SourceInstance->bOverrideSubsurfaceProfile = bOverrideSubsurfaceProfile;
SourceInstance->SubsurfaceProfile = SubsurfaceProfile;
// Update object references and parameter names.
SourceInstance->UpdateParameterNames();
VisibleExpressions.Empty();
// force refresh of visibility of properties
if (Parent)
{
UMaterial* ParentMaterial = Parent->GetMaterial();
IMaterialEditorModule* MaterialEditorModule = &FModuleManager::LoadModuleChecked<IMaterialEditorModule>("MaterialEditor");
MaterialEditorModule->GetVisibleMaterialParameters(ParentMaterial, SourceInstance, VisibleExpressions);
}
}
}
void UMaterialEditorInstanceConstant::ApplySourceFunctionChanges()
{
if (bIsFunctionPreviewMaterial && bIsFunctionInstanceDirty)
{
CopyToSourceInstance();
// Copy updated function parameter values
SourceFunction->ScalarParameterValues = SourceInstance->ScalarParameterValues;
SourceFunction->VectorParameterValues = SourceInstance->VectorParameterValues;
SourceFunction->TextureParameterValues = SourceInstance->TextureParameterValues;
SourceFunction->RuntimeVirtualTextureParameterValues = SourceInstance->RuntimeVirtualTextureParameterValues;
SourceFunction->FontParameterValues = SourceInstance->FontParameterValues;
const FStaticParameterSet& StaticParameters = SourceInstance->GetStaticParameters();
SourceFunction->StaticSwitchParameterValues = StaticParameters.StaticSwitchParameters;
SourceFunction->StaticComponentMaskParameterValues = StaticParameters.StaticComponentMaskParameters;
SourceFunction->MarkPackageDirty();
bIsFunctionInstanceDirty = false;
UMaterialEditingLibrary::UpdateMaterialFunction(SourceFunction, nullptr);
}
}
void UMaterialEditorInstanceConstant::SetSourceInstance(UMaterialInstanceConstant* MaterialInterface)
{
check(MaterialInterface);
SourceInstance = MaterialInterface;
Parent = SourceInstance->Parent;
PhysMaterial = SourceInstance->PhysMaterial;
CopyBasePropertiesFromParent();
RegenerateArrays();
//propagate changes to the base material so the instance will be updated if it has a static permutation resource
FMaterialInstanceParameterUpdateContext UpdateContext(SourceInstance, EMaterialInstanceClearParameterFlag::Static);
for (int32 GroupIdx = 0; GroupIdx < ParameterGroups.Num(); GroupIdx++)
{
FEditorParameterGroup& Group = ParameterGroups[GroupIdx];
for (int32 ParameterIdx = 0; ParameterIdx < Group.Parameters.Num(); ParameterIdx++)
{
UDEditorParameterValue* Parameter = Group.Parameters[ParameterIdx];
if (Parameter && Parameter->bOverride)
{
FMaterialParameterMetadata EditorValue;
if (Parameter->GetValue(EditorValue))
{
// Only want to update static parameters here
if (IsStaticMaterialParameter(EditorValue.Value.Type))
{
UpdateContext.SetParameterValueEditorOnly(Parameter->ParameterInfo, EditorValue);
}
}
else
{
// Material layers param
UDEditorMaterialLayersParameterValue* MaterialLayersParameterValue = Cast<UDEditorMaterialLayersParameterValue>(Group.Parameters[ParameterIdx]);
if (MaterialLayersParameterValue)
{
const FMaterialLayersFunctions& MaterialLayers = MaterialLayersParameterValue->ParameterValue;
FGuid ExpressionIdValue = MaterialLayersParameterValue->ExpressionId;
FStaticMaterialLayersParameter* NewParameter = new(UpdateContext.GetStaticParameters().MaterialLayersParameters)
FStaticMaterialLayersParameter(MaterialLayersParameterValue->ParameterInfo, MaterialLayers, MaterialLayersParameterValue->bOverride, ExpressionIdValue);
}
}
}
}
}
}
void UMaterialEditorInstanceConstant::SetSourceFunction(UMaterialFunctionInstance* MaterialFunction)
{
SourceFunction = MaterialFunction;
bIsFunctionPreviewMaterial = !!(SourceFunction);
}
void UMaterialEditorInstanceConstant::UpdateSourceInstanceParent()
{
// If the parent was changed to the source instance, set it to NULL
if( Parent == SourceInstance )
{
Parent = NULL;
}
SourceInstance->SetParentEditorOnly( Parent );
SourceInstance->PostEditChange();
}
void UMaterialEditorInstanceConstant::CopyBasePropertiesFromParent()
{
BasePropertyOverrides = SourceInstance->BasePropertyOverrides;
// Copy the overrides (if not yet overridden), so they match their true values in the UI
if (!BasePropertyOverrides.bOverride_OpacityMaskClipValue)
{
BasePropertyOverrides.OpacityMaskClipValue = SourceInstance->GetOpacityMaskClipValue();
}
if (!BasePropertyOverrides.bOverride_BlendMode)
{
BasePropertyOverrides.BlendMode = SourceInstance->GetBlendMode();
}
if (!BasePropertyOverrides.bOverride_ShadingModel)
{
if (SourceInstance->IsShadingModelFromMaterialExpression())
{
BasePropertyOverrides.ShadingModel = MSM_FromMaterialExpression;
}
else
{
BasePropertyOverrides.ShadingModel = SourceInstance->GetShadingModels().GetFirstShadingModel();
}
}
if (!BasePropertyOverrides.bOverride_TwoSided)
{
BasePropertyOverrides.TwoSided = SourceInstance->IsTwoSided();
}
if (!BasePropertyOverrides.bOverride_OutputTranslucentVelocity)
{
BasePropertyOverrides.bOutputTranslucentVelocity = SourceInstance->IsTranslucencyWritingVelocity();
}
if (!BasePropertyOverrides.DitheredLODTransition)
{
BasePropertyOverrides.DitheredLODTransition = SourceInstance->IsDitheredLODTransition();
}
// Copy the Lightmass settings...
// The lightmass functions (GetCastShadowAsMasked, etc.) check if the value is overridden and returns the current value if so, otherwise returns the parent value
// So we don't need to wrap these in the same "if not overriding" as above
LightmassSettings.CastShadowAsMasked.ParameterValue = SourceInstance->GetCastShadowAsMasked();
LightmassSettings.EmissiveBoost.ParameterValue = SourceInstance->GetEmissiveBoost();
LightmassSettings.DiffuseBoost.ParameterValue = SourceInstance->GetDiffuseBoost();
LightmassSettings.ExportResolutionScale.ParameterValue = SourceInstance->GetExportResolutionScale();
//Copy refraction settings
SourceInstance->GetRefractionSettings(RefractionDepthBias);
bOverrideSubsurfaceProfile = SourceInstance->bOverrideSubsurfaceProfile;
// Copy the subsurface profile. GetSubsurfaceProfile_Internal() will return either the overridden profile or one from a parent
SubsurfaceProfile = SourceInstance->GetSubsurfaceProfile_Internal();
}
#if WITH_EDITOR
void UMaterialEditorInstanceConstant::PostEditUndo()
{
Super::PostEditUndo();
if (bIsFunctionPreviewMaterial && SourceFunction)
{
bIsFunctionInstanceDirty = true;
ApplySourceFunctionChanges();
}
else if (SourceInstance)
{
FMaterialUpdateContext Context;
UpdateSourceInstanceParent();
Context.AddMaterialInstance(SourceInstance);
}
}
#endif
UMaterialEditorMeshComponent::UMaterialEditorMeshComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}