Files
UnrealEngine/Engine/Source/Editor/MaterialEditor/Private/MaterialEditingLibrary.cpp
2025-05-18 13:04:45 +08:00

1573 lines
50 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "MaterialEditingLibrary.h"
#include "Engine/Texture.h"
#include "Editor.h"
#include "MaterialEditor.h"
#include "MaterialInstanceEditor.h"
#include "MaterialEditorUtilities.h"
#include "MaterialShared.h"
#include "MaterialGraph/MaterialGraphNode.h"
#include "Materials/MaterialFunction.h"
#include "Materials/MaterialInstance.h"
#include "Materials/MaterialInstanceDynamic.h"
#include "Materials/MaterialInstanceConstant.h"
#include "Materials/MaterialFunctionInstance.h"
#include "Materials/MaterialExpressionTextureBase.h"
#include "Materials/MaterialExpressionCollectionParameter.h"
#include "Materials/MaterialExpressionCollectionTransform.h"
#include "Materials/MaterialExpressionFunctionInput.h"
#include "Materials/MaterialExpressionFunctionOutput.h"
#include "Materials/MaterialExpressionComponentMask.h"
#include "Materials/MaterialExpressionStaticComponentMaskParameter.h"
#include "Materials/MaterialExpressionTransformPosition.h"
#include "Materials/MaterialExpressionDynamicParameter.h"
#include "Materials/MaterialParameterCollection.h"
#include "Materials/MaterialExpressionMaterialFunctionCall.h"
#include "Materials/MaterialEnumeration.h"
#include "MaterialEditor/MaterialEditorInstanceConstant.h"
#include "MaterialStatsCommon.h"
#include "Particles/ParticleSystemComponent.h"
#include "EditorSupportDelegates.h"
#include "Misc/RuntimeErrors.h"
#include "SceneTypes.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "DebugViewModeHelpers.h"
#include "Subsystems/AssetEditorSubsystem.h"
#include "ShaderCompiler.h"
#include "UObject/UObjectIterator.h"
#include "MaterialCachedData.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(MaterialEditingLibrary)
DEFINE_LOG_CATEGORY_STATIC(LogMaterialEditingLibrary, Warning, All);
/** Util to find expression */
static FExpressionInput* GetExpressionInputByName(UMaterialExpression* Expression, const FName InputName)
{
check(Expression);
FExpressionInput* Result = nullptr;
// Return first input if no name specified
if (InputName.IsNone())
{
return Expression->GetInput(0);
}
// Get all inputs. Get name of each input, see if its the one we want
for (FExpressionInputIterator It{ Expression }; It; ++It)
{
FName TestName;
if (UMaterialExpressionMaterialFunctionCall* FuncCall = Cast<UMaterialExpressionMaterialFunctionCall>(Expression))
{
// If a function call, don't want to compare string with type postfix
TestName = FuncCall->GetInputNameWithType(It.Index, false);
}
else
{
const FName ExpressionInputName = Expression->GetInputName(It.Index);
TestName = UMaterialGraphNode::GetShortenPinName(ExpressionInputName);
}
if (TestName == InputName)
{
return It.Input;
}
}
return nullptr;
}
static int32 GetExpressionOutputIndexByName(UMaterialExpression* Expression, const FName OutputName)
{
check(Expression);
int32 Result = INDEX_NONE;
if (Expression->Outputs.Num() == 0)
{
// leave as INDEX_NONE
}
// Return first output if no name specified
else if (OutputName.IsNone())
{
Result = 0;
}
else
{
// Iterate over outputs and look for name match
for (int OutIdx = 0; OutIdx < Expression->Outputs.Num(); OutIdx++)
{
bool bFoundMatch = false;
FExpressionOutput& Output = Expression->Outputs[OutIdx];
// If output name is no empty - see if it matches
if(!Output.OutputName.IsNone())
{
if (OutputName == Output.OutputName)
{
bFoundMatch = true;
}
}
// if it is empty we look for R/G/B/A
else
{
if (Output.MaskR && !Output.MaskG && !Output.MaskB && !Output.MaskA && OutputName == TEXT("R"))
{
bFoundMatch = true;
}
else if (!Output.MaskR && Output.MaskG && !Output.MaskB && !Output.MaskA && OutputName == TEXT("G"))
{
bFoundMatch = true;
}
else if (!Output.MaskR && !Output.MaskG && Output.MaskB && !Output.MaskA && OutputName == TEXT("B"))
{
bFoundMatch = true;
}
else if (!Output.MaskR && !Output.MaskG && !Output.MaskB && Output.MaskA && OutputName == TEXT("A"))
{
bFoundMatch = true;
}
}
// Got a match, remember the index, exit iteration
if (bFoundMatch)
{
Result = OutIdx;
break;
}
}
}
return Result;
}
namespace MaterialEditingLibraryImpl
{
struct FMaterialExpressionLayoutInfo
{
static const int32 LayoutWidth = 260;
UMaterialExpression* Connected = nullptr;
int32 Column = 0;
int32 Row = 0;
};
void LayoutMaterialExpression( UMaterialExpression* MaterialExpression, UMaterialExpression* ConnectedExpression, TMap< UMaterialExpression*, FMaterialExpressionLayoutInfo >& MaterialExpressionsToLayout, int32 Row, int32 Depth )
{
if ( !MaterialExpression )
{
return;
}
FMaterialExpressionLayoutInfo LayoutInfo;
if ( MaterialExpressionsToLayout.Contains( MaterialExpression ) )
{
LayoutInfo = MaterialExpressionsToLayout[ MaterialExpression ];
}
LayoutInfo.Row = FMath::Max( LayoutInfo.Row, Row );
if ( Depth > LayoutInfo.Column )
{
LayoutInfo.Connected = ConnectedExpression;
}
LayoutInfo.Column = FMath::Max( LayoutInfo.Column, Depth );
MaterialExpressionsToLayout.Add( MaterialExpression ) = MoveTemp( LayoutInfo );
for (FExpressionInputIterator It{ MaterialExpression }; It; ++It)
{
LayoutMaterialExpression( It->Expression, MaterialExpression, MaterialExpressionsToLayout, Row, Depth + 1 );
}
}
void LayoutMaterialExpressions( UObject* MaterialOrMaterialFunction )
{
if ( !MaterialOrMaterialFunction )
{
return;
}
TMap< UMaterialExpression*, FMaterialExpressionLayoutInfo > MaterialExpressionsToLayout;
if ( UMaterial* Material = Cast< UMaterial >( MaterialOrMaterialFunction ) )
{
for ( int32 MaterialPropertyIndex = 0; MaterialPropertyIndex < MP_MAX; ++MaterialPropertyIndex )
{
FExpressionInput* ExpressionInput = Material->GetExpressionInputForProperty( EMaterialProperty(MaterialPropertyIndex) );
if ( ExpressionInput )
{
LayoutMaterialExpression( ExpressionInput->Expression, nullptr, MaterialExpressionsToLayout, MaterialPropertyIndex, 0 );
}
}
}
else if ( UMaterialFunction* MaterialFunction = Cast< UMaterialFunction >( MaterialOrMaterialFunction ) )
{
TArray< FFunctionExpressionInput > Inputs;
TArray< FFunctionExpressionOutput > Outputs;
MaterialFunction->GetInputsAndOutputs( Inputs, Outputs );
int32 InputIndex = 0;
if ( Inputs.Num() > 0 )
{
for ( FFunctionExpressionInput& FunctionExpressionInput : Inputs )
{
LayoutMaterialExpression( FunctionExpressionInput.ExpressionInput, nullptr, MaterialExpressionsToLayout, ++InputIndex, 0 );
}
}
else
{
for ( FFunctionExpressionOutput& FunctionExpressionOutput : Outputs )
{
LayoutMaterialExpression( FunctionExpressionOutput.ExpressionOutput, nullptr, MaterialExpressionsToLayout, ++InputIndex, 0 );
}
}
}
TMap< int32, TMap< int32, bool > > UsedColumnRows;
TMap< int32, int32 > ColumnsHeights;
for ( TMap< UMaterialExpression*, FMaterialExpressionLayoutInfo >::TIterator It = MaterialExpressionsToLayout.CreateIterator(); It; ++It )
{
UMaterialExpression* MaterialExpression = It->Key;
FMaterialExpressionLayoutInfo& LayoutInfo = It->Value;
if ( !UsedColumnRows.Contains( LayoutInfo.Column ) )
{
UsedColumnRows.Add( LayoutInfo.Column );
}
while ( UsedColumnRows[ LayoutInfo.Column ].Contains( LayoutInfo.Row ) )
{
++LayoutInfo.Row;
}
UsedColumnRows[ LayoutInfo.Column ].Add( LayoutInfo.Row ) = true;
if ( !ColumnsHeights.Contains( LayoutInfo.Column ) )
{
ColumnsHeights.Add( LayoutInfo.Column ) = 0;
}
int32& ColumnHeight = ColumnsHeights[ LayoutInfo.Column ];
MaterialExpression->MaterialExpressionEditorX = -FMaterialExpressionLayoutInfo::LayoutWidth * ( LayoutInfo.Column + 1 );
int32 ConnectedHeight = LayoutInfo.Connected ? LayoutInfo.Connected->MaterialExpressionEditorY : 0;
MaterialExpression->MaterialExpressionEditorY = FMath::Max( ColumnHeight, ConnectedHeight );
ColumnHeight = MaterialExpression->MaterialExpressionEditorY + MaterialExpression->GetHeight() + ME_STD_HPADDING;
}
}
IMaterialEditor* FindMaterialEditorForAsset(UObject* InAsset)
{
if (IAssetEditorInstance* AssetEditorInstance = (InAsset != nullptr) ? GEditor->GetEditorSubsystem<UAssetEditorSubsystem>()->FindEditorForAsset(InAsset, false) : nullptr)
{
// Ensure this is not a UMaterialInstanceDynamic, as that doesn't use IMaterialEditor as its editor
if (!InAsset->IsA(UMaterialInstanceDynamic::StaticClass()))
{
return static_cast<IMaterialEditor*>(AssetEditorInstance);
}
}
return nullptr;
}
FMaterialInstanceEditor* FindMaterialInstanceEditorForAsset(UObject* InAsset)
{
if (IAssetEditorInstance* AssetEditorInstance = (InAsset != nullptr) ? GEditor->GetEditorSubsystem<UAssetEditorSubsystem>()->FindEditorForAsset(InAsset, false) : nullptr)
{
// Ensure this is not a UMaterialInstanceDynamic, as that doesn't use FMaterialInstanceEditor as its editor
if (!InAsset->IsA(UMaterialInstanceDynamic::StaticClass()))
{
return static_cast<FMaterialInstanceEditor*>(AssetEditorInstance);
}
}
return nullptr;
}
}
void UMaterialEditingLibrary::RebuildMaterialInstanceEditors(UMaterial* BaseMaterial)
{
UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>();
TArray<UObject*> EditedAssets = AssetEditorSubsystem->GetAllEditedAssets();
for (int32 AssetIdx = 0; AssetIdx < EditedAssets.Num(); AssetIdx++)
{
UObject* EditedAsset = EditedAssets[AssetIdx];
UMaterialInstance* SourceInstance = Cast<UMaterialInstance>(EditedAsset);
if (!SourceInstance)
{
// Check to see if the EditedAssets are from material instance editor
UMaterialEditorInstanceConstant* EditorInstance = Cast<UMaterialEditorInstanceConstant>(EditedAsset);
if (EditorInstance && EditorInstance->SourceInstance)
{
SourceInstance = EditorInstance->SourceInstance;
}
}
if (SourceInstance != nullptr)
{
UMaterial* MICOriginalMaterial = SourceInstance->GetMaterial();
if (MICOriginalMaterial == BaseMaterial)
{
if (FMaterialInstanceEditor* MaterialInstanceEditor = MaterialEditingLibraryImpl::FindMaterialInstanceEditorForAsset(SourceInstance))
{
MaterialInstanceEditor->RebuildMaterialInstanceEditor();
}
}
}
}
}
void UMaterialEditingLibrary::RebuildMaterialInstanceEditors(UMaterialFunction* BaseFunction)
{
UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>();
TArray<UObject*> EditedAssets = AssetEditorSubsystem->GetAllEditedAssets();
for (int32 AssetIdx = 0; AssetIdx < EditedAssets.Num(); AssetIdx++)
{
UObject* EditedAsset = EditedAssets[AssetIdx];
UMaterialFunctionInstance* FunctionInstance = Cast<UMaterialFunctionInstance>(EditedAsset);
UMaterialInstance* SourceInstance = Cast<UMaterialInstance>(EditedAsset);
if (FunctionInstance)
{
// Update function instances that are children of this material function
if (BaseFunction && BaseFunction == FunctionInstance->GetBaseFunction())
{
if (FMaterialInstanceEditor* MaterialInstanceEditor = MaterialEditingLibraryImpl::FindMaterialInstanceEditorForAsset(EditedAsset))
{
MaterialInstanceEditor->RebuildMaterialInstanceEditor();
}
}
}
else
{
if (!SourceInstance)
{
// Check to see if the EditedAssets are from material instance editor
UMaterialEditorInstanceConstant* EditorInstance = Cast<UMaterialEditorInstanceConstant>(EditedAsset);
if (EditorInstance && EditorInstance->SourceInstance)
{
SourceInstance = EditorInstance->SourceInstance;
}
}
// Ensure the material instance is valid and not a UMaterialInstanceDynamic, as that doesn't use FMaterialInstanceEditor as its editor
if (SourceInstance != nullptr && !SourceInstance->IsA(UMaterialInstanceDynamic::StaticClass()))
{
TArray<UMaterialFunctionInterface*> DependentFunctions;
SourceInstance->GetDependentFunctions(DependentFunctions);
if (BaseFunction && (DependentFunctions.Contains(BaseFunction) || DependentFunctions.Contains(BaseFunction->ParentFunction)))
{
if (FMaterialInstanceEditor* MaterialInstanceEditor = MaterialEditingLibraryImpl::FindMaterialInstanceEditorForAsset(EditedAsset))
{
MaterialInstanceEditor->RebuildMaterialInstanceEditor();
}
}
}
}
}
}
int32 UMaterialEditingLibrary::GetNumMaterialExpressions(const UMaterial* Material)
{
int32 Result = 0;
if (Material)
{
Result = Material->GetExpressions().Num();
}
return Result;
}
void UMaterialEditingLibrary::DeleteAllMaterialExpressions(UMaterial* Material)
{
if (Material)
{
for (UMaterialExpression* Expression : Material->GetExpressions())
{
DeleteMaterialExpression(Material, Expression);
}
}
}
/** Util to iterate over list of expressions, and break any links to specified expression */
static void BreakLinksToExpression(TConstArrayView<TObjectPtr<UMaterialExpression>> Expressions, UMaterialExpression* Expression)
{
// Need to find any other expressions which are connected to this one, and break link
for (UMaterialExpression* TestExp : Expressions)
{
// Don't check myself, though that shouldn't really matter...
if (TestExp != Expression)
{
for (FExpressionInputIterator It{ TestExp }; It; ++It)
{
if (It->Expression == Expression)
{
It->Expression = nullptr;
}
}
}
}
}
void UMaterialEditingLibrary::DeleteMaterialExpression(UMaterial* Material, UMaterialExpression* Expression)
{
if (Material && Expression && Expression->GetOuter() == Material)
{
// Break any links to this expression
BreakLinksToExpression(Material->GetExpressions(), Expression);
// Check material parameter inputs, to make sure expression is not connected to it
for (int32 InputIndex = 0; InputIndex < MP_MAX; InputIndex++)
{
FExpressionInput* Input = Material->GetExpressionInputForProperty((EMaterialProperty)InputIndex);
if (Input && Input->Expression == Expression)
{
Input->Expression = nullptr;
}
}
Material->RemoveExpressionParameter(Expression);
Material->GetExpressionCollection().RemoveExpression(Expression);
Expression->MarkAsGarbage();
Material->MarkPackageDirty();
}
}
UMaterialExpression* UMaterialEditingLibrary::CreateMaterialExpression(UMaterial* Material, TSubclassOf<UMaterialExpression> ExpressionClass, int32 NodePosX, int32 NodePosY)
{
return CreateMaterialExpressionEx(Material, nullptr, ExpressionClass, nullptr, NodePosX, NodePosY);
}
UMaterialExpression* UMaterialEditingLibrary::DuplicateMaterialExpression(UMaterial* Material, UMaterialFunction* MaterialFunction, UMaterialExpression* Expression)
{
UMaterialExpression* NewExpression = nullptr;
if (Material || MaterialFunction)
{
UObject* ExpressionOuter = Material;
if (MaterialFunction)
{
ExpressionOuter = MaterialFunction;
}
NewExpression = DuplicateObject(Expression, ExpressionOuter);
if (Material)
{
Material->GetExpressionCollection().AddExpression(NewExpression);
NewExpression->Material = Material;
}
if (MaterialFunction && !Material)
{
MaterialFunction->GetExpressionCollection().AddExpression(NewExpression);
}
// Create a GUID for the node
NewExpression->UpdateMaterialExpressionGuid(true, true);
if (Material)
{
Material->AddExpressionParameter(NewExpression, Material->EditorParameters);
}
NewExpression->MarkPackageDirty();
}
return NewExpression;
}
UMaterialExpression* UMaterialEditingLibrary::CreateMaterialExpressionInFunction(UMaterialFunction* MaterialFunction, TSubclassOf<UMaterialExpression> ExpressionClass, int32 NodePosX, int32 NodePosY)
{
return CreateMaterialExpressionEx(nullptr, MaterialFunction, ExpressionClass, nullptr, NodePosX, NodePosY);
}
UMaterialExpression* UMaterialEditingLibrary::CreateMaterialExpressionEx(UMaterial* Material, UMaterialFunction* MaterialFunction, TSubclassOf<UMaterialExpression> ExpressionClass,
UObject* SelectedAsset, int32 NodePosX, int32 NodePosY, bool bAllowMarkingPackageDirty)
{
UMaterialExpression* NewExpression = nullptr;
if (Material || MaterialFunction)
{
UObject* ExpressionOuter = Material;
if (MaterialFunction)
{
ExpressionOuter = MaterialFunction;
}
NewExpression = NewObject<UMaterialExpression>(ExpressionOuter, ExpressionClass.Get(), NAME_None, RF_Transactional);
if (Material)
{
Material->GetExpressionCollection().AddExpression(NewExpression);
NewExpression->Material = Material;
}
if (MaterialFunction && !Material)
{
MaterialFunction->GetExpressionCollection().AddExpression(NewExpression);
}
NewExpression->MaterialExpressionEditorX = NodePosX;
NewExpression->MaterialExpressionEditorY = NodePosY;
// Create a GUID for the node
NewExpression->UpdateMaterialExpressionGuid(true, bAllowMarkingPackageDirty);
if (SelectedAsset)
{
// If the user is adding a texture, automatically assign the currently selected texture to it.
UMaterialExpressionTextureBase* METextureBase = Cast<UMaterialExpressionTextureBase>(NewExpression);
if (METextureBase)
{
if (UTexture* SelectedTexture = Cast<UTexture>(SelectedAsset))
{
METextureBase->Texture = SelectedTexture;
}
METextureBase->AutoSetSampleType();
}
UMaterialExpressionMaterialFunctionCall* MEMaterialFunction = Cast<UMaterialExpressionMaterialFunctionCall>(NewExpression);
if (MEMaterialFunction)
{
MEMaterialFunction->SetMaterialFunction(Cast<UMaterialFunction>(SelectedAsset));
}
UMaterialExpressionCollectionParameter* MECollectionParameter = Cast<UMaterialExpressionCollectionParameter>(NewExpression);
if (MECollectionParameter)
{
MECollectionParameter->Collection = Cast<UMaterialParameterCollection>(SelectedAsset);
}
UMaterialExpressionCollectionTransform* MECollectionTransform = Cast<UMaterialExpressionCollectionTransform>(NewExpression);
if (MECollectionTransform)
{
MECollectionTransform->Collection = Cast<UMaterialParameterCollection>(SelectedAsset);
}
}
UMaterialExpressionFunctionInput* FunctionInput = Cast<UMaterialExpressionFunctionInput>(NewExpression);
if (FunctionInput)
{
FunctionInput->ConditionallyGenerateId(true);
FunctionInput->ValidateName();
}
UMaterialExpressionFunctionOutput* FunctionOutput = Cast<UMaterialExpressionFunctionOutput>(NewExpression);
if (FunctionOutput)
{
FunctionOutput->ConditionallyGenerateId(true);
FunctionOutput->ValidateName();
}
NewExpression->UpdateParameterGuid(true, bAllowMarkingPackageDirty);
if (NewExpression->HasAParameterName())
{
NewExpression->ValidateParameterName(false);
}
UMaterialExpressionComponentMask* ComponentMaskExpression = Cast<UMaterialExpressionComponentMask>(NewExpression);
// Setup defaults for the most likely use case
// Can't change default properties as that will affect existing content
if (ComponentMaskExpression)
{
ComponentMaskExpression->R = true;
ComponentMaskExpression->G = true;
}
UMaterialExpressionStaticComponentMaskParameter* StaticComponentMaskExpression = Cast<UMaterialExpressionStaticComponentMaskParameter>(NewExpression);
// Setup defaults for the most likely use case
// Can't change default properties as that will affect existing content
if (StaticComponentMaskExpression)
{
StaticComponentMaskExpression->DefaultR = true;
}
// Setup defaults for the most likely use case
// Can't change default properties as that will affect existing content
UMaterialExpressionTransformPosition* PositionTransform = Cast<UMaterialExpressionTransformPosition>(NewExpression);
if (PositionTransform)
{
PositionTransform->TransformSourceType = TRANSFORMPOSSOURCE_Local;
PositionTransform->TransformType = TRANSFORMPOSSOURCE_World;
}
// Make sure the dynamic parameters are named based on existing ones
UMaterialExpressionDynamicParameter* DynamicExpression = Cast<UMaterialExpressionDynamicParameter>(NewExpression);
if (DynamicExpression)
{
DynamicExpression->UpdateDynamicParameterProperties();
}
if (Material)
{
Material->AddExpressionParameter(NewExpression, Material->EditorParameters);
}
if (bAllowMarkingPackageDirty)
{
NewExpression->MarkPackageDirty();
}
}
return NewExpression;
}
bool UMaterialEditingLibrary::SetMaterialUsage(UMaterial* Material, EMaterialUsage Usage, bool& bNeedsRecompile)
{
bool bResult = false;
bNeedsRecompile = false;
if (Material)
{
bResult = Material->SetMaterialUsage(bNeedsRecompile, Usage);
}
return bResult;
}
bool UMaterialEditingLibrary::HasMaterialUsage(UMaterial* Material, EMaterialUsage Usage)
{
bool bResult = false;
if (Material)
{
bResult = Material->GetUsageByFlag(Usage);
}
return bResult;
}
bool UMaterialEditingLibrary::ConnectMaterialProperty(UMaterialExpression* FromExpression, FString FromOutputName, EMaterialProperty Property)
{
bool bResult = false;
if (FromExpression)
{
// Get material that owns this expression
UMaterial* Material = Cast<UMaterial>(FromExpression->GetOuter());
if (Material)
{
FExpressionInput* Input = Material->GetExpressionInputForProperty(Property);
int32 FromIndex = GetExpressionOutputIndexByName(FromExpression, *FromOutputName);
if (Input && FromIndex != INDEX_NONE)
{
Input->Connect(FromIndex, FromExpression);
bResult = true;
}
}
}
return bResult;
}
bool UMaterialEditingLibrary::ConnectMaterialExpressions(UMaterialExpression* FromExpression, FString FromOutputName, UMaterialExpression* ToExpression, FString ToInputName)
{
bool bResult = false;
if (FromExpression && ToExpression)
{
FExpressionInput* Input = GetExpressionInputByName(ToExpression, *ToInputName);
int32 FromIndex = GetExpressionOutputIndexByName(FromExpression, *FromOutputName);
if (Input && FromIndex != INDEX_NONE)
{
Input->Connect(FromIndex, FromExpression);
bResult = true;
}
}
return bResult;
}
void UMaterialEditingLibrary::RecompileMaterialInternal(FMaterialUpdateContext& UpdateContext, UMaterial* Material)
{
UpdateContext.AddMaterial(Material);
// Propagate the change to this material
Material->PreEditChange(nullptr);
Material->PostEditChange();
Material->MarkPackageDirty();
// Force particle components to update their view relevance.
for (TObjectIterator<UParticleSystemComponent> It(/*AdditionalExclusionFlags = */RF_ClassDefaultObject, /*bIncludeDerivedClasses = */true, /*InInternalExclusionFlags = */EInternalObjectFlags::Garbage); It; ++It)
{
It->bIsViewRelevanceDirty = true;
}
// Update parameter names on any child material instances
for (TObjectIterator<UMaterialInstance> It(/*AdditionalExclusionFlags = */RF_ClassDefaultObject, /*bIncludeDerivedClasses = */true, /*InInternalExclusionFlags = */EInternalObjectFlags::Garbage); It; ++It)
{
if (It->Parent == Material)
{
It->UpdateParameterNames();
}
}
UMaterialEditingLibrary::RebuildMaterialInstanceEditors(Material);
}
void UMaterialEditingLibrary::RecompileMaterial(UMaterial* Material)
{
if (!ensureAsRuntimeWarning(Material != nullptr))
{
return;
}
{
// Create a material update context so we can safely update materials using this function.
FMaterialUpdateContext UpdateContext;
RecompileMaterialInternal(UpdateContext, Material);
}
// Update the world's viewports
FEditorDelegates::RefreshEditor.Broadcast();
FEditorSupportDelegates::RedrawAllViewports.Broadcast();
// Update streaming data
FMaterialEditorUtilities::BuildTextureStreamingData(Material);
}
void UMaterialEditingLibrary::RecompileMaterials(TArray<UMaterial*>& Materials, const FOnItemComplete& OnItemComplete)
{
if (Materials.Num() == 0)
{
return;
}
{
// Create a material update context so we can safely update materials using this function.
FMaterialUpdateContext UpdateContext;
for (UMaterial* Material : Materials)
{
if (ensureAsRuntimeWarning(Material != nullptr))
{
RecompileMaterialInternal(UpdateContext, Material);
OnItemComplete.Execute();
}
}
}
// Update the world's viewports
FEditorDelegates::RefreshEditor.Broadcast();
FEditorSupportDelegates::RedrawAllViewports.Broadcast();
// Update streaming data
for (UMaterial* Material : Materials)
{
if (Material != nullptr)
{
FMaterialEditorUtilities::BuildTextureStreamingData(Material);
}
}
}
void UMaterialEditingLibrary::LayoutMaterialExpressions(UMaterial* Material)
{
MaterialEditingLibraryImpl::LayoutMaterialExpressions( Material );
}
float UMaterialEditingLibrary::GetMaterialDefaultScalarParameterValue(UMaterial* Material, FName ParameterName)
{
float Result = 0.f;
if (Material)
{
Material->GetScalarParameterDefaultValue(ParameterName, Result);
}
return Result;
}
UTexture* UMaterialEditingLibrary::GetMaterialDefaultTextureParameterValue(UMaterial* Material, FName ParameterName)
{
UTexture* Result = nullptr;
if (Material)
{
Material->GetTextureParameterDefaultValue(ParameterName, Result);
}
return Result;
}
FLinearColor UMaterialEditingLibrary::GetMaterialDefaultVectorParameterValue(UMaterial* Material, FName ParameterName)
{
FLinearColor Result = FLinearColor::Black;
if (Material)
{
Material->GetVectorParameterDefaultValue(ParameterName, Result);
}
return Result;
}
bool UMaterialEditingLibrary::GetMaterialDefaultStaticSwitchParameterValue(UMaterial* Material, FName ParameterName)
{
bool bResult = false;
if (Material)
{
FGuid OutGuid;
Material->GetStaticSwitchParameterDefaultValue(ParameterName, bResult, OutGuid);
}
return bResult;
}
TSet<UObject*> UMaterialEditingLibrary::GetMaterialSelectedNodes(UMaterial* Material)
{
if (IMaterialEditor* MaterialEditor = MaterialEditingLibraryImpl::FindMaterialEditorForAsset(Material))
{
TSet<UObject*> SelectedMaterialObjects;
for (const FFieldVariant SelectedNode : MaterialEditor->GetSelectedNodes())
{
check(SelectedNode.IsUObject());
SelectedMaterialObjects.Add(SelectedNode.ToUObject());
}
return SelectedMaterialObjects;
}
return TSet<UObject*>();
}
UMaterialExpression* UMaterialEditingLibrary::GetMaterialPropertyInputNode(UMaterial* Material, EMaterialProperty Property)
{
if (Material)
{
FExpressionInput* ExpressionInput = Material->GetExpressionInputForProperty(Property);
return ExpressionInput->Expression;
}
return nullptr;
}
static FString GetExpressionOutputName(const FExpressionOutput& Output)
{
if (!Output.OutputName.IsNone())
{
return Output.OutputName.ToString();
}
else if (Output.Mask)
{
if (Output.MaskR && !Output.MaskG && !Output.MaskB && !Output.MaskA)
{
return TEXT("R");
}
else if (!Output.MaskR && Output.MaskG && !Output.MaskB && !Output.MaskA)
{
return TEXT("G");
}
else if (!Output.MaskR && !Output.MaskG && Output.MaskB && !Output.MaskA)
{
return TEXT("B");
}
else if (!Output.MaskR && !Output.MaskG && !Output.MaskB && Output.MaskA)
{
return TEXT("A");
}
}
return FString();
}
FString UMaterialEditingLibrary::GetMaterialPropertyInputNodeOutputName(UMaterial* Material, EMaterialProperty Property)
{
if (Material)
{
FExpressionInput* ExpressionInput = Material->GetExpressionInputForProperty(Property);
if (ExpressionInput->OutputIndex != INDEX_NONE
&& ExpressionInput->Expression
&& ExpressionInput->OutputIndex < ExpressionInput->Expression->Outputs.Num())
{
FExpressionOutput& Output = ExpressionInput->Expression->Outputs[ExpressionInput->OutputIndex];
return GetExpressionOutputName(Output);
}
}
return FString();
}
TArray<FString> UMaterialEditingLibrary::GetMaterialExpressionInputNames(UMaterialExpression* MaterialExpression)
{
TArray<FString> InputNames;
for (FExpressionInputIterator It{ MaterialExpression }; It; ++It)
{
FName Name;
if (UMaterialExpressionMaterialFunctionCall* FuncCall = Cast<UMaterialExpressionMaterialFunctionCall>(MaterialExpression))
{
// If a function call, don't want to compare string with type postfix
Name = FuncCall->GetInputNameWithType(It.Index, false);
}
else
{
const FName ExpressionInputName = MaterialExpression->GetInputName(It.Index);
Name = UMaterialGraphNode::GetShortenPinName(ExpressionInputName);
}
InputNames.Add(Name.ToString());
}
return InputNames;
}
TArray<int32> UMaterialEditingLibrary::GetMaterialExpressionInputTypes(UMaterialExpression* MaterialExpression)
{
TArray<int32> InputTypes;
for (FExpressionInputIterator It{ MaterialExpression }; It; ++It)
{
UMaterialExpression* Expression = It->Expression;
if (Expression != nullptr)
{
InputTypes.Add(Expression->GetOutputValueType(It->OutputIndex));
}
else
{
InputTypes.Add(MaterialExpression->GetInputValueType(It.Index));
}
}
return InputTypes;
}
TArray<UMaterialExpression*> UMaterialEditingLibrary::GetInputsForMaterialExpression(UMaterial* Material, UMaterialExpression* MaterialExpression)
{
TArray<UMaterialExpression*> MaterialExpressions;
if (Material)
{
for (FExpressionInputIterator It{ MaterialExpression }; It; ++It)
{
MaterialExpressions.Add(It->Expression);
}
}
return MaterialExpressions;
}
bool UMaterialEditingLibrary::GetInputNodeOutputNameForMaterialExpression(UMaterialExpression* MaterialExpression, UMaterialExpression* InputNode, FString& OutputName)
{
OutputName = TEXT("");
for (FExpressionInputIterator It{ MaterialExpression }; It; ++It)
{
if (It->Expression == InputNode)
{
if(It->OutputIndex != INDEX_NONE && It->OutputIndex < InputNode->Outputs.Num())
{
FExpressionOutput& Output = InputNode->Outputs[It->OutputIndex];
OutputName = GetExpressionOutputName(Output);
return true;
}
}
}
return false;
}
void UMaterialEditingLibrary::GetMaterialExpressionNodePosition(UMaterialExpression* MaterialExpression, int32& NodePosX, int32& NodePosY)
{
if (MaterialExpression)
{
NodePosX = MaterialExpression->MaterialExpressionEditorX;
NodePosY = MaterialExpression->MaterialExpressionEditorY;
}
}
TArray<UTexture*> UMaterialEditingLibrary::GetUsedTextures(UMaterial* Material)
{
TArray<UTexture*> OutTextures;
Material->GetUsedTextures(OutTextures, EMaterialQualityLevel::Num, false, GMaxRHIFeatureLevel, true);
return OutTextures;
}
//////////////////////////////////////////////////////////////////////////
int32 UMaterialEditingLibrary::GetNumMaterialExpressionsInFunction(const UMaterialFunction* MaterialFunction)
{
int32 Result = 0;
if (MaterialFunction)
{
Result = MaterialFunction->GetExpressions().Num();
}
return Result;
}
void UMaterialEditingLibrary::DeleteAllMaterialExpressionsInFunction(UMaterialFunction* MaterialFunction)
{
if (MaterialFunction)
{
for (UMaterialExpression* Expression : MaterialFunction->GetExpressions())
{
DeleteMaterialExpressionInFunction(MaterialFunction, Expression);
}
}
}
void UMaterialEditingLibrary::DeleteMaterialExpressionInFunction(UMaterialFunction* MaterialFunction, UMaterialExpression* Expression)
{
if (MaterialFunction && Expression && Expression->GetOuter() == MaterialFunction)
{
// Break any links to this expression
BreakLinksToExpression(MaterialFunction->GetExpressions(), Expression);
MaterialFunction->GetExpressionCollection().RemoveExpression(Expression);
Expression->MarkAsGarbage();
MaterialFunction->MarkPackageDirty();
}
}
void UMaterialEditingLibrary::UpdateMaterialFunctionInternal(FMaterialUpdateContext& UpdateContext, UMaterialFunctionInterface* MaterialFunction, UMaterial* PreviewMaterial)
{
TRACE_CPUPROFILER_EVENT_SCOPE(UMaterialEditingLibrary::UpdateMaterialFunction)
// mark the function as changed
MaterialFunction->ForceRecompileForRendering(UpdateContext, PreviewMaterial);
MaterialFunction->MarkPackageDirty();
{
TRACE_CPUPROFILER_EVENT_SCOPE(UpdateAllMaterialInstances)
// Go through all function instances in memory and recompile them if they are children
for (TObjectIterator<UMaterialFunctionInstance> It(/*AdditionalExclusionFlags = */RF_ClassDefaultObject, /*bIncludeDerivedClasses = */true, /*InInternalExclusionFlags = */EInternalObjectFlags::Garbage); It; ++It)
{
UMaterialFunctionInstance* FunctionInstance = *It;
TArray<UMaterialFunctionInterface*> Functions;
FunctionInstance->GetDependentFunctions(Functions);
if (Functions.Contains(MaterialFunction))
{
FunctionInstance->UpdateParameterSet();
FunctionInstance->ForceRecompileForRendering(UpdateContext, PreviewMaterial);
// ForceRecompileForRendering will update StateId, so need to mark the package as dirty
FunctionInstance->MarkPackageDirty();
}
}
}
// Notify material editor for any materials that we are updating
for (UMaterialInterface* CurrentMaterial : UpdateContext.GetUpdatedMaterials())
{
if (IMaterialEditor* MaterialEditor = MaterialEditingLibraryImpl::FindMaterialEditorForAsset(CurrentMaterial))
{
MaterialEditor->NotifyExternalMaterialChange();
}
}
UMaterialFunction* BaseFunction = MaterialFunction->GetBaseFunction();
UMaterialEditingLibrary::RebuildMaterialInstanceEditors(BaseFunction);
}
void UMaterialEditingLibrary::UpdateMaterialFunction(UMaterialFunctionInterface* MaterialFunction, UMaterial* PreviewMaterial)
{
if (MaterialFunction == nullptr)
{
return;
}
{
// Create a material update context so we can safely update materials using this function.
FMaterialUpdateContext UpdateContext;
UpdateMaterialFunctionInternal(UpdateContext, MaterialFunction, PreviewMaterial);
}
// Update the world's viewports
FEditorDelegates::RefreshEditor.Broadcast();
FEditorSupportDelegates::RedrawAllViewports.Broadcast();
}
void UMaterialEditingLibrary::UpdateMaterialFunctions(TArray<UMaterialFunctionInterface*>& MaterialFunctions, const FOnItemComplete& OnItemComplete)
{
if (MaterialFunctions.Num() == 0)
{
return;
}
{
// Create a material update context so we can safely update materials using this function.
FMaterialUpdateContext UpdateContext;
for (UMaterialFunctionInterface* MaterialFunction : MaterialFunctions)
{
if (MaterialFunction != nullptr)
{
UpdateMaterialFunctionInternal(UpdateContext, MaterialFunction, nullptr);
OnItemComplete.Execute();
}
}
}
// Update the world's viewports
FEditorDelegates::RefreshEditor.Broadcast();
FEditorSupportDelegates::RedrawAllViewports.Broadcast();
}
void UMaterialEditingLibrary::LayoutMaterialFunctionExpressions(UMaterialFunction* MaterialFunction)
{
MaterialEditingLibraryImpl::LayoutMaterialExpressions( MaterialFunction );
}
void UMaterialEditingLibrary::SetMaterialInstanceParent(UMaterialInstanceConstant* Instance, UMaterialInterface* NewParent)
{
if (Instance)
{
Instance->SetParentEditorOnly(NewParent);
}
}
void UMaterialEditingLibrary::ClearAllMaterialInstanceParameters(UMaterialInstanceConstant* Instance)
{
if (Instance)
{
Instance->ClearParameterValuesEditorOnly();
}
}
float UMaterialEditingLibrary::GetMaterialInstanceScalarParameterValue(UMaterialInstanceConstant* Instance, FName ParameterName, EMaterialParameterAssociation Association)
{
float Result = 0.f;
if (Instance)
{
Instance->GetScalarParameterValue(FHashedMaterialParameterInfo(ParameterName, Association, Association == EMaterialParameterAssociation::LayerParameter ? 0 : INDEX_NONE), Result);
}
return Result;
}
bool UMaterialEditingLibrary::SetMaterialInstanceScalarParameterValue(UMaterialInstanceConstant* Instance, FName ParameterName, float Value, EMaterialParameterAssociation Association)
{
bool bResult = false;
if (Instance)
{
Instance->SetScalarParameterValueEditorOnly(FMaterialParameterInfo(ParameterName, Association, Association == EMaterialParameterAssociation::LayerParameter ? 0 : INDEX_NONE), Value);
UMaterialEditingLibrary::UpdateMaterialInstance(Instance);
}
return bResult;
}
UTexture* UMaterialEditingLibrary::GetMaterialInstanceTextureParameterValue(UMaterialInstanceConstant* Instance, FName ParameterName, EMaterialParameterAssociation Association)
{
UTexture* Result = nullptr;
if (Instance)
{
Instance->GetTextureParameterValue(FHashedMaterialParameterInfo(ParameterName, Association, Association == EMaterialParameterAssociation::LayerParameter ? 0 : INDEX_NONE), Result);
}
return Result;
}
bool UMaterialEditingLibrary::SetMaterialInstanceTextureParameterValue(UMaterialInstanceConstant* Instance, FName ParameterName, UTexture* Value, EMaterialParameterAssociation Association)
{
bool bResult = false;
if (Instance)
{
Instance->SetTextureParameterValueEditorOnly(FMaterialParameterInfo(ParameterName, Association, Association == EMaterialParameterAssociation::LayerParameter ? 0 : INDEX_NONE), Value);
UMaterialEditingLibrary::UpdateMaterialInstance(Instance);
}
return bResult;
}
URuntimeVirtualTexture* UMaterialEditingLibrary::GetMaterialInstanceRuntimeVirtualTextureParameterValue(UMaterialInstanceConstant* Instance, FName ParameterName, EMaterialParameterAssociation Association)
{
URuntimeVirtualTexture* Result = nullptr;
if (Instance)
{
Instance->GetRuntimeVirtualTextureParameterValue(FHashedMaterialParameterInfo(ParameterName, Association, Association == EMaterialParameterAssociation::LayerParameter ? 0 : INDEX_NONE), Result);
}
return Result;
}
bool UMaterialEditingLibrary::SetMaterialInstanceRuntimeVirtualTextureParameterValue(UMaterialInstanceConstant* Instance, FName ParameterName, URuntimeVirtualTexture* Value, EMaterialParameterAssociation Association)
{
bool bResult = false;
if (Instance)
{
Instance->SetRuntimeVirtualTextureParameterValueEditorOnly(FMaterialParameterInfo(ParameterName, Association, Association == EMaterialParameterAssociation::LayerParameter ? 0 : INDEX_NONE), Value);
UMaterialEditingLibrary::UpdateMaterialInstance(Instance);
}
return bResult;
}
USparseVolumeTexture* UMaterialEditingLibrary::GetMaterialInstanceSparseVolumeTextureParameterValue(UMaterialInstanceConstant* Instance, FName ParameterName, EMaterialParameterAssociation Association)
{
USparseVolumeTexture* Result = nullptr;
if (Instance)
{
Instance->GetSparseVolumeTextureParameterValue(FHashedMaterialParameterInfo(ParameterName, Association, Association == EMaterialParameterAssociation::LayerParameter ? 0 : INDEX_NONE), Result);
}
return Result;
}
bool UMaterialEditingLibrary::SetMaterialInstanceSparseVolumeTextureParameterValue(UMaterialInstanceConstant* Instance, FName ParameterName, USparseVolumeTexture* Value, EMaterialParameterAssociation Association)
{
bool bResult = false;
if (Instance)
{
Instance->SetSparseVolumeTextureParameterValueEditorOnly(FMaterialParameterInfo(ParameterName, Association, Association == EMaterialParameterAssociation::LayerParameter ? 0 : INDEX_NONE), Value);
UMaterialEditingLibrary::UpdateMaterialInstance(Instance);
}
return bResult;
}
FLinearColor UMaterialEditingLibrary::GetMaterialInstanceVectorParameterValue(UMaterialInstanceConstant* Instance, FName ParameterName, EMaterialParameterAssociation Association)
{
FLinearColor Result = FLinearColor::Black;
if (Instance)
{
Instance->GetVectorParameterValue(FHashedMaterialParameterInfo(ParameterName, Association, Association == EMaterialParameterAssociation::LayerParameter ? 0 : INDEX_NONE), Result);
}
return Result;
}
bool UMaterialEditingLibrary::SetMaterialInstanceVectorParameterValue(UMaterialInstanceConstant* Instance, FName ParameterName, FLinearColor Value, EMaterialParameterAssociation Association)
{
bool bResult = false;
if (Instance)
{
Instance->SetVectorParameterValueEditorOnly(FMaterialParameterInfo(ParameterName, Association, Association == EMaterialParameterAssociation::LayerParameter ? 0 : INDEX_NONE), Value);
UMaterialEditingLibrary::UpdateMaterialInstance(Instance);
}
return bResult;
}
bool UMaterialEditingLibrary::GetMaterialInstanceStaticSwitchParameterValue(UMaterialInstanceConstant* Instance, FName ParameterName, EMaterialParameterAssociation Association)
{
bool bResult = false;
if (Instance)
{
FGuid OutGuid;
Instance->GetStaticSwitchParameterValue(FHashedMaterialParameterInfo(ParameterName, Association, Association == EMaterialParameterAssociation::LayerParameter ? 0 : INDEX_NONE), bResult, OutGuid);
}
return bResult;
}
bool UMaterialEditingLibrary::SetMaterialInstanceStaticSwitchParameterValue(UMaterialInstanceConstant* Instance, FName ParameterName, bool Value, EMaterialParameterAssociation Association, bool bUpdateMaterialInstance)
{
bool bResult = false;
if (Instance)
{
Instance->SetStaticSwitchParameterValueEditorOnly(FMaterialParameterInfo(ParameterName, Association, Association == EMaterialParameterAssociation::LayerParameter ? 0 : INDEX_NONE), Value);
// The material instance editor window puts MaterialLayersParameters into our StaticParameters, if we don't do this, our settings could get wiped out on first launch of the material editor.
// If there's ever a cleaner and more isolated way of populating MaterialLayersParameters, we should do that instead.
UMaterialEditorInstanceConstant* MaterialEditorInstance = NewObject<UMaterialEditorInstanceConstant>(GetTransientPackage(), NAME_None, RF_Transactional);
MaterialEditorInstance->SetSourceInstance(Instance);
if (bUpdateMaterialInstance)
{
UMaterialEditingLibrary::UpdateMaterialInstance(Instance);
}
}
return bResult;
}
FName UMaterialEditingLibrary::GetMaterialInstanceEnumerationParameterValue(UMaterialInstanceConstant* Instance, FName ParameterName, EMaterialParameterAssociation Association)
{
FName Result = {};
if (Instance)
{
Instance->GetEnumerationParameterValue(FHashedMaterialParameterInfo(ParameterName, Association, Association == EMaterialParameterAssociation::LayerParameter ? 0 : INDEX_NONE), Result);
}
return Result;
}
bool UMaterialEditingLibrary::SetMaterialInstanceEnumerationParameterValue(UMaterialInstanceConstant* Instance, FName ParameterName, FName Value, EMaterialParameterAssociation Association)
{
bool bResult = false;
if (Instance)
{
Instance->SetEnumerationParameterValueEditorOnly(FMaterialParameterInfo(ParameterName, Association, Association == EMaterialParameterAssociation::LayerParameter ? 0 : INDEX_NONE), Value);
UMaterialEditingLibrary::UpdateMaterialInstance(Instance);
}
return bResult;
}
void UMaterialEditingLibrary::UpdateMaterialInstance(UMaterialInstanceConstant* Instance)
{
if (Instance)
{
Instance->MarkPackageDirty();
Instance->PreEditChange(nullptr);
Instance->PostEditChange();
Instance->UpdateStaticPermutation();
Instance->UpdateParameterNames();
// Refresh the material instance editor to reflect parameter value changes in the property window.
if (IMaterialEditor* MaterialEditor = MaterialEditingLibraryImpl::FindMaterialEditorForAsset(Instance))
{
MaterialEditor->NotifyExternalMaterialChange();
}
// update the world's viewports
FEditorDelegates::RefreshEditor.Broadcast();
FEditorSupportDelegates::RedrawAllViewports.Broadcast();
}
}
void UMaterialEditingLibrary::GetChildInstances(UMaterialInterface* Parent, TArray< FAssetData>& ChildInstances)
{
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
TArray<FAssetData> AssetList;
TMultiMap<FName, FString> TagsAndValues;
const FString ParentNameString = FAssetData(Parent).GetExportTextName();
TagsAndValues.Add(GET_MEMBER_NAME_CHECKED(UMaterialInstance, Parent), ParentNameString);
AssetRegistryModule.Get().GetAssetsByTagValues(TagsAndValues, AssetList);
for (const FAssetData& MatInstRef : AssetList)
{
ChildInstances.Add(MatInstRef);
}
}
void UMaterialEditingLibrary::GetScalarParameterNames(UMaterialInterface* Material, TArray<FName>& ParameterNames)
{
ParameterNames.Empty();
if (Material)
{
TArray<FMaterialParameterInfo> MaterialInfo;
TArray<FGuid> MaterialGuids;
Material->GetAllScalarParameterInfo(MaterialInfo, MaterialGuids);
for (const FMaterialParameterInfo& Info : MaterialInfo)
{
ParameterNames.Add(Info.Name);
}
}
}
void UMaterialEditingLibrary::GetVectorParameterNames(UMaterialInterface* Material, TArray<FName>& ParameterNames)
{
ParameterNames.Empty();
if (Material)
{
TArray<FMaterialParameterInfo> MaterialInfo;
TArray<FGuid> MaterialGuids;
Material->GetAllVectorParameterInfo(MaterialInfo, MaterialGuids);
for (const FMaterialParameterInfo& Info : MaterialInfo)
{
ParameterNames.Add(Info.Name);
}
}
}
void UMaterialEditingLibrary::GetTextureParameterNames(UMaterialInterface* Material, TArray<FName>& ParameterNames)
{
ParameterNames.Empty();
if (Material)
{
TArray<FMaterialParameterInfo> MaterialInfo;
TArray<FGuid> MaterialGuids;
Material->GetAllTextureParameterInfo(MaterialInfo, MaterialGuids);
for (const FMaterialParameterInfo& Info : MaterialInfo)
{
ParameterNames.Add(Info.Name);
}
}
}
void UMaterialEditingLibrary::GetStaticSwitchParameterNames(UMaterialInterface* Material, TArray<FName>& ParameterNames)
{
ParameterNames.Empty();
if (Material)
{
TArray<FMaterialParameterInfo> MaterialInfo;
TArray<FGuid> MaterialGuids;
Material->GetAllStaticSwitchParameterInfo(MaterialInfo, MaterialGuids);
for (const FMaterialParameterInfo& Info : MaterialInfo)
{
ParameterNames.Add(Info.Name);
}
}
}
void UMaterialEditingLibrary::GetEnumerationParameterNames(UMaterialInterface* Material, TArray<FName>& ParameterNames)
{
ParameterNames.Empty();
if (Material)
{
TArray<FMaterialParameterInfo> MaterialInfo;
TArray<FGuid> MaterialGuids;
Material->GetAllEnumerationParameterInfo(MaterialInfo, MaterialGuids);
for (const FMaterialParameterInfo& Info : MaterialInfo)
{
ParameterNames.Add(Info.Name);
}
}
}
static bool GetParameterSource(UMaterialInterface* Material, const TArray<FMaterialParameterInfo>& Info, const TArray<FGuid>& Guids, const FName& ParameterName, FSoftObjectPath& OutParameterSource)
{
bool bResult = false;
for (int32 Index = 0; Index < Info.Num(); ++Index)
{
if (Info[Index].Name == ParameterName)
{
UMaterial* BaseMaterial = Material->GetBaseMaterial();
UMaterialExpression* Expression = BaseMaterial->FindExpressionByGUID<UMaterialExpression>(Guids[Index]);
if (Expression)
{
bResult = true;
OutParameterSource = Expression->GetAssetOwner();
}
break;
}
}
return bResult;
}
bool UMaterialEditingLibrary::GetScalarParameterSource(UMaterialInterface* Material, const FName ParameterName, FSoftObjectPath& ParameterSource)
{
if (Material)
{
TArray<FMaterialParameterInfo> MaterialInfo;
TArray<FGuid> MaterialGuids;
Material->GetAllScalarParameterInfo(MaterialInfo, MaterialGuids);
return GetParameterSource(Material, MaterialInfo, MaterialGuids, ParameterName, ParameterSource);
}
return false;
}
bool UMaterialEditingLibrary::GetVectorParameterSource(UMaterialInterface* Material, const FName ParameterName, FSoftObjectPath& ParameterSource)
{
if (Material)
{
TArray<FMaterialParameterInfo> MaterialInfo;
TArray<FGuid> MaterialGuids;
Material->GetAllVectorParameterInfo(MaterialInfo, MaterialGuids);
return GetParameterSource(Material, MaterialInfo, MaterialGuids, ParameterName, ParameterSource);
}
return false;
}
bool UMaterialEditingLibrary::GetTextureParameterSource(UMaterialInterface* Material, const FName ParameterName, FSoftObjectPath& ParameterSource)
{
if (Material)
{
TArray<FMaterialParameterInfo> MaterialInfo;
TArray<FGuid> MaterialGuids;
Material->GetAllTextureParameterInfo(MaterialInfo, MaterialGuids);
return GetParameterSource(Material, MaterialInfo, MaterialGuids, ParameterName, ParameterSource);
}
return false;
}
bool UMaterialEditingLibrary::GetStaticSwitchParameterSource(UMaterialInterface* Material, const FName ParameterName, FSoftObjectPath& ParameterSource)
{
if (Material)
{
TArray<FMaterialParameterInfo> MaterialInfo;
TArray<FGuid> MaterialGuids;
Material->GetAllStaticSwitchParameterInfo(MaterialInfo, MaterialGuids);
return GetParameterSource(Material, MaterialInfo, MaterialGuids, ParameterName, ParameterSource);
}
return false;
}
bool UMaterialEditingLibrary::GetEnumerationParameterSource(UMaterialInterface* Material, const FName ParameterName, FSoftObjectPath& ParameterSource)
{
if (Material)
{
TArray<FMaterialParameterInfo> MaterialInfo;
TArray<FGuid> MaterialGuids;
Material->GetAllEnumerationParameterInfo(MaterialInfo, MaterialGuids);
return GetParameterSource(Material, MaterialInfo, MaterialGuids, ParameterName, ParameterSource);
}
return false;
}
bool UMaterialEditingLibrary::GetEnumerationParameterEntryNames(UMaterialInterface* Material, FName ParameterName, EMaterialParameterAssociation Association, TArray<FName>& OutEntryNames)
{
if (!Material)
{
return false;
}
FHashedMaterialParameterInfo ParameterInfo(ParameterName, Association, Association == EMaterialParameterAssociation::LayerParameter ? 0 : INDEX_NONE);
FMaterialParameterMetadata ParameterMetadata;
if (!Material->GetCachedExpressionData().GetParameterValue(EMaterialParameterType::Enumeration, ParameterInfo, ParameterMetadata))
{
return false;
}
if (!ParameterMetadata.Enumeration)
{
return false;
}
Cast<IMaterialEnumerationProvider>(ParameterMetadata.Enumeration)->ForEachEntry([&OutEntryNames](FName EntryName, int32 EntryValue)
{
OutEntryNames.Add(EntryName);
});
return true;
}
FMaterialStatistics UMaterialEditingLibrary::GetStatistics(class UMaterialInterface* Material)
{
FMaterialStatistics Result;
FMaterialResource* Resource = Material ? Material->GetMaterialResource(GMaxRHIFeatureLevel) : nullptr;
if (Resource)
{
if (!Resource->IsGameThreadShaderMapComplete())
{
Resource->SubmitCompileJobs_GameThread(EShaderCompileJobPriority::High);
}
Resource->FinishCompilation();
TArray<FMaterialStatsUtils::FShaderInstructionsInfo> InstructionInfos;
FMaterialStatsUtils::GetRepresentativeInstructionCounts(InstructionInfos, Resource);
for (const FMaterialStatsUtils::FShaderInstructionsInfo& Info : InstructionInfos)
{
const int32 ShaderType = (int32)Info.ShaderType;
if (ShaderType >= (int32)ERepresentativeShader::FirstFragmentShader && ShaderType <= (int32)ERepresentativeShader::LastFragmentShader)
{
Result.NumPixelShaderInstructions = FMath::Max(Result.NumPixelShaderInstructions, Info.InstructionCount);
}
else if (ShaderType >= (int32)ERepresentativeShader::FirstVertexShader && ShaderType <= (int32)ERepresentativeShader::LastVertexShader)
{
Result.NumVertexShaderInstructions = FMath::Max(Result.NumVertexShaderInstructions, Info.InstructionCount);
}
}
Result.NumSamplers = Resource->GetSamplerUsage();
uint32 NumVSTextureSamples = 0, NumPSTextureSamples = 0;
Resource->GetEstimatedNumTextureSamples(NumVSTextureSamples, NumPSTextureSamples);
Result.NumVertexTextureSamples = (int32)NumVSTextureSamples;
Result.NumPixelTextureSamples = (int32)NumPSTextureSamples;
Result.NumVirtualTextureSamples = Resource->GetEstimatedNumVirtualTextureLookups();
uint32 UVScalarsUsed, CustomInterpolatorScalarsUsed;
Resource->GetUserInterpolatorUsage(UVScalarsUsed, CustomInterpolatorScalarsUsed);
Result.NumUVScalars = (int32)UVScalarsUsed;
Result.NumInterpolatorScalars = (int32)CustomInterpolatorScalarsUsed;
}
return Result;
}
UMaterialInterface* UMaterialEditingLibrary::GetNaniteOverrideMaterial(UMaterialInterface* Material)
{
return Material->GetNaniteOverride();
}