EM_Task/UnrealEd/Private/PreviewMaterial.cpp
Boshuang Zhao 5144a49c9b add
2026-02-13 16:18:33 +08:00

1933 lines
96 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/DEditorMPCValue.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"
#include "Materials/MaterialExpressionSimpleRuntimeTextureSampleParameter.h"
#include "Materials/MaterialParameterCollection.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;
}
return bShaderTypeMatches;
}
}
/**
* Should shaders compiled for this material be saved to disk?
*/
virtual bool IsPersistent() const override { return false; }
};
/** Implementation of Preview Material functions*/
UPreviewMaterial::UPreviewMaterial(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
FMaterialResource* UPreviewMaterial::AllocateResource()
{
return new FPreviewMaterial();
}
// Helper struct to cache data for UMaterialEditorInstanceConstant/UMaterialEditorPreviewParameters::RegenerateArrays()
struct FMaterialParamExpressionData
{
FName Name = NAME_None;
FName Group = NAME_None;
UClass* ParamType = nullptr;
int32 SortPriority = 32;
};
// Helper struct to cache data for UMaterialEditorInstanceConstant/UMaterialEditorPreviewParameters::RegenerateArrays()
struct FMaterialExpressionParameterDataCache
{
TMap<FName, FMaterialParamExpressionData> GlobalParameters;
FName LayerParameterName = NAME_None;
TArray<TMap<FName, FMaterialParamExpressionData>> LayerParameters;
TArray<TMap<FName, FMaterialParamExpressionData>> BlendParameters;
};
// Helper function for UMaterialEditorInstanceConstant/UMaterialEditorPreviewParameters::RegenerateArrays()
// Cache material expression parameter for group and sort priority for quick lookup while creating UDEditorParameterValue
FMaterialExpressionParameterDataCache CacheMaterialExpressionParameterData(const UMaterial* InBaseMaterial, const FStaticParameterSet& InStaticParameters)
{
FMaterialExpressionParameterDataCache ParamCache;
ParamCache.GlobalParameters.Reserve(InBaseMaterial->Expressions.Num());
// Function replicating UMaterialFunctionInterface::GetParameterGroupName & UMaterialFunctionInterface::GetParameterSortPriority behavior
// but caching all the data in one pass
auto CacheMaterialFunctionParameterData = [](UMaterialFunctionInterface* ParameterFunction, TMap<FName, FMaterialParamExpressionData>& ParamDatas)
{
if (ParameterFunction)
{
TArray<UMaterialFunctionInterface*> Functions;
ParameterFunction->GetDependentFunctions(Functions);
Functions.AddUnique(ParameterFunction);
for (UMaterialFunctionInterface* Function: Functions)
{
for (UMaterialExpression* FunctionExpression: *Function->GetFunctionExpressions())
{
if (const UMaterialExpressionParameter* Parameter = Cast<const UMaterialExpressionParameter>(FunctionExpression))
{
FMaterialParamExpressionData ParamData;
ParamData.ParamType = UMaterialExpressionParameter::StaticClass();
ParamData.Name = Parameter->ParameterName;
ParamData.SortPriority = Parameter->SortPriority;
ParamData.Group = Parameter->Group;
// ensure(!ParamDatas.Contains(ParamData.Name));
ParamDatas.Add(ParamData.Name, ParamData);
}
else if (const UMaterialExpressionTextureSampleParameter* TexParameter = Cast<const UMaterialExpressionTextureSampleParameter>(FunctionExpression))
{
FMaterialParamExpressionData ParamData;
ParamData.ParamType = UMaterialExpressionTextureSampleParameter::StaticClass();
ParamData.Name = TexParameter->ParameterName;
ParamData.SortPriority = TexParameter->SortPriority;
ParamData.Group = TexParameter->Group;
// ensure(!ParamDatas.Contains(ParamData.Name));
ParamDatas.Add(ParamData.Name, ParamData);
}
else if (const UMaterialExpressionFontSampleParameter* FontParameter = Cast<const UMaterialExpressionFontSampleParameter>(FunctionExpression))
{
FMaterialParamExpressionData ParamData;
ParamData.ParamType = UMaterialExpressionFontSampleParameter::StaticClass();
ParamData.Name = FontParameter->ParameterName;
ParamData.SortPriority = FontParameter->SortPriority;
ParamData.Group = FontParameter->Group;
// ensure(!ParamDatas.Contains(ParamData.Name));
ParamDatas.Add(ParamData.Name, ParamData);
}
}
}
}
};
for (int32 Index = 0; Index < InBaseMaterial->Expressions.Num(); ++Index)
{
UMaterialExpression* Expression = InBaseMaterial->Expressions[Index];
if (UMaterialExpressionParameter* Parameter = Cast<UMaterialExpressionParameter>(Expression))
{
FMaterialParamExpressionData ParamData;
ParamData.ParamType = UMaterialExpressionParameter::StaticClass();
ParamData.Name = Parameter->GetParameterName();
ParamData.SortPriority = Parameter->SortPriority;
ParamData.Group = Parameter->Group;
// ensure(ParamCache.GlobalParameters.Contains(ParamData.Name));
ParamCache.GlobalParameters.Add(ParamData.Name, ParamData);
}
else if (UMaterialExpressionTextureSampleParameter* TexParameter = Cast<UMaterialExpressionTextureSampleParameter>(Expression))
{
FMaterialParamExpressionData ParamData;
ParamData.ParamType = UMaterialExpressionTextureSampleParameter::StaticClass();
ParamData.Name = TexParameter->GetParameterName();
ParamData.SortPriority = TexParameter->SortPriority;
ParamData.Group = TexParameter->Group;
// ensure(!ParamCache.GlobalParameters.Contains(ParamData.Name));
ParamCache.GlobalParameters.Add(ParamData.Name, ParamData);
}
else if (UMaterialExpressionRuntimeVirtualTextureSampleParameter* VTTexParameter = Cast<UMaterialExpressionRuntimeVirtualTextureSampleParameter>(Expression))
{
FMaterialParamExpressionData ParamData;
ParamData.ParamType = UMaterialExpressionRuntimeVirtualTextureSampleParameter::StaticClass();
ParamData.Name = VTTexParameter->GetParameterName();
ParamData.SortPriority = VTTexParameter->SortPriority;
ParamData.Group = VTTexParameter->Group;
// ensure(!ParamCache.GlobalParameters.Contains(ParamData.Name));
ParamCache.GlobalParameters.Add(ParamData.Name, ParamData);
}
else if (UMaterialExpressionSimpleRuntimeTextureSampleParameter* SRTTexParameter = Cast<UMaterialExpressionSimpleRuntimeTextureSampleParameter>(Expression))
{
FMaterialParamExpressionData ParamData;
ParamData.ParamType = UMaterialExpressionSimpleRuntimeTextureSampleParameter::StaticClass();
ParamData.Name = SRTTexParameter->GetParameterName();
ParamData.SortPriority = SRTTexParameter->SortPriority;
ParamData.Group = SRTTexParameter->Group;
// ensure(!ParamCache.GlobalParameters.Contains(ParamData.Name));
ParamCache.GlobalParameters.Add(ParamData.Name, ParamData);
}
else if (UMaterialExpressionFontSampleParameter* FontParameter = Cast<UMaterialExpressionFontSampleParameter>(Expression))
{
FMaterialParamExpressionData ParamData;
ParamData.ParamType = UMaterialExpressionFontSampleParameter::StaticClass();
ParamData.Name = FontParameter->GetParameterName();
ParamData.SortPriority = FontParameter->SortPriority;
ParamData.Group = FontParameter->Group;
// ensure(!ParamCache.GlobalParameters.Contains(ParamData.Name));
ParamCache.GlobalParameters.Add(ParamData.Name, ParamData);
}
else if (UMaterialExpressionMaterialFunctionCall* FuncParameter = Cast<UMaterialExpressionMaterialFunctionCall>(Expression))
{
if (FuncParameter->MaterialFunction)
{
if (UMaterialFunctionInterface* ParameterFunction = FuncParameter->MaterialFunction->GetBaseFunction())
{
CacheMaterialFunctionParameterData(ParameterFunction, ParamCache.GlobalParameters);
}
}
}
else if (UMaterialExpressionMaterialAttributeLayers* LayerParameter = Cast<UMaterialExpressionMaterialAttributeLayers>(Expression))
{
// there should only be one Material attribute layer expression per material
check(ParamCache.LayerParameterName == NAME_None);
ParamCache.LayerParameterName = LayerParameter->ParameterName;
// look into the instance static parameters first for overrides
UMaterialFunctionInterface* Function = nullptr;
const FStaticMaterialLayersParameter* StaticLayers = InStaticParameters.MaterialLayersParameters.FindByPredicate([LayerParameterName = LayerParameter->ParameterName](const FStaticMaterialLayersParameter& Layers)
{
return LayerParameterName == Layers.ParameterInfo.Name;
});
// If we found one cache those instead of what is on the material itself since they take precedence
if (StaticLayers)
{
// Replicate FStaticMaterialLayersParameter::GetParameterAssociatedFunction behavior while caching all function needed info
// Cache layer parameters
for (UMaterialFunctionInterface* Layer: StaticLayers->Value.Layers)
{
TMap<FName, FMaterialParamExpressionData>& LayerCache = ParamCache.LayerParameters.AddDefaulted_GetRef();
CacheMaterialFunctionParameterData(Layer, LayerCache);
}
// Cache blend parameters
for (UMaterialFunctionInterface* Blend: StaticLayers->Value.Blends)
{
TMap<FName, FMaterialParamExpressionData>& BlendCache = ParamCache.LayerParameters.AddDefaulted_GetRef();
CacheMaterialFunctionParameterData(Blend, BlendCache);
}
}
else
{
// Cache layer parameters
for (UMaterialFunctionInterface* Layer: LayerParameter->GetLayers())
{
TMap<FName, FMaterialParamExpressionData>& LayerCache = ParamCache.LayerParameters.AddDefaulted_GetRef();
CacheMaterialFunctionParameterData(Layer, LayerCache);
}
// Cache blend parameters
for (UMaterialFunctionInterface* Blend: LayerParameter->GetBlends())
{
TMap<FName, FMaterialParamExpressionData>& BlendCache = ParamCache.LayerParameters.AddDefaulted_GetRef();
CacheMaterialFunctionParameterData(Blend, BlendCache);
}
}
}
}
return ParamCache;
}
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(UMaterial* ParentMaterial, UDEditorParameterValue* ParameterValue, FName* OptionalGroupName)
{
check(ParentMaterial);
check(ParameterValue);
FName ParameterGroupName;
if (OptionalGroupName)
{
ParameterGroupName = *OptionalGroupName;
}
else
{
ParentMaterial->GetGroupName(ParameterValue->ParameterInfo, ParameterGroupName);
}
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;
// Use param cache to lookup group and sort priority
auto AssignGroupAndSortPriority = [this, ParentMaterial](UDEditorParameterValue* InEditorParamValue, const FMaterialExpressionParameterDataCache& InCachedExpressionData)
{
const FMaterialParamExpressionData* ParamData = nullptr;
if (InEditorParamValue->ParameterInfo.Association == EMaterialParameterAssociation::GlobalParameter)
{
ParamData = InCachedExpressionData.GlobalParameters.Find(InEditorParamValue->ParameterInfo.Name);
}
// if the association is not 'global parameter', look into attribute layers if we have a potentially valid index
else if (InEditorParamValue->ParameterInfo.Index >= 0)
{
if (InEditorParamValue->ParameterInfo.Association == EMaterialParameterAssociation::LayerParameter && InCachedExpressionData.LayerParameters.IsValidIndex(InEditorParamValue->ParameterInfo.Index))
{
ParamData = InCachedExpressionData.LayerParameters[InEditorParamValue->ParameterInfo.Index].Find(InEditorParamValue->ParameterInfo.Name);
}
else if (InEditorParamValue->ParameterInfo.Association == EMaterialParameterAssociation::BlendParameter && InCachedExpressionData.BlendParameters.IsValidIndex(InEditorParamValue->ParameterInfo.Index))
{
ParamData = InCachedExpressionData.BlendParameters[InEditorParamValue->ParameterInfo.Index].Find(InEditorParamValue->ParameterInfo.Name);
}
}
FName GroupName = NAME_None;
if (ParamData)
{
InEditorParamValue->SortPriority = ParamData->SortPriority;
GroupName = ParamData->Group;
}
AssignParameterToGroup(ParentMaterial, InEditorParamValue, &GroupName);
};
// This can run before UMaterial::PostEditChangeProperty has a chance to run, so explicitly call UpdateCachedExpressionData here
PreviewMaterial->UpdateCachedExpressionData();
// Cache relevant material expression data used to resolve editor param value info in RegenerateArrays
//@todo FH: can this be/should be part of `UpdateCachedExpressionData`?
FMaterialExpressionParameterDataCache ExpressionParameterDataCache = CacheMaterialExpressionParameterData(PreviewMaterial, FStaticParameterSet());
// Loop through all types of parameters for this material and add them to the parameter arrays.
TArray<FMaterialParameterInfo> ParameterInfo;
TArray<FGuid> Guids;
ParentMaterial->GetAllVectorParameterInfo(ParameterInfo, Guids);
// Vector Parameters.
for (int32 ParameterIdx = 0; ParameterIdx < ParameterInfo.Num(); ParameterIdx++)
{
UDEditorVectorParameterValue& ParameterValue = *(NewObject<UDEditorVectorParameterValue>(this));
FName ParameterName = ParameterInfo[ParameterIdx].Name;
FLinearColor Value;
ParameterValue.bOverride = true;
ParameterValue.ExpressionId = Guids[ParameterIdx];
ParameterValue.ParameterInfo = ParameterInfo[ParameterIdx];
if (PreviewMaterial->GetVectorParameterValue(ParameterValue.ParameterInfo, Value))
{
ParameterValue.ParameterValue = Value;
PreviewMaterial->IsVectorParameterUsedAsChannelMask(ParameterValue.ParameterInfo, ParameterValue.bIsUsedAsChannelMask);
PreviewMaterial->GetVectorParameterChannelNames(ParameterValue.ParameterInfo, ParameterValue.ChannelNames);
}
AssignGroupAndSortPriority(&ParameterValue, ExpressionParameterDataCache);
}
// Scalar Parameters.
ParentMaterial->GetAllScalarParameterInfo(ParameterInfo, Guids);
for (int32 ParameterIdx = 0; ParameterIdx < ParameterInfo.Num(); ParameterIdx++)
{
UDEditorScalarParameterValue& ParameterValue = *(NewObject<UDEditorScalarParameterValue>(this));
FName ParameterName = ParameterInfo[ParameterIdx].Name;
float Value;
ParameterValue.bOverride = true;
ParameterValue.ParameterInfo = ParameterInfo[ParameterIdx];
ParameterValue.ExpressionId = Guids[ParameterIdx];
if (PreviewMaterial->GetScalarParameterValue(ParameterValue.ParameterInfo, Value))
{
ParentMaterial->GetScalarParameterSliderMinMax(ParameterName, ParameterValue.SliderMin, ParameterValue.SliderMax);
ParentMaterial->IsScalarParameterUsedAsAtlasPosition(ParameterName, ParameterValue.AtlasData.bIsUsedAsAtlasPosition, ParameterValue.AtlasData.Curve, ParameterValue.AtlasData.Atlas);
ParameterValue.ParameterValue = Value;
}
AssignGroupAndSortPriority(&ParameterValue, ExpressionParameterDataCache);
}
// Texture Parameters.
ParentMaterial->GetAllTextureParameterInfo(ParameterInfo, Guids);
for (int32 ParameterIdx = 0; ParameterIdx < ParameterInfo.Num(); ParameterIdx++)
{
UDEditorTextureParameterValue& ParameterValue = *(NewObject<UDEditorTextureParameterValue>(this));
FName ParameterName = ParameterInfo[ParameterIdx].Name;
UTexture* Value;
ParameterValue.bOverride = true;
ParameterValue.ParameterInfo = ParameterInfo[ParameterIdx];
ParameterValue.ExpressionId = Guids[ParameterIdx];
if (PreviewMaterial->GetTextureParameterValue(ParameterValue.ParameterInfo, Value))
{
ParameterValue.ParameterValue = Value;
PreviewMaterial->GetTextureParameterChannelNames(ParameterValue.ParameterInfo, ParameterValue.ChannelNames);
}
AssignGroupAndSortPriority(&ParameterValue, ExpressionParameterDataCache);
}
// Runtime Virtual Texture Parameters.
ParentMaterial->GetAllTextureParameterInfo(ParameterInfo, Guids);
for (int32 ParameterIdx = 0; ParameterIdx < ParameterInfo.Num(); ParameterIdx++)
{
UDEditorRuntimeVirtualTextureParameterValue& ParameterValue = *(NewObject<UDEditorRuntimeVirtualTextureParameterValue>(this));
FName ParameterName = ParameterInfo[ParameterIdx].Name;
URuntimeVirtualTexture* Value;
ParameterValue.bOverride = true;
ParameterValue.ParameterInfo = ParameterInfo[ParameterIdx];
ParameterValue.ExpressionId = Guids[ParameterIdx];
if (PreviewMaterial->GetRuntimeVirtualTextureParameterValue(ParameterValue.ParameterInfo, Value))
{
ParameterValue.ParameterValue = Value;
}
AssignGroupAndSortPriority(&ParameterValue, ExpressionParameterDataCache);
}
// Font Parameters.
ParentMaterial->GetAllFontParameterInfo(ParameterInfo, Guids);
for (int32 ParameterIdx = 0; ParameterIdx < ParameterInfo.Num(); ParameterIdx++)
{
UDEditorFontParameterValue& ParameterValue = *(NewObject<UDEditorFontParameterValue>(this));
FName ParameterName = ParameterInfo[ParameterIdx].Name;
UFont* FontValue;
int32 FontPage;
ParameterValue.bOverride = true;
ParameterValue.ParameterInfo = ParameterInfo[ParameterIdx];
ParameterValue.ExpressionId = Guids[ParameterIdx];
if (PreviewMaterial->GetFontParameterValue(ParameterValue.ParameterInfo, FontValue, FontPage))
{
ParameterValue.ParameterValue.FontValue = FontValue;
ParameterValue.ParameterValue.FontPage = FontPage;
}
AssignGroupAndSortPriority(&ParameterValue, ExpressionParameterDataCache);
}
// Get all static parameters from the source instance. This will handle inheriting parent values.
FStaticParameterSet SourceStaticParameters;
// Static Material Layers Parameters
ParentMaterial->GetAllMaterialLayersParameterInfo(ParameterInfo, Guids);
SourceStaticParameters.MaterialLayersParameters.AddZeroed(ParameterInfo.Num());
for (int32 ParameterIdx = 0; ParameterIdx < ParameterInfo.Num(); ParameterIdx++)
{
FStaticMaterialLayersParameter& ParameterValue = SourceStaticParameters.MaterialLayersParameters[ParameterIdx];
ParameterValue.ParameterInfo = ParameterInfo[ParameterIdx];
FMaterialLayersFunctions Value = FMaterialLayersFunctions();
FGuid ExpressionId = Guids[ParameterIdx];
ParameterValue.bOverride = true;
// get the settings from the parent in the MIC chain
if (PreviewMaterial->GetMaterialLayersParameterValue(ParameterValue.ParameterInfo, Value, ExpressionId))
{
ParameterValue.Value = Value;
}
ParameterValue.ExpressionGUID = ExpressionId;
}
// Static Switch Parameters
ParentMaterial->GetAllStaticSwitchParameterInfo(ParameterInfo, Guids);
SourceStaticParameters.StaticSwitchParameters.AddZeroed(ParameterInfo.Num());
for (int32 ParameterIdx = 0; ParameterIdx < ParameterInfo.Num(); ParameterIdx++)
{
FStaticSwitchParameter& ParameterValue = SourceStaticParameters.StaticSwitchParameters[ParameterIdx];
ParameterValue.ParameterInfo = ParameterInfo[ParameterIdx];
bool Value = false;
FGuid ExpressionId = Guids[ParameterIdx];
ParameterValue.bOverride = true;
// get the settings from the parent in the MIC chain
if (PreviewMaterial->GetStaticSwitchParameterValue(ParameterValue.ParameterInfo, Value, ExpressionId))
{
ParameterValue.Value = Value;
}
ParameterValue.ExpressionGUID = ExpressionId;
}
// Static Component Mask Parameters
ParentMaterial->GetAllStaticComponentMaskParameterInfo(ParameterInfo, Guids);
SourceStaticParameters.StaticComponentMaskParameters.AddZeroed(ParameterInfo.Num());
for (int32 ParameterIdx = 0; ParameterIdx < ParameterInfo.Num(); ParameterIdx++)
{
FStaticComponentMaskParameter& ParameterValue = SourceStaticParameters.StaticComponentMaskParameters[ParameterIdx];
bool R = false;
bool G = false;
bool B = false;
bool A = false;
FGuid ExpressionId = Guids[ParameterIdx];
ParameterValue.bOverride = true;
ParameterValue.ParameterInfo = ParameterInfo[ParameterIdx];
// get the settings from the parent in the MIC chain
if (PreviewMaterial->GetStaticComponentMaskParameterValue(ParameterValue.ParameterInfo, R, G, B, A, ExpressionId))
{
ParameterValue.R = R;
ParameterValue.G = G;
ParameterValue.B = B;
ParameterValue.A = A;
}
ParameterValue.ExpressionGUID = ExpressionId;
}
// Copy material layer Parameters
for (int32 ParameterIdx = 0; ParameterIdx < SourceStaticParameters.MaterialLayersParameters.Num(); ParameterIdx++)
{
FStaticMaterialLayersParameter MaterialLayersParameterValue = FStaticMaterialLayersParameter(SourceStaticParameters.MaterialLayersParameters[ParameterIdx]);
UDEditorMaterialLayersParameterValue& ParameterValue = *(NewObject<UDEditorMaterialLayersParameterValue>(this));
ParameterValue.ParameterValue = MaterialLayersParameterValue.Value;
ParameterValue.bOverride = MaterialLayersParameterValue.bOverride;
ParameterValue.ParameterInfo = MaterialLayersParameterValue.ParameterInfo;
ParameterValue.ExpressionId = MaterialLayersParameterValue.ExpressionGUID;
AssignGroupAndSortPriority(&ParameterValue, ExpressionParameterDataCache);
}
// Copy Static Switch Parameters
for (int32 ParameterIdx = 0; ParameterIdx < SourceStaticParameters.StaticSwitchParameters.Num(); ParameterIdx++)
{
FStaticSwitchParameter StaticSwitchParameterValue = FStaticSwitchParameter(SourceStaticParameters.StaticSwitchParameters[ParameterIdx]);
UDEditorStaticSwitchParameterValue& ParameterValue = *(NewObject<UDEditorStaticSwitchParameterValue>(this));
ParameterValue.ParameterValue = StaticSwitchParameterValue.Value;
ParameterValue.bOverride = StaticSwitchParameterValue.bOverride;
ParameterValue.ParameterInfo = StaticSwitchParameterValue.ParameterInfo;
ParameterValue.ExpressionId = StaticSwitchParameterValue.ExpressionGUID;
AssignGroupAndSortPriority(&ParameterValue, ExpressionParameterDataCache);
}
// Copy Static Component Mask Parameters
for (int32 ParameterIdx = 0; ParameterIdx < SourceStaticParameters.StaticComponentMaskParameters.Num(); ParameterIdx++)
{
FStaticComponentMaskParameter StaticComponentMaskParameterValue = FStaticComponentMaskParameter(SourceStaticParameters.StaticComponentMaskParameters[ParameterIdx]);
UDEditorStaticComponentMaskParameterValue& ParameterValue = *(NewObject<UDEditorStaticComponentMaskParameterValue>(this));
ParameterValue.ParameterValue.R = StaticComponentMaskParameterValue.R;
ParameterValue.ParameterValue.G = StaticComponentMaskParameterValue.G;
ParameterValue.ParameterValue.B = StaticComponentMaskParameterValue.B;
ParameterValue.ParameterValue.A = StaticComponentMaskParameterValue.A;
ParameterValue.bOverride = StaticComponentMaskParameterValue.bOverride;
ParameterValue.ParameterInfo = StaticComponentMaskParameterValue.ParameterInfo;
ParameterValue.ExpressionId = StaticComponentMaskParameterValue.ExpressionGUID;
AssignGroupAndSortPriority(&ParameterValue, ExpressionParameterDataCache);
}
}
// 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++)
{
if (Group.Parameters[ParameterIdx] == NULL)
{
continue;
}
UDEditorScalarParameterValue* ScalarParameterValue = Cast<UDEditorScalarParameterValue>(Group.Parameters[ParameterIdx]);
if (ScalarParameterValue)
{
PreviewMaterial->SetScalarParameterValueEditorOnly(ScalarParameterValue->ParameterInfo.Name, ScalarParameterValue->ParameterValue);
continue;
}
UDEditorFontParameterValue* FontParameterValue = Cast<UDEditorFontParameterValue>(Group.Parameters[ParameterIdx]);
if (FontParameterValue)
{
PreviewMaterial->SetFontParameterValueEditorOnly(FontParameterValue->ParameterInfo.Name, FontParameterValue->ParameterValue.FontValue, FontParameterValue->ParameterValue.FontPage);
continue;
}
UDEditorTextureParameterValue* TextureParameterValue = Cast<UDEditorTextureParameterValue>(Group.Parameters[ParameterIdx]);
if (TextureParameterValue)
{
PreviewMaterial->SetTextureParameterValueEditorOnly(TextureParameterValue->ParameterInfo.Name, TextureParameterValue->ParameterValue);
continue;
}
UDEditorRuntimeVirtualTextureParameterValue* RuntimeVirtualTextureParameterValue = Cast<UDEditorRuntimeVirtualTextureParameterValue>(Group.Parameters[ParameterIdx]);
if (RuntimeVirtualTextureParameterValue)
{
PreviewMaterial->SetRuntimeVirtualTextureParameterValueEditorOnly(RuntimeVirtualTextureParameterValue->ParameterInfo.Name, RuntimeVirtualTextureParameterValue->ParameterValue);
continue;
}
UDEditorVectorParameterValue* VectorParameterValue = Cast<UDEditorVectorParameterValue>(Group.Parameters[ParameterIdx]);
if (VectorParameterValue)
{
PreviewMaterial->SetVectorParameterValueEditorOnly(VectorParameterValue->ParameterInfo.Name, VectorParameterValue->ParameterValue);
continue;
}
UDEditorStaticComponentMaskParameterValue* MaskParameterValue = Cast<UDEditorStaticComponentMaskParameterValue>(Group.Parameters[ParameterIdx]);
if (MaskParameterValue)
{
bool MaskR = MaskParameterValue->ParameterValue.R;
bool MaskG = MaskParameterValue->ParameterValue.G;
bool MaskB = MaskParameterValue->ParameterValue.B;
bool MaskA = MaskParameterValue->ParameterValue.A;
FGuid ExpressionIdValue = MaskParameterValue->ExpressionId;
PreviewMaterial->SetStaticComponentMaskParameterValueEditorOnly(MaskParameterValue->ParameterInfo.Name, MaskR, MaskG, MaskB, MaskA, ExpressionIdValue);
continue;
}
UDEditorStaticSwitchParameterValue* SwitchParameterValue = Cast<UDEditorStaticSwitchParameterValue>(Group.Parameters[ParameterIdx]);
if (SwitchParameterValue)
{
bool SwitchValue = SwitchParameterValue->ParameterValue;
PreviewMaterial->SetStaticSwitchParameterValueEditorOnly(SwitchParameterValue->ParameterInfo.Name, SwitchValue, SwitchParameterValue->ExpressionId);
continue;
}
}
}
}
}
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++)
{
if (Group.Parameters[ParameterIdx] == NULL)
{
continue;
}
UDEditorScalarParameterValue* ScalarParameterValue = Cast<UDEditorScalarParameterValue>(Group.Parameters[ParameterIdx]);
if (ScalarParameterValue)
{
OriginalFunction->SetScalarParameterValueEditorOnly(ScalarParameterValue->ParameterInfo.Name, ScalarParameterValue->ParameterValue);
continue;
}
UDEditorFontParameterValue* FontParameterValue = Cast<UDEditorFontParameterValue>(Group.Parameters[ParameterIdx]);
if (FontParameterValue)
{
OriginalFunction->SetFontParameterValueEditorOnly(FontParameterValue->ParameterInfo.Name, FontParameterValue->ParameterValue.FontValue, FontParameterValue->ParameterValue.FontPage);
continue;
}
UDEditorTextureParameterValue* TextureParameterValue = Cast<UDEditorTextureParameterValue>(Group.Parameters[ParameterIdx]);
if (TextureParameterValue)
{
OriginalFunction->SetTextureParameterValueEditorOnly(TextureParameterValue->ParameterInfo.Name, TextureParameterValue->ParameterValue);
continue;
}
UDEditorRuntimeVirtualTextureParameterValue* RuntimeVirtualTextureParameterValue = Cast<UDEditorRuntimeVirtualTextureParameterValue>(Group.Parameters[ParameterIdx]);
if (RuntimeVirtualTextureParameterValue)
{
OriginalFunction->SetRuntimeVirtualTextureParameterValueEditorOnly(RuntimeVirtualTextureParameterValue->ParameterInfo.Name, RuntimeVirtualTextureParameterValue->ParameterValue);
continue;
}
UDEditorVectorParameterValue* VectorParameterValue = Cast<UDEditorVectorParameterValue>(Group.Parameters[ParameterIdx]);
if (VectorParameterValue)
{
OriginalFunction->SetVectorParameterValueEditorOnly(VectorParameterValue->ParameterInfo.Name, VectorParameterValue->ParameterValue);
continue;
}
UDEditorStaticComponentMaskParameterValue* MaskParameterValue = Cast<UDEditorStaticComponentMaskParameterValue>(Group.Parameters[ParameterIdx]);
if (MaskParameterValue)
{
bool MaskR = MaskParameterValue->ParameterValue.R;
bool MaskG = MaskParameterValue->ParameterValue.G;
bool MaskB = MaskParameterValue->ParameterValue.B;
bool MaskA = MaskParameterValue->ParameterValue.A;
FGuid ExpressionIdValue = MaskParameterValue->ExpressionId;
OriginalFunction->SetStaticComponentMaskParameterValueEditorOnly(MaskParameterValue->ParameterInfo.Name, MaskR, MaskG, MaskB, MaskA, ExpressionIdValue);
continue;
}
UDEditorStaticSwitchParameterValue* SwitchParameterValue = Cast<UDEditorStaticSwitchParameterValue>(Group.Parameters[ParameterIdx]);
if (SwitchParameterValue)
{
bool SwitchValue = SwitchParameterValue->ParameterValue;
OriginalFunction->SetStaticSwitchParameterValueEditorOnly(SwitchParameterValue->ParameterInfo.Name, SwitchValue, SwitchParameterValue->ExpressionId);
continue;
}
}
}
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 (PropertyThatChanged && PropertyThatChanged->GetName() == TEXT("NextPass"))
{
SourceInstance->NextPass = NextPass;
FMaterialUpdateContext Context;
Context.AddMaterialInstance(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(UMaterial*, UDEditorParameterValue* ParameterValue, const FName* OptionalGroupName)
{
check(ParameterValue);
FName ParameterGroupName;
if (OptionalGroupName)
{
ParameterGroupName = *OptionalGroupName;
}
else
{
SourceInstance->GetGroupName(ParameterValue->ParameterInfo, ParameterGroupName);
}
if (ParameterGroupName == TEXT("") || ParameterGroupName == TEXT("None"))
{
if (bUseOldStyleMICEditorGroups == true)
{
if (Cast<UDEditorVectorParameterValue>(ParameterValue))
{
ParameterGroupName = TEXT("Vector Parameter Values");
}
else if (Cast<UDEditorTextureParameterValue>(ParameterValue))
{
ParameterGroupName = TEXT("Texture Parameter Values");
}
else if (Cast<UDEditorRuntimeVirtualTextureParameterValue>(ParameterValue))
{
ParameterGroupName = TEXT("Texture Parameter Values");
}
else if (Cast<UDEditorScalarParameterValue>(ParameterValue))
{
ParameterGroupName = TEXT("Scalar Parameter Values");
}
else if (Cast<UDEditorStaticSwitchParameterValue>(ParameterValue))
{
ParameterGroupName = TEXT("Static Switch Parameter Values");
}
else if (Cast<UDEditorStaticComponentMaskParameterValue>(ParameterValue))
{
ParameterGroupName = TEXT("Static Component Mask Parameter Values");
}
else if (Cast<UDEditorFontParameterValue>(ParameterValue))
{
ParameterGroupName = TEXT("Font Parameter Values");
}
else if (Cast<UDEditorMaterialLayersParameterValue>(ParameterValue))
{
ParameterGroupName = TEXT("Material Layers Parameter Values");
}
else
{
ParameterGroupName = TEXT("None");
}
}
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)
{
// Use param cache to lookup group and sort priority
auto AssignGroupAndSortPriority = [this](UDEditorParameterValue* InEditorParamValue, const FMaterialExpressionParameterDataCache& InCachedExpressionData)
{
const FMaterialParamExpressionData* ParamData = nullptr;
if (InEditorParamValue->ParameterInfo.Association == EMaterialParameterAssociation::GlobalParameter)
{
ParamData = InCachedExpressionData.GlobalParameters.Find(InEditorParamValue->ParameterInfo.Name);
}
// if the association is not 'global parameter', look into attribute layers if we have a potentially valid index
else if (InEditorParamValue->ParameterInfo.Index >= 0)
{
if (InEditorParamValue->ParameterInfo.Association == EMaterialParameterAssociation::LayerParameter && InCachedExpressionData.LayerParameters.IsValidIndex(InEditorParamValue->ParameterInfo.Index))
{
ParamData = InCachedExpressionData.LayerParameters[InEditorParamValue->ParameterInfo.Index].Find(InEditorParamValue->ParameterInfo.Name);
}
else if (InEditorParamValue->ParameterInfo.Association == EMaterialParameterAssociation::BlendParameter && InCachedExpressionData.BlendParameters.IsValidIndex(InEditorParamValue->ParameterInfo.Index))
{
ParamData = InCachedExpressionData.BlendParameters[InEditorParamValue->ParameterInfo.Index].Find(InEditorParamValue->ParameterInfo.Name);
}
}
if (ParamData)
{
InEditorParamValue->SortPriority = ParamData->SortPriority;
}
AssignParameterToGroup(nullptr /*useless param: Parent->GetMaterial()*/, InEditorParamValue, ParamData ? &ParamData->Group : nullptr);
};
// Only operate on base materials
UMaterial* ParentMaterial = Parent->GetMaterial();
SourceInstance->UpdateParameterNames(); // Update any parameter names that may have changed.
SourceInstance->UpdateCachedLayerParameters();
// Get all static parameters from the source instance. This will handle inheriting parent values.
FStaticParameterSet SourceStaticParameters;
SourceInstance->GetStaticParameterValues(SourceStaticParameters);
// Loop through all types of parameters for this material and add them to the parameter arrays.
TArray<FMaterialParameterInfo> OutParameterInfo;
TArray<FGuid> Guids;
// Need to get layer info first as other params are collected from layers
SourceInstance->GetAllMaterialLayersParameterInfo(OutParameterInfo, Guids);
// Copy Static Material Layers Parameters
for (int32 ParameterIdx = 0; ParameterIdx < SourceStaticParameters.MaterialLayersParameters.Num(); ParameterIdx++)
{
FStaticMaterialLayersParameter MaterialLayersParameterParameterValue = FStaticMaterialLayersParameter(SourceStaticParameters.MaterialLayersParameters[ParameterIdx]);
UDEditorMaterialLayersParameterValue& ParameterValue = *(NewObject<UDEditorMaterialLayersParameterValue>(this));
ParameterValue.ParameterValue = MaterialLayersParameterParameterValue.Value;
ParameterValue.bOverride = MaterialLayersParameterParameterValue.bOverride;
ParameterValue.ParameterInfo = MaterialLayersParameterParameterValue.ParameterInfo;
ParameterValue.ExpressionId = MaterialLayersParameterParameterValue.ExpressionGUID;
AssignParameterToGroup(ParentMaterial, &ParameterValue);
}
// Cache relevant material expression data to resolve editor param value info
FMaterialExpressionParameterDataCache ExpressionParameterDataCache = CacheMaterialExpressionParameterData(SourceInstance->GetMaterial(), SourceInstance->GetStaticParameters());
// Scalar Parameters.
SourceInstance->GetAllScalarParameterInfo(OutParameterInfo, Guids);
for (int32 ParameterIdx = 0; ParameterIdx < OutParameterInfo.Num(); ParameterIdx++)
{
UDEditorScalarParameterValue* ParamValue = NewObject<UDEditorScalarParameterValue>(this);
UDEditorScalarParameterValue& ParameterValue = *ParamValue;
const FMaterialParameterInfo& ParameterInfo = OutParameterInfo[ParameterIdx];
ParameterValue.bOverride = false;
ParameterValue.ParameterInfo = ParameterInfo;
ParameterValue.ExpressionId = Guids[ParameterIdx];
if (SourceInstance->GetScalarParameterValue(ParameterInfo, ParameterValue.ParameterValue))
{
SourceInstance->IsScalarParameterUsedAsAtlasPosition(ParameterInfo, ParameterValue.AtlasData.bIsUsedAsAtlasPosition, ParameterValue.AtlasData.Curve, ParameterValue.AtlasData.Atlas);
SourceInstance->GetScalarParameterSliderMinMax(ParameterInfo, ParameterValue.SliderMin, ParameterValue.SliderMax);
}
// @todo: This is kind of slow, maybe store these in a map for lookup?
// See if this keyname exists in the source instance.
for (int32 ScalarParameterIdx = 0; ScalarParameterIdx < SourceInstance->ScalarParameterValues.Num(); ScalarParameterIdx++)
{
FScalarParameterValue& SourceParam = SourceInstance->ScalarParameterValues[ScalarParameterIdx];
if (ParameterInfo == SourceParam.ParameterInfo)
{
ParameterValue.bOverride = true;
ParameterValue.ParameterValue = SourceParam.ParameterValue;
}
}
AssignGroupAndSortPriority(ParamValue, ExpressionParameterDataCache);
}
// Vector Parameters.
SourceInstance->GetAllVectorParameterInfo(OutParameterInfo, Guids);
for (int32 ParameterIdx = 0; ParameterIdx < OutParameterInfo.Num(); ParameterIdx++)
{
UDEditorVectorParameterValue* ParamValue = NewObject<UDEditorVectorParameterValue>(this);
UDEditorVectorParameterValue& ParameterValue = *ParamValue;
const FMaterialParameterInfo& ParameterInfo = OutParameterInfo[ParameterIdx];
ParameterValue.bOverride = false;
ParameterValue.ParameterInfo = ParameterInfo;
ParameterValue.ExpressionId = Guids[ParameterIdx];
SourceInstance->GetVectorParameterValue(ParameterInfo, ParameterValue.ParameterValue);
SourceInstance->IsVectorParameterUsedAsChannelMask(ParameterInfo, ParameterValue.bIsUsedAsChannelMask);
SourceInstance->GetVectorParameterChannelNames(ParameterInfo, ParameterValue.ChannelNames);
// @todo: This is kind of slow, maybe store these in a map for lookup?
// See if this keyname exists in the source instance.
for (int32 VectorParameterIdx = 0; VectorParameterIdx < SourceInstance->VectorParameterValues.Num(); VectorParameterIdx++)
{
FVectorParameterValue& SourceParam = SourceInstance->VectorParameterValues[VectorParameterIdx];
if (ParameterInfo == SourceParam.ParameterInfo)
{
ParameterValue.bOverride = true;
ParameterValue.ParameterValue = SourceParam.ParameterValue;
}
}
AssignGroupAndSortPriority(ParamValue, ExpressionParameterDataCache);
}
// Texture Parameters.
SourceInstance->GetAllTextureParameterInfo(OutParameterInfo, Guids);
for (int32 ParameterIdx = 0; ParameterIdx < OutParameterInfo.Num(); ParameterIdx++)
{
UDEditorTextureParameterValue* ParamValue = NewObject<UDEditorTextureParameterValue>(this);
UDEditorTextureParameterValue& ParameterValue = *ParamValue;
const FMaterialParameterInfo& ParameterInfo = OutParameterInfo[ParameterIdx];
ParameterValue.bOverride = false;
ParameterValue.ParameterInfo = ParameterInfo;
ParameterValue.ExpressionId = Guids[ParameterIdx];
ParameterValue.ParameterValue = nullptr;
SourceInstance->GetTextureParameterValue(ParameterInfo, ParameterValue.ParameterValue);
SourceInstance->GetTextureParameterChannelNames(ParameterInfo, ParameterValue.ChannelNames);
// @todo: This is kind of slow, maybe store these in a map for lookup?
// See if this keyname exists in the source instance.
for (int32 TextureParameterIdx = 0; TextureParameterIdx < SourceInstance->TextureParameterValues.Num(); TextureParameterIdx++)
{
FTextureParameterValue& SourceParam = SourceInstance->TextureParameterValues[TextureParameterIdx];
if (ParameterInfo == SourceParam.ParameterInfo)
{
ParameterValue.bOverride = true;
ParameterValue.ParameterValue = SourceParam.ParameterValue;
}
}
AssignGroupAndSortPriority(ParamValue, ExpressionParameterDataCache);
}
// Runtime Virtual Texture Parameters.
SourceInstance->GetAllRuntimeVirtualTextureParameterInfo(OutParameterInfo, Guids);
for (int32 ParameterIdx = 0; ParameterIdx < OutParameterInfo.Num(); ParameterIdx++)
{
UDEditorRuntimeVirtualTextureParameterValue* ParamValue = NewObject<UDEditorRuntimeVirtualTextureParameterValue>(this);
UDEditorRuntimeVirtualTextureParameterValue& ParameterValue = *ParamValue;
const FMaterialParameterInfo& ParameterInfo = OutParameterInfo[ParameterIdx];
ParameterValue.bOverride = false;
ParameterValue.ParameterInfo = ParameterInfo;
ParameterValue.ExpressionId = Guids[ParameterIdx];
ParameterValue.ParameterValue = nullptr;
SourceInstance->GetRuntimeVirtualTextureParameterValue(ParameterInfo, ParameterValue.ParameterValue);
// @todo: This is kind of slow, maybe store these in a map for lookup?
// See if this keyname exists in the source instance.
for (int32 TextureParameterIdx = 0; TextureParameterIdx < SourceInstance->RuntimeVirtualTextureParameterValues.Num(); TextureParameterIdx++)
{
FRuntimeVirtualTextureParameterValue& SourceParam = SourceInstance->RuntimeVirtualTextureParameterValues[TextureParameterIdx];
if (ParameterInfo == SourceParam.ParameterInfo)
{
ParameterValue.bOverride = true;
ParameterValue.ParameterValue = SourceParam.ParameterValue;
}
if (ParameterInfo.Name.IsEqual(SourceParam.ParameterInfo.Name) && ParameterInfo.Association == SourceParam.ParameterInfo.Association && ParameterInfo.Index == SourceParam.ParameterInfo.Index)
{
ParameterValue.bOverride = true;
ParameterValue.ParameterValue = SourceParam.ParameterValue;
}
}
AssignGroupAndSortPriority(ParamValue, ExpressionParameterDataCache);
}
// Font Parameters.
SourceInstance->GetAllFontParameterInfo(OutParameterInfo, Guids);
for (int32 ParameterIdx = 0; ParameterIdx < OutParameterInfo.Num(); ParameterIdx++)
{
UDEditorFontParameterValue* ParamValue = NewObject<UDEditorFontParameterValue>(this);
UDEditorFontParameterValue& ParameterValue = *ParamValue;
const FMaterialParameterInfo& ParameterInfo = OutParameterInfo[ParameterIdx];
ParameterValue.bOverride = false;
ParameterValue.ParameterInfo = ParameterInfo;
ParameterValue.ExpressionId = Guids[ParameterIdx];
ParameterValue.ParameterValue.FontValue = nullptr;
ParameterValue.ParameterValue.FontPage = 0;
SourceInstance->GetFontParameterValue(ParameterInfo, ParameterValue.ParameterValue.FontValue, ParameterValue.ParameterValue.FontPage);
// @todo: This is kind of slow, maybe store these in a map for lookup?
// See if this keyname exists in the source instance.
for (int32 FontParameterIdx = 0; FontParameterIdx < SourceInstance->FontParameterValues.Num(); FontParameterIdx++)
{
FFontParameterValue& SourceParam = SourceInstance->FontParameterValues[FontParameterIdx];
if (ParameterInfo == SourceParam.ParameterInfo)
{
ParameterValue.bOverride = true;
ParameterValue.ParameterValue.FontValue = SourceParam.FontValue;
ParameterValue.ParameterValue.FontPage = SourceParam.FontPage;
}
}
AssignGroupAndSortPriority(ParamValue, ExpressionParameterDataCache);
}
// Copy Static Switch Parameters
SourceInstance->GetAllStaticSwitchParameterInfo(OutParameterInfo, Guids);
for (int32 ParameterIdx = 0; ParameterIdx < SourceStaticParameters.StaticSwitchParameters.Num(); ParameterIdx++)
{
FStaticSwitchParameter StaticSwitchParameterValue = FStaticSwitchParameter(SourceStaticParameters.StaticSwitchParameters[ParameterIdx]);
UDEditorStaticSwitchParameterValue* ParamValue = NewObject<UDEditorStaticSwitchParameterValue>(this);
UDEditorStaticSwitchParameterValue& ParameterValue = *ParamValue;
ParameterValue.ParameterValue = StaticSwitchParameterValue.Value;
ParameterValue.bOverride = StaticSwitchParameterValue.bOverride;
ParameterValue.ParameterInfo = StaticSwitchParameterValue.ParameterInfo;
ParameterValue.ExpressionId = StaticSwitchParameterValue.ExpressionGUID;
AssignGroupAndSortPriority(ParamValue, ExpressionParameterDataCache);
}
// Copy Static Component Mask Parameters
SourceInstance->GetAllStaticComponentMaskParameterInfo(OutParameterInfo, Guids);
for (int32 ParameterIdx = 0; ParameterIdx < SourceStaticParameters.StaticComponentMaskParameters.Num(); ParameterIdx++)
{
FStaticComponentMaskParameter StaticComponentMaskParameterValue = FStaticComponentMaskParameter(SourceStaticParameters.StaticComponentMaskParameters[ParameterIdx]);
UDEditorStaticComponentMaskParameterValue* ParamValue = NewObject<UDEditorStaticComponentMaskParameterValue>(this);
UDEditorStaticComponentMaskParameterValue& ParameterValue = *ParamValue;
ParameterValue.ParameterValue.R = StaticComponentMaskParameterValue.R;
ParameterValue.ParameterValue.G = StaticComponentMaskParameterValue.G;
ParameterValue.ParameterValue.B = StaticComponentMaskParameterValue.B;
ParameterValue.ParameterValue.A = StaticComponentMaskParameterValue.A;
ParameterValue.bOverride = StaticComponentMaskParameterValue.bOverride;
ParameterValue.ParameterInfo = StaticComponentMaskParameterValue.ParameterInfo;
ParameterValue.ExpressionId = StaticComponentMaskParameterValue.ExpressionGUID;
AssignGroupAndSortPriority(ParamValue, ExpressionParameterDataCache);
}
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);
}
// Parameter Collection
TArray<FMaterialParameterCollectionInfo> OutParameterInfo;
SourceInstance->GetAllCollectionParameterInfo(OutParameterInfo);
ParameterCollections.Empty();
for (const FMaterialParameterCollectionInfo& CollectionInfo: OutParameterInfo)
{
UDEditorMPCValue* MPCValue = NewObject<UDEditorMPCValue>(this);
UMaterialParameterCollection* OriginalCollection = CollectionInfo.ParameterCollection;
UDEditorMPCValue& ParameterValue = *MPCValue;
ParameterValue.bOverride = false;
ParameterValue.bIsValid = true;
// ParameterValue.ParameterInfo = ParameterInfo; // unused
// ParameterValue.ExpressionId = Guids[ParameterIdx]; // unused
ParameterValue.ParameterInfo.Name = FName(*CollectionInfo.ParameterCollection->GetName());
ParameterValue.OriginalCollection = OriginalCollection;
ParameterValue.OverridenCollection = OriginalCollection;
// Get collection along hierarchy
SourceInstance->GetCollectionParameterValue(OriginalCollection, ParameterValue.OverridenCollection);
FOverridenCollectionInfo* OverridenCollectionInfo = SourceInstance->ParameterCollectionMap.Find(OriginalCollection);
if (OverridenCollectionInfo)
{
ParameterValue.bOverride = true;
ParameterValue.bIsValid = OverridenCollectionInfo->bIsValid;
ParameterValue.OverridenCollection = OverridenCollectionInfo->Collection;
}
MPCValue->SetFlags(RF_Transactional); // Enable redo/undo
ParameterCollections.Add(MPCValue);
}
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 (FEditorParameterGroup Group: ParameterGroups)
{
for (UDEditorParameterValue* Parameter: Group.Parameters)
{
if (Parameter->ParameterInfo.Association == MaterialType && Parameter->ParameterInfo.Index == Index)
{
UDEditorScalarParameterValue* ScalarParameterValue = Cast<UDEditorScalarParameterValue>(Parameter);
UDEditorVectorParameterValue* VectorParameterValue = Cast<UDEditorVectorParameterValue>(Parameter);
UDEditorTextureParameterValue* TextureParameterValue = Cast<UDEditorTextureParameterValue>(Parameter);
UDEditorRuntimeVirtualTextureParameterValue* RuntimeVirtualTextureParameterValue = Cast<UDEditorRuntimeVirtualTextureParameterValue>(Parameter);
UDEditorFontParameterValue* FontParameterValue = Cast<UDEditorFontParameterValue>(Parameter);
UDEditorStaticSwitchParameterValue* StaticSwitchParameterValue = Cast<UDEditorStaticSwitchParameterValue>(Parameter);
UDEditorStaticComponentMaskParameterValue* StaticMaskParameterValue = Cast<UDEditorStaticComponentMaskParameterValue>(Parameter);
if (ScalarParameterValue)
{
float Value;
Parameter->bOverride = SourceInstance->GetScalarParameterValue(Parameter->ParameterInfo, Value, true);
}
if (VectorParameterValue)
{
FLinearColor Value;
Parameter->bOverride = SourceInstance->GetVectorParameterValue(Parameter->ParameterInfo, Value, true);
}
if (TextureParameterValue)
{
UTexture* Value;
Parameter->bOverride = SourceInstance->GetTextureParameterValue(Parameter->ParameterInfo, Value, true);
}
if (RuntimeVirtualTextureParameterValue)
{
URuntimeVirtualTexture* Value;
Parameter->bOverride = SourceInstance->GetRuntimeVirtualTextureParameterValue(Parameter->ParameterInfo, Value, true);
}
if (FontParameterValue)
{
UFont* FontValue;
int32 FontPage;
Parameter->bOverride = SourceInstance->GetFontParameterValue(Parameter->ParameterInfo, FontValue, FontPage, true);
}
if (StaticSwitchParameterValue)
{
bool Value;
FGuid ExpressionId;
Parameter->bOverride = SourceInstance->GetStaticSwitchParameterValue(Parameter->ParameterInfo, Value, ExpressionId, true);
}
if (StaticMaskParameterValue)
{
bool R;
bool G;
bool B;
bool A;
FGuid ExpressionId;
Parameter->bOverride = SourceInstance->GetStaticComponentMaskParameterValue(Parameter->ParameterInfo, R, G, B, A, ExpressionId, true);
}
}
}
}
CopyToSourceInstance(true);
}
#endif
void UMaterialEditorInstanceConstant::CopyToSourceInstance(const bool bForceStaticPermutationUpdate)
{
if (SourceInstance && !SourceInstance->IsTemplate(RF_ClassDefaultObject))
{
if (bIsFunctionPreviewMaterial)
{
bIsFunctionInstanceDirty = true;
}
else
{
SourceInstance->MarkPackageDirty();
}
SourceInstance->ClearParameterValuesEditorOnly();
for (int32 GroupIdx = 0; GroupIdx < ParameterGroups.Num(); GroupIdx++)
{
FEditorParameterGroup& Group = ParameterGroups[GroupIdx];
for (int32 ParameterIdx = 0; ParameterIdx < Group.Parameters.Num(); ParameterIdx++)
{
if (Group.Parameters[ParameterIdx] == NULL)
{
continue;
}
UDEditorScalarParameterValue* ScalarParameterValue = Cast<UDEditorScalarParameterValue>(Group.Parameters[ParameterIdx]);
UDEditorVectorParameterValue* VectorParameterValue = Cast<UDEditorVectorParameterValue>(Group.Parameters[ParameterIdx]);
UDEditorTextureParameterValue* TextureParameterValue = Cast<UDEditorTextureParameterValue>(Group.Parameters[ParameterIdx]);
UDEditorRuntimeVirtualTextureParameterValue* RuntimeVirtualTextureParameterValue = Cast<UDEditorRuntimeVirtualTextureParameterValue>(Group.Parameters[ParameterIdx]);
UDEditorFontParameterValue* FontParameterValue = Cast<UDEditorFontParameterValue>(Group.Parameters[ParameterIdx]);
if (ScalarParameterValue && ScalarParameterValue->bOverride)
{
SourceInstance->SetScalarParameterValueEditorOnly(ScalarParameterValue->ParameterInfo, ScalarParameterValue->ParameterValue);
// Copy from editor parameter to saved FParameter
if (ScalarParameterValue->AtlasData.bIsUsedAsAtlasPosition)
{
FScalarParameterAtlasInstanceData InAtlasData = FScalarParameterAtlasInstanceData();
InAtlasData.bIsUsedAsAtlasPosition = ScalarParameterValue->AtlasData.bIsUsedAsAtlasPosition;
InAtlasData.Curve = ScalarParameterValue->AtlasData.Curve;
InAtlasData.Atlas = ScalarParameterValue->AtlasData.Atlas;
SourceInstance->SetScalarParameterAtlasEditorOnly(ScalarParameterValue->ParameterInfo, InAtlasData);
}
}
else if (VectorParameterValue && VectorParameterValue->bOverride)
{
SourceInstance->SetVectorParameterValueEditorOnly(VectorParameterValue->ParameterInfo, VectorParameterValue->ParameterValue);
}
else if (TextureParameterValue && TextureParameterValue->bOverride)
{
SourceInstance->SetTextureParameterValueEditorOnly(TextureParameterValue->ParameterInfo, TextureParameterValue->ParameterValue);
}
else if (RuntimeVirtualTextureParameterValue && RuntimeVirtualTextureParameterValue->bOverride)
{
SourceInstance->SetRuntimeVirtualTextureParameterValueEditorOnly(RuntimeVirtualTextureParameterValue->ParameterInfo, RuntimeVirtualTextureParameterValue->ParameterValue);
}
else if (FontParameterValue && FontParameterValue->bOverride)
{
SourceInstance->SetFontParameterValueEditorOnly(FontParameterValue->ParameterInfo, FontParameterValue->ParameterValue.FontValue, FontParameterValue->ParameterValue.FontPage);
}
}
}
// Parameter Collection
for (const auto& MPCValue: ParameterCollections)
{
if (MPCValue->bHasChange)
{
SourceInstance->bOverrideMPC = true;
MPCValue->bHasChange = false;
}
if (MPCValue->bOverride && MPCValue->OverridenCollection != nullptr)
{
MPCValue->bIsValid = SourceInstance->SetCollectionParameterValueEditorOnly(MPCValue->OriginalCollection, MPCValue->OverridenCollection);
}
}
FStaticParameterSet NewStaticParameters;
BuildStaticParametersForSourceInstance(NewStaticParameters);
SourceInstance->UpdateStaticPermutation(NewStaticParameters, BasePropertyOverrides, bForceStaticPermutationUpdate);
// Copy phys material back to source instance
SourceInstance->PhysMaterial = PhysMaterial;
SourceInstance->NextPass = NextPass;
// SourceInstance->ParameterCollections = ParameterCollections;
// 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::BuildStaticParametersForSourceInstance(FStaticParameterSet& OutStaticParameters)
{
for (int32 GroupIdx = 0; GroupIdx < ParameterGroups.Num(); GroupIdx++)
{
FEditorParameterGroup& Group = ParameterGroups[GroupIdx];
for (int32 ParameterIdx = 0; ParameterIdx < Group.Parameters.Num(); ParameterIdx++)
{
if (Group.Parameters[ParameterIdx] == NULL)
{
continue;
}
// Static switch
UDEditorStaticSwitchParameterValue* StaticSwitchParameterValue = Cast<UDEditorStaticSwitchParameterValue>(Group.Parameters[ParameterIdx]);
if (StaticSwitchParameterValue && StaticSwitchParameterValue->bOverride)
{
bool SwitchValue = StaticSwitchParameterValue->ParameterValue;
FGuid ExpressionIdValue = StaticSwitchParameterValue->ExpressionId;
FStaticSwitchParameter* NewParameter = new (OutStaticParameters.StaticSwitchParameters)
FStaticSwitchParameter(StaticSwitchParameterValue->ParameterInfo, SwitchValue, StaticSwitchParameterValue->bOverride, ExpressionIdValue);
}
// Static component mask
UDEditorStaticComponentMaskParameterValue* StaticComponentMaskParameterValue = Cast<UDEditorStaticComponentMaskParameterValue>(Group.Parameters[ParameterIdx]);
if (StaticComponentMaskParameterValue && StaticComponentMaskParameterValue->bOverride)
{
bool MaskR = StaticComponentMaskParameterValue->ParameterValue.R;
bool MaskG = StaticComponentMaskParameterValue->ParameterValue.G;
bool MaskB = StaticComponentMaskParameterValue->ParameterValue.B;
bool MaskA = StaticComponentMaskParameterValue->ParameterValue.A;
FGuid ExpressionIdValue = StaticComponentMaskParameterValue->ExpressionId;
FStaticComponentMaskParameter* NewParameter = new (OutStaticParameters.StaticComponentMaskParameters)
FStaticComponentMaskParameter(StaticComponentMaskParameterValue->ParameterInfo, MaskR, MaskG, MaskB, MaskA, StaticComponentMaskParameterValue->bOverride, ExpressionIdValue);
}
// Material layers param
UDEditorMaterialLayersParameterValue* MaterialLayersParameterValue = Cast<UDEditorMaterialLayersParameterValue>(Group.Parameters[ParameterIdx]);
if (MaterialLayersParameterValue && MaterialLayersParameterValue->bOverride)
{
const FMaterialLayersFunctions& MaterialLayers = MaterialLayersParameterValue->ParameterValue;
FGuid ExpressionIdValue = MaterialLayersParameterValue->ExpressionId;
FStaticMaterialLayersParameter* NewParameter = new (OutStaticParameters.MaterialLayersParameters)
FStaticMaterialLayersParameter(MaterialLayersParameterValue->ParameterInfo, MaterialLayers, MaterialLayersParameterValue->bOverride, ExpressionIdValue);
}
}
}
}
void UMaterialEditorInstanceConstant::SetSourceInstance(UMaterialInstanceConstant* MaterialInterface)
{
check(MaterialInterface);
SourceInstance = MaterialInterface;
Parent = SourceInstance->Parent;
PhysMaterial = SourceInstance->PhysMaterial;
NextPass = SourceInstance->NextPass;
CopyBasePropertiesFromParent();
RegenerateArrays();
// propagate changes to the base material so the instance will be updated if it has a static permutation resource
FStaticParameterSet NewStaticParameters;
BuildStaticParametersForSourceInstance(NewStaticParameters);
SourceInstance->UpdateStaticPermutation(NewStaticParameters);
}
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.DitheredLODTransition)
{
BasePropertyOverrides.DitheredLODTransition = SourceInstance->IsDitheredLODTransition();
}
if (!BasePropertyOverrides.bOverride_ResponsiveAA)
{
BasePropertyOverrides.ResponsiveAA = SourceInstance->ShouldEnableResponsiveAA();
}
if (!BasePropertyOverrides.bOverride_AlphaOutline)
{
BasePropertyOverrides.AlphaOutline = SourceInstance->ShouldEnableAlphaOutline();
}
if (!BasePropertyOverrides.bOverride_OnlyWriteStencil)
{
BasePropertyOverrides.OnlyWriteStencil = SourceInstance->ShouldOnlyWriteStencil();
}
if (!BasePropertyOverrides.bOverride_WriteStencil)
{
BasePropertyOverrides.WriteStencil = SourceInstance->ShouldWriteStencil();
}
if (!BasePropertyOverrides.bOverride_BlendInBasePass)
{
BasePropertyOverrides.BlendInBasePass = SourceInstance->ShouldBlendInBasePass();
}
if (!BasePropertyOverrides.bOverride_BasePassSortPriority)
{
BasePropertyOverrides.BasePassSortPriority = SourceInstance->GetBasePassSortPriority();
}
// 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)
{
}