Files
UnrealEngineUWP/Engine/Source/Editor/MaterialEditor/Public/MaterialEditorUtilities.h
luc rosenzweig c9eb19e6cd This changelist contains both a refactor of the material graph system, as well as an improved custom HLSL node.
The first series of changes relate to modifying the way that SGraphNode* inheritants are created. Previously, NodeFactory was responsible of creating the SGraphNode* inheritants based on some runtime type checking (which basically means a series of if statements checking if the passed in pointer is of a certain child class type). Now, the UMaterialGraph* inheritants (the controller classes in the MVC paradigm of the material graph) are responsible for creating their own UI components (SGraphNode* inheritants). This just means that we now polymorphically create the proper SGraphNode* for the given UMaterialGraph* inheritant.

This refactor allows us to more flexibly create a variety of nodes types instead of relying on SGraphNodeMaterialBase as a one size fits all solution, leading to the new HLSL custom node. Previously, this node didn't support showing the syntax-highlighted code inline in the node itself. Because we now use polymorphism to create the nodes, it was very easy to create new UMaterialGraph* and SGraphNode* inheritants to support this new change.

Other changes which relate to propagating changes (to affect previews) in MaterialGraphNode.cpp are needed because there previously wasn't the mechanism to do so (which may have been a bug). For instance, suppose you had a constant vector3 feeding into a custom HLSL node which just outputs the color coming from that constant vector3 node. It would be obvious that changing the vector3, requires a preview update of the HLSL node. However, that wouldn't happen - the preview on the HLSL node just stayed the same. These changes addresses that issue.

There is one last series of changes which relates to collapsing the HLSL code in the node. We want to make sure that this change doesn't mess up the layouts of artists' already made material graphs which use the custom HLSL node. In order to address this, we added the ability to collapse the code in order to hide it. Furthermore, we had to make sure that projects which previously used a custom HLSL node, have the code collapsed by default (because it wasn't there before). However, creating a new HLSL node once this change comes in, would yield an uncollapsed node. The collapsing of nodes uses the "Advanced Pins" chevron/collapser which doesn't save its state after saving and exiting the material. It was crucial, in order to preserve the layout of the material graph, that the saving happens. However, previously, the only way to save state, was to go through a recompile/regeneration of the previews. Therefore, it was necessary to add a special function in the material editor utilities which would just mark the material as dirty such that states like whether the code was collapsed save throughout sessions.

#jira UE-146779
#rb jason.nadro

[CL 26677757 by luc rosenzweig in ue5-main branch]
2023-07-28 13:01:00 -04:00

279 lines
11 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Misc/Guid.h"
#include "EdGraph/EdGraphNode.h"
#include "MaterialShared.h"
#include "Materials/MaterialExpressionMaterialFunctionCall.h"
class IMaterialEditor;
class UMaterial;
class UMaterialExpressionComment;
class UMaterialExpressionComposite;
class UMaterialExpressionFunctionInput;
class UMaterialInstance;
class UMaterialInterface;
struct FGraphActionMenuBuilder;
struct FMaterialParameterInfo;
//////////////////////////////////////////////////////////////////////////
// FMaterialEditorUtilities
class FGetVisibleMaterialParametersFunctionState
{
public:
FGetVisibleMaterialParametersFunctionState(UMaterialExpressionMaterialFunctionCall* InFunctionCall) :
FunctionCall(InFunctionCall)
{
if (FunctionCall)
{
StackParameterInfo = FunctionCall->FunctionParameterInfo;
}
}
class UMaterialExpressionMaterialFunctionCall* FunctionCall;
TArray<FMaterialExpressionKey> ExpressionStack;
TSet<FMaterialExpressionKey> VisitedExpressions;
FMaterialParameterInfo StackParameterInfo;
};
class MATERIALEDITOR_API FMaterialEditorUtilities
{
public:
/**
* Creates a new material expression of the specified class on the material represented by a graph.
*
* @param Graph Graph representing a material.
* @param NewExpressionClass The type of material expression to add. Must be a child of UMaterialExpression.
* @param NodePos Position of the new node.
* @param bAutoSelect If true, deselect all expressions and select the newly created one.
* @param bAutoAssignResource If true, assign resources to new expression.
*/
static UMaterialExpression* CreateNewMaterialExpression(const class UEdGraph* Graph, UClass* NewExpressionClass, const FVector2D& NodePos, bool bAutoSelect, bool bAutoAssignResource);
/**
* Creates a new material expression of the specified class on the material represented by a graph.
*
* @param Graph Graph representing a material.
* @param NodePos Position of the new node.
* @param bAutoSelect If true, deselect all expressions and select the newly created one.
* @param bAutoAssignResource If true, assign resources to new expression.
*/
template<typename MaterialExpressionClass>
static MaterialExpressionClass* CreateNewMaterialExpression(const UEdGraph* Graph, const FVector2D& NodePos, bool bAutoSelect, bool bAutoAssignResource = false)
{
static_assert(TIsDerivedFrom<MaterialExpressionClass, UMaterialExpression>::Value, "MaterialExpressionClass needs to inherit from UMaterialExpression");
return CastChecked<MaterialExpressionClass>(CreateNewMaterialExpression(Graph, MaterialExpressionClass::StaticClass(), NodePos, bAutoSelect, bAutoAssignResource), ECastCheckedType::NullAllowed);
}
/**
* Creates a new material expression composite on the material represented by a graph.
*
* @param Graph Graph to add comment to
* @param NodePos Position to place new comment at
*
* @return UMaterialExpressionComment* Newly created comment
*/
static UMaterialExpressionComposite* CreateNewMaterialExpressionComposite(const class UEdGraph* Graph, const FVector2D& NodePos);
/**
* Creates a new material expression comment on the material represented by a graph.
*
* @param Graph Graph to add comment to
* @param NodePos Position to place new comment at
*
* @return UMaterialExpressionComment* Newly created comment
*/
static UMaterialExpressionComment* CreateNewMaterialExpressionComment(const class UEdGraph* Graph, const FVector2D& NodePos);
/**
* Refreshes all material expression previews, regardless of whether or not realtime previews are enabled.
*
* @param Graph Graph representing a material.
*/
static void ForceRefreshExpressionPreviews(const class UEdGraph* Graph);
/**
* Add the specified object to the list of selected objects
*
* @param Graph Graph representing a material.
* @param Obj Object to add to selection.
*/
static void AddToSelection(const class UEdGraph* Graph, UMaterialExpression* Expression);
/**
* Disconnects and removes the selected material graph nodes.
*
* @param Graph Graph representing a material.
*/
static void DeleteSelectedNodes(const class UEdGraph* Graph);
/**
* Delete the specified nodes from the graph.
* @param Graph Graph representing the material.
* @param NodesToDelete Array of nodes to be removed from the graph.
*/
static void DeleteNodes(const class UEdGraph* Graph, const TArray<UEdGraphNode*>& NodesToDelete);
/**
* Gets the name of the material or material function that we are editing
*
* @param Graph Graph representing a material or material function.
*/
static FText GetOriginalObjectName(const class UEdGraph* Graph);
/**
* Re-links the material and updates its representation in the editor
*
* @param Graph Graph representing a material or material function.
*/
static void UpdateMaterialAfterGraphChange(const class UEdGraph* Graph);
/** Mark the material as dirty. */
static void MarkMaterialDirty(const class UEdGraph* Graph);
static void UpdateDetailView(const class UEdGraph* Graph);
/** Can we paste to this graph? */
static bool CanPasteNodes(const class UEdGraph* Graph);
/** Perform paste on graph, at location */
static void PasteNodesHere(class UEdGraph* Graph, const FVector2D& Location);
/** Gets the number of selected nodes on this graph */
static int32 GetNumberOfSelectedNodes(const class UEdGraph* Graph);
/**
* Get all actions for placing material expressions
*
* @param [in,out] ActionMenuBuilder The output menu builder.
* @param bMaterialFunction Whether we're dealing with a Material Function
*/
static void GetMaterialExpressionActions(FGraphActionMenuBuilder& ActionMenuBuilder, bool bMaterialFunction);
/**
* Check whether an expression is in the favourites list
*
* @param InExpression The Expression we are checking
*/
static bool IsMaterialExpressionInFavorites(UMaterialExpression* InExpression);
/**
* Get a preview material render proxy for an expression
*
* @param Graph Graph representing material
* @param InExpression Expression we want preview for
*
* @return FMaterialRenderProxy* Preview for this expression or NULL
*/
static FMaterialRenderProxy* GetExpressionPreview(const class UEdGraph* Graph, UMaterialExpression* InExpression);
/**
* Updates the material editor search results
*
* @param Graph Graph representing material
*/
static void UpdateSearchResults(const class UEdGraph* Graph);
/////////////////////////////////////////////////////
// Static functions moved from SMaterialEditorCanvas
/**
* Retrieves all visible parameters within the material.
*
* @param Material The material to retrieve the parameters from.
* @param MaterialInstance The material instance that contains all parameter overrides.
* @param VisisbleExpressions The array that will contain the name's of the visible parameter expressions.
*/
static void GetVisibleMaterialParameters(const UMaterial *Material, UMaterialInstance *MaterialInstance, TArray<FMaterialParameterInfo> &VisibleExpressions);
/** Finds an input in the passed in array with a matching Id. */
static const FFunctionExpressionInput* FindInputById(const UMaterialExpressionFunctionInput* InputExpression, const TArray<FFunctionExpressionInput>& Inputs);
/**
* Returns the value for a static switch material expression.
*
* @param MaterialInstance The material instance that contains all parameter overrides.
* @param SwitchValueExpression The switch expression to find the value for.
* @param OutValue The value for the switch expression.
* @param OutExpressionID The Guid of the expression that is input as the switch value.
* @param FunctionStack The current function stack frame.
*
* @return Returns true if a value for the switch expression is found, otherwise returns false.
*/
static bool GetStaticSwitchExpressionValue(UMaterialInstance* MaterialInstance, UMaterialExpression *SwitchValueExpression, bool& OutValue, FGuid& OutExpressionID, TArray<FGetVisibleMaterialParametersFunctionState*>& FunctionStack);
/**
* Populates the specified material's Expressions array (eg if cooked out or old content).
* Also ensures materials and expressions are RF_Transactional for undo/redo support.
*
* @param Material Material we are initializing
*/
static void InitExpressions(UMaterial* Material);
/**
* Build the texture streaming data for a given material. Also update the parent hierarchy has only the delta are stored.
*
* @param MaterialInterface The material to update.
*/
static void BuildTextureStreamingData(UMaterialInterface* MaterialInterface);
/** Get IMaterialEditor for given object, if it exists */
static TSharedPtr<class IMaterialEditor> GetIMaterialEditorForObject(const UObject* ObjectToFocusOn);
/** Get IMaterialEditor to focus on given object, if it exists */
static void BringFocusAttentionOnObject(const UObject* ObjectToFocusOn);
/** Commands for the Parents menu */
static void OnOpenMaterial(const FAssetData InMaterial);
static void OnOpenFunction(const FAssetData InFunction);
static void OnShowMaterialInContentBrowser(const FAssetData InMaterial);
static void OnShowFunctionInContentBrowser(const FAssetData InFunction);
private:
static void OpenSelectedParentEditor(UMaterialFunctionInterface* InMaterialFunction);
static void OpenSelectedParentEditor(UMaterialInterface* InMaterialInterface);
/**
* Recursively walks the expression tree and parses the visible expression branches.
*
* @param MaterialExpression The expression to parse.
* @param MaterialInstance The material instance that contains all parameter overrides.
* @param VisisbleExpressions The array that will contain the name's of the visible parameter expressions.
*/
static void GetVisibleMaterialParametersFromExpression(
FMaterialExpressionKey MaterialExpressionKey,
UMaterialInstance* MaterialInstance,
TArray<FMaterialParameterInfo>& VisibleExpressions,
TArray<FGetVisibleMaterialParametersFunctionState*>& FunctionStack);
/**
* Adds a category of Material Expressions to an action builder
*
* @param ActionMenuBuilder The builder to add to.
* @param CategoryName The name of the category.
* @param MaterialExpressions List of Material Expressions in the category.
* @param bMaterialFunction Whether we are building for a material function.
*/
static void AddMaterialExpressionCategory(FGraphActionMenuBuilder& ActionMenuBuilder, FText CategoryName, TArray<struct FMaterialExpression>* MaterialExpressions, bool bMaterialFunction);
/**
* Checks whether a Material Expression class has any connections that are compatible with a type/direction
*
* @param ExpressionClass Class of Expression we are testing against.
* @param TestType Material Value Type we are testing.
* @param TestDirection Pin Direction we are testing.
* @param bMaterialFunction Whether we are testing for a material function.
*/
static bool HasCompatibleConnection(UClass* ExpressionClass, uint32 TestType, EEdGraphPinDirection TestDirection, bool bMaterialFunction);
/** Constructor */
FMaterialEditorUtilities() {}
};