uint32 UMaterialGraphSchema::GetMaterialValueType(const UEdGraphPin* MaterialPin) { if (MaterialPin->Direction == EGPD_Output) { UMaterialGraphNode* OwningNode = CastChecked<UMaterialGraphNode>(MaterialPin->GetOwningNode()); return OwningNode->GetOutputType(MaterialPin); } else { UMaterialGraphNode_Base* OwningNode = CastChecked<UMaterialGraphNode_Base>(MaterialPin->GetOwningNode()); return OwningNode->GetInputType(MaterialPin); } }
void UMaterialGraph::LinkGraphNodesFromMaterial() { for (int32 Index = 0; Index < Nodes.Num(); ++Index) { Nodes[Index]->BreakAllNodeLinks(); } if (RootNode) { // Use Material Inputs to make GraphNode Connections for (int32 Index = 0; Index < MaterialInputs.Num(); ++Index) { UEdGraphPin* InputPin = RootNode->GetInputPin(Index); auto ExpressionInput = MaterialInputs[Index].GetExpressionInput(Material); if (ExpressionInput.Expression) { UMaterialGraphNode* GraphNode = CastChecked<UMaterialGraphNode>(ExpressionInput.Expression->GraphNode); InputPin->MakeLinkTo(GraphNode->GetOutputPin(GetValidOutputIndex(&ExpressionInput))); } } } for (int32 Index = 0; Index < Material->Expressions.Num(); Index++) { UMaterialExpression* Expression = Material->Expressions[Index]; if (Expression) { const TArray<FExpressionInput*> ExpressionInputs = Expression->GetInputs(); for (int32 InputIndex = 0; InputIndex < ExpressionInputs.Num(); ++InputIndex) { UEdGraphPin* InputPin = CastChecked<UMaterialGraphNode>(Expression->GraphNode)->GetInputPin(InputIndex); if ( ExpressionInputs[InputIndex]->Expression) { UMaterialGraphNode* GraphNode = CastChecked<UMaterialGraphNode>(ExpressionInputs[InputIndex]->Expression->GraphNode); InputPin->MakeLinkTo(GraphNode->GetOutputPin(GetValidOutputIndex(ExpressionInputs[InputIndex]))); } } } } NotifyGraphChanged(); }
bool UMaterialGraphSchema::ArePinsCompatible(const UEdGraphPin* InputPin, const UEdGraphPin* OutputPin, FText& ResponseMessage) const { UMaterialGraphNode_Base* InputNode = CastChecked<UMaterialGraphNode_Base>(InputPin->GetOwningNode()); UMaterialGraphNode* OutputNode = CastChecked<UMaterialGraphNode>(OutputPin->GetOwningNode()); uint32 InputType = InputNode->GetInputType(InputPin); uint32 OutputType = OutputNode->GetOutputType(OutputPin); bool bPinsCompatible = CanConnectMaterialValueTypes(InputType, OutputType); if (!bPinsCompatible) { TArray<FText> InputDescriptions; TArray<FText> OutputDescriptions; GetMaterialValueTypeDescriptions(InputType, InputDescriptions); GetMaterialValueTypeDescriptions(OutputType, OutputDescriptions); FString CombinedInputDescription; FString CombinedOutputDescription; for (int32 Index = 0; Index < InputDescriptions.Num(); ++Index) { if ( CombinedInputDescription.Len() > 0 ) { CombinedInputDescription += TEXT(", "); } CombinedInputDescription += InputDescriptions[Index].ToString(); } for (int32 Index = 0; Index < OutputDescriptions.Num(); ++Index) { if ( CombinedOutputDescription.Len() > 0 ) { CombinedOutputDescription += TEXT(", "); } CombinedOutputDescription += OutputDescriptions[Index].ToString(); } FFormatNamedArguments Args; Args.Add( TEXT("InputType"), FText::FromString(CombinedInputDescription) ); Args.Add( TEXT("OutputType"), FText::FromString(CombinedOutputDescription) ); ResponseMessage = FText::Format( LOCTEXT("IncompatibleDesc", "{OutputType} is not compatible with {InputType}"), Args ); } return bPinsCompatible; }
void UMaterialGraphSchema::GetContextMenuActions(const UEdGraph* CurrentGraph, const UEdGraphNode* InGraphNode, const UEdGraphPin* InGraphPin, class FMenuBuilder* MenuBuilder, bool bIsDebugging) const { if (InGraphPin) { const UMaterialGraph* MaterialGraph = CastChecked<UMaterialGraph>(CurrentGraph); MenuBuilder->BeginSection("MaterialGraphSchemaPinActions", LOCTEXT("PinActionsMenuHeader", "Pin Actions")); { // Only display the 'Break Link' option if there is a link to break! if (InGraphPin->LinkedTo.Num() > 0) { MenuBuilder->AddMenuEntry(FGraphEditorCommands::Get().BreakPinLinks); // add sub menu for break link to if(InGraphPin->LinkedTo.Num() > 1) { MenuBuilder->AddSubMenu( LOCTEXT("BreakLinkTo", "Break Link To..." ), LOCTEXT("BreakSpecificLinks", "Break a specific link..." ), FNewMenuDelegate::CreateUObject( (UMaterialGraphSchema*const)this, &UMaterialGraphSchema::GetBreakLinkToSubMenuActions, const_cast<UEdGraphPin*>(InGraphPin))); } else { ((UMaterialGraphSchema*const)this)->GetBreakLinkToSubMenuActions(*MenuBuilder, const_cast<UEdGraphPin*>(InGraphPin)); } } } MenuBuilder->EndSection(); // add menu items to expression output for material connection if ( InGraphPin->Direction == EGPD_Output ) { MenuBuilder->BeginSection("MaterialEditorMenuConnector2"); { // If we are editing a material function, display options to connect to function outputs if (MaterialGraph->MaterialFunction) { for (int32 Index = 0; Index < MaterialGraph->Nodes.Num(); Index++) { UMaterialGraphNode* GraphNode = Cast<UMaterialGraphNode>(MaterialGraph->Nodes[Index]); if (GraphNode) { UMaterialExpressionFunctionOutput* FunctionOutput = Cast<UMaterialExpressionFunctionOutput>(GraphNode->MaterialExpression); if (FunctionOutput) { FFormatNamedArguments Arguments; Arguments.Add(TEXT("Name"), FText::FromString( *FunctionOutput->OutputName )); const FText Label = FText::Format( LOCTEXT( "ConnectToFunction", "Connect To {Name}" ), Arguments ); const FText ToolTip = FText::Format( LOCTEXT( "ConnectToFunctionTooltip", "Connects to the function output {Name}" ), Arguments ); MenuBuilder->AddMenuEntry(Label, ToolTip, FSlateIcon(), FUIAction(FExecuteAction::CreateUObject((UMaterialGraphSchema*const)this, &UMaterialGraphSchema::OnConnectToFunctionOutput, const_cast< UEdGraphPin* >(InGraphPin), GraphNode->GetInputPin(0)))); } } } } else { for (int32 Index = 0; Index < MaterialGraph->MaterialInputs.Num(); ++Index) { if( MaterialGraph->IsInputVisible( Index ) ) { FFormatNamedArguments Arguments; Arguments.Add(TEXT("Name"), MaterialGraph->MaterialInputs[Index].Name); const FText Label = FText::Format( LOCTEXT( "ConnectToInput", "Connect To {Name}" ), Arguments ); const FText ToolTip = FText::Format( LOCTEXT( "ConnectToInputTooltip", "Connects to the material input {Name}" ), Arguments ); MenuBuilder->AddMenuEntry(Label, ToolTip, FSlateIcon(), FUIAction(FExecuteAction::CreateUObject((UMaterialGraphSchema*const)this, &UMaterialGraphSchema::OnConnectToMaterial, const_cast< UEdGraphPin* >(InGraphPin), Index))); } } } } MenuBuilder->EndSection(); //MaterialEditorMenuConnector2 } } else if (InGraphNode) { //Moved all functionality to relevant node classes } Super::GetContextMenuActions(CurrentGraph, InGraphNode, InGraphPin, MenuBuilder, bIsDebugging); }
void UMaterialGraph::GetUnusedExpressions(TArray<UEdGraphNode*>& UnusedNodes) const { UnusedNodes.Empty(); TArray<UEdGraphNode*> NodesToCheck; if (RootNode) { TArray<UEdGraphPin*> InputPins; RootNode->GetInputPins(InputPins); for (int32 Index = 0; Index < InputPins.Num(); ++Index) { check(Index < MaterialInputs.Num()); if (MaterialInputs[Index].IsVisiblePin(Material) && InputPins[Index]->LinkedTo.Num() > 0 && InputPins[Index]->LinkedTo[0]) { NodesToCheck.Push(InputPins[Index]->LinkedTo[0]->GetOwningNode()); } } } else if (MaterialFunction) { for (int32 Index = 0; Index < Nodes.Num(); Index++) { UMaterialGraphNode* GraphNode = Cast<UMaterialGraphNode>(Nodes[Index]); if (GraphNode) { UMaterialExpressionFunctionOutput* FunctionOutput = Cast<UMaterialExpressionFunctionOutput>(GraphNode->MaterialExpression); if (FunctionOutput) { NodesToCheck.Push(GraphNode); } } } } // Depth-first traverse the material expression graph. TArray<UEdGraphNode*> UsedNodes; TMap<UEdGraphNode*, int32> ReachableNodes; while (NodesToCheck.Num() > 0) { UMaterialGraphNode* GraphNode = Cast<UMaterialGraphNode>(NodesToCheck.Pop()); if (GraphNode) { int32* AlreadyVisited = ReachableNodes.Find(GraphNode); if (!AlreadyVisited) { // Mark the expression as reachable. ReachableNodes.Add(GraphNode, 0); UsedNodes.Add(GraphNode); // Iterate over the expression's inputs and add them to the pending stack. TArray<UEdGraphPin*> InputPins; GraphNode->GetInputPins(InputPins); for (int32 Index = 0; Index < InputPins.Num(); ++Index) { if (InputPins[Index]->LinkedTo.Num() > 0 && InputPins[Index]->LinkedTo[0]) { NodesToCheck.Push(InputPins[Index]->LinkedTo[0]->GetOwningNode()); } } } } } for (int32 Index = 0; Index < Nodes.Num(); ++Index) { UMaterialGraphNode* GraphNode = Cast<UMaterialGraphNode>(Nodes[Index]); if (GraphNode && !UsedNodes.Contains(GraphNode)) { UnusedNodes.Add(GraphNode); } } }
void UMaterialGraph::LinkMaterialExpressionsFromGraph() const { // Use GraphNodes to make Material Expression Connections TArray<UEdGraphPin*> InputPins; TArray<UEdGraphPin*> OutputPins; for (int32 NodeIndex = 0; NodeIndex < Nodes.Num(); ++NodeIndex) { if (RootNode && RootNode == Nodes[NodeIndex]) { // Setup Material's inputs from root node Material->Modify(); InputPins = RootNode->Pins; Material->EditorX = RootNode->NodePosX; Material->EditorY = RootNode->NodePosY; check(InputPins.Num() == MaterialInputs.Num()); for (int32 PinIndex = 0; PinIndex < InputPins.Num() && PinIndex < MaterialInputs.Num(); ++PinIndex) { FExpressionInput& MaterialInput = MaterialInputs[PinIndex].GetExpressionInput(Material); if (InputPins[PinIndex]->LinkedTo.Num() > 0) { UMaterialGraphNode* ConnectedNode = CastChecked<UMaterialGraphNode>(InputPins[PinIndex]->LinkedTo[0]->GetOwningNode()); ConnectedNode->GetOutputPins(OutputPins); // Work out the index of the connected pin for (int32 OutPinIndex = 0; OutPinIndex < OutputPins.Num(); ++OutPinIndex) { if (OutputPins[OutPinIndex] == InputPins[PinIndex]->LinkedTo[0]) { if (MaterialInput.OutputIndex != OutPinIndex || MaterialInput.Expression != ConnectedNode->MaterialExpression) { ConnectedNode->MaterialExpression->Modify(); MaterialInput.Connect(OutPinIndex, ConnectedNode->MaterialExpression); } break; } } } else if (MaterialInput.Expression) { MaterialInput.Expression = NULL; } } } else { if (UMaterialGraphNode* GraphNode = Cast<UMaterialGraphNode>(Nodes[NodeIndex])) { // Need to be sure that we are changing the expression before calling modify - // triggers a rebuild of its preview when it is called UMaterialExpression* Expression = GraphNode->MaterialExpression; bool bModifiedExpression = false; if (Expression->MaterialExpressionEditorX != GraphNode->NodePosX || Expression->MaterialExpressionEditorY != GraphNode->NodePosY || Expression->Desc != GraphNode->NodeComment) { bModifiedExpression = true; Expression->Modify(); // Update positions and comments Expression->MaterialExpressionEditorX = GraphNode->NodePosX; Expression->MaterialExpressionEditorY = GraphNode->NodePosY; Expression->Desc = GraphNode->NodeComment; } GraphNode->GetInputPins(InputPins); const TArray<FExpressionInput*> ExpressionInputs = Expression->GetInputs(); checkf(InputPins.Num() == ExpressionInputs.Num(), TEXT("Mismatched inputs for '%s'"), *Expression->GetFullName()); for (int32 PinIndex = 0; PinIndex < InputPins.Num() && PinIndex < ExpressionInputs.Num(); ++PinIndex) { FExpressionInput* ExpressionInput = ExpressionInputs[PinIndex]; if (InputPins[PinIndex]->LinkedTo.Num() > 0) { UMaterialGraphNode* ConnectedNode = CastChecked<UMaterialGraphNode>(InputPins[PinIndex]->LinkedTo[0]->GetOwningNode()); ConnectedNode->GetOutputPins(OutputPins); // Work out the index of the connected pin for (int32 OutPinIndex = 0; OutPinIndex < OutputPins.Num(); ++OutPinIndex) { if (OutputPins[OutPinIndex] == InputPins[PinIndex]->LinkedTo[0]) { if (ExpressionInput->OutputIndex != OutPinIndex || ExpressionInput->Expression != ConnectedNode->MaterialExpression) { if (!bModifiedExpression) { bModifiedExpression = true; Expression->Modify(); } ConnectedNode->MaterialExpression->Modify(); ExpressionInput->Connect(OutPinIndex, ConnectedNode->MaterialExpression); } break; } } } else if (ExpressionInput->Expression) { if (!bModifiedExpression) { bModifiedExpression = true; Expression->Modify(); } ExpressionInput->Expression = NULL; } } } else if (UMaterialGraphNode_Comment* CommentNode = Cast<UMaterialGraphNode_Comment>(Nodes[NodeIndex])) { UMaterialExpressionComment* Comment = CommentNode->MaterialExpressionComment; if (Comment->MaterialExpressionEditorX != CommentNode->NodePosX || Comment->MaterialExpressionEditorY != CommentNode->NodePosY || Comment->Text != CommentNode->NodeComment || Comment->SizeX != CommentNode->NodeWidth || Comment->SizeY != CommentNode->NodeHeight || Comment->CommentColor != CommentNode->CommentColor) { Comment->Modify(); // Update positions and comments Comment->MaterialExpressionEditorX = CommentNode->NodePosX; Comment->MaterialExpressionEditorY = CommentNode->NodePosY; Comment->Text = CommentNode->NodeComment; Comment->SizeX = CommentNode->NodeWidth; Comment->SizeY = CommentNode->NodeHeight; Comment->CommentColor = CommentNode->CommentColor; } } } } }