1257 lines
44 KiB
C++
1257 lines
44 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "MaterialEditor/PreviewMaterial.h"
|
|
#include "Modules/ModuleManager.h"
|
|
#include "MaterialEditor/DEditorParameterValue.h"
|
|
#include "MaterialEditor/DEditorFontParameterValue.h"
|
|
#include "MaterialEditor/DEditorMaterialLayersParameterValue.h"
|
|
#include "MaterialEditor/DEditorRuntimeVirtualTextureParameterValue.h"
|
|
#include "MaterialEditor/DEditorScalarParameterValue.h"
|
|
#include "MaterialEditor/DEditorStaticComponentMaskParameterValue.h"
|
|
#include "MaterialEditor/DEditorStaticSwitchParameterValue.h"
|
|
#include "MaterialEditor/DEditorTextureParameterValue.h"
|
|
#include "MaterialEditor/DEditorVectorParameterValue.h"
|
|
#include "MaterialEditor/DEditorDoubleVectorParameterValue.h"
|
|
#include "AI/NavigationSystemBase.h"
|
|
#include "MaterialEditor/MaterialEditorInstanceConstant.h"
|
|
#include "MaterialEditor/MaterialEditorPreviewParameters.h"
|
|
#include "MaterialEditor/MaterialEditorMeshComponent.h"
|
|
#include "MaterialEditorModule.h"
|
|
#include "MaterialCachedData.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 "Materials/MaterialFunction.h"
|
|
#include "UObject/UObjectIterator.h"
|
|
#include "PropertyEditorDelegates.h"
|
|
#include "IDetailsView.h"
|
|
#include "MaterialEditingLibrary.h"
|
|
#include "MaterialPropertyHelpers.h"
|
|
#include "MaterialStatsCommon.h"
|
|
#include "MaterialDomain.h"
|
|
#include "RenderUtils.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)) &&
|
|
VertexFactoryType != FindVertexFactoryType(FName(TEXT("FNaniteVertexFactory"), 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;
|
|
}
|
|
|
|
if (
|
|
VertexFactoryType != FindVertexFactoryType(FName(TEXT("TGPUSkinVertexFactoryDefault"), FNAME_Find)) &&
|
|
VertexFactoryType != FindVertexFactoryType(FName(TEXT("TGPUSkinVertexFactoryUnlimited"), FNAME_Find))
|
|
)
|
|
{
|
|
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("TBasePassCSFNoLightMapPolicy")))
|
|
{
|
|
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("FDebugViewModePS")))
|
|
{
|
|
bShaderTypeMatches = true;
|
|
}
|
|
else if (FCString::Stristr(ShaderType->GetName(), TEXT("FVelocity")))
|
|
{
|
|
bShaderTypeMatches = true;
|
|
}
|
|
else if (FCString::Stristr(ShaderType->GetName(), TEXT("FAnisotropy")))
|
|
{
|
|
bShaderTypeMatches = true;
|
|
}
|
|
else if (FCString::Stristr(ShaderType->GetName(), TEXT("RayTracingDynamicGeometryConverter")))
|
|
{
|
|
bShaderTypeMatches = true;
|
|
}
|
|
else if (FCString::Stristr(ShaderType->GetName(), TEXT("FLumenCard")))
|
|
{
|
|
bShaderTypeMatches = true;
|
|
}
|
|
else if (FCString::Stristr(ShaderType->GetName(), TEXT("FLumenFrontLayerTranslucencyGBuffer")))
|
|
{
|
|
bShaderTypeMatches = true;
|
|
}
|
|
|
|
return bShaderTypeMatches;
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Should shaders compiled for this material be saved to disk?
|
|
*/
|
|
virtual bool IsPersistent() const override { return false; }
|
|
virtual FString GetAssetName() const override { return FString::Printf(TEXT("Preview:%s"), *FMaterialResource::GetAssetName()); }
|
|
|
|
virtual bool IsPreview() const override { return true; }
|
|
};
|
|
|
|
/** Implementation of Preview Material functions*/
|
|
UPreviewMaterial::UPreviewMaterial(const FObjectInitializer& ObjectInitializer)
|
|
: Super(ObjectInitializer)
|
|
{
|
|
}
|
|
|
|
FMaterialResource* UPreviewMaterial::AllocateResource()
|
|
{
|
|
return new FPreviewMaterial();
|
|
}
|
|
|
|
void UMaterialEditorPreviewParameters::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
|
|
{
|
|
if (PreviewMaterial && PropertyChangedEvent.ChangeType != EPropertyChangeType::Interactive)
|
|
{
|
|
FProperty* PropertyThatChanged = PropertyChangedEvent.Property;
|
|
if (OriginalFunction == nullptr)
|
|
{
|
|
bool bLayersParameterChanged = false;
|
|
// 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))
|
|
{
|
|
UMaterialExpressionMaterialAttributeLayers* LayersNode = nullptr;
|
|
for(UMaterialExpression* Expression : PreviewMaterial->GetExpressions())
|
|
{
|
|
if(Expression->IsA<UMaterialExpressionMaterialAttributeLayers>())
|
|
{
|
|
LayersNode = Cast<UMaterialExpressionMaterialAttributeLayers>(Expression);
|
|
LayersNode->DefaultLayers = LayersParam->ParameterValue;
|
|
bLayersParameterChanged = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bLayersParameterChanged)
|
|
{
|
|
RegenerateArrays();
|
|
}
|
|
|
|
CopyToSourceInstance();
|
|
PreviewMaterial->PostEditChangeProperty(PropertyChangedEvent);
|
|
}
|
|
else
|
|
{
|
|
ApplySourceFunctionChanges();
|
|
if (OriginalFunction->PreviewMaterial)
|
|
{
|
|
OriginalFunction->PreviewMaterial->PostEditChangeProperty(PropertyChangedEvent);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UMaterialEditorPreviewParameters::AssignParameterToGroup(UDEditorParameterValue* ParameterValue, const FName& InParameterGroupName)
|
|
{
|
|
check(ParameterValue);
|
|
|
|
FName ParameterGroupName = InParameterGroupName;
|
|
if (ParameterGroupName == TEXT("") || ParameterGroupName == TEXT("None"))
|
|
{
|
|
ParameterGroupName = TEXT("None");
|
|
}
|
|
IMaterialEditorModule* MaterialEditorModule = &FModuleManager::LoadModuleChecked<IMaterialEditorModule>("MaterialEditor");
|
|
|
|
// Material layers
|
|
UDEditorMaterialLayersParameterValue* MaterialLayerParam = Cast<UDEditorMaterialLayersParameterValue>(ParameterValue);
|
|
if (ParameterValue->ParameterInfo.Association == EMaterialParameterAssociation::GlobalParameter)
|
|
{
|
|
if (MaterialLayerParam)
|
|
{
|
|
ParameterGroupName = FMaterialPropertyHelpers::LayerParamName;
|
|
}
|
|
else
|
|
{
|
|
FString AppendedGroupName = GlobalGroupPrefix.ToString();
|
|
if (ParameterGroupName != TEXT("None"))
|
|
{
|
|
ParameterGroupName.AppendString(AppendedGroupName);
|
|
ParameterGroupName = FName(*AppendedGroupName);
|
|
}
|
|
else
|
|
{
|
|
ParameterGroupName = TEXT("Global");
|
|
}
|
|
}
|
|
}
|
|
|
|
FEditorParameterGroup& CurrentGroup = FMaterialPropertyHelpers::GetParameterGroup(PreviewMaterial, ParameterGroupName, ParameterGroups);
|
|
CurrentGroup.GroupAssociation = ParameterValue->ParameterInfo.Association;
|
|
ParameterValue->SetFlags(RF_Transactional);
|
|
CurrentGroup.Parameters.Add(ParameterValue);
|
|
}
|
|
|
|
void UMaterialEditorPreviewParameters::RegenerateArrays()
|
|
{
|
|
ParameterGroups.Empty();
|
|
if (PreviewMaterial)
|
|
{
|
|
// Only operate on base materials
|
|
UMaterial* ParentMaterial = PreviewMaterial;
|
|
|
|
// This can run before UMaterial::PostEditChangeProperty has a chance to run, so explicitly call UpdateCachedExpressionData here
|
|
PreviewMaterial->UpdateCachedExpressionData();
|
|
|
|
// Loop through all types of parameters for this material and add them to the parameter arrays.
|
|
TMap<FMaterialParameterInfo, FMaterialParameterMetadata> ParameterValues;
|
|
for (int32 TypeIndex = 0; TypeIndex < NumMaterialParameterTypes; ++TypeIndex)
|
|
{
|
|
const EMaterialParameterType ParameterType = (EMaterialParameterType)TypeIndex;
|
|
|
|
ParentMaterial->GetAllParametersOfType(ParameterType, ParameterValues);
|
|
for (const auto& It : ParameterValues)
|
|
{
|
|
UDEditorParameterValue* Parameter = UDEditorParameterValue::Create(this, ParameterType, It.Key, It.Value);
|
|
Parameter->bOverride = true;
|
|
AssignParameterToGroup(Parameter, It.Value.Group);
|
|
}
|
|
}
|
|
|
|
// Static Material Layers
|
|
{
|
|
FMaterialLayersFunctions MaterialLayers;
|
|
if (ParentMaterial->GetMaterialLayers(MaterialLayers))
|
|
{
|
|
UDEditorMaterialLayersParameterValue* ParameterValue = NewObject<UDEditorMaterialLayersParameterValue>(this);
|
|
ParameterValue->bOverride = true;
|
|
ParameterValue->ParameterValue = MoveTemp(MaterialLayers);
|
|
AssignParameterToGroup(ParameterValue, FName());
|
|
}
|
|
}
|
|
}
|
|
// sort contents of groups
|
|
for (int32 ParameterIdx = 0; ParameterIdx < ParameterGroups.Num(); ParameterIdx++)
|
|
{
|
|
FEditorParameterGroup & ParamGroup = ParameterGroups[ParameterIdx];
|
|
struct FCompareUDEditorParameterValueByParameterName
|
|
{
|
|
FORCEINLINE bool operator()(const UDEditorParameterValue& A, const UDEditorParameterValue& B) const
|
|
{
|
|
FString AName = A.ParameterInfo.Name.ToString();
|
|
FString BName = B.ParameterInfo.Name.ToString();
|
|
return A.SortPriority != B.SortPriority ? A.SortPriority < B.SortPriority : AName < BName;
|
|
}
|
|
};
|
|
ParamGroup.Parameters.Sort(FCompareUDEditorParameterValueByParameterName());
|
|
}
|
|
|
|
// sort groups itself pushing defaults to end
|
|
struct FCompareFEditorParameterGroupByName
|
|
{
|
|
FORCEINLINE bool operator()(const FEditorParameterGroup& A, const FEditorParameterGroup& B) const
|
|
{
|
|
FString AName = A.GroupName.ToString();
|
|
FString BName = B.GroupName.ToString();
|
|
if (AName == TEXT("none"))
|
|
{
|
|
return false;
|
|
}
|
|
if (BName == TEXT("none"))
|
|
{
|
|
return false;
|
|
}
|
|
return A.GroupSortPriority != B.GroupSortPriority ? A.GroupSortPriority < B.GroupSortPriority : AName < BName;
|
|
}
|
|
};
|
|
ParameterGroups.Sort(FCompareFEditorParameterGroupByName());
|
|
TArray<struct FEditorParameterGroup> ParameterDefaultGroups;
|
|
for (int32 ParameterIdx = 0; ParameterIdx < ParameterGroups.Num(); ParameterIdx++)
|
|
{
|
|
FEditorParameterGroup & ParamGroup = ParameterGroups[ParameterIdx];
|
|
|
|
if (ParamGroup.GroupName == TEXT("None"))
|
|
{
|
|
ParameterDefaultGroups.Add(ParamGroup);
|
|
ParameterGroups.RemoveAt(ParameterIdx);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ParameterDefaultGroups.Num() > 0)
|
|
{
|
|
ParameterGroups.Append(ParameterDefaultGroups);
|
|
}
|
|
|
|
}
|
|
|
|
TObjectPtr<UMaterialInterface> UMaterialEditorPreviewParameters::GetMaterialInterface()
|
|
{
|
|
return Cast<UMaterialInterface>(PreviewMaterial);
|
|
}
|
|
|
|
void UMaterialEditorPreviewParameters::CopyToSourceInstance(const bool bForceStaticPermutationUpdate/* = false*/)
|
|
{
|
|
if (PreviewMaterial->IsTemplate(RF_ClassDefaultObject) == false && OriginalMaterial != nullptr)
|
|
{
|
|
OriginalMaterial->MarkPackageDirty();
|
|
|
|
// Scalar Parameters
|
|
for (int32 GroupIdx = 0; GroupIdx < ParameterGroups.Num(); GroupIdx++)
|
|
{
|
|
FEditorParameterGroup & Group = ParameterGroups[GroupIdx];
|
|
for (int32 ParameterIdx = 0; ParameterIdx < Group.Parameters.Num(); ParameterIdx++)
|
|
{
|
|
UDEditorParameterValue* Parameter = Group.Parameters[ParameterIdx];
|
|
if (Parameter)
|
|
{
|
|
FMaterialParameterMetadata EditorValue;
|
|
if (Parameter->GetValue(EditorValue))
|
|
{
|
|
PreviewMaterial->SetParameterValueEditorOnly(Parameter->ParameterInfo.Name, EditorValue);
|
|
}
|
|
else if (UDEditorMaterialLayersParameterValue* LayersParameter = Cast<UDEditorMaterialLayersParameterValue>(Parameter))
|
|
{
|
|
// find material expression material attribute layer and update it's default/param layers
|
|
for(UMaterialExpression* Expression : PreviewMaterial->GetExpressions())
|
|
{
|
|
if(Expression->IsA<UMaterialExpressionMaterialAttributeLayers>())
|
|
{
|
|
UMaterialExpressionMaterialAttributeLayers* LayersNode = Cast<UMaterialExpressionMaterialAttributeLayers>(Expression);
|
|
LayersNode->DefaultLayers = LayersParameter->ParameterValue;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FName UMaterialEditorPreviewParameters::GlobalGroupPrefix = FName("Global ");
|
|
|
|
void UMaterialEditorPreviewParameters::ApplySourceFunctionChanges()
|
|
{
|
|
if (OriginalFunction != nullptr)
|
|
{
|
|
CopyToSourceInstance();
|
|
|
|
OriginalFunction->MarkPackageDirty();
|
|
// Scalar Parameters
|
|
for (int32 GroupIdx = 0; GroupIdx < ParameterGroups.Num(); GroupIdx++)
|
|
{
|
|
FEditorParameterGroup & Group = ParameterGroups[GroupIdx];
|
|
for (int32 ParameterIdx = 0; ParameterIdx < Group.Parameters.Num(); ParameterIdx++)
|
|
{
|
|
UDEditorParameterValue* Parameter = Group.Parameters[ParameterIdx];
|
|
if (Parameter)
|
|
{
|
|
FMaterialParameterMetadata EditorValue;
|
|
if (Parameter->GetValue(EditorValue))
|
|
{
|
|
OriginalFunction->SetParameterValueEditorOnly(Parameter->ParameterInfo.Name, EditorValue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
UMaterialEditingLibrary::UpdateMaterialFunction(OriginalFunction, PreviewMaterial);
|
|
}
|
|
}
|
|
|
|
|
|
#if WITH_EDITOR
|
|
void UMaterialEditorPreviewParameters::PostEditUndo()
|
|
{
|
|
Super::PostEditUndo();
|
|
}
|
|
#endif
|
|
|
|
|
|
FName UMaterialEditorInstanceConstant::GlobalGroupPrefix = FName("Global ");
|
|
|
|
UMaterialEditorInstanceConstant::UMaterialEditorInstanceConstant(const FObjectInitializer& ObjectInitializer)
|
|
: Super(ObjectInitializer)
|
|
{
|
|
bIsFunctionPreviewMaterial = false;
|
|
bShowOnlyOverrides = false;
|
|
|
|
// Default to override with nothing on MIC (don't inherit parent setting).
|
|
bNaniteOverride = true;
|
|
}
|
|
|
|
void UMaterialEditorInstanceConstant::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
|
|
{
|
|
if (SourceInstance)
|
|
{
|
|
// Warn our source instance that it is about to be updated.
|
|
SourceInstance->PreEditChange(PropertyChangedEvent.Property);
|
|
|
|
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);
|
|
|
|
ClearInvalidParameterOverrides();
|
|
}
|
|
}
|
|
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->SetMaterialLayers(LayersParam->ParameterValue))
|
|
{
|
|
bLayersParameterChanged = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bLayersParameterChanged)
|
|
{
|
|
RegenerateArrays();
|
|
}
|
|
}
|
|
else if (bIsFunctionPreviewMaterial)
|
|
{
|
|
// If a property changed and we are editing a material function instance
|
|
// we should dirty the function and apply the parameter changes to the source material
|
|
// This way you get live previews and updates of the thumbnails.
|
|
bIsFunctionInstanceDirty = true;
|
|
ApplySourceFunctionChanges();
|
|
}
|
|
|
|
CopyToSourceInstance(bLayersParameterChanged);
|
|
|
|
// Tell our source instance to update itself so the preview updates.
|
|
SourceInstance->PostEditChangeProperty(PropertyChangedEvent);
|
|
|
|
// Invalidate the streaming data so that it gets rebuilt.
|
|
SourceInstance->TextureStreamingData.Empty();
|
|
}
|
|
}
|
|
|
|
void UMaterialEditorInstanceConstant::AssignParameterToGroup(UDEditorParameterValue* ParameterValue, const FName& InParameterGroupName)
|
|
{
|
|
check(ParameterValue);
|
|
|
|
FName ParameterGroupName = InParameterGroupName;
|
|
if (ParameterGroupName == TEXT("") || ParameterGroupName == TEXT("None"))
|
|
{
|
|
if (bUseOldStyleMICEditorGroups == true)
|
|
{
|
|
ParameterGroupName = ParameterValue->GetDefaultGroupName();
|
|
}
|
|
else
|
|
{
|
|
ParameterGroupName = TEXT("None");
|
|
}
|
|
|
|
IMaterialEditorModule* MaterialEditorModule = &FModuleManager::LoadModuleChecked<IMaterialEditorModule>("MaterialEditor");
|
|
|
|
// Material layers
|
|
if (ParameterValue->ParameterInfo.Association == EMaterialParameterAssociation::GlobalParameter)
|
|
{
|
|
FString AppendedGroupName = GlobalGroupPrefix.ToString();
|
|
if (ParameterGroupName != TEXT("None"))
|
|
{
|
|
ParameterGroupName.AppendString(AppendedGroupName);
|
|
ParameterGroupName = FName(*AppendedGroupName);
|
|
}
|
|
else
|
|
{
|
|
ParameterGroupName = TEXT("Global");
|
|
}
|
|
}
|
|
}
|
|
|
|
FEditorParameterGroup& CurrentGroup = FMaterialPropertyHelpers::GetParameterGroup(Parent->GetMaterial(), ParameterGroupName, ParameterGroups);
|
|
CurrentGroup.GroupAssociation = ParameterValue->ParameterInfo.Association;
|
|
ParameterValue->SetFlags(RF_Transactional);
|
|
CurrentGroup.Parameters.Add(ParameterValue);
|
|
}
|
|
|
|
TObjectPtr<UMaterialInterface> UMaterialEditorInstanceConstant::GetMaterialInterface()
|
|
{
|
|
return Cast<UMaterialInterface>(SourceInstance);
|
|
}
|
|
|
|
TObjectPtr<UMaterialInterface> UMaterialEditorInstanceConstant::GetParentMaterialInterface()
|
|
{
|
|
return SourceInstance ? SourceInstance->Parent : nullptr;
|
|
}
|
|
|
|
void UMaterialEditorInstanceConstant::RegenerateArrays()
|
|
{
|
|
VisibleExpressions.Empty();
|
|
ParameterGroups.Empty();
|
|
PostProcessOverrides.bIsOverrideable = false;
|
|
PostProcessOverrides.UserSceneTextureInputs.Empty();
|
|
|
|
if (Parent)
|
|
{
|
|
// Only operate on base materials
|
|
UMaterial* ParentMaterial = Parent->GetMaterial();
|
|
SourceInstance->UpdateParameterNames(); // Update any parameter names that may have changed.
|
|
SourceInstance->UpdateCachedData();
|
|
|
|
// Need to get layer info first as other params are collected from layers
|
|
{
|
|
FMaterialLayersFunctions MaterialLayers;
|
|
if (SourceInstance->GetMaterialLayers(MaterialLayers))
|
|
{
|
|
UDEditorMaterialLayersParameterValue& ParameterValue = *(NewObject<UDEditorMaterialLayersParameterValue>(this));
|
|
ParameterValue.bOverride = true;
|
|
ParameterValue.ParameterValue = MoveTemp(MaterialLayers);
|
|
AssignParameterToGroup(&ParameterValue, FName());
|
|
}
|
|
}
|
|
|
|
TMap<FMaterialParameterInfo, FMaterialParameterMetadata> ParameterValues;
|
|
for (int32 TypeIndex = 0; TypeIndex < NumMaterialParameterTypes; ++TypeIndex)
|
|
{
|
|
const EMaterialParameterType ParameterType = (EMaterialParameterType)TypeIndex;
|
|
SourceInstance->GetAllParametersOfType(ParameterType, ParameterValues);
|
|
for (const auto& It : ParameterValues)
|
|
{
|
|
UDEditorParameterValue* Parameter = UDEditorParameterValue::Create(this, ParameterType, It.Key, It.Value);
|
|
AssignParameterToGroup(Parameter, It.Value.Group);
|
|
}
|
|
}
|
|
|
|
if (ParentMaterial->MaterialDomain == MD_PostProcess && ParentMaterial->BlendableLocation != BL_ReplacingTonemapper)
|
|
{
|
|
PostProcessOverrides.bIsOverrideable = true;
|
|
|
|
UMaterialInterfaceEditorOnlyData* ParentMaterialEditorData = ParentMaterial->GetEditorOnlyData();
|
|
if (ParentMaterialEditorData && ParentMaterialEditorData->CachedExpressionData.IsValid())
|
|
{
|
|
const FMaterialCachedExpressionEditorOnlyData* ParentMaterialExpressionData = ParentMaterialEditorData->CachedExpressionData.Get();
|
|
for (FName UserSceneTextureInput : ParentMaterialExpressionData->UserSceneTextureInputs)
|
|
{
|
|
FName OverrideFound = NAME_None;
|
|
for (const FUserSceneTextureOverride& Override : SourceInstance->UserSceneTextureOverrides)
|
|
{
|
|
if (Override.Key == UserSceneTextureInput)
|
|
{
|
|
OverrideFound = Override.Value;
|
|
break;
|
|
}
|
|
}
|
|
|
|
PostProcessOverrides.UserSceneTextureInputs.Add({ UserSceneTextureInput, OverrideFound });
|
|
}
|
|
}
|
|
}
|
|
|
|
IMaterialEditorModule* MaterialEditorModule = &FModuleManager::LoadModuleChecked<IMaterialEditorModule>( "MaterialEditor" );
|
|
MaterialEditorModule->GetVisibleMaterialParameters(ParentMaterial, SourceInstance, VisibleExpressions);
|
|
}
|
|
|
|
// sort contents of groups
|
|
for(int32 ParameterIdx = 0; ParameterIdx < ParameterGroups.Num(); ParameterIdx++)
|
|
{
|
|
FEditorParameterGroup & ParamGroup = ParameterGroups[ParameterIdx];
|
|
struct FCompareUDEditorParameterValueByParameterName
|
|
{
|
|
FORCEINLINE bool operator()(const UDEditorParameterValue& A, const UDEditorParameterValue& B) const
|
|
{
|
|
FString AName = A.ParameterInfo.Name.ToString();
|
|
FString BName = B.ParameterInfo.Name.ToString();
|
|
return A.SortPriority != B.SortPriority ? A.SortPriority < B.SortPriority : AName < BName;
|
|
}
|
|
};
|
|
ParamGroup.Parameters.Sort( FCompareUDEditorParameterValueByParameterName() );
|
|
}
|
|
|
|
// sort groups itself pushing defaults to end
|
|
struct FCompareFEditorParameterGroupByName
|
|
{
|
|
FORCEINLINE bool operator()(const FEditorParameterGroup& A, const FEditorParameterGroup& B) const
|
|
{
|
|
FString AName = A.GroupName.ToString();
|
|
FString BName = B.GroupName.ToString();
|
|
if (AName == TEXT("none"))
|
|
{
|
|
return false;
|
|
}
|
|
if (BName == TEXT("none"))
|
|
{
|
|
return false;
|
|
}
|
|
return A.GroupSortPriority != B.GroupSortPriority ? A.GroupSortPriority < B.GroupSortPriority : AName < BName;
|
|
}
|
|
};
|
|
ParameterGroups.Sort( FCompareFEditorParameterGroupByName() );
|
|
|
|
TArray<struct FEditorParameterGroup> ParameterDefaultGroups;
|
|
for(int32 ParameterIdx=0; ParameterIdx<ParameterGroups.Num(); ParameterIdx++)
|
|
{
|
|
FEditorParameterGroup & ParamGroup = ParameterGroups[ParameterIdx];
|
|
if (bUseOldStyleMICEditorGroups == false)
|
|
{
|
|
if (ParamGroup.GroupName == TEXT("None"))
|
|
{
|
|
ParameterDefaultGroups.Add(ParamGroup);
|
|
ParameterGroups.RemoveAt(ParameterIdx);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ParamGroup.GroupName == TEXT("Vector Parameter Values") ||
|
|
ParamGroup.GroupName == TEXT("Scalar Parameter Values") ||
|
|
ParamGroup.GroupName == TEXT("Texture Parameter Values") ||
|
|
ParamGroup.GroupName == TEXT("Static Switch Parameter Values") ||
|
|
ParamGroup.GroupName == TEXT("Static Component Mask Parameter Values") ||
|
|
ParamGroup.GroupName == TEXT("Font Parameter Values") ||
|
|
ParamGroup.GroupName == TEXT("Material Layers Parameter Values"))
|
|
{
|
|
ParameterDefaultGroups.Add(ParamGroup);
|
|
ParameterGroups.RemoveAt(ParameterIdx);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ParameterDefaultGroups.Num() >0)
|
|
{
|
|
ParameterGroups.Append(ParameterDefaultGroups);
|
|
}
|
|
|
|
if (DetailsView.IsValid())
|
|
{
|
|
// Tell our source instance to update itself so the preview updates.
|
|
DetailsView.Pin()->ForceRefresh();
|
|
}
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
void UMaterialEditorInstanceConstant::CleanParameterStack(int32 Index, EMaterialParameterAssociation MaterialType)
|
|
{
|
|
check(GIsEditor);
|
|
TArray<FEditorParameterGroup> CleanedGroups;
|
|
for (FEditorParameterGroup Group : ParameterGroups)
|
|
{
|
|
FEditorParameterGroup DuplicatedGroup = FEditorParameterGroup();
|
|
DuplicatedGroup.GroupAssociation = Group.GroupAssociation;
|
|
DuplicatedGroup.GroupName = Group.GroupName;
|
|
DuplicatedGroup.GroupSortPriority = Group.GroupSortPriority;
|
|
for (UDEditorParameterValue* Parameter : Group.Parameters)
|
|
{
|
|
if (Parameter->ParameterInfo.Association != MaterialType
|
|
|| Parameter->ParameterInfo.Index != Index)
|
|
{
|
|
DuplicatedGroup.Parameters.Add(Parameter);
|
|
}
|
|
}
|
|
CleanedGroups.Add(DuplicatedGroup);
|
|
}
|
|
|
|
ParameterGroups = CleanedGroups;
|
|
CopyToSourceInstance(true);
|
|
}
|
|
|
|
void UMaterialEditorInstanceConstant::ResetOverrides(int32 Index, EMaterialParameterAssociation MaterialType)
|
|
{
|
|
check(GIsEditor);
|
|
|
|
for (const FEditorParameterGroup& Group : ParameterGroups)
|
|
{
|
|
for (UDEditorParameterValue* Parameter : Group.Parameters)
|
|
{
|
|
if (Parameter->ParameterInfo.Association == MaterialType
|
|
&& Parameter->ParameterInfo.Index == Index)
|
|
{
|
|
const EMaterialParameterType ParameterType = Parameter->GetParameterType();
|
|
if (ParameterType != EMaterialParameterType::None)
|
|
{
|
|
FMaterialParameterMetadata SourceValue;
|
|
bool bOverride = false;
|
|
if (SourceInstance->GetParameterValue(ParameterType, Parameter->ParameterInfo, SourceValue, EMaterialGetParameterValueFlags::CheckInstanceOverrides))
|
|
{
|
|
bOverride = SourceValue.bOverride;
|
|
}
|
|
Parameter->bOverride = bOverride;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
CopyToSourceInstance(true);
|
|
|
|
}
|
|
|
|
void UMaterialEditorInstanceConstant::ClearInvalidParameterOverrides()
|
|
{
|
|
const FMaterialCachedExpressionData* CachedExpressionData = Parent ? &Parent->GetCachedExpressionData() : nullptr;
|
|
|
|
// Look for all Atlas Scalar parameters in each parameter group, then if a parameter has
|
|
// an override, disable it unless the atlas texture matches that originally set in the parent Material.
|
|
for (int32 GroupIdx = 0; GroupIdx < ParameterGroups.Num(); GroupIdx++)
|
|
{
|
|
FEditorParameterGroup& Group = ParameterGroups[GroupIdx];
|
|
for (int32 ParameterIdx = 0; ParameterIdx < Group.Parameters.Num(); ParameterIdx++)
|
|
{
|
|
UDEditorParameterValue* Parameter = Group.Parameters[ParameterIdx];
|
|
if (UDEditorScalarParameterValue* ScalarParameter = Cast<UDEditorScalarParameterValue>(Parameter))
|
|
{
|
|
// Ignore parameters without override.
|
|
if (!Parameter->bOverride)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Get parent's parameter atlas texture. If identical to the one the editor parameter is using,
|
|
// we can keep the override on (as the selected atlas curve will still make sense).
|
|
FMaterialParameterMetadata Value;
|
|
if (CachedExpressionData && CachedExpressionData->GetParameterValue(EMaterialParameterType::Scalar, ScalarParameter->ParameterInfo, Value)
|
|
&& Value.ScalarAtlas == ScalarParameter->AtlasData.Atlas)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// The atlas texture of the newly bound material are different. Disable the override.
|
|
Parameter->bOverride = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
void UMaterialEditorInstanceConstant::CopyToSourceInstance(const bool bForceStaticPermutationUpdate)
|
|
{
|
|
if (SourceInstance && !SourceInstance->IsTemplate(RF_ClassDefaultObject))
|
|
{
|
|
if (bIsFunctionPreviewMaterial)
|
|
{
|
|
bIsFunctionInstanceDirty = true;
|
|
}
|
|
else
|
|
{
|
|
SourceInstance->MarkPackageDirty();
|
|
}
|
|
|
|
{
|
|
FMaterialInstanceParameterUpdateContext UpdateContext(SourceInstance, EMaterialInstanceClearParameterFlag::All);
|
|
UpdateContext.SetBasePropertyOverrides(BasePropertyOverrides);
|
|
UpdateContext.SetForceStaticPermutationUpdate(bForceStaticPermutationUpdate);
|
|
|
|
for (int32 GroupIdx = 0; GroupIdx < ParameterGroups.Num(); GroupIdx++)
|
|
{
|
|
FEditorParameterGroup& Group = ParameterGroups[GroupIdx];
|
|
for (int32 ParameterIdx = 0; ParameterIdx < Group.Parameters.Num(); ParameterIdx++)
|
|
{
|
|
UDEditorParameterValue* Parameter = Group.Parameters[ParameterIdx];
|
|
if (Parameter && Parameter->bOverride)
|
|
{
|
|
FMaterialParameterMetadata EditorValue;
|
|
if (Parameter->GetValue(EditorValue))
|
|
{
|
|
UpdateContext.SetParameterValueEditorOnly(Parameter->ParameterInfo, EditorValue, EMaterialSetParameterValueFlags::SetCurveAtlas);
|
|
}
|
|
else if (UDEditorMaterialLayersParameterValue* LayersParameter = Cast<UDEditorMaterialLayersParameterValue>(Parameter))
|
|
{
|
|
UpdateContext.SetMaterialLayers(LayersParameter->ParameterValue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy phys material back to source instance
|
|
SourceInstance->PhysMaterial = PhysMaterial;
|
|
SourceInstance->NaniteOverrideMaterial.bEnableOverride = bNaniteOverride;
|
|
SourceInstance->NaniteOverrideMaterial.OverrideMaterialEditor = NaniteOverrideMaterial;
|
|
|
|
// 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);
|
|
|
|
// Copy UserSceneTextureOverrides
|
|
SourceInstance->UserSceneTextureOverrides.Empty();
|
|
for (FEditorUserSceneTextureOverride& Override : PostProcessOverrides.UserSceneTextureInputs)
|
|
{
|
|
if (Override.Value != NAME_None && Override.Value != Override.Key)
|
|
{
|
|
SourceInstance->UserSceneTextureOverrides.Add({ Override.Key, Override.Value });
|
|
}
|
|
}
|
|
if (PostProcessOverrides.UserSceneTextureOutput != NAME_None)
|
|
{
|
|
// UserSceneTextureOutput override uses key of NAME_None
|
|
SourceInstance->UserSceneTextureOverrides.Add({ NAME_None, PostProcessOverrides.UserSceneTextureOutput });
|
|
}
|
|
|
|
// Copy other post process overrides (BlendableLocation / BlendablePriority) -- BL_ReplacingTonemapper is disallowed on overrides
|
|
SourceInstance->bOverrideBlendableLocation = PostProcessOverrides.bOverrideBlendableLocation && PostProcessOverrides.BlendableLocationOverride != BL_ReplacingTonemapper;
|
|
SourceInstance->bOverrideBlendablePriority = PostProcessOverrides.bOverrideBlendablePriority;
|
|
if (SourceInstance->bOverrideBlendableLocation)
|
|
{
|
|
SourceInstance->BlendableLocationOverride = PostProcessOverrides.BlendableLocationOverride;
|
|
}
|
|
else
|
|
{
|
|
SourceInstance->BlendableLocationOverride = Parent ? Parent->GetMaterial()->BlendableLocation : TEnumAsByte<EBlendableLocation>(BL_SceneColorAfterTonemapping);
|
|
}
|
|
if (SourceInstance->bOverrideBlendablePriority)
|
|
{
|
|
SourceInstance->BlendablePriorityOverride = PostProcessOverrides.BlendablePriorityOverride;
|
|
}
|
|
else
|
|
{
|
|
SourceInstance->BlendablePriorityOverride = Parent ? Parent->GetMaterial()->BlendablePriority : 0;
|
|
}
|
|
|
|
SourceInstance->bOverrideSubsurfaceProfile = bOverrideSubsurfaceProfile;
|
|
SourceInstance->SubsurfaceProfile = SubsurfaceProfile;
|
|
|
|
SourceInstance->bOverrideSpecularProfile = bOverrideSpecularProfile;
|
|
SourceInstance->SpecularProfileOverride = SpecularProfile;
|
|
|
|
// 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->DoubleVectorParameterValues = SourceInstance->DoubleVectorParameterValues;
|
|
SourceFunction->TextureParameterValues = SourceInstance->TextureParameterValues;
|
|
SourceFunction->TextureCollectionParameterValues = SourceInstance->TextureCollectionParameterValues;
|
|
SourceFunction->RuntimeVirtualTextureParameterValues = SourceInstance->RuntimeVirtualTextureParameterValues;
|
|
SourceFunction->SparseVolumeTextureParameterValues = SourceInstance->SparseVolumeTextureParameterValues;
|
|
SourceFunction->FontParameterValues = SourceInstance->FontParameterValues;
|
|
SourceFunction->EnumerationParameterValues = SourceInstance->EnumerationParameterValues;
|
|
|
|
SourceFunction->StaticSwitchParameterValues = SourceInstance->GetStaticParameters().StaticSwitchParameters;
|
|
const FStaticParameterSetEditorOnlyData& StaticParameters = SourceInstance->GetEditorOnlyStaticParameters();
|
|
SourceFunction->StaticComponentMaskParameterValues = StaticParameters.StaticComponentMaskParameters;
|
|
|
|
SourceFunction->MarkPackageDirty();
|
|
bIsFunctionInstanceDirty = false;
|
|
|
|
UMaterialEditingLibrary::UpdateMaterialFunction(SourceFunction, nullptr);
|
|
}
|
|
}
|
|
|
|
void UMaterialEditorInstanceConstant::SetSourceInstance(UMaterialInstanceConstant* MaterialInterface)
|
|
{
|
|
check(MaterialInterface);
|
|
SourceInstance = MaterialInterface;
|
|
Parent = SourceInstance->Parent;
|
|
PhysMaterial = SourceInstance->PhysMaterial;
|
|
bNaniteOverride = SourceInstance->NaniteOverrideMaterial.bEnableOverride;
|
|
NaniteOverrideMaterial = SourceInstance->NaniteOverrideMaterial.OverrideMaterialEditor;
|
|
|
|
CopyBasePropertiesFromParent();
|
|
|
|
RegenerateArrays();
|
|
|
|
//propagate changes to the base material so the instance will be updated if it has a static permutation resource
|
|
FMaterialInstanceParameterUpdateContext UpdateContext(SourceInstance, EMaterialInstanceClearParameterFlag::Static);
|
|
|
|
for (int32 GroupIdx = 0; GroupIdx < ParameterGroups.Num(); GroupIdx++)
|
|
{
|
|
FEditorParameterGroup& Group = ParameterGroups[GroupIdx];
|
|
for (int32 ParameterIdx = 0; ParameterIdx < Group.Parameters.Num(); ParameterIdx++)
|
|
{
|
|
UDEditorParameterValue* Parameter = Group.Parameters[ParameterIdx];
|
|
if (Parameter && Parameter->bOverride)
|
|
{
|
|
FMaterialParameterMetadata EditorValue;
|
|
if (Parameter->GetValue(EditorValue))
|
|
{
|
|
// Only want to update static parameters here
|
|
if (IsStaticMaterialParameter(EditorValue.Value.Type))
|
|
{
|
|
UpdateContext.SetParameterValueEditorOnly(Parameter->ParameterInfo, EditorValue);
|
|
}
|
|
}
|
|
else if (UDEditorMaterialLayersParameterValue* LayersParameter = Cast<UDEditorMaterialLayersParameterValue>(Parameter))
|
|
{
|
|
UpdateContext.SetMaterialLayers(LayersParameter->ParameterValue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UMaterialEditorInstanceConstant::SetSourceFunction(UMaterialFunctionInstance* MaterialFunction)
|
|
{
|
|
SourceFunction = MaterialFunction;
|
|
bIsFunctionPreviewMaterial = !!(SourceFunction);
|
|
}
|
|
|
|
void UMaterialEditorInstanceConstant::UpdateSourceInstanceParent()
|
|
{
|
|
// If the parent was changed to the source instance, set it to NULL
|
|
if( Parent == SourceInstance )
|
|
{
|
|
Parent = NULL;
|
|
}
|
|
|
|
SourceInstance->SetParentEditorOnly( Parent );
|
|
SourceInstance->PostEditChange();
|
|
}
|
|
|
|
|
|
void UMaterialEditorInstanceConstant::CopyBasePropertiesFromParent()
|
|
{
|
|
BasePropertyOverrides = SourceInstance->BasePropertyOverrides;
|
|
// Copy the overrides (if not yet overridden), so they match their true values in the UI
|
|
if (!BasePropertyOverrides.bOverride_OpacityMaskClipValue)
|
|
{
|
|
BasePropertyOverrides.OpacityMaskClipValue = SourceInstance->GetOpacityMaskClipValue();
|
|
}
|
|
if (!BasePropertyOverrides.bOverride_BlendMode)
|
|
{
|
|
BasePropertyOverrides.BlendMode = SourceInstance->GetBlendMode();
|
|
}
|
|
if (!BasePropertyOverrides.bOverride_ShadingModel)
|
|
{
|
|
if (SourceInstance->IsShadingModelFromMaterialExpression())
|
|
{
|
|
BasePropertyOverrides.ShadingModel = MSM_FromMaterialExpression;
|
|
}
|
|
else
|
|
{
|
|
BasePropertyOverrides.ShadingModel = SourceInstance->GetShadingModels().GetFirstShadingModel();
|
|
}
|
|
}
|
|
if (!BasePropertyOverrides.bOverride_TwoSided)
|
|
{
|
|
BasePropertyOverrides.TwoSided = SourceInstance->IsTwoSided();
|
|
}
|
|
if (!BasePropertyOverrides.bOverride_bIsThinSurface)
|
|
{
|
|
BasePropertyOverrides.bIsThinSurface = SourceInstance->IsThinSurface();
|
|
}
|
|
if (!BasePropertyOverrides.bOverride_OutputTranslucentVelocity)
|
|
{
|
|
BasePropertyOverrides.bOutputTranslucentVelocity = SourceInstance->IsTranslucencyWritingVelocity();
|
|
}
|
|
if (!BasePropertyOverrides.bOverride_bHasPixelAnimation)
|
|
{
|
|
BasePropertyOverrides.bHasPixelAnimation = SourceInstance->HasPixelAnimation();
|
|
}
|
|
if (!BasePropertyOverrides.bOverride_bEnableTessellation)
|
|
{
|
|
BasePropertyOverrides.bEnableTessellation = SourceInstance->IsTessellationEnabled();
|
|
}
|
|
if (!BasePropertyOverrides.DitheredLODTransition)
|
|
{
|
|
BasePropertyOverrides.DitheredLODTransition = SourceInstance->IsDitheredLODTransition();
|
|
}
|
|
if (!BasePropertyOverrides.bOverride_DisplacementScaling)
|
|
{
|
|
BasePropertyOverrides.DisplacementScaling = SourceInstance->GetDisplacementScaling();
|
|
}
|
|
if (!BasePropertyOverrides.bOverride_bEnableDisplacementFade)
|
|
{
|
|
BasePropertyOverrides.bEnableDisplacementFade = SourceInstance->IsDisplacementFadeEnabled();
|
|
}
|
|
if (!BasePropertyOverrides.bOverride_DisplacementFadeRange)
|
|
{
|
|
BasePropertyOverrides.DisplacementFadeRange = SourceInstance->GetDisplacementFadeRange();
|
|
}
|
|
if (!BasePropertyOverrides.bOverride_MaxWorldPositionOffsetDisplacement)
|
|
{
|
|
BasePropertyOverrides.MaxWorldPositionOffsetDisplacement = SourceInstance->GetMaxWorldPositionOffsetDisplacement();
|
|
}
|
|
if (!BasePropertyOverrides.bOverride_CastDynamicShadowAsMasked)
|
|
{
|
|
BasePropertyOverrides.bCastDynamicShadowAsMasked = SourceInstance->GetCastDynamicShadowAsMasked();
|
|
}
|
|
if (!BasePropertyOverrides.bOverride_CompatibleWithLumenCardSharing)
|
|
{
|
|
BasePropertyOverrides.bCompatibleWithLumenCardSharing = SourceInstance->IsCompatibleWithLumenCardSharing();
|
|
}
|
|
|
|
// 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 (Substrate::IsSubstrateEnabled())
|
|
{
|
|
bOverrideSpecularProfile = SourceInstance->bOverrideSpecularProfile;
|
|
if (SourceInstance->NumSpecularProfile_Internal() > 0)
|
|
{
|
|
SpecularProfile = SourceInstance->GetSpecularProfileOverride_Internal();
|
|
}
|
|
}
|
|
|
|
// Post process blendable location and priority overrides
|
|
PostProcessOverrides.bOverrideBlendableLocation = SourceInstance->bOverrideBlendableLocation;
|
|
if (SourceInstance->bOverrideBlendableLocation)
|
|
{
|
|
PostProcessOverrides.BlendableLocationOverride = SourceInstance->BlendableLocationOverride;
|
|
}
|
|
else
|
|
{
|
|
PostProcessOverrides.BlendableLocationOverride = Parent ? Parent->GetMaterial()->BlendableLocation : TEnumAsByte<EBlendableLocation>(BL_SceneColorAfterTonemapping);
|
|
}
|
|
|
|
PostProcessOverrides.bOverrideBlendablePriority = SourceInstance->bOverrideBlendablePriority;
|
|
if (SourceInstance->bOverrideBlendablePriority)
|
|
{
|
|
PostProcessOverrides.BlendablePriorityOverride = SourceInstance->BlendablePriorityOverride;
|
|
}
|
|
else
|
|
{
|
|
PostProcessOverrides.BlendablePriorityOverride = Parent ? Parent->GetMaterial()->BlendablePriority : 0;
|
|
}
|
|
|
|
// UserSceneTextureOutput override uses Key == NAME_None. UserSceneTextureInputs are initialized in RegenerateArrays, as those are affected
|
|
// by graph reachability.
|
|
for (const FUserSceneTextureOverride& Override : SourceInstance->UserSceneTextureOverrides)
|
|
{
|
|
if (Override.Key == NAME_None)
|
|
{
|
|
PostProcessOverrides.UserSceneTextureOutput = Override.Value;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
void UMaterialEditorInstanceConstant::PostEditUndo()
|
|
{
|
|
Super::PostEditUndo();
|
|
|
|
if (bIsFunctionPreviewMaterial && SourceFunction)
|
|
{
|
|
bIsFunctionInstanceDirty = true;
|
|
ApplySourceFunctionChanges();
|
|
}
|
|
else if (SourceInstance)
|
|
{
|
|
SourceInstance->PostEditUndo();
|
|
|
|
FMaterialUpdateContext Context;
|
|
|
|
UpdateSourceInstanceParent();
|
|
|
|
Context.AddMaterialInstance(SourceInstance);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
UMaterialEditorMeshComponent::UMaterialEditorMeshComponent(const FObjectInitializer& ObjectInitializer)
|
|
: Super(ObjectInitializer)
|
|
{
|
|
}
|