// 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> 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 GlobalParameters; FName LayerParameterName = NAME_None; TArray> LayerParameters; TArray> 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& ParamDatas) { if (ParameterFunction) { TArray Functions; ParameterFunction->GetDependentFunctions(Functions); Functions.AddUnique(ParameterFunction); for (UMaterialFunctionInterface* Function: Functions) { for (UMaterialExpression* FunctionExpression: *Function->GetFunctionExpressions()) { if (const UMaterialExpressionParameter* Parameter = Cast(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(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(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(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(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(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(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(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(Expression)) { if (FuncParameter->MaterialFunction) { if (UMaterialFunctionInterface* ParameterFunction = FuncParameter->MaterialFunction->GetBaseFunction()) { CacheMaterialFunctionParameterData(ParameterFunction, ParamCache.GlobalParameters); } } } else if (UMaterialExpressionMaterialAttributeLayers* LayerParameter = Cast(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& LayerCache = ParamCache.LayerParameters.AddDefaulted_GetRef(); CacheMaterialFunctionParameterData(Layer, LayerCache); } // Cache blend parameters for (UMaterialFunctionInterface* Blend: StaticLayers->Value.Blends) { TMap& BlendCache = ParamCache.LayerParameters.AddDefaulted_GetRef(); CacheMaterialFunctionParameterData(Blend, BlendCache); } } else { // Cache layer parameters for (UMaterialFunctionInterface* Layer: LayerParameter->GetLayers()) { TMap& LayerCache = ParamCache.LayerParameters.AddDefaulted_GetRef(); CacheMaterialFunctionParameterData(Layer, LayerCache); } // Cache blend parameters for (UMaterialFunctionInterface* Blend: LayerParameter->GetBlends()) { TMap& 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("MaterialEditor"); // Material layers UDEditorMaterialLayersParameterValue* MaterialLayerParam = Cast(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 ParameterInfo; TArray Guids; ParentMaterial->GetAllVectorParameterInfo(ParameterInfo, Guids); // Vector Parameters. for (int32 ParameterIdx = 0; ParameterIdx < ParameterInfo.Num(); ParameterIdx++) { UDEditorVectorParameterValue& ParameterValue = *(NewObject(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(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(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(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(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(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(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(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 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(Group.Parameters[ParameterIdx]); if (ScalarParameterValue) { PreviewMaterial->SetScalarParameterValueEditorOnly(ScalarParameterValue->ParameterInfo.Name, ScalarParameterValue->ParameterValue); continue; } UDEditorFontParameterValue* FontParameterValue = Cast(Group.Parameters[ParameterIdx]); if (FontParameterValue) { PreviewMaterial->SetFontParameterValueEditorOnly(FontParameterValue->ParameterInfo.Name, FontParameterValue->ParameterValue.FontValue, FontParameterValue->ParameterValue.FontPage); continue; } UDEditorTextureParameterValue* TextureParameterValue = Cast(Group.Parameters[ParameterIdx]); if (TextureParameterValue) { PreviewMaterial->SetTextureParameterValueEditorOnly(TextureParameterValue->ParameterInfo.Name, TextureParameterValue->ParameterValue); continue; } UDEditorRuntimeVirtualTextureParameterValue* RuntimeVirtualTextureParameterValue = Cast(Group.Parameters[ParameterIdx]); if (RuntimeVirtualTextureParameterValue) { PreviewMaterial->SetRuntimeVirtualTextureParameterValueEditorOnly(RuntimeVirtualTextureParameterValue->ParameterInfo.Name, RuntimeVirtualTextureParameterValue->ParameterValue); continue; } UDEditorVectorParameterValue* VectorParameterValue = Cast(Group.Parameters[ParameterIdx]); if (VectorParameterValue) { PreviewMaterial->SetVectorParameterValueEditorOnly(VectorParameterValue->ParameterInfo.Name, VectorParameterValue->ParameterValue); continue; } UDEditorStaticComponentMaskParameterValue* MaskParameterValue = Cast(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(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(Group.Parameters[ParameterIdx]); if (ScalarParameterValue) { OriginalFunction->SetScalarParameterValueEditorOnly(ScalarParameterValue->ParameterInfo.Name, ScalarParameterValue->ParameterValue); continue; } UDEditorFontParameterValue* FontParameterValue = Cast(Group.Parameters[ParameterIdx]); if (FontParameterValue) { OriginalFunction->SetFontParameterValueEditorOnly(FontParameterValue->ParameterInfo.Name, FontParameterValue->ParameterValue.FontValue, FontParameterValue->ParameterValue.FontPage); continue; } UDEditorTextureParameterValue* TextureParameterValue = Cast(Group.Parameters[ParameterIdx]); if (TextureParameterValue) { OriginalFunction->SetTextureParameterValueEditorOnly(TextureParameterValue->ParameterInfo.Name, TextureParameterValue->ParameterValue); continue; } UDEditorRuntimeVirtualTextureParameterValue* RuntimeVirtualTextureParameterValue = Cast(Group.Parameters[ParameterIdx]); if (RuntimeVirtualTextureParameterValue) { OriginalFunction->SetRuntimeVirtualTextureParameterValueEditorOnly(RuntimeVirtualTextureParameterValue->ParameterInfo.Name, RuntimeVirtualTextureParameterValue->ParameterValue); continue; } UDEditorVectorParameterValue* VectorParameterValue = Cast(Group.Parameters[ParameterIdx]); if (VectorParameterValue) { OriginalFunction->SetVectorParameterValueEditorOnly(VectorParameterValue->ParameterInfo.Name, VectorParameterValue->ParameterValue); continue; } UDEditorStaticComponentMaskParameterValue* MaskParameterValue = Cast(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(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(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(ParameterValue)) { ParameterGroupName = TEXT("Vector Parameter Values"); } else if (Cast(ParameterValue)) { ParameterGroupName = TEXT("Texture Parameter Values"); } else if (Cast(ParameterValue)) { ParameterGroupName = TEXT("Texture Parameter Values"); } else if (Cast(ParameterValue)) { ParameterGroupName = TEXT("Scalar Parameter Values"); } else if (Cast(ParameterValue)) { ParameterGroupName = TEXT("Static Switch Parameter Values"); } else if (Cast(ParameterValue)) { ParameterGroupName = TEXT("Static Component Mask Parameter Values"); } else if (Cast(ParameterValue)) { ParameterGroupName = TEXT("Font Parameter Values"); } else if (Cast(ParameterValue)) { ParameterGroupName = TEXT("Material Layers Parameter Values"); } else { ParameterGroupName = TEXT("None"); } } else { ParameterGroupName = TEXT("None"); } IMaterialEditorModule* MaterialEditorModule = &FModuleManager::LoadModuleChecked("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 OutParameterInfo; TArray 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(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(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(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(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(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(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(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(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("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 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 OutParameterInfo; SourceInstance->GetAllCollectionParameterInfo(OutParameterInfo); ParameterCollections.Empty(); for (const FMaterialParameterCollectionInfo& CollectionInfo: OutParameterInfo) { UDEditorMPCValue* MPCValue = NewObject(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 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(Parameter); UDEditorVectorParameterValue* VectorParameterValue = Cast(Parameter); UDEditorTextureParameterValue* TextureParameterValue = Cast(Parameter); UDEditorRuntimeVirtualTextureParameterValue* RuntimeVirtualTextureParameterValue = Cast(Parameter); UDEditorFontParameterValue* FontParameterValue = Cast(Parameter); UDEditorStaticSwitchParameterValue* StaticSwitchParameterValue = Cast(Parameter); UDEditorStaticComponentMaskParameterValue* StaticMaskParameterValue = Cast(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(Group.Parameters[ParameterIdx]); UDEditorVectorParameterValue* VectorParameterValue = Cast(Group.Parameters[ParameterIdx]); UDEditorTextureParameterValue* TextureParameterValue = Cast(Group.Parameters[ParameterIdx]); UDEditorRuntimeVirtualTextureParameterValue* RuntimeVirtualTextureParameterValue = Cast(Group.Parameters[ParameterIdx]); UDEditorFontParameterValue* FontParameterValue = Cast(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("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(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(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(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) { }