// Copyright Epic Games, Inc. All Rights Reserved. #include "Graph/ControlRigGraphSchema.h" #include "Graph/ControlRigGraph.h" #include "Graph/ControlRigGraphNode.h" #include "IControlRigEditorModule.h" #include "UObject/UObjectIterator.h" #include "Units/RigUnit.h" #include "Kismet2/BlueprintEditorUtils.h" #include "EdGraphNode_Comment.h" #include "EdGraphSchema_K2_Actions.h" #include "ScopedTransaction.h" #include "Framework/MultiBox/MultiBoxBuilder.h" #include "GraphEditorActions.h" #include "ControlRig.h" #include "ControlRigBlueprint.h" #include "ControlRigBlueprintGeneratedClass.h" #include "ControlRigDeveloper.h" #include "Widgets/Notifications/SNotificationList.h" #include "Framework/Notifications/NotificationManager.h" #include "EulerTransform.h" #include "Curves/CurveFloat.h" #include "RigVMModel/Nodes/RigVMLibraryNode.h" #include "RigVMModel/Nodes/RigVMCollapseNode.h" #include "RigVMModel/Nodes/RigVMFunctionEntryNode.h" #include "RigVMModel/Nodes/RigVMFunctionReturnNode.h" #include "RigVMModel/RigVMVariableDescription.h" #include "RigVMCore/RigVMUnknownType.h" #include "Kismet2/Kismet2NameValidators.h" #include "Algo/Count.h" #include "ControlRig/Private/Units/Execution/RigUnit_BeginExecution.h" #include "ControlRig/Private/Units/Execution/RigUnit_PrepareForExecution.h" #include "ControlRig/Private/Units/Execution/RigUnit_InverseExecution.h" #include "ControlRig/Private/Units/Execution/RigUnit_InteractionExecution.h" #if WITH_EDITOR #include "ControlRigEditor/Private/Editor/SControlRigFunctionLocalizationWidget.h" #include "Misc/MessageDialog.h" #endif #define LOCTEXT_NAMESPACE "ControlRigGraphSchema" const FName UControlRigGraphSchema::GraphName_ControlRig(TEXT("Rig Graph")); FControlRigLocalVariableNameValidator::FControlRigLocalVariableNameValidator(const UBlueprint* Blueprint, const URigVMGraph* Graph, FName InExistingName) : FStringSetNameValidator(InExistingName.ToString()) { if (Blueprint) { TSet NamesTemp; // We allow local variables with same name as blueprint variable FBlueprintEditorUtils::GetFunctionNameList(Blueprint, NamesTemp); FBlueprintEditorUtils::GetAllGraphNames(Blueprint, NamesTemp); FBlueprintEditorUtils::GetSCSVariableNameList(Blueprint, NamesTemp); FBlueprintEditorUtils::GetImplementingBlueprintsFunctionNameList(Blueprint, NamesTemp); for (FName & Name : NamesTemp) { Names.Add(Name.ToString()); } } if (Graph) { for (const FRigVMGraphVariableDescription& LocalVariable : Graph->GetLocalVariables()) { Names.Add(LocalVariable.Name.ToString()); } for (const FRigVMGraphVariableDescription& InputArgument : Graph->GetInputArguments()) { Names.Add(InputArgument.Name.ToString()); } for (const FRigVMGraphVariableDescription& OutputArgument : Graph->GetOutputArguments()) { Names.Add(OutputArgument.Name.ToString()); } } } EValidatorResult FControlRigLocalVariableNameValidator::IsValid(const FString& Name, bool bOriginal) { EValidatorResult Result = FStringSetNameValidator::IsValid(Name, bOriginal); if (Result == EValidatorResult::Ok) { if (URigVMController::GetSanitizedName(Name, false, true) == Name) { return Result; } return EValidatorResult::ContainsInvalidCharacters; } return Result; } EValidatorResult FControlRigLocalVariableNameValidator::IsValid(const FName& Name, bool bOriginal) { return IsValid(Name.ToString(), bOriginal); } FControlRigNameValidator::FControlRigNameValidator(const UBlueprint* Blueprint, const UStruct* ValidationScope, FName InExistingName) : FStringSetNameValidator(InExistingName.ToString()) { if (Blueprint) { TSet NamesTemp; FBlueprintEditorUtils::GetClassVariableList(Blueprint, NamesTemp, true); FBlueprintEditorUtils::GetFunctionNameList(Blueprint, NamesTemp); FBlueprintEditorUtils::GetAllGraphNames(Blueprint, NamesTemp); FBlueprintEditorUtils::GetSCSVariableNameList(Blueprint, NamesTemp); FBlueprintEditorUtils::GetImplementingBlueprintsFunctionNameList(Blueprint, NamesTemp); for (FName & Name : NamesTemp) { Names.Add(Name.ToString()); } } } EValidatorResult FControlRigNameValidator::IsValid(const FString& Name, bool bOriginal) { EValidatorResult Result = FStringSetNameValidator::IsValid(Name, bOriginal); if (Result == EValidatorResult::Ok) { if (URigVMController::GetSanitizedName(Name, false, true) == Name) { return Result; } return EValidatorResult::ContainsInvalidCharacters; } return Result; } EValidatorResult FControlRigNameValidator::IsValid(const FName& Name, bool bOriginal) { return IsValid(Name.ToString(), bOriginal); } FEdGraphPinType FControlRigGraphSchemaAction_LocalVar::GetPinType() const { if (UControlRigGraph* Graph = Cast(GetVariableScope())) { for (FRigVMGraphVariableDescription Variable : Graph->GetModel()->GetLocalVariables()) { if (Variable.Name == GetVariableName()) { return Variable.ToPinType(); } } for (FRigVMGraphVariableDescription Variable : Graph->GetModel()->GetInputArguments()) { if (Variable.Name == GetVariableName()) { return Variable.ToPinType(); } } } return FEdGraphPinType(); } void FControlRigGraphSchemaAction_LocalVar::ChangeVariableType(const FEdGraphPinType& NewPinType) { if (UControlRigGraph* Graph = Cast(GetVariableScope())) { FString NewCPPType; UObject* NewCPPTypeObject = nullptr; RigVMTypeUtils::CPPTypeFromPinType(NewPinType, NewCPPType, &NewCPPTypeObject); Graph->GetController()->SetLocalVariableType(GetVariableName(), NewCPPType, NewCPPTypeObject, true, true); } } void FControlRigGraphSchemaAction_LocalVar::RenameVariable(const FName& NewName) { const FName OldName = GetVariableName(); if (OldName == NewName) { return; } if (UControlRigGraph* Graph = Cast(GetVariableScope())) { if (Graph->GetController()->RenameLocalVariable(OldName, NewName, true, true)) { SetVariableInfo(NewName, GetVariableScope(), GetPinType().PinCategory == TEXT("bool")); } } } bool FControlRigGraphSchemaAction_LocalVar::IsValidName(const FName& NewName, FText& OutErrorMessage) const { if (UControlRigGraph* ControlRigGraph = Cast(GetVariableScope())) { FControlRigLocalVariableNameValidator NameValidator(ControlRigGraph->GetBlueprint(), ControlRigGraph->GetModel(), GetVariableName()); EValidatorResult Result = NameValidator.IsValid(NewName.ToString(), false); if (Result != EValidatorResult::Ok && Result != EValidatorResult::ExistingName) { OutErrorMessage = FText::FromString(TEXT("Name with invalid format")); return false; } } return true; } void FControlRigGraphSchemaAction_LocalVar::DeleteVariable() { if (UControlRigGraph* Graph = Cast(GetVariableScope())) { #if WITH_EDITOR if (GEditor) { GEditor->CancelTransaction(0); } #endif Graph->GetController()->RemoveLocalVariable(GetVariableName(), true, true); } } bool FControlRigGraphSchemaAction_LocalVar::IsVariableUsed() { if (UControlRigGraph* ControlRigGraph = Cast(GetVariableScope())) { const FString VarNameStr = GetVariableName().ToString(); for (URigVMNode* Node : ControlRigGraph->GetModel()->GetNodes()) { if (URigVMVariableNode* VarNode = Cast(Node)) { if (VarNode->FindPin(TEXT("Variable"))->GetDefaultValue() == VarNameStr) { return true; } } } } return false; } FControlRigGraphSchemaAction_PromoteToVariable::FControlRigGraphSchemaAction_PromoteToVariable(UEdGraphPin* InEdGraphPin, bool InLocalVariable) : FEdGraphSchemaAction( FText(), InLocalVariable ? LOCTEXT("PromoteToLocalVariable", "Promote to local variable") : LOCTEXT("PromoteToVariable", "Promote to variable"), InLocalVariable ? LOCTEXT("PromoteToLocalVariable", "Promote to local variable") : LOCTEXT("PromoteToVariable", "Promote to variable"), 1) , EdGraphPin(InEdGraphPin) , bLocalVariable(InLocalVariable) { } UEdGraphNode* FControlRigGraphSchemaAction_PromoteToVariable::PerformAction(UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode) { UControlRigGraph* RigGraph = Cast(ParentGraph); if(RigGraph == nullptr) { return nullptr; } UControlRigBlueprint* Blueprint = RigGraph->GetBlueprint(); URigVMGraph* Model = RigGraph->GetModel(); URigVMController* Controller = RigGraph->GetController(); if((Blueprint == nullptr) || (Model == nullptr) || (Controller == nullptr)) { return nullptr; } URigVMPin* ModelPin = Model->FindPin(FromPin->GetName()); FName VariableName(NAME_None); const FScopedTransaction Transaction( bLocalVariable ? LOCTEXT("GraphEd_PromoteToLocalVariable", "Promote Pin To Local Variable") : LOCTEXT("GraphEd_PromoteToVariable", "Promote Pin To Variable")); if(bLocalVariable) { #if WITH_EDITOR if (GEditor) { GEditor->CancelTransaction(0); } #endif const FRigVMGraphVariableDescription VariableDescription = Controller->AddLocalVariable( *ModelPin->GetPinPath(), ModelPin->GetCPPType(), ModelPin->GetCPPTypeObject(), ModelPin->GetDefaultValue(), true, true ); VariableName = VariableDescription.Name; } else { Blueprint->Modify(); FString DefaultValue = ModelPin->GetDefaultValue(); if(!DefaultValue.IsEmpty()) { if(UScriptStruct* ScriptStruct = Cast(ModelPin->GetCPPTypeObject())) { if(ScriptStruct == TBaseStructure::Get()) { FVector2D Value = FVector2D::ZeroVector; ScriptStruct->ImportText(*DefaultValue, &Value, nullptr, PPF_None, nullptr, FString()); DefaultValue = Value.ToString(); } if(ScriptStruct == TBaseStructure::Get()) { FVector Value = FVector::ZeroVector; ScriptStruct->ImportText(*DefaultValue, &Value, nullptr, PPF_None, nullptr, FString()); DefaultValue = Value.ToString(); } if(ScriptStruct == TBaseStructure::Get()) { FQuat Value = FQuat::Identity; ScriptStruct->ImportText(*DefaultValue, &Value, nullptr, PPF_None, nullptr, FString()); DefaultValue = Value.ToString(); } if(ScriptStruct == TBaseStructure::Get()) { FRotator Value = FRotator::ZeroRotator; ScriptStruct->ImportText(*DefaultValue, &Value, nullptr, PPF_None, nullptr, FString()); DefaultValue = Value.ToString(); } if(ScriptStruct == TBaseStructure::Get()) { FTransform Value = FTransform::Identity; ScriptStruct->ImportText(*DefaultValue, &Value, nullptr, PPF_None, nullptr, FString()); DefaultValue = Value.ToString(); } } } FRigVMExternalVariable ExternalVariable; ExternalVariable.Name = FromPin->GetFName(); ExternalVariable.bIsArray = ModelPin->IsArray(); ExternalVariable.TypeName = ModelPin->IsArray() ? *ModelPin->GetArrayElementCppType() : *ModelPin->GetCPPType(); ExternalVariable.TypeObject = ModelPin->GetCPPTypeObject(); VariableName = Blueprint->AddCRMemberVariableFromExternal( ExternalVariable, DefaultValue ); } if(!VariableName.IsNone()) { URigVMNode* ModelNode = Controller->AddVariableNode( VariableName, ModelPin->GetCPPType(), ModelPin->GetCPPTypeObject(), FromPin->Direction == EGPD_Input, ModelPin->GetDefaultValue(), Location, FString(), true, true ); if(ModelNode) { if(FromPin->Direction == EGPD_Input) { Controller->AddLink(ModelNode->FindPin(TEXT("Value")), ModelPin, true); } else { Controller->AddLink(ModelPin, ModelNode->FindPin(TEXT("Value")), true); } return RigGraph->FindNodeForModelNodeName(ModelNode->GetFName()); } } return nullptr; } FControlRigGraphSchemaAction_Event::FControlRigGraphSchemaAction_Event(const FName& InEventName, const FString& InNodePath, const FText& InNodeCategory) : FEdGraphSchemaAction( InNodeCategory, FText::FromName(InEventName), FText(), 1) , NodePath(InNodePath) { } FReply FControlRigGraphSchemaAction_Event::OnDoubleClick(UBlueprint* InBlueprint) { if (UControlRigBlueprint* Blueprint = Cast(InBlueprint)) { if(const URigVMNode* ModelNode = Blueprint->GetRigVMClient()->FindNode(NodePath)) { Blueprint->OnRequestJumpToHyperlink().Execute(ModelNode); return FReply::Handled(); } } return FReply::Unhandled();; } FSlateBrush const* FControlRigGraphSchemaAction_Event::GetPaletteIcon() const { static FSlateIcon EventIcon(FAppStyle::GetAppStyleSetName(), "GraphEditor.Event_16x"); return EventIcon.GetIcon(); } FReply FControlRigFunctionDragDropAction::DroppedOnPanel(const TSharedRef< class SWidget >& Panel, FVector2D ScreenPosition, FVector2D GraphPosition, UEdGraph& Graph) { // For local variables if (SourceAction->GetTypeId() == FControlRigGraphSchemaAction_LocalVar::StaticGetTypeId()) { if (UControlRigGraph* TargetRigGraph = Cast(&Graph)) { if (TargetRigGraph == SourceRigGraph) { FControlRigGraphSchemaAction_LocalVar* VarAction = (FControlRigGraphSchemaAction_LocalVar*) SourceAction.Get(); for (FRigVMGraphVariableDescription LocalVariable : TargetRigGraph->GetModel()->GetLocalVariables()) { if (LocalVariable.Name == VarAction->GetVariableName()) { URigVMController* Controller = TargetRigGraph->GetController(); FMenuBuilder MenuBuilder(true, NULL); const FText VariableNameText = FText::FromName( LocalVariable.Name ); MenuBuilder.BeginSection("BPVariableDroppedOn", VariableNameText ); MenuBuilder.AddMenuEntry( FText::Format( LOCTEXT("CreateGetVariable", "Get {0}"), VariableNameText ), FText::Format( LOCTEXT("CreateVariableGetterToolTip", "Create Getter for variable '{0}'\n(Ctrl-drag to automatically create a getter)"), VariableNameText ), FSlateIcon(), FUIAction( FExecuteAction::CreateLambda([Controller, LocalVariable, GraphPosition]() { Controller->AddVariableNode(LocalVariable.Name, LocalVariable.CPPType, LocalVariable.CPPTypeObject, true, LocalVariable.DefaultValue, GraphPosition, FString(), true, true); }), FCanExecuteAction())); MenuBuilder.AddMenuEntry( FText::Format( LOCTEXT("CreateSetVariable", "Set {0}"), VariableNameText ), FText::Format( LOCTEXT("CreateVariableSetterToolTip", "Create Setter for variable '{0}'\n(Alt-drag to automatically create a setter)"), VariableNameText ), FSlateIcon(), FUIAction( FExecuteAction::CreateLambda([Controller, LocalVariable, GraphPosition]() { Controller->AddVariableNode(LocalVariable.Name, LocalVariable.CPPType, LocalVariable.CPPTypeObject, false, LocalVariable.DefaultValue, GraphPosition, FString(), true, true); }), FCanExecuteAction())); TSharedRef< SWidget > PanelWidget = Panel; // Show dialog to choose getter vs setter FSlateApplication::Get().PushMenu( PanelWidget, FWidgetPath(), MenuBuilder.MakeWidget(), ScreenPosition, FPopupTransitionEffect( FPopupTransitionEffect::ContextMenu) ); MenuBuilder.EndSection(); } } } } } // For functions else if (UControlRigGraph* TargetRigGraph = Cast(&Graph)) { if (UControlRigBlueprint* TargetRigBlueprint = Cast(FBlueprintEditorUtils::FindBlueprintForGraph(TargetRigGraph))) { if (URigVMGraph* FunctionDefinitionGraph = SourceRigBlueprint->GetModel(SourceRigGraph)) { if (URigVMLibraryNode* FunctionDefinitionNode = Cast(FunctionDefinitionGraph->GetOuter())) { if(URigVMController* TargetController = TargetRigBlueprint->GetController(TargetRigGraph)) { if(URigVMFunctionLibrary* FunctionLibrary = Cast(FunctionDefinitionNode->GetOuter())) { if(UControlRigBlueprint* FunctionRigBlueprint = Cast(FunctionLibrary->GetOuter())) { #if WITH_EDITOR if(FunctionRigBlueprint != TargetRigBlueprint) { if(!FunctionRigBlueprint->IsFunctionPublic(FunctionDefinitionNode->GetFName())) { TargetRigBlueprint->BroadcastRequestLocalizeFunctionDialog(FunctionDefinitionNode); FunctionDefinitionNode = TargetRigBlueprint->GetLocalFunctionLibrary()->FindPreviouslyLocalizedFunction(FunctionDefinitionNode); } } #endif TargetController->AddFunctionReferenceNode(FunctionDefinitionNode, GraphPosition, FString(), true, true); } } } } } } } return FReply::Unhandled(); } FReply FControlRigFunctionDragDropAction::DroppedOnPin(FVector2D ScreenPosition, FVector2D GraphPosition) { return FReply::Unhandled(); } FReply FControlRigFunctionDragDropAction::DroppedOnAction(TSharedRef Action) { return FReply::Unhandled(); } FReply FControlRigFunctionDragDropAction::DroppedOnCategory(FText Category) { // todo /* if (SourceAction.IsValid()) { SourceAction->MovePersistentItemToCategory(Category); } */ return FReply::Unhandled(); } void FControlRigFunctionDragDropAction::HoverTargetChanged() { // todo - see FMyBlueprintItemDragDropAction FGraphSchemaActionDragDropAction::HoverTargetChanged(); // check for category + graph, everything else we won't allow for now. bDropTargetValid = true; } FControlRigFunctionDragDropAction::FControlRigFunctionDragDropAction() : FGraphSchemaActionDragDropAction() , SourceRigBlueprint(nullptr) , SourceRigGraph(nullptr) , bControlDrag(false) , bAltDrag(false) { } TSharedRef FControlRigFunctionDragDropAction::New(TSharedPtr InAction, UControlRigBlueprint* InRigBlueprint, UControlRigGraph* InRigGraph) { TSharedRef Action = MakeShareable(new FControlRigFunctionDragDropAction); Action->SourceAction = InAction; Action->SourceRigBlueprint = InRigBlueprint; Action->SourceRigGraph = InRigGraph; Action->Construct(); return Action; } UControlRigGraphSchema::UControlRigGraphSchema() { } void UControlRigGraphSchema::GetGraphContextActions(FGraphContextMenuBuilder& ContextMenuBuilder) const { } void UControlRigGraphSchema::GetContextMenuActions(class UToolMenu* Menu, class UGraphNodeContextMenuContext* Context) const { /* // this seems to be taken care of by ControlRigGraphNode #if WITH_EDITOR return IControlRigEditorModule::Get().GetContextMenuActions(this, Menu, Context); #else check(0); #endif */ } bool UControlRigGraphSchema::TryCreateConnection(UEdGraphPin* PinA, UEdGraphPin* PinB) const { #if WITH_EDITOR if (GEditor) { GEditor->CancelTransaction(0); } #endif if (PinA == PinB) { return false; } if (PinA->GetOwningNode() == PinB->GetOwningNode()) { return false; } UControlRigGraphSchema* MutableThis = (UControlRigGraphSchema*)this; IRigVMClientHost* Host = PinA->GetOwningNode()->GetImplementingOuter(); if (Host != nullptr) { if (URigVMController* Controller = Host->GetRigVMClient()->GetOrCreateController(PinA->GetOwningNode()->GetGraph())) { ERigVMPinDirection UserLinkDirection = ERigVMPinDirection::Output; if (PinA->Direction == EGPD_Input) { Swap(PinA, PinB); UserLinkDirection = ERigVMPinDirection::Input; } const bool bPinAIsWildCard = PinA->PinType.PinSubCategoryObject == RigVMTypeUtils::GetWildCardCPPTypeObject(); const bool bPinBIsWildCard = PinB->PinType.PinSubCategoryObject == RigVMTypeUtils::GetWildCardCPPTypeObject(); if(bPinAIsWildCard != bPinBIsWildCard) { // switch the user link direction if only one of the pins is a wildcard if(UserLinkDirection == ERigVMPinDirection::Input && bPinBIsWildCard) { UserLinkDirection = ERigVMPinDirection::Output; } else if(UserLinkDirection == ERigVMPinDirection::Output && bPinAIsWildCard) { UserLinkDirection = ERigVMPinDirection::Input; } } #if WITH_EDITOR // check if we are trying to connect a loop iteration pin to a return if(URigVMGraph* Graph = Controller->GetGraph()) { if(URigVMPin* TargetPin = Graph->FindPin(PinB->GetName())) { if(TargetPin->IsExecuteContext() && TargetPin->GetNode()->IsA()) { bool bIsInLoopIteration = false; if(URigVMPin* SourcePin = Graph->FindPin(PinA->GetName())) { while(SourcePin) { if(!SourcePin->IsExecuteContext()) { break; } URigVMPin* CurrentSourcePin = SourcePin; SourcePin = nullptr; if(URigVMUnitNode* UnitNode = Cast(CurrentSourcePin->GetNode())) { TSharedPtr UnitScope = UnitNode->ConstructStructInstance(); if(UnitScope.IsValid()) { FRigVMStruct* Unit = (FRigVMStruct*)UnitScope->GetStructMemory(); if(Unit->IsForLoop()) { if(CurrentSourcePin->GetFName() != FRigVMStruct::ForLoopCompletedPinName) { bIsInLoopIteration = true; break; } } } } for(URigVMPin* PinOnSourceNode : CurrentSourcePin->GetNode()->GetPins()) { if(!PinOnSourceNode->IsExecuteContext()) { continue; } if(PinOnSourceNode->GetDirection() != ERigVMPinDirection::Input && PinOnSourceNode->GetDirection() != ERigVMPinDirection::IO) { continue; } TArray NextSourcePins = PinOnSourceNode->GetLinkedSourcePins(); if(NextSourcePins.Num() > 0) { SourcePin = NextSourcePins[0]; break; } } } } if(bIsInLoopIteration) { const EAppReturnType::Type Answer = FMessageDialog::Open( EAppMsgType::YesNo, FText::FromString( TEXT("Linking a function return within a loop is not recommended.\nAre you sure?") ) ); if(Answer == EAppReturnType::No) { return false; } } } } } #endif return Controller->AddLink(PinA->GetName(), PinB->GetName(), true, true, UserLinkDirection); } } return false; } static bool HasParentConnection_Recursive(const UEdGraphPin* InPin) { if(InPin->ParentPin) { return InPin->ParentPin->LinkedTo.Num() > 0 || HasParentConnection_Recursive(InPin->ParentPin); } return false; } static bool HasChildConnection_Recursive(const UEdGraphPin* InPin) { for(const UEdGraphPin* SubPin : InPin->SubPins) { if(SubPin->LinkedTo.Num() > 0 || HasChildConnection_Recursive(SubPin)) { return true; } } return false; } const FPinConnectionResponse UControlRigGraphSchema::CanCreateConnection(const UEdGraphPin* A, const UEdGraphPin* B) const { UControlRigGraphNode* RigNodeA = Cast(A->GetOwningNode()); UControlRigGraphNode* RigNodeB = Cast(B->GetOwningNode()); if (RigNodeA && RigNodeB && RigNodeA != RigNodeB) { URigVMPin* PinA = RigNodeA->GetModelPinFromPinPath(A->GetName()); if (PinA) { PinA = PinA->GetPinForLink(); RigNodeA->GetModel()->PrepareCycleChecking(PinA, A->Direction == EGPD_Input); } URigVMPin* PinB = RigNodeB->GetModelPinFromPinPath(B->GetName()); if (PinB) { PinB = PinB->GetPinForLink(); } ERigVMPinDirection UserLinkDirection = ERigVMPinDirection::Output; if (A->Direction == EGPD_Input) { Swap(PinA, PinB); UserLinkDirection = ERigVMPinDirection::Input; } if (!PinA) { return FPinConnectionResponse(ECanCreateConnectionResponse::CONNECT_RESPONSE_DISALLOW, FString::Printf(TEXT("Pin %s not found"), *A->GetName())); } if (!PinB) { return FPinConnectionResponse(ECanCreateConnectionResponse::CONNECT_RESPONSE_DISALLOW, FString::Printf(TEXT("Pin %s not found"), *B->GetName())); } if(PinA->IsWildCard() != PinB->IsWildCard()) { // switch the user link direction if only one of the pins is a wildcard if(UserLinkDirection == ERigVMPinDirection::Input && PinB->IsWildCard()) { UserLinkDirection = ERigVMPinDirection::Output; } else if(UserLinkDirection == ERigVMPinDirection::Output && PinA->IsWildCard()) { UserLinkDirection = ERigVMPinDirection::Input; } } const FRigVMByteCode* ByteCode = RigNodeA->GetController()->GetCurrentByteCode(); FString FailureReason; bool bResult = RigNodeA->GetModel()->CanLink(PinA, PinB, &FailureReason, ByteCode, UserLinkDirection); if (!bResult) { return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, FText::FromString(FailureReason)); } return FPinConnectionResponse(CONNECT_RESPONSE_MAKE, LOCTEXT("ConnectResponse_Allowed", "Connect")); } return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, LOCTEXT("ConnectResponse_Disallowed_Unexpected", "Unexpected error")); } FLinearColor UControlRigGraphSchema::GetPinTypeColor(const FEdGraphPinType& PinType) const { const FName& TypeName = PinType.PinCategory; if (TypeName == UEdGraphSchema_K2::PC_Struct) { if (UStruct* Struct = Cast(PinType.PinSubCategoryObject)) { if (Struct->IsChildOf(FRigVMExecuteContext::StaticStruct())) { return FLinearColor::White; } if (Struct->IsChildOf(RigVMTypeUtils::GetWildCardCPPTypeObject())) { return FLinearColor(FVector3f::OneVector * 0.25f); } if (Struct == FRigElementKey::StaticStruct() || Struct == FRigElementKeyCollection::StaticStruct()) { return FLinearColor(0.0, 0.6588, 0.9490); } if (Struct == FRigElementKey::StaticStruct() || Struct == FRigPose::StaticStruct()) { return FLinearColor(0.0, 0.3588, 0.5490); } // external types can register their own colors, check if there are any if (IControlRigDeveloperModule* Module = FModuleManager::GetModulePtr("ControlRigDeveloper")) { if (const FLinearColor* Color = Module->FindPinTypeColor(Struct)) { return *Color; } } } } return GetDefault()->GetPinTypeColor(PinType); } void UControlRigGraphSchema::InsertAdditionalActions(TArray InBlueprints, TArray EdGraphs, TArray EdGraphPins, FGraphActionListBuilderBase& OutAllActions) const { Super::InsertAdditionalActions(InBlueprints, EdGraphs, EdGraphPins, OutAllActions); if(EdGraphPins.Num() > 0) { if(UControlRigGraphNode* RigNode = Cast(EdGraphPins[0]->GetOwningNode())) { if(URigVMPin* ModelPin = RigNode->GetModelPinFromPinPath(EdGraphPins[0]->GetName())) { if(!ModelPin->IsExecuteContext() && !ModelPin->IsWildCard()) { if(!ModelPin->GetNode()->IsA()) { OutAllActions.AddAction(TSharedPtr( new FControlRigGraphSchemaAction_PromoteToVariable(EdGraphPins[0], false) )); if(!ModelPin->GetGraph()->IsRootGraph()) { OutAllActions.AddAction(TSharedPtr( new FControlRigGraphSchemaAction_PromoteToVariable(EdGraphPins[0], true) )); } } } } } } } TSharedPtr UControlRigGraphSchema::GetNameValidator(const UBlueprint* BlueprintObj, const FName& OriginalName, const UStruct* ValidationScope, const FName& ActionTypeId) const { if (ActionTypeId == FControlRigGraphSchemaAction_LocalVar::StaticGetTypeId()) { if (const UControlRigGraph* ControlRigGraph = Cast(ValidationScope)) { if (const URigVMGraph* Graph = ControlRigGraph->GetModel()) { return MakeShareable(new FControlRigLocalVariableNameValidator(BlueprintObj, Graph, OriginalName)); } } } return MakeShareable(new FControlRigNameValidator(BlueprintObj, ValidationScope, OriginalName)); } bool UControlRigGraphSchema::SupportsPinType(UScriptStruct* ScriptStruct) const { if(!ScriptStruct) { return false; } for (TFieldIterator It(ScriptStruct); It; ++It) { FName PropertyName = It->GetFName(); FProperty* Property = *It; if (FArrayProperty* ArrayProperty = CastField(Property)) { Property = ArrayProperty->Inner; } FString CPPType = Property->GetCPPType(); if (CPPType == TEXT("bool") || CPPType == TEXT("float") || CPPType == TEXT("double") || CPPType == TEXT("int32") || CPPType == TEXT("FString") || CPPType == TEXT("FName") || CPPType == TEXT("uint16")) { continue; } if (FStructProperty* StructProperty = CastField(Property)) { if (SupportsPinType(StructProperty->Struct)) { continue; } } else if (FEnumProperty* EnumProperty = CastField(Property)) { continue; } else if (FByteProperty* ByteProperty = CastField(Property)) { if (ByteProperty->Enum) { continue; } } else if (CastField(Property) && RigVMCore::SupportsUObjects()) { continue; } else if (CastField(Property) && RigVMCore::SupportsUInterfaces()) { continue; } return false; } return true; } bool UControlRigGraphSchema::SupportsPinType(TWeakPtr SchemaAction, const FEdGraphPinType& PinType) const { if (PinType.IsContainer()) { return false; } const FName TypeName = PinType.PinCategory; if (TypeName == UEdGraphSchema_K2::PC_Boolean || TypeName == UEdGraphSchema_K2::PC_Int || TypeName == UEdGraphSchema_K2::PC_Real || TypeName == UEdGraphSchema_K2::PC_Name || TypeName == UEdGraphSchema_K2::PC_String || TypeName == UEdGraphSchema_K2::PC_Enum) { return true; } if(RigVMCore::SupportsUObjects()) { if (PinType.PinCategory == UEdGraphSchema_K2::PC_Object || PinType.PinCategory == UEdGraphSchema_K2::PC_SoftObject || PinType.PinCategory == UEdGraphSchema_K2::AllObjectTypes) { if (PinType.PinSubCategoryObject.IsValid()) { return PinType.PinSubCategoryObject->IsA(); } } } if (RigVMCore::SupportsUInterfaces()) { if (PinType.PinCategory == UEdGraphSchema_K2::PC_Interface) { if (PinType.PinSubCategoryObject.IsValid()) { return PinType.PinSubCategoryObject->IsA(); } } } if (PinType.PinCategory == UEdGraphSchema_K2::PC_Struct) { if (UScriptStruct* ScriptStruct = Cast(PinType.PinSubCategoryObject)) { if(SchemaAction.IsValid() && SchemaAction.Pin()->IsAVariable()) { if(ScriptStruct->IsChildOf(FRigVMExecuteContext::StaticStruct())) { return false; } } return SupportsPinType(ScriptStruct); } else if (PinType.PinSubCategoryObject == UUserDefinedStruct::StaticClass()) { // if a user defined struct hasn't been loaded yet, // its PinSubCategoryObject equals UUserDefinedStruct::StaticClass() // and since it is not practical to load every user defined struct to check if they only contain supported types, // we always return true so that they at least show up in the drop down menu // if they contain members of unsupported type, the spawned node will generate error return true; } } if (PinType.PinCategory == UEdGraphSchema_K2::PC_Byte) { if (PinType.PinSubCategoryObject.IsValid()) { return PinType.PinSubCategoryObject->IsA(); } } return false; } bool UControlRigGraphSchema::SupportsPinTypeContainer(TWeakPtr SchemaAction, const FEdGraphPinType& PinType, const EPinContainerType& ContainerType) const { // Do not allow containers for execute context type if(const UScriptStruct* ExecuteContextScriptStruct = Cast(PinType.PinSubCategoryObject)) { if (ExecuteContextScriptStruct->IsChildOf(FRigVMExecuteContext::StaticStruct())) { return ContainerType == EPinContainerType::None; } } return ContainerType == EPinContainerType::None || ContainerType == EPinContainerType::Array; } void UControlRigGraphSchema::BreakPinLinks(UEdGraphPin& TargetPin, bool bSendsNodeNotifcation) const { //const FScopedTransaction Transaction( LOCTEXT("GraphEd_BreakPinLinks", "Break Pin Links") ); // cache this here, as BreakPinLinks can trigger a node reconstruction invalidating the TargetPin referenceS if (UControlRigGraphNode* Node = Cast< UControlRigGraphNode>(TargetPin.GetOwningNode())) { Node->GetController()->BreakAllLinks(TargetPin.GetName(), TargetPin.Direction == EGPD_Input, true, true); } } void UControlRigGraphSchema::BreakSinglePinLink(UEdGraphPin* SourcePin, UEdGraphPin* TargetPin) const { //const FScopedTransaction Transaction(LOCTEXT("GraphEd_BreakSinglePinLink", "Break Pin Link") ); if (UControlRigGraphNode* Node = Cast< UControlRigGraphNode>(TargetPin->GetOwningNode())) { if (SourcePin->Direction == EGPD_Input) { UEdGraphPin* Temp = TargetPin; TargetPin = SourcePin; SourcePin = Temp; } Node->GetController()->BreakLink(SourcePin->GetName(), TargetPin->GetName(), true, true); } } bool UControlRigGraphSchema::CanGraphBeDropped(TSharedPtr InAction) const { if (!InAction.IsValid()) { return false; } if (InAction->GetTypeId() == FEdGraphSchemaAction_K2Graph::StaticGetTypeId()) { FEdGraphSchemaAction_K2Graph* FuncAction = (FEdGraphSchemaAction_K2Graph*)InAction.Get(); if (UControlRigGraph* RigGraph = Cast((UEdGraph*)FuncAction->EdGraph)) { return true; } } else if (InAction->GetTypeId() == FControlRigGraphSchemaAction_LocalVar::StaticGetTypeId()) { FControlRigGraphSchemaAction_LocalVar* VarAction = (FControlRigGraphSchemaAction_LocalVar*)InAction.Get(); if (UControlRigGraph* RigGraph = Cast((UEdGraph*)VarAction->GetVariableScope())) { return true; } } return false; } FString UControlRigGraphSchema::GetFindReferenceSearchTerm(const FEdGraphSchemaAction* InGraphAction) const { if(InGraphAction) { if(InGraphAction->GetTypeId() == FEdGraphSchemaAction_K2Var::StaticGetTypeId()) { const FEdGraphSchemaAction_K2Var* VarAction = (const FEdGraphSchemaAction_K2Var*)InGraphAction; return VarAction->GetVariableName().ToString(); } } return Super::GetFindReferenceSearchTerm(InGraphAction); } FReply UControlRigGraphSchema::BeginGraphDragAction(TSharedPtr InAction, const FPointerEvent& MouseEvent) const { if (!InAction.IsValid()) { return FReply::Unhandled(); } if (InAction->GetTypeId() == FEdGraphSchemaAction_K2Graph::StaticGetTypeId()) { FEdGraphSchemaAction_K2Graph* FuncAction = (FEdGraphSchemaAction_K2Graph*)InAction.Get(); if (UControlRigGraph* RigGraph = Cast(FuncAction->EdGraph)) { if (UControlRigBlueprint* RigBlueprint = Cast(FBlueprintEditorUtils::FindBlueprintForGraph(RigGraph))) { TSharedRef Action = FControlRigFunctionDragDropAction::New(InAction, RigBlueprint, RigGraph); Action->SetAltDrag(MouseEvent.IsAltDown()); Action->SetCtrlDrag(MouseEvent.IsControlDown()); return FReply::Handled().BeginDragDrop(Action); } } } else if(InAction->GetTypeId() == FControlRigGraphSchemaAction_LocalVar::StaticGetTypeId()) { FControlRigGraphSchemaAction_LocalVar* VarAction = (FControlRigGraphSchemaAction_LocalVar*)InAction.Get(); if (UControlRigGraph* RigGraph = Cast(VarAction->GetVariableScope())) { if (UControlRigBlueprint* RigBlueprint = Cast(FBlueprintEditorUtils::FindBlueprintForGraph(RigGraph))) { TSharedRef Action = FControlRigFunctionDragDropAction::New(InAction, RigBlueprint, RigGraph); Action->SetAltDrag(MouseEvent.IsAltDown()); Action->SetCtrlDrag(MouseEvent.IsControlDown()); return FReply::Handled().BeginDragDrop(Action); } } } return FReply::Unhandled(); } FConnectionDrawingPolicy* UControlRigGraphSchema::CreateConnectionDrawingPolicy(int32 InBackLayerID, int32 InFrontLayerID, float InZoomFactor, const FSlateRect& InClippingRect, class FSlateWindowElementList& InDrawElements, class UEdGraph* InGraphObj) const { #if WITH_EDITOR return IControlRigEditorModule::Get().CreateConnectionDrawingPolicy(InBackLayerID, InFrontLayerID, InZoomFactor, InClippingRect, InDrawElements, InGraphObj); #else check(0); return nullptr; #endif } bool UControlRigGraphSchema::ShouldHidePinDefaultValue(UEdGraphPin* Pin) const { // we should hide default values if any of our parents are connected return HasParentConnection_Recursive(Pin); } bool UControlRigGraphSchema::IsPinBeingWatched(UEdGraphPin const* Pin) const { if (UControlRigGraphNode* Node = Cast< UControlRigGraphNode>(Pin->GetOwningNode())) { if (URigVMPin* ModelPin = Node->GetModel()->FindPin(Pin->GetName())) { return ModelPin->RequiresWatch(); } } return false; } void UControlRigGraphSchema::ClearPinWatch(UEdGraphPin const* Pin) const { if (UControlRigGraphNode* Node = Cast< UControlRigGraphNode>(Pin->GetOwningNode())) { Node->GetController()->SetPinIsWatched(Pin->GetName(), false); } } void UControlRigGraphSchema::OnPinConnectionDoubleCicked(UEdGraphPin* PinA, UEdGraphPin* PinB, const FVector2D& GraphPosition) const { if (UControlRigGraphNode* Node = Cast< UControlRigGraphNode>(PinA->GetOwningNode())) { if (URigVMLink* Link = Node->GetModel()->FindLink(FString::Printf(TEXT("%s -> %s"), *PinA->GetName(), *PinB->GetName()))) { Node->GetController()->AddRerouteNodeOnLink(Link, false, GraphPosition, FString(), true, true); } } } bool UControlRigGraphSchema::MarkBlueprintDirtyFromNewNode(UBlueprint* InBlueprint, UEdGraphNode* InEdGraphNode) const { if (InBlueprint == nullptr || InEdGraphNode == nullptr) { return false; } return true; } bool UControlRigGraphSchema::IsStructEditable(UStruct* InStruct) const { if (InStruct == FRuntimeFloatCurve::StaticStruct()) { return true; } return false; } void UControlRigGraphSchema::SetNodePosition(UEdGraphNode* Node, const FVector2D& Position) const { return SetNodePosition(Node, Position, true); } void UControlRigGraphSchema::SetNodePosition(UEdGraphNode* Node, const FVector2D& Position, bool bSetupUndo) const { StartGraphNodeInteraction(Node); if (UControlRigGraphNode* RigNode = Cast(Node)) { RigNode->GetController()->SetNodePosition(RigNode->GetModelNode(), Position, bSetupUndo, false, false); } if (UEdGraphNode_Comment* CommentNode = Cast(Node)) { if(UControlRigGraph* Graph = CommentNode->GetTypedOuter()) { Graph->GetController()->SetNodePositionByName(CommentNode->GetFName(), Position, bSetupUndo, false, false); } } } void UControlRigGraphSchema::GetGraphDisplayInformation(const UEdGraph& Graph, /*out*/ FGraphDisplayInfo& DisplayInfo) const { Super::GetGraphDisplayInformation(Graph, DisplayInfo); if (UControlRigGraph* RigGraph = Cast((UEdGraph*)&Graph)) { if(URigVMGraph* Model = RigGraph->GetModel()) { TArray NodePathParts; if (URigVMNode::SplitNodePath(RigGraph->ModelNodePath, NodePathParts)) { if(NodePathParts.Num() > 1) { DisplayInfo.DisplayName = FText::FromString(NodePathParts.Last()); DisplayInfo.PlainName = DisplayInfo.DisplayName; static const FText LocalFunctionText = FText::FromString(TEXT("A local function.")); DisplayInfo.Tooltip = LocalFunctionText; } // if this is a riggraph within a collapse node - let's use that for the tooltip if(URigVMCollapseNode* CollapseNode = Model->GetTypedOuter()) { DisplayInfo.Tooltip = CollapseNode->GetToolTipText(); } } if(Model->IsRootGraph()) { // let's see if there is only one event FString EventName; if(Algo::CountIf(Model->GetNodes(), [&EventName](const URigVMNode* NodeToCount) -> bool { if(NodeToCount->IsEvent() && NodeToCount->CanOnlyExistOnce()) { if(EventName.IsEmpty()) { if(const URigVMUnitNode* UnitNode = Cast(NodeToCount)) { if(UnitNode->GetScriptStruct()) { EventName = UnitNode->GetScriptStruct()->GetDisplayNameText().ToString(); } } } return true; } return false; }) == 1) { static constexpr TCHAR EventGraphNameFormat[] = TEXT("%s Graph"); const FString DesiredGraphName = FString::Printf(EventGraphNameFormat, *EventName); DisplayInfo.DisplayName = FText::FromString(DesiredGraphName); DisplayInfo.PlainName = DisplayInfo.DisplayName; } else { static const FText MainGraphText = FText::FromString(TEXT("A top level graph for the rig.")); DisplayInfo.Tooltip = MainGraphText; if(!NodePathParts.IsEmpty()) { static constexpr TCHAR NodePathSuffix[] = TEXT("::"); FString NodePath = NodePathParts[0]; NodePath.RemoveFromEnd(NodePathSuffix); NodePath.RemoveFromStart(UControlRigBlueprint::RigVMModelPrefix); NodePath.TrimStartAndEndInline(); DisplayInfo.DisplayName = FText::FromString(NodePath); DisplayInfo.PlainName = DisplayInfo.DisplayName; } } } } } } bool UControlRigGraphSchema::GetLocalVariables(const UEdGraph* InGraph, TArray& OutLocalVariables) const { OutLocalVariables.Reset(); if (UControlRigGraph* RigGraph = Cast((UEdGraph*)InGraph)) { if (URigVMGraph* Model = RigGraph->GetModel()) { TArray LocalVariables = Model->GetLocalVariables(); for (FRigVMGraphVariableDescription LocalVariable : LocalVariables) { FBPVariableDescription VariableDescription; VariableDescription.VarName = LocalVariable.Name; VariableDescription.FriendlyName = LocalVariable.Name.ToString(); VariableDescription.DefaultValue = LocalVariable.DefaultValue; VariableDescription.VarType = LocalVariable.ToPinType(); VariableDescription.PropertyFlags |= CPF_BlueprintVisible; OutLocalVariables.Add(VariableDescription); } } } return true; } TSharedPtr UControlRigGraphSchema::MakeActionFromVariableDescription(const UEdGraph* InEdGraph, const FBPVariableDescription& Variable) const { if (UControlRigGraph* RigGraph = Cast((UEdGraph*)InEdGraph)) { FText Category = Variable.Category; if (Variable.Category.EqualTo(UEdGraphSchema_K2::VR_DefaultCategory)) { Category = FText::GetEmpty(); } TSharedPtr Action = MakeShareable(new FControlRigGraphSchemaAction_LocalVar(Category, FText::FromName(Variable.VarName), FText::GetEmpty(), 0, NodeSectionID::LOCAL_VARIABLE)); Action->SetVariableInfo(Variable.VarName, RigGraph, Variable.VarType.PinCategory == UEdGraphSchema_K2::PC_Boolean); return Action; } return nullptr; } FText UControlRigGraphSchema::GetGraphCategory(const UEdGraph* InGraph) const { if (UControlRigGraph* RigGraph = Cast((UEdGraph*)InGraph)) { if (URigVMGraph* Model = RigGraph->GetModel()) { if (URigVMCollapseNode* CollapseNode = Cast(Model->GetOuter())) { return FText::FromString(CollapseNode->GetNodeCategory()); } } } return FText(); } EGraphType UControlRigGraphSchema::GetGraphType(const UEdGraph* TestEdGraph) const { if (UControlRigGraph* RigGraph = Cast((UEdGraph*)TestEdGraph)) { if (UControlRigBlueprint* RigBlueprint = Cast(FBlueprintEditorUtils::FindBlueprintForGraph(RigGraph))) { if (URigVMGraph* Model = RigGraph->GetModel()) { if(Model->IsRootGraph()) { if(Model->IsA()) { return EGraphType::GT_Function; } else { return EGraphType::GT_Ubergraph; } } else if(URigVMLibraryNode* LibraryNode = Cast(Model->GetOuter())) { if(LibraryNode->GetGraph()->IsA()) { return EGraphType::GT_Function; } else { // collapse nodes show up as uber graphs return EGraphType::GT_Ubergraph; } } } } } return Super::GetGraphType(TestEdGraph); } FReply UControlRigGraphSchema::TrySetGraphCategory(const UEdGraph* InGraph, const FText& InCategory) { if (UControlRigGraph* RigGraph = Cast((UEdGraph*)InGraph)) { if (UControlRigBlueprint* RigBlueprint = Cast(FBlueprintEditorUtils::FindBlueprintForGraph(RigGraph))) { if (URigVMGraph* Model = RigGraph->GetModel()) { if (URigVMCollapseNode* CollapseNode = Cast(Model->GetOuter())) { if (URigVMController* Controller = RigBlueprint->GetOrCreateController(CollapseNode->GetGraph())) { if (Controller->SetNodeCategory(CollapseNode, InCategory.ToString(), true, false, true)) { return FReply::Handled(); } } } } } } return FReply::Unhandled(); } bool UControlRigGraphSchema::TryDeleteGraph(UEdGraph* GraphToDelete) const { if (UControlRigGraph* RigGraph = Cast(GraphToDelete)) { if (UControlRigBlueprint* RigBlueprint = Cast(FBlueprintEditorUtils::FindBlueprintForGraph(RigGraph))) { if (URigVMGraph* Model = RigBlueprint->GetModel(GraphToDelete)) { if(Model->IsRootGraph()) { RigBlueprint->RemoveModel(Model->GetNodePath()); return true; } else if (URigVMCollapseNode* LibraryNode = Cast(Model->GetOuter())) { if (URigVMController* Controller = RigBlueprint->GetOrCreateController(LibraryNode->GetGraph())) { // check if there is a "bulk remove function" transaction going on. // which implies that a category is being deleted if (GEditor->CanTransact()) { if (GEditor->Trans->GetQueueLength() > 0) { const FTransaction* LastTransaction = GEditor->Trans->GetTransaction(GEditor->Trans->GetQueueLength() - 1); if (LastTransaction) { if (LastTransaction->GetTitle().ToString() == TEXT("Bulk Remove Functions")) { // instead of deleting the graph, let's set its category to none // and thus moving it to the top of the tree return Controller->SetNodeCategory(LibraryNode, FString()); } } } } bool bSetupUndoRedo = true; // if the element to remove is a function, check if it is public and referenced. If so, // warn the user about a bulk remove if (URigVMFunctionLibrary* Library = Cast(LibraryNode->GetGraph())) { const FName& FunctionName = LibraryNode->GetFName(); if (RigBlueprint->IsFunctionPublic(FunctionName)) { for (auto Reference : Library->GetReferencesForFunction(FunctionName)) { if (Reference.IsValid()) { UControlRigBlueprint* OtherBlueprint = Reference->GetTypedOuter(); if (OtherBlueprint != RigBlueprint) { if(RigBlueprint->OnRequestBulkEditDialog().IsBound()) { URigVMController* FunctionController = RigBlueprint->GetController(LibraryNode->GetContainedGraph()); FRigVMController_BulkEditResult Result = RigBlueprint->OnRequestBulkEditDialog().Execute(RigBlueprint, FunctionController, LibraryNode, ERigVMControllerBulkEditType::RemoveFunction); if(Result.bCanceled) { return false; } bSetupUndoRedo = Result.bSetupUndoRedo; } break; } } } } } return Controller->RemoveNode(LibraryNode, bSetupUndoRedo, false, true); } } } } } return false; } bool UControlRigGraphSchema::TryRenameGraph(UEdGraph* GraphToRename, const FName& InNewName) const { if (UControlRigGraph* RigGraph = Cast(GraphToRename)) { if (UControlRigBlueprint* RigBlueprint = Cast(FBlueprintEditorUtils::FindBlueprintForGraph(RigGraph))) { if (URigVMGraph* Model = RigGraph->GetModel()) { if(Model->IsRootGraph()) { const FString NewName = FString::Printf(TEXT("%s %s"), RigBlueprint->RigVMModelPrefix, *InNewName.ToString()); RigBlueprint->RenameGraph(Model->GetNodePath(), *NewName); } else if (URigVMGraph* RootModel = Model->GetRootGraph()) { URigVMLibraryNode* LibraryNode = Cast(RootModel->FindNode(RigGraph->ModelNodePath)); if (LibraryNode) { if (URigVMController* Controller = RigBlueprint->GetOrCreateController(LibraryNode->GetGraph())) { Controller->RenameNode(LibraryNode, InNewName, true, true); return true; } } } } } } return false; } bool UControlRigGraphSchema::TryToGetChildEvents(const UEdGraph* Graph, const int32 SectionId, TArray>& Actions, const FText& ParentCategory) const { check(Graph); if(const UControlRigGraph* RigGraph = Cast(Graph)) { if(URigVMGraph* Model = RigGraph->GetModel()) { TArray EventNames; TMap EventNameToNode; for(const URigVMNode* Node : Model->GetNodes()) { if(Node->IsEvent()) { if(!EventNames.Contains(Node->GetEventName())) { EventNames.Add(Node->GetEventName()); EventNameToNode.Add(Node->GetEventName(), Node); } } } if(!EventNames.IsEmpty()) { FGraphDisplayInfo EdGraphDisplayInfo; GetGraphDisplayInformation(*Graph, EdGraphDisplayInfo); FText EdGraphDisplayName = EdGraphDisplayInfo.DisplayName; FText ActionCategory; if (!ParentCategory.IsEmpty()) { ActionCategory = FText::Format(FText::FromString(TEXT("{0}|{1}")), ParentCategory, EdGraphDisplayName); } else { ActionCategory = MoveTemp(EdGraphDisplayName); } for(const FName& EventName : EventNames) { const URigVMNode* Node = EventNameToNode.FindChecked(EventName); TSharedPtr Action = MakeShareable( new FControlRigGraphSchemaAction_Event(EventName, Node->GetNodePath(true), ActionCategory) ); Action->SectionID = SectionId; Actions.Add(Action); } } return true; } } return false; } UEdGraphPin* UControlRigGraphSchema::DropPinOnNode(UEdGraphNode* InTargetNode, const FName& InSourcePinName, const FEdGraphPinType& InSourcePinType, EEdGraphPinDirection InSourcePinDirection) const { FString NewPinName; if (UControlRigBlueprint* RigBlueprint = Cast(FBlueprintEditorUtils::FindBlueprintForNode(InTargetNode))) { if (UControlRigGraphNode* RigNode = Cast(InTargetNode)) { if (URigVMNode* ModelNode = RigNode->GetModelNode()) { URigVMGraph* Model = nullptr; ERigVMPinDirection PinDirection = InSourcePinDirection == EGPD_Input ? ERigVMPinDirection::Input : ERigVMPinDirection::Output; if (URigVMCollapseNode* CollapseNode = Cast(ModelNode)) { Model = CollapseNode->GetContainedGraph(); PinDirection = PinDirection == ERigVMPinDirection::Output ? ERigVMPinDirection::Input : ERigVMPinDirection::Output; } else if (ModelNode->IsA() || ModelNode->IsA()) { Model = ModelNode->GetGraph(); } if (Model) { ensure(!Model->IsTopLevelGraph()); FRigVMExternalVariable ExternalVar = RigVMTypeUtils::ExternalVariableFromPinType(InSourcePinName, InSourcePinType); if (ExternalVar.IsValid(true /* allow null memory */)) { if (URigVMController* Controller = RigBlueprint->GetController(Model)) { FString TypeName = ExternalVar.TypeName.ToString(); if (ExternalVar.bIsArray) { TypeName = RigVMTypeUtils::ArrayTypeFromBaseType(*TypeName); } FName TypeObjectPathName = NAME_None; if (ExternalVar.TypeObject) { TypeObjectPathName = *ExternalVar.TypeObject->GetPathName(); } FString DefaultValue; if (PinBeingDropped) { if (UControlRigGraphNode* SourceNode = Cast(PinBeingDropped->GetOwningNode())) { if (URigVMPin* SourcePin = SourceNode->GetModelPinFromPinPath(PinBeingDropped->GetName())) { DefaultValue = SourcePin->GetDefaultValue(); } } } FName ExposedPinName = Controller->AddExposedPin( InSourcePinName, PinDirection, TypeName, TypeObjectPathName, DefaultValue, true, true ); if (!ExposedPinName.IsNone()) { NewPinName = ExposedPinName.ToString(); } } } } if (!NewPinName.IsEmpty()) { if (URigVMPin* NewModelPin = ModelNode->FindPin(NewPinName)) { return RigNode->FindPin(*NewModelPin->GetPinPath()); } } } } } return nullptr; } bool UControlRigGraphSchema::SupportsDropPinOnNode(UEdGraphNode* InTargetNode, const FEdGraphPinType& InSourcePinType, EEdGraphPinDirection InSourcePinDirection, FText& OutErrorMessage) const { if (UControlRigGraphNode* RigNode = Cast(InTargetNode)) { if(URigVMNode* ModelNode = RigNode->GetModelNode()) { if (ModelNode->IsA()) { if (InSourcePinDirection == EGPD_Output) { OutErrorMessage = LOCTEXT("AddPinToReturnNode", "Add Pin to Return Node"); return false; } return true; } else if (ModelNode->IsA()) { if (InSourcePinDirection == EGPD_Input) { OutErrorMessage = LOCTEXT("AddPinToEntryNode", "Add Pin to Entry Node"); return false; } return true; } else if (ModelNode->IsA()) { return true; } } } return false; } UControlRigGraphNode* UControlRigGraphSchema::CreateGraphNode(UControlRigGraph* InGraph, const FName& InPropertyName) const { const bool bSelectNewNode = true; FGraphNodeCreator GraphNodeCreator(*InGraph); UControlRigGraphNode* ControlRigGraphNode = GraphNodeCreator.CreateNode(bSelectNewNode); ControlRigGraphNode->ModelNodePath = InPropertyName.ToString(); GraphNodeCreator.Finalize(); return ControlRigGraphNode; } void UControlRigGraphSchema::TrySetDefaultValue(UEdGraphPin& InPin, const FString& InNewDefaultValue, bool bMarkAsModified) const { #if WITH_EDITOR if (GEditor) { GEditor->CancelTransaction(0); } #endif GetDefault()->TrySetDefaultValue(InPin, InNewDefaultValue, false); } void UControlRigGraphSchema::TrySetDefaultObject(UEdGraphPin& InPin, UObject* InNewDefaultObject, bool bMarkAsModified) const { #if WITH_EDITOR if (GEditor) { GEditor->CancelTransaction(0); } #endif GetDefault()->TrySetDefaultObject(InPin, InNewDefaultObject, false); } void UControlRigGraphSchema::TrySetDefaultText(UEdGraphPin& InPin, const FText& InNewDefaultText, bool bMarkAsModified) const { #if WITH_EDITOR if (GEditor) { GEditor->CancelTransaction(0); } #endif GetDefault()->TrySetDefaultText(InPin, InNewDefaultText, false); } bool UControlRigGraphSchema::ArePinsCompatible(const UEdGraphPin* PinA, const UEdGraphPin* PinB, const UClass* CallingContext, bool bIgnoreArray /*= false*/) const { // filter out pins which have a parent if (PinB->ParentPin != nullptr) { return false; } // if we are looking at a polymorphic node if((PinA->PinType.ContainerType == PinB->PinType.ContainerType) || (PinA->PinType.PinSubCategoryObject != PinB->PinType.PinSubCategoryObject)) { auto IsPinCompatibleWithType = [](const UEdGraphPin* InPin, const FEdGraphPinType& InPinType) -> bool { if(const UControlRigGraphNode* RigNode = Cast(InPin->GetOwningNode())) { if(const FRigVMTemplate* Template = RigNode->GetTemplate()) { FString CPPType; UObject* CPPTypeObject = nullptr; if(RigVMTypeUtils::CPPTypeFromPinType(InPinType, CPPType, &CPPTypeObject)) { FString PinPath, PinName; URigVMPin::SplitPinPathAtEnd(InPin->GetName(), PinPath, PinName); if((InPin->ParentPin != nullptr) && (InPin->ParentPin->ParentPin == nullptr) && (InPin->ParentPin->PinType.ContainerType == EPinContainerType::Array)) { URigVMPin::SplitPinPathAtEnd(InPin->ParentPin->GetName(), PinPath, PinName); CPPType = RigVMTypeUtils::ArrayTypeFromBaseType(CPPType); } if(!Template->ArgumentSupportsType(*PinName, CPPType)) { return false; } } } } return true; }; auto IsTemplateNodePin = [](const UEdGraphPin* InPin) { if(const UControlRigGraphNode* RigNode = Cast(InPin->GetOwningNode())) { return RigNode->GetTemplate() != nullptr; }; return false; }; if(PinA->PinType.PinSubCategoryObject == RigVMTypeUtils::GetWildCardCPPTypeObject() || IsTemplateNodePin(PinA)) { if(IsPinCompatibleWithType(PinA, PinB->PinType)) { UControlRigGraphSchema* MutableThis = (UControlRigGraphSchema*)this; MutableThis->LastPinForCompatibleCheck = PinB; MutableThis->bLastPinWasInput = PinB->Direction == EGPD_Input; return true; } } if(PinB->PinType.PinSubCategoryObject == RigVMTypeUtils::GetWildCardCPPTypeObject() || IsTemplateNodePin(PinB)) { if(IsPinCompatibleWithType(PinB, PinA->PinType)) { UControlRigGraphSchema* MutableThis = (UControlRigGraphSchema*)this; MutableThis->LastPinForCompatibleCheck = PinA; MutableThis->bLastPinWasInput = PinA->Direction == EGPD_Input; return true; } } } // for large world coordinate support we should allow connections // between float and double if(PinA->PinType.ContainerType == EPinContainerType::None && PinB->PinType.ContainerType == EPinContainerType::None) { if((PinA->PinType.PinCategory == UEdGraphSchema_K2::PC_Float && PinB->PinType.PinCategory == UEdGraphSchema_K2::PC_Double) || (PinA->PinType.PinCategory == UEdGraphSchema_K2::PC_Double && PinB->PinType.PinCategory == UEdGraphSchema_K2::PC_Float)) { return true; } } return GetDefault()->ArePinsCompatible(PinA, PinB, CallingContext, bIgnoreArray); } void UControlRigGraphSchema::RenameNode(UControlRigGraphNode* Node, const FName& InNewNodeName) const { Node->NodeTitle = FText::FromName(InNewNodeName); Node->Modify(); } void UControlRigGraphSchema::ResetPinDefaultsRecursive(UEdGraphPin* InPin) const { UControlRigGraphNode* RigNode = Cast(InPin->GetOwningNode()); if (RigNode == nullptr) { return; } RigNode->CopyPinDefaultsToModel(InPin); for (UEdGraphPin* SubPin : InPin->SubPins) { ResetPinDefaultsRecursive(SubPin); } } void UControlRigGraphSchema::GetVariablePinTypes(TArray& PinTypes) const { PinTypes.Add(FEdGraphPinType(UEdGraphSchema_K2::PC_Boolean, FName(NAME_None), nullptr, EPinContainerType::None, false, FEdGraphTerminalType())); PinTypes.Add(FEdGraphPinType(UEdGraphSchema_K2::PC_Real, FName(NAME_None), nullptr, EPinContainerType::None, false, FEdGraphTerminalType())); PinTypes.Add(FEdGraphPinType(UEdGraphSchema_K2::PC_Int, FName(NAME_None), nullptr, EPinContainerType::None, false, FEdGraphTerminalType())); PinTypes.Add(FEdGraphPinType(UEdGraphSchema_K2::PC_Struct, FName(NAME_None), TBaseStructure::Get(), EPinContainerType::None, false, FEdGraphTerminalType())); PinTypes.Add(FEdGraphPinType(UEdGraphSchema_K2::PC_Struct, FName(NAME_None), TBaseStructure::Get(), EPinContainerType::None, false, FEdGraphTerminalType())); PinTypes.Add(FEdGraphPinType(UEdGraphSchema_K2::PC_Struct, FName(NAME_None), TBaseStructure::Get(), EPinContainerType::None, false, FEdGraphTerminalType())); PinTypes.Add(FEdGraphPinType(UEdGraphSchema_K2::PC_Struct, FName(NAME_None), TBaseStructure::Get(), EPinContainerType::None, false, FEdGraphTerminalType())); PinTypes.Add(FEdGraphPinType(UEdGraphSchema_K2::PC_Struct, FName(NAME_None), TBaseStructure::Get(), EPinContainerType::None, false, FEdGraphTerminalType())); PinTypes.Add(FEdGraphPinType(UEdGraphSchema_K2::PC_Struct, FName(NAME_None), TBaseStructure::Get(), EPinContainerType::None, false, FEdGraphTerminalType())); } bool UControlRigGraphSchema::SafeDeleteNodeFromGraph(UEdGraph* Graph, UEdGraphNode* Node) const { if (UControlRigGraphNode* RigNode = Cast(Node)) { if (GEditor) { GEditor->CancelTransaction(0); } return RigNode->GetController()->RemoveNode(RigNode->GetModelNode(), true, true, true); } return false; } bool UControlRigGraphSchema::CanVariableBeDropped(UEdGraph* InGraph, FProperty* InVariableToDrop) const { FRigVMExternalVariable ExternalVariable = FRigVMExternalVariable::Make(InVariableToDrop, nullptr); return ExternalVariable.IsValid(true /* allow nullptr */); } bool UControlRigGraphSchema::RequestVariableDropOnPanel(UEdGraph* InGraph, FProperty* InVariableToDrop, const FVector2D& InDropPosition, const FVector2D& InScreenPosition) { #if WITH_EDITOR if (CanVariableBeDropped(InGraph, InVariableToDrop)) { FRigVMExternalVariable ExternalVariable = FRigVMExternalVariable::Make(InVariableToDrop, nullptr); UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraph(InGraph); UControlRigBlueprint* RigBlueprint = Cast(Blueprint); if (RigBlueprint != nullptr) { RigBlueprint->OnVariableDropped().Broadcast(InGraph, InVariableToDrop, InDropPosition, InScreenPosition); return true; } } #endif return false; } bool UControlRigGraphSchema::RequestVariableDropOnPin(UEdGraph* InGraph, FProperty* InVariableToDrop, UEdGraphPin* InPin, const FVector2D& InDropPosition, const FVector2D& InScreenPosition) { #if WITH_EDITOR if (CanVariableBeDropped(InGraph, InVariableToDrop)) { if(UControlRigGraph* Graph = Cast(InGraph)) { if (URigVMPin* ModelPin = Graph->GetModel()->FindPin(InPin->GetName())) { FRigVMExternalVariable ExternalVariable = FRigVMExternalVariable::Make(InVariableToDrop, nullptr); if (ModelPin->CanBeBoundToVariable(ExternalVariable)) { FModifierKeysState KeyState = FSlateApplication::Get().GetModifierKeys(); if (KeyState.IsAltDown()) { return Graph->GetController()->BindPinToVariable(ModelPin->GetPinPath(), InVariableToDrop->GetName(), true, true); } else { Graph->GetController()->OpenUndoBracket(TEXT("Bind Variable to Pin")); if (URigVMVariableNode* VariableNode = Graph->GetController()->AddVariableNode(ExternalVariable.Name, ExternalVariable.TypeName.ToString(), ExternalVariable.TypeObject, true, FString(), InDropPosition + FVector2D(0.f, -34.f))) { Graph->GetController()->AddLink(VariableNode->FindPin(TEXT("Value"))->GetPinPath(), ModelPin->GetPinPath(), true); } Graph->GetController()->CloseUndoBracket(); return true; } } } } } #endif return false; } void UControlRigGraphSchema::StartGraphNodeInteraction(UEdGraphNode* InNode) const { #if WITH_EDITOR check(InNode); if(NodesBeingInteracted.Contains(InNode)) { return; } NodePositionsDuringStart.Reset(); NodesBeingInteracted.Reset(); UControlRigGraph* Graph = Cast(InNode->GetOuter()); if (Graph == nullptr) { return; } check(Graph->GetController()); check(Graph->GetModel()); NodesBeingInteracted = GetNodesToMoveForNode(InNode); for (UEdGraphNode* NodeToMove : NodesBeingInteracted) { FName NodeName = NodeToMove->GetFName(); if (URigVMNode* ModelNode = Graph->GetModel()->FindNodeByName(NodeName)) { NodePositionsDuringStart.FindOrAdd(NodeName, ModelNode->GetPosition()); } } #endif } void UControlRigGraphSchema::EndGraphNodeInteraction(UEdGraphNode* InNode) const { #if WITH_EDITOR UControlRigGraph* Graph = Cast(InNode->GetOuter()); if (Graph == nullptr) { return; } check(Graph->GetController()); check(Graph->GetModel()); TArray NodesToMove = GetNodesToMoveForNode(InNode); bool bMovedSomething = false; Graph->GetController()->OpenUndoBracket(TEXT("Move Nodes")); for (UEdGraphNode* NodeToMove : NodesToMove) { FName NodeName = NodeToMove->GetFName(); if (URigVMNode* ModelNode = Graph->GetModel()->FindNodeByName(NodeName)) { FVector2D NewPosition(NodeToMove->NodePosX, NodeToMove->NodePosY); if(FVector2D* OldPosition = NodePositionsDuringStart.Find(NodeName)) { TGuardValue SuspendNotification(Graph->bSuspendModelNotifications, true); Graph->GetController()->SetNodePositionByName(NodeName, *OldPosition, false, false); } if(Graph->GetController()->SetNodePositionByName(NodeName, NewPosition, true, false, true)) { bMovedSomething = true; } } } if (bMovedSomething) { if (GEditor) { GEditor->CancelTransaction(0); } Graph->GetController()->CloseUndoBracket(); } else { Graph->GetController()->CancelUndoBracket(); } NodesBeingInteracted.Reset(); NodePositionsDuringStart.Reset(); #endif } TArray UControlRigGraphSchema::GetNodesToMoveForNode(UEdGraphNode* InNode) { TArray NodesToMove; #if WITH_EDITOR UControlRigGraph* Graph = Cast(InNode->GetOuter()); if (Graph == nullptr) { return NodesToMove; } NodesToMove.Add(InNode); for (UEdGraphNode* SelectedGraphNode : Graph->Nodes) { if (SelectedGraphNode->IsSelected()) { NodesToMove.AddUnique(SelectedGraphNode); } } for (int32 NodeIndex = 0; NodeIndex < NodesToMove.Num(); NodeIndex++) { if (UEdGraphNode_Comment* CommentNode = Cast(NodesToMove[NodeIndex])) { if (CommentNode->MoveMode == ECommentBoxMode::GroupMovement) { for (FCommentNodeSet::TConstIterator NodeIt(CommentNode->GetNodesUnderComment()); NodeIt; ++NodeIt) { if (UEdGraphNode* NodeUnderComment = Cast(*NodeIt)) { NodesToMove.AddUnique(NodeUnderComment); } } } } } #endif return NodesToMove; } FVector2D UControlRigGraphSchema::GetNodePositionAtStartOfInteraction(UEdGraphNode* InNode) const { #if WITH_EDITOR if(InNode) { if(const FVector2D* Position = NodePositionsDuringStart.Find(InNode->GetFName())) { return *Position; } return FVector2D(InNode->NodePosX, InNode->NodePosY); } #endif return FVector2D::ZeroVector; } void UControlRigGraphSchema::HandleModifiedEvent(ERigVMGraphNotifType InNotifType, URigVMGraph* InGraph, UObject* InSubject) { switch(InNotifType) { case ERigVMGraphNotifType::NodeAdded: case ERigVMGraphNotifType::NodeRemoved: case ERigVMGraphNotifType::PinAdded: case ERigVMGraphNotifType::PinRemoved: case ERigVMGraphNotifType::PinRenamed: case ERigVMGraphNotifType::PinArraySizeChanged: case ERigVMGraphNotifType::PinTypeChanged: case ERigVMGraphNotifType::LinkAdded: case ERigVMGraphNotifType::LinkRemoved: { LastPinForCompatibleCheck = nullptr; break; } default: { break; } } } bool UControlRigGraphSchema::IsControlRigDefaultEvent(const FName& InEventName) { return InEventName == FRigUnit_BeginExecution::EventName || InEventName == FRigUnit_InverseExecution::EventName || InEventName == FRigUnit_PrepareForExecution::EventName || InEventName == FRigUnit_InteractionExecution::EventName; } #undef LOCTEXT_NAMESPACE