// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "UObject/ObjectMacros.h" #include "Templates/UniquePtr.h" #include "EdGraph/EdGraph.h" #include "Materials/Material.h" #include "MaterialGraph.generated.h" class UMaterialExpressionComment; class UMaterialExpressionComposite; namespace UE::Shader { enum class EValueType : uint8; } DECLARE_DELEGATE_RetVal( bool, FRealtimeStateGetter ); DECLARE_DELEGATE( FSetMaterialDirty ); DECLARE_DELEGATE_OneParam( FToggleExpressionCollapsed, UMaterialExpression* ); /** * A human-readable name - material expression input pair. */ struct FMaterialInputInfo { /** Constructor */ FMaterialInputInfo() { } /** Constructor */ FMaterialInputInfo(const FText& InName, EMaterialProperty InProperty, const FText& InToolTip) : Name( InName ) , Property( InProperty ) , ToolTip( InToolTip ) { } FExpressionInput& GetExpressionInput(UMaterial* Material) const { check(Material); auto Ret = Material->GetExpressionInputForProperty(Property); // if this happens UMaterialGraph::RebuildGraph() registered something it shouldn't check(Ret); return *Ret; } bool IsVisiblePin(const UMaterial* Material, bool bIgnoreMaterialAttributes = false) const { if (!Material->IsPropertySupported(Property) && !bIgnoreMaterialAttributes) { return false; } if(Material->bUseMaterialAttributes && !bIgnoreMaterialAttributes) { return Property == MP_MaterialAttributes; } else if( Material->IsUIMaterial() ) { if( Property == MP_EmissiveColor || Property == MP_Opacity || Property == MP_OpacityMask || Property == MP_WorldPositionOffset ) { return true; } if (Property >= MP_CustomizedUVs0 && Property <= MP_CustomizedUVs7) { return (Property - MP_CustomizedUVs0) < Material->NumCustomizedUVs; } return false; } else { if(Property == MP_MaterialAttributes) { return false; } if(Property >= MP_CustomizedUVs0 && Property <= MP_CustomizedUVs7) { return (Property - MP_CustomizedUVs0) < Material->NumCustomizedUVs; } return true; } } const FText& GetName() const { return Name; } EMaterialProperty GetProperty() const { return Property; } const FText& GetToolTip() const { return ToolTip; } private: /** Name of the input shown to user */ FText Name; /** Type of the input */ EMaterialProperty Property; /** The tool-tip describing this input's purpose */ FText ToolTip; }; UCLASS(Optional) class UNREALED_API UMaterialGraph : public UEdGraph { GENERATED_UCLASS_BODY() /** Material this Graph represents */ UPROPERTY() TObjectPtr Material; /** Material Function this Graph represents (NULL for Materials) */ UPROPERTY() TObjectPtr MaterialFunction; /** Root node representing Material inputs (NULL for Material Functions) */ UPROPERTY() TObjectPtr RootNode; /** Expression this subgraph represents (NULL if not subgraph, Material [Function] still populated) */ UPROPERTY() TObjectPtr SubgraphExpression; /** List of Material Inputs (not set up for Material Functions) */ TArray MaterialInputs; /** Checks if Material Editor is in realtime mode, so we update SGraphNodes every frame */ FRealtimeStateGetter RealtimeDelegate; /** Marks the Material Editor as dirty so that user prompted to apply change */ FSetMaterialDirty MaterialDirtyDelegate; /** Toggles the bCollapsed flag of a material expression and updates material editor */ FToggleExpressionCollapsed ToggleCollapsedDelegate; /** The name of the material that we are editing */ UPROPERTY() FString OriginalMaterialFullName; //~ Begin UEdGraph interface virtual void NotifyGraphChanged() override; protected: virtual void NotifyGraphChanged(const FEdGraphEditAction& Action) override; //~ End UEdGraph interface public: UMaterialGraph(); UMaterialGraph(FVTableHelper& Helper); virtual ~UMaterialGraph(); /** * Completely rebuild the graph from the material, removing all old nodes */ void RebuildGraph(); /** * Add an Expression to the Graph * * @param Expression Expression to add * * @return UMaterialGraphNode* Newly created Graph node to represent expression */ class UMaterialGraphNode* AddExpression(UMaterialExpression* Expression, bool bUserInvoked); /** * Add a Comment to the Graph * * @param Comment Comment to add * @param bIsUserInvoked Whether the comment has just been created by the user via the UI * * @return UMaterialGraphNode_Comment* Newly created Graph node to represent comment */ class UMaterialGraphNode_Comment* AddComment(UMaterialExpressionComment* Comment, bool bIsUserInvoked = false); /** * Add a Subgraph to the Graph * * @param InSubgraphExpression, expression that will represent the new subgraph in this graph. * * @return UMaterialGraph* Newly created subgraph */ UMaterialGraph* AddSubGraph(UMaterialExpression* InSubgraphExpression); /** Link all of the Graph nodes using the Material's connections */ void LinkGraphNodesFromMaterial(); /** Link the Material using the Graph node's connections */ void LinkMaterialExpressionsFromGraph(); /** * Check whether a material input should be marked as active * * @param GraphPin Pin representing the material input */ bool IsInputActive(class UEdGraphPin* GraphPin) const; /** Returns the input index associated with the given property */ int32 GetInputIndexForProperty(EMaterialProperty Property) const; /** * Get a list of nodes representing expressions that are not used in the Material * * @param UnusedNodes Array to contain nodes representing unused expressions */ void GetUnusedExpressions(TArray& UnusedNodes) const; void UpdatePinTypes(); private: /** * Remove all Nodes from the graph */ void RemoveAllNodes(); void RebuildGraphInternal(const TMap>& SubgraphExpressionMap, const TMap>& SubgraphCommentMap); /** * Gets a valid output index, matching mask values if necessary * * @param Input Input we are finding an output index for */ int32 GetValidOutputIndex(FExpressionInput* Input) const; };