You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
Also restores follow-up CLs 19973746, 19973782 FStaticParameterSet::MaterialLayers can't be deprecated, since a property with the same name is included via FStaticParameterSetRuntimeData So instead, FMaterialLayersFunctionsRuntimeData is updated with SerializeFromMismatchedTag, to allow serializing a full FMaterialLayersFunction. When this happens, the EditorOnly portion is stored in a separate heap allocation, and then transferred to FStaticParameterSet::EditorOnly::MaterialLayers #preflight 626c3405e31dbb512cef1e98 [Backout] - CL19973745 #fyi bob.tellez Original CL Desc ----------------------------------------------------------------- [Backout] - CL19964485 #fyi Ben.Ingram Original CL Desc ----------------------------------------------------------------- Add 'Optional' EditorOnly data for UMaterialInterface and UMaterialFunctionInterface These are separate UObject hierarchies that store editor-only UPROPERTIES, but can be included with cooked content, which allows full editor support. In principle, all editor-only properties could be moved over. So far, this has been limited to UMaterialExpressions, and data related to material parameters. FStaticParameterSet, FMaterialLayersParameters, and FMaterialCachedExpressionData have all been split into separate editor-only/non-editor-only classes, which allows the editor-only portion to be stored on the optional editor-only UObject. #preflight 626ab21dad56c0cbbea32dc4 #rb jason.nadro, francis.hurteau #jira FORT-463329 [CL 20043286 by Jason Nadro in ue5-main branch]
1253 lines
40 KiB
C++
1253 lines
40 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "MaterialEditingLibrary.h"
|
|
#include "Editor.h"
|
|
#include "MaterialEditor.h"
|
|
#include "MaterialInstanceEditor.h"
|
|
#include "MaterialEditorUtilities.h"
|
|
#include "MaterialShared.h"
|
|
#include "MaterialGraph/MaterialGraphNode.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/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 "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"
|
|
|
|
DEFINE_LOG_CATEGORY_STATIC(LogMaterialEditingLibrary, Warning, All);
|
|
|
|
/** Util to find expression */
|
|
static FExpressionInput* GetExpressionInputByName(UMaterialExpression* Expression, const FName InputName)
|
|
{
|
|
check(Expression);
|
|
FExpressionInput* Result = nullptr;
|
|
|
|
TArray<FExpressionInput*> Inputs = Expression->GetInputs();
|
|
|
|
// Return first input if no name specified
|
|
if (InputName.IsNone())
|
|
{
|
|
if (Inputs.Num() > 0)
|
|
{
|
|
return Inputs[0];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Get all inputs
|
|
// Get name of each input, see if its the one we want
|
|
for (int InputIdx = 0; InputIdx < Inputs.Num(); InputIdx++)
|
|
{
|
|
FName TestName;
|
|
if (UMaterialExpressionMaterialFunctionCall* FuncCall = Cast<UMaterialExpressionMaterialFunctionCall>(Expression))
|
|
{
|
|
// If a function call, don't want to compare string with type postfix
|
|
TestName = FuncCall->GetInputNameWithType(InputIdx, false);
|
|
}
|
|
else
|
|
{
|
|
const FName ExpressionInputName = Expression->GetInputName(InputIdx);
|
|
TestName = UMaterialGraphNode::GetShortenPinName(ExpressionInputName);
|
|
}
|
|
|
|
if (TestName == InputName)
|
|
{
|
|
Result = Inputs[InputIdx];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
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 ( FExpressionInput* ExpressionInput : MaterialExpression->GetInputs() )
|
|
{
|
|
LayoutMaterialExpression( ExpressionInput->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)
|
|
{
|
|
TArray<FExpressionInput*> Inputs = TestExp->GetInputs();
|
|
for (FExpressionInput* Input : Inputs)
|
|
{
|
|
if (Input->Expression == Expression)
|
|
{
|
|
Input->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);
|
|
}
|
|
}
|
|
|
|
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::RecompileMaterial(UMaterial* Material)
|
|
{
|
|
if (ensureAsRuntimeWarning(Material != nullptr))
|
|
{
|
|
{
|
|
FMaterialUpdateContext UpdateContext;
|
|
|
|
UpdateContext.AddMaterial(Material);
|
|
|
|
// Propagate the change to this material
|
|
Material->PreEditChange(nullptr);
|
|
Material->PostEditChange();
|
|
|
|
Material->MarkPackageDirty();
|
|
|
|
// update the world's viewports
|
|
FEditorDelegates::RefreshEditor.Broadcast();
|
|
FEditorSupportDelegates::RedrawAllViewports.Broadcast();
|
|
|
|
// Force particle components to update their view relevance.
|
|
for (TObjectIterator<UParticleSystemComponent> It; It; ++It)
|
|
{
|
|
It->bIsViewRelevanceDirty = true;
|
|
}
|
|
|
|
// Update parameter names on any child material instances
|
|
for (TObjectIterator<UMaterialInstance> It; It; ++It)
|
|
{
|
|
if (It->Parent == Material)
|
|
{
|
|
It->UpdateParameterNames();
|
|
}
|
|
}
|
|
|
|
// Leaving this scope will update all dependent material instances.
|
|
}
|
|
|
|
UMaterialEditingLibrary::RebuildMaterialInstanceEditors(Material);
|
|
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;
|
|
}
|
|
|
|
TArray<UMaterialExpression*> UMaterialEditingLibrary::GetInputsForMaterialExpression(UMaterial* Material, UMaterialExpression* MaterialExpression)
|
|
{
|
|
TArray<UMaterialExpression*> MaterialExpressions;
|
|
if (Material)
|
|
{
|
|
for (const FExpressionInput* Input : MaterialExpression->GetInputs())
|
|
{
|
|
MaterialExpressions.Add(Input->Expression);
|
|
}
|
|
}
|
|
|
|
return MaterialExpressions;
|
|
}
|
|
|
|
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::UpdateMaterialFunction(UMaterialFunctionInterface* MaterialFunction, UMaterial* PreviewMaterial)
|
|
{
|
|
if (MaterialFunction)
|
|
{
|
|
// Create a material update context so we can safely update materials using this function.
|
|
{
|
|
FMaterialUpdateContext UpdateContext;
|
|
|
|
// mark the function as changed
|
|
MaterialFunction->ForceRecompileForRendering(UpdateContext, PreviewMaterial);
|
|
MaterialFunction->MarkPackageDirty();
|
|
|
|
// Go through all function instances in memory and recompile them if they are children
|
|
for (TObjectIterator<UMaterialFunctionInstance> It; 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();
|
|
}
|
|
}
|
|
}
|
|
|
|
// update the world's viewports
|
|
UMaterialFunction* BaseFunction = MaterialFunction->GetBaseFunction();
|
|
|
|
UMaterialEditingLibrary::RebuildMaterialInstanceEditors(BaseFunction);
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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 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);
|
|
}
|
|
return bResult;
|
|
}
|
|
|
|
void UMaterialEditingLibrary::UpdateMaterialInstance(UMaterialInstanceConstant* Instance)
|
|
{
|
|
if (Instance)
|
|
{
|
|
Instance->MarkPackageDirty();
|
|
Instance->PreEditChange(nullptr);
|
|
Instance->PostEditChange();
|
|
|
|
Instance->UpdateStaticPermutation();
|
|
Instance->UpdateParameterNames();
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
} |