Files
UnrealEngineUWP/Engine/Source/Editor/MaterialEditor/Private/MaterialEditorUtilities.cpp
2014-10-08 15:27:19 -04:00

645 lines
24 KiB
C++

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
#include "MaterialEditorModule.h"
#include "Materials/MaterialExpressionFunctionInput.h"
#include "Materials/MaterialExpressionFunctionOutput.h"
#include "Materials/MaterialExpressionStaticBoolParameter.h"
#include "Materials/MaterialExpressionStaticBool.h"
#include "Materials/MaterialExpressionStaticSwitch.h"
#include "Materials/MaterialExpressionComment.h"
#include "Materials/MaterialExpressionParameter.h"
#include "Materials/MaterialExpressionTextureSampleParameter.h"
#include "Materials/MaterialExpressionFontSampleParameter.h"
#include "Materials/MaterialExpressionScalarParameter.h"
#include "Materials/MaterialExpressionVectorParameter.h"
#include "Materials/MaterialExpressionStaticSwitchParameter.h"
#include "Materials/MaterialFunction.h"
#include "MaterialEditorUtilities.h"
#include "Toolkits/ToolkitManager.h"
#include "MaterialExpressionClasses.h"
#define LOCTEXT_NAMESPACE "MaterialEditorUtilities"
DEFINE_LOG_CATEGORY_STATIC(LogMaterialEditorUtilities, Log, All);
UMaterialExpression* FMaterialEditorUtilities::CreateNewMaterialExpression(const class UEdGraph* Graph, UClass* NewExpressionClass, const FVector2D& NodePos, bool bAutoSelect, bool bAutoAssignResource)
{
TSharedPtr<class IMaterialEditor> MaterialEditor = GetIMaterialEditorForObject(Graph);
if (MaterialEditor.IsValid())
{
return MaterialEditor->CreateNewMaterialExpression(NewExpressionClass, NodePos, bAutoSelect, bAutoAssignResource);
}
return NULL;
}
UMaterialExpressionComment* FMaterialEditorUtilities::CreateNewMaterialExpressionComment(const class UEdGraph* Graph, const FVector2D& NodePos)
{
TSharedPtr<class IMaterialEditor> MaterialEditor = GetIMaterialEditorForObject(Graph);
if (MaterialEditor.IsValid())
{
return MaterialEditor->CreateNewMaterialExpressionComment(NodePos);
}
return NULL;
}
void FMaterialEditorUtilities::ForceRefreshExpressionPreviews(const class UEdGraph* Graph)
{
TSharedPtr<class IMaterialEditor> MaterialEditor = GetIMaterialEditorForObject(Graph);
if (MaterialEditor.IsValid())
{
MaterialEditor->ForceRefreshExpressionPreviews();
}
}
void FMaterialEditorUtilities::AddToSelection(const class UEdGraph* Graph, UMaterialExpression* Expression)
{
TSharedPtr<class IMaterialEditor> MaterialEditor = GetIMaterialEditorForObject(Graph);
if (MaterialEditor.IsValid())
{
MaterialEditor->AddToSelection(Expression);
}
}
void FMaterialEditorUtilities::DeleteSelectedNodes(const class UEdGraph* Graph)
{
TSharedPtr<class IMaterialEditor> MaterialEditor = GetIMaterialEditorForObject(Graph);
if (MaterialEditor.IsValid())
{
MaterialEditor->DeleteSelectedNodes();
}
}
FString FMaterialEditorUtilities::GetOriginalObjectName(const class UEdGraph* Graph)
{
TSharedPtr<class IMaterialEditor> MaterialEditor = GetIMaterialEditorForObject(Graph);
if (MaterialEditor.IsValid())
{
return MaterialEditor->GetOriginalObjectName();
}
return TEXT("");
}
void FMaterialEditorUtilities::UpdateMaterialAfterGraphChange(const class UEdGraph* Graph)
{
TSharedPtr<class IMaterialEditor> MaterialEditor = GetIMaterialEditorForObject(Graph);
if (MaterialEditor.IsValid())
{
MaterialEditor->UpdateMaterialAfterGraphChange();
}
}
bool FMaterialEditorUtilities::CanPasteNodes(const class UEdGraph* Graph)
{
bool bCanPaste = false;
TSharedPtr<class IMaterialEditor> MaterialEditor = GetIMaterialEditorForObject(Graph);
if(MaterialEditor.IsValid())
{
bCanPaste = MaterialEditor->CanPasteNodes();
}
return bCanPaste;
}
void FMaterialEditorUtilities::PasteNodesHere(class UEdGraph* Graph, const FVector2D& Location)
{
TSharedPtr<class IMaterialEditor> MaterialEditor = GetIMaterialEditorForObject(Graph);
if(MaterialEditor.IsValid())
{
MaterialEditor->PasteNodesHere(Location);
}
}
int32 FMaterialEditorUtilities::GetNumberOfSelectedNodes(const class UEdGraph* Graph)
{
int32 SelectedNodes = 0;
TSharedPtr<class IMaterialEditor> MaterialEditor = GetIMaterialEditorForObject(Graph);
if(MaterialEditor.IsValid())
{
SelectedNodes = MaterialEditor->GetNumberOfSelectedNodes();
}
return SelectedNodes;
}
void FMaterialEditorUtilities::GetMaterialExpressionActions(FGraphActionMenuBuilder& ActionMenuBuilder, bool bMaterialFunction)
{
// TODO: Not sure if this is necessary/usable anymore
// Get all menu extenders for this context menu from the material editor module
/*IMaterialEditorModule& MaterialEditor = FModuleManager::GetModuleChecked<IMaterialEditorModule>( TEXT("MaterialEditor") );
TArray<IMaterialEditorModule::FMaterialMenuExtender> MenuExtenderDelegates = MaterialEditor.GetAllMaterialCanvasMenuExtenders();
TArray<TSharedPtr<FExtender>> Extenders;
for (int32 i = 0; i < MenuExtenderDelegates.Num(); ++i)
{
if (MenuExtenderDelegates[i].IsBound())
{
Extenders.Add(MenuExtenderDelegates[i].Execute(MaterialEditorPtr.Pin()->GetToolkitCommands()));
}
}
TSharedPtr<FExtender> MenuExtender = FExtender::Combine(Extenders);*/
bool bUseUnsortedMenus = false;
MaterialExpressionClasses* ExpressionClasses = MaterialExpressionClasses::Get();
if (bUseUnsortedMenus)
{
AddMaterialExpressionCategory(ActionMenuBuilder, TEXT(""), &ExpressionClasses->AllExpressionClasses, bMaterialFunction);
}
else
{
// Add Favourite expressions as a category
const FText FavouritesCategory = LOCTEXT("FavoritesMenu", "Favorites");
AddMaterialExpressionCategory(ActionMenuBuilder, FavouritesCategory.ToString(), &ExpressionClasses->FavoriteExpressionClasses, bMaterialFunction);
// Add each category to the menu
for (int32 CategoryIndex = 0; CategoryIndex < ExpressionClasses->CategorizedExpressionClasses.Num(); ++CategoryIndex)
{
FCategorizedMaterialExpressionNode* CategoryNode = &(ExpressionClasses->CategorizedExpressionClasses[CategoryIndex]);
AddMaterialExpressionCategory(ActionMenuBuilder, CategoryNode->CategoryName, &CategoryNode->MaterialExpressions, bMaterialFunction);
}
if (ExpressionClasses->UnassignedExpressionClasses.Num() > 0)
{
AddMaterialExpressionCategory(ActionMenuBuilder, TEXT(""), &ExpressionClasses->UnassignedExpressionClasses, bMaterialFunction);
}
}
}
bool FMaterialEditorUtilities::IsMaterialExpressionInFavorites(UMaterialExpression* InExpression)
{
return MaterialExpressionClasses::Get()->IsMaterialExpressionInFavorites(InExpression);
}
FMaterialRenderProxy* FMaterialEditorUtilities::GetExpressionPreview(const class UEdGraph* Graph, UMaterialExpression* InExpression)
{
FMaterialRenderProxy* ExpressionPreview = NULL;
TSharedPtr<class IMaterialEditor> MaterialEditor = GetIMaterialEditorForObject(Graph);
if(MaterialEditor.IsValid())
{
ExpressionPreview = MaterialEditor->GetExpressionPreview(InExpression);
}
return ExpressionPreview;
}
void FMaterialEditorUtilities::UpdateSearchResults(const class UEdGraph* Graph)
{
TSharedPtr<class IMaterialEditor> MaterialEditor = GetIMaterialEditorForObject(Graph);
if(MaterialEditor.IsValid())
{
MaterialEditor->UpdateSearch(false);
}
}
/////////////////////////////////////////////////////
// Static functions moved from SMaterialEditorCanvas
void FMaterialEditorUtilities::GetVisibleMaterialParameters(const UMaterial *Material, UMaterialInstance *MaterialInstance, TArray<FGuid> &VisibleExpressions)
{
VisibleExpressions.Empty();
TArray<UMaterialExpression*> ProcessedExpressions;
for(uint32 i = 0; i < MP_MAX; ++i)
{
FExpressionInput* ExpressionInput = ((UMaterial *)Material)->GetExpressionInputForProperty((EMaterialProperty)i);
if(ExpressionInput)
{
GetVisibleMaterialParametersFromExpression(ExpressionInput->Expression, MaterialInstance, VisibleExpressions, ProcessedExpressions);
}
}
}
bool FMaterialEditorUtilities::GetStaticSwitchExpressionValue(UMaterialInstance* MaterialInstance, UMaterialExpression *SwitchValueExpression, bool& OutValue, FGuid& OutExpressionID, const TArray<FFunctionExpressionInput>* FunctionInputs)
{
// If switch value is a function input expression then we must recursively find the associated input expressions from the parent function/material to evaluate the value.
UMaterialExpressionFunctionInput* FunctionInputExpression = Cast<UMaterialExpressionFunctionInput>(SwitchValueExpression);
if(FunctionInputExpression && FunctionInputExpression->InputType == FunctionInput_StaticBool)
{
// Get the FFunctionExpressionInput which stores information about the input node from the parent that this is linked to.
const FFunctionExpressionInput* MatchingInput = FindInputById(FunctionInputExpression, *FunctionInputs);
if (MatchingInput && (MatchingInput->Input.Expression || !FunctionInputExpression->bUsePreviewValueAsDefault))
{
GetStaticSwitchExpressionValue(MaterialInstance, MatchingInput->Input.Expression, OutValue, OutExpressionID, FunctionInputs);
}
else
{
GetStaticSwitchExpressionValue(MaterialInstance, FunctionInputExpression->Preview.Expression, OutValue, OutExpressionID, FunctionInputs);
}
}
// The expression can only be a static bool parameter when the current scope is not within a function
if(SwitchValueExpression && SwitchValueExpression->Material != NULL)
{
UMaterialExpressionStaticBoolParameter* SwitchParamValue = Cast<UMaterialExpressionStaticBoolParameter>(SwitchValueExpression);
if(SwitchParamValue)
{
MaterialInstance->GetStaticSwitchParameterValue(SwitchParamValue->ParameterName, OutValue, OutExpressionID);
return true;
}
}
UMaterialExpressionStaticBool* StaticSwitchValue = Cast<UMaterialExpressionStaticBool>(SwitchValueExpression);
if(StaticSwitchValue)
{
OutValue = StaticSwitchValue->Value;
return true;
}
return false;
}
bool FMaterialEditorUtilities::IsFunctionContainingSwitchExpressions(UMaterialFunction* MaterialFunction)
{
if (MaterialFunction)
{
TArray<UMaterialFunction*> DependentFunctions;
MaterialFunction->GetDependentFunctions(DependentFunctions);
DependentFunctions.AddUnique(MaterialFunction);
for (int32 FunctionIndex = 0; FunctionIndex < DependentFunctions.Num(); ++FunctionIndex)
{
UMaterialFunction* CurrentFunction = DependentFunctions[FunctionIndex];
for(int32 ExpressionIndex = 0; ExpressionIndex < CurrentFunction->FunctionExpressions.Num(); ++ExpressionIndex )
{
UMaterialExpressionStaticSwitch* StaticSwitchExpression = Cast<UMaterialExpressionStaticSwitch>(CurrentFunction->FunctionExpressions[ExpressionIndex]);
if (StaticSwitchExpression)
{
return true;
}
}
}
}
return false;
}
const FFunctionExpressionInput* FMaterialEditorUtilities::FindInputById(const UMaterialExpressionFunctionInput* InputExpression, const TArray<FFunctionExpressionInput>& Inputs)
{
for (int32 InputIndex = 0; InputIndex < Inputs.Num(); InputIndex++)
{
const FFunctionExpressionInput& CurrentInput = Inputs[InputIndex];
if (CurrentInput.ExpressionInputId == InputExpression->Id && CurrentInput.ExpressionInput->GetOuter() == InputExpression->GetOuter())
{
return &CurrentInput;
}
}
return NULL;
}
void FMaterialEditorUtilities::InitExpressions(UMaterial* Material)
{
FString ParmName;
Material->EditorComments.Empty();
Material->Expressions.Empty();
TArray<UObject*> ChildObjects;
GetObjectsWithOuter(Material, ChildObjects, /*bIncludeNestedObjects=*/false);
for ( int32 ChildIdx = 0; ChildIdx < ChildObjects.Num(); ++ChildIdx )
{
UMaterialExpression* MaterialExpression = Cast<UMaterialExpression>(ChildObjects[ChildIdx]);
if( MaterialExpression != NULL && !MaterialExpression->IsPendingKill() )
{
// Comment expressions are stored in a separate list.
if ( MaterialExpression->IsA( UMaterialExpressionComment::StaticClass() ) )
{
Material->EditorComments.Add( static_cast<UMaterialExpressionComment*>(MaterialExpression) );
}
else
{
Material->Expressions.Add( MaterialExpression );
}
}
}
Material->BuildEditorParameterList();
// Propagate RF_Transactional to all referenced material expressions.
Material->SetFlags( RF_Transactional );
for( int32 MaterialExpressionIndex = 0 ; MaterialExpressionIndex < Material->Expressions.Num() ; ++MaterialExpressionIndex )
{
UMaterialExpression* MaterialExpression = Material->Expressions[ MaterialExpressionIndex ];
if(MaterialExpression)
{
MaterialExpression->SetFlags( RF_Transactional );
}
}
for( int32 MaterialExpressionIndex = 0 ; MaterialExpressionIndex < Material->EditorComments.Num() ; ++MaterialExpressionIndex )
{
UMaterialExpressionComment* Comment = Material->EditorComments[ MaterialExpressionIndex ];
Comment->SetFlags( RF_Transactional );
}
}
///////////
// private
void FMaterialEditorUtilities::GetVisibleMaterialParametersFromExpression(UMaterialExpression *MaterialExpression, UMaterialInstance *MaterialInstance, TArray<FGuid> &VisibleExpressions, TArray<UMaterialExpression*> &ProcessedExpressions, const TArray<FFunctionExpressionInput>* FunctionInputs, TArray<FGuid>* VisibleFunctionInputExpressions)
{
if(!MaterialExpression)
{
return;
}
check(MaterialInstance);
//don't allow re-entrant expressions to continue
if (ProcessedExpressions.Contains(MaterialExpression))
{
return;
}
ProcessedExpressions.Push(MaterialExpression);
if(MaterialExpression->Material != NULL)
{
// if it's a material parameter it must be visible so add it to the map
UMaterialExpressionParameter *Param = Cast<UMaterialExpressionParameter>( MaterialExpression );
UMaterialExpressionTextureSampleParameter *TexParam = Cast<UMaterialExpressionTextureSampleParameter>( MaterialExpression );
UMaterialExpressionFontSampleParameter *FontParam = Cast<UMaterialExpressionFontSampleParameter>( MaterialExpression );
if( Param )
{
VisibleExpressions.AddUnique(Param->ExpressionGUID);
UMaterialExpressionScalarParameter *ScalarParam = Cast<UMaterialExpressionScalarParameter>( MaterialExpression );
UMaterialExpressionVectorParameter *VectorParam = Cast<UMaterialExpressionVectorParameter>( MaterialExpression );
TArray<FName> Names;
TArray<FGuid> Ids;
if( ScalarParam )
{
MaterialInstance->GetMaterial()->GetAllScalarParameterNames( Names, Ids );
for( int32 i = 0; i < Names.Num(); i++ )
{
if( Names[i] == ScalarParam->ParameterName )
{
VisibleExpressions.AddUnique( Ids[ i ] );
}
}
}
else if ( VectorParam )
{
MaterialInstance->GetMaterial()->GetAllVectorParameterNames( Names, Ids );
for( int32 i = 0; i < Names.Num(); i++ )
{
if( Names[i] == VectorParam->ParameterName )
{
VisibleExpressions.AddUnique( Ids[ i ] );
}
}
}
}
else if(TexParam)
{
VisibleExpressions.AddUnique( TexParam->ExpressionGUID );
TArray<FName> Names;
TArray<FGuid> Ids;
MaterialInstance->GetMaterial()->GetAllTextureParameterNames( Names, Ids );
for( int32 i = 0; i < Names.Num(); i++ )
{
if( Names[i] == TexParam->ParameterName )
{
VisibleExpressions.AddUnique( Ids[ i ] );
}
}
}
else if(FontParam)
{
VisibleExpressions.AddUnique( FontParam->ExpressionGUID );
TArray<FName> Names;
TArray<FGuid> Ids;
MaterialInstance->GetMaterial()->GetAllFontParameterNames( Names, Ids );
for( int32 i = 0; i < Names.Num(); i++ )
{
if( Names[i] == FontParam->ParameterName )
{
VisibleExpressions.AddUnique( Ids[ i ] );
}
}
}
}
UMaterialExpressionFunctionInput* InputExpression = Cast<UMaterialExpressionFunctionInput>(MaterialExpression);
if(InputExpression)
{
VisibleFunctionInputExpressions->AddUnique(InputExpression->Id);
}
// check if it's a switch expression and branch according to its value
UMaterialExpressionStaticSwitchParameter* StaticSwitchParamExpression = Cast<UMaterialExpressionStaticSwitchParameter>(MaterialExpression);
UMaterialExpressionStaticSwitch* StaticSwitchExpression = Cast<UMaterialExpressionStaticSwitch>(MaterialExpression);
UMaterialExpressionMaterialFunctionCall* FunctionCallExpression = Cast<UMaterialExpressionMaterialFunctionCall>(MaterialExpression);
if(StaticSwitchParamExpression)
{
bool Value = false;
FGuid ExpressionID;
MaterialInstance->GetStaticSwitchParameterValue(StaticSwitchParamExpression->ParameterName, Value, ExpressionID);
VisibleExpressions.AddUnique(ExpressionID);
if(Value)
{
GetVisibleMaterialParametersFromExpression(StaticSwitchParamExpression->A.Expression, MaterialInstance, VisibleExpressions, ProcessedExpressions);
}
else
{
GetVisibleMaterialParametersFromExpression(StaticSwitchParamExpression->B.Expression, MaterialInstance, VisibleExpressions, ProcessedExpressions);
}
}
else if(StaticSwitchExpression)
{
bool bValue = StaticSwitchExpression->DefaultValue;
FGuid ExpressionID;
if(StaticSwitchExpression->Value.Expression)
{
GetStaticSwitchExpressionValue(MaterialInstance, StaticSwitchExpression->Value.Expression, bValue, ExpressionID, FunctionInputs );
// Flag the switch value as a visible expression
UMaterialExpressionFunctionInput* FunctionInputExpression = Cast<UMaterialExpressionFunctionInput>(StaticSwitchExpression->Value.Expression);
if(FunctionInputExpression)
{
VisibleFunctionInputExpressions->AddUnique(FunctionInputExpression->Id);
}
else if (StaticSwitchExpression && StaticSwitchExpression->Material != NULL)
{
if(ExpressionID.IsValid())
{
VisibleExpressions.AddUnique(ExpressionID);
}
}
}
if(bValue)
{
GetVisibleMaterialParametersFromExpression(StaticSwitchExpression->A.Expression, MaterialInstance, VisibleExpressions, ProcessedExpressions, FunctionInputs, VisibleFunctionInputExpressions);
}
else
{
GetVisibleMaterialParametersFromExpression(StaticSwitchExpression->B.Expression, MaterialInstance, VisibleExpressions, ProcessedExpressions, FunctionInputs, VisibleFunctionInputExpressions);
}
}
else if(FunctionCallExpression)
{
// Only process functions if any of the dependent functions contain static switches that could potentially cull the material parameters.
if(IsFunctionContainingSwitchExpressions(FunctionCallExpression->MaterialFunction))
{
// Pass an accumalated array of FFunctionExpressionInput so that input expressions can be matched to the input from the parent.
TArray<FGuid> VisibleFunctionInputs;
TArray<FFunctionExpressionInput> FunctionCallInputs = FunctionCallExpression->FunctionInputs;
if(FunctionInputs)
{
FunctionCallInputs.Append(*FunctionInputs);
}
for(int32 FunctionOutputIndex = 0; FunctionOutputIndex < FunctionCallExpression->FunctionOutputs.Num(); FunctionOutputIndex++)
{
// Recurse material functions. Returns an array of input ids that are visible and have not been culled by static switches.
GetVisibleMaterialParametersFromExpression( FunctionCallExpression->FunctionOutputs[FunctionOutputIndex].ExpressionOutput->A.Expression, MaterialInstance, VisibleExpressions, ProcessedExpressions, &FunctionCallInputs, &VisibleFunctionInputs);
}
// Parse children of function call inputs that have not been culled
for(int32 ExpressionInputIndex = 0; ExpressionInputIndex < FunctionCallExpression->FunctionInputs.Num(); ExpressionInputIndex++)
{
const FFunctionExpressionInput& Input = FunctionCallExpression->FunctionInputs[ExpressionInputIndex];
if(VisibleFunctionInputs.Contains(Input.ExpressionInputId))
{
// Retrieve the expression input and then start parsing its children
GetVisibleMaterialParametersFromExpression(Input.Input.Expression, MaterialInstance, VisibleExpressions, ProcessedExpressions, FunctionInputs, VisibleFunctionInputExpressions );
}
}
}
else
{
const TArray<FExpressionInput*>& ExpressionInputs = MaterialExpression->GetInputs();
for(int32 ExpressionInputIndex = 0; ExpressionInputIndex < ExpressionInputs.Num(); ExpressionInputIndex++)
{
//retrieve the expression input and then start parsing its children
FExpressionInput* Input = ExpressionInputs[ExpressionInputIndex];
GetVisibleMaterialParametersFromExpression(Input->Expression, MaterialInstance, VisibleExpressions, ProcessedExpressions, FunctionInputs, VisibleFunctionInputExpressions);
}
}
}
else
{
const TArray<FExpressionInput*>& ExpressionInputs = MaterialExpression->GetInputs();
for(int32 ExpressionInputIndex = 0; ExpressionInputIndex < ExpressionInputs.Num(); ExpressionInputIndex++)
{
//retrieve the expression input and then start parsing its children
FExpressionInput* Input = ExpressionInputs[ExpressionInputIndex];
GetVisibleMaterialParametersFromExpression(Input->Expression, MaterialInstance, VisibleExpressions, ProcessedExpressions, FunctionInputs, VisibleFunctionInputExpressions);
}
}
UMaterialExpression* TopExpression = ProcessedExpressions.Pop();
//ensure that the top of the stack matches what we expect (the same as MaterialExpression)
check(MaterialExpression == TopExpression);
}
TSharedPtr<class IMaterialEditor> FMaterialEditorUtilities::GetIMaterialEditorForObject(const UObject* ObjectToFocusOn)
{
check(ObjectToFocusOn);
// Find the associated Material
UMaterial* Material = Cast<UMaterial>(ObjectToFocusOn->GetOuter());
TSharedPtr<IMaterialEditor> MaterialEditor;
if (Material != NULL)
{
TSharedPtr< IToolkit > FoundAssetEditor = FToolkitManager::Get().FindEditorForAsset(Material);
if (FoundAssetEditor.IsValid())
{
MaterialEditor = StaticCastSharedPtr<IMaterialEditor>(FoundAssetEditor);
}
}
return MaterialEditor;
}
void FMaterialEditorUtilities::AddMaterialExpressionCategory(FGraphActionMenuBuilder& ActionMenuBuilder, FString CategoryName, TArray<struct FMaterialExpression>* MaterialExpressions, bool bMaterialFunction)
{
// Get type of dragged pin
uint32 FromPinType = 0;
if (ActionMenuBuilder.FromPin)
{
FromPinType = UMaterialGraphSchema::GetMaterialValueType(ActionMenuBuilder.FromPin);
}
for (int32 Index = 0; Index < MaterialExpressions->Num(); ++Index)
{
const FMaterialExpression& MaterialExpression = (*MaterialExpressions)[Index];
if (IsAllowedExpressionType(MaterialExpression.MaterialClass, bMaterialFunction))
{
if (!ActionMenuBuilder.FromPin || HasCompatibleConnection(MaterialExpression.MaterialClass, FromPinType, ActionMenuBuilder.FromPin->Direction, bMaterialFunction))
{
FFormatNamedArguments Arguments;
Arguments.Add(TEXT("Name"), FText::FromString( *MaterialExpression.Name ));
const FText ToolTip = FText::Format( LOCTEXT( "NewMaterialExpressionTooltip", "Adds a {Name} node here" ), Arguments );
TSharedPtr<FMaterialGraphSchemaAction_NewNode> NewNodeAction(new FMaterialGraphSchemaAction_NewNode(
CategoryName,
FText::FromString(MaterialExpression.Name),
ToolTip.ToString(), 0));
ActionMenuBuilder.AddAction(NewNodeAction);
NewNodeAction->MaterialExpressionClass = MaterialExpression.MaterialClass;
NewNodeAction->Keywords = CastChecked<UMaterialExpression>(MaterialExpression.MaterialClass->GetDefaultObject())->GetKeywords();
}
}
}
}
bool FMaterialEditorUtilities::HasCompatibleConnection(UClass* ExpressionClass, uint32 TestType, EEdGraphPinDirection TestDirection, bool bMaterialFunction)
{
if (TestType != 0)
{
UMaterialExpression* DefaultExpression = CastChecked<UMaterialExpression>(ExpressionClass->GetDefaultObject());
if (TestDirection == EGPD_Output)
{
int32 NumInputs = DefaultExpression->GetInputs().Num();
for (int32 Index = 0; Index < NumInputs; ++Index)
{
uint32 InputType = DefaultExpression->GetInputType(Index);
if (CanConnectMaterialValueTypes(InputType, TestType))
{
return true;
}
}
}
else
{
int32 NumOutputs = DefaultExpression->GetOutputs().Num();
for (int32 Index = 0; Index < NumOutputs; ++Index)
{
uint32 OutputType = DefaultExpression->GetOutputType(Index);
if (CanConnectMaterialValueTypes(TestType, OutputType))
{
return true;
}
}
}
if (bMaterialFunction)
{
// Specific test as Default object won't have texture input
if (ExpressionClass == UMaterialExpressionTextureSample::StaticClass() && TestType & MCT_Texture && TestDirection == EGPD_Output)
{
return true;
}
// Always allow creation of new inputs as they can take any type
else if (ExpressionClass == UMaterialExpressionFunctionInput::StaticClass())
{
return true;
}
// Allow creation of outputs for floats and material attributes
else if (ExpressionClass == UMaterialExpressionFunctionOutput::StaticClass() && TestType & (MCT_Float|MCT_MaterialAttributes))
{
return true;
}
}
}
return false;
}
#undef LOCTEXT_NAMESPACE