// Copyright Epic Games, Inc. All Rights Reserved. #include "NiagaraNode.h" #include "NiagaraCompileHashVisitor.h" #include "NiagaraGraph.h" #include "EdGraphSchema_Niagara.h" #include "NiagaraHlslTranslator.h" #include "GraphEditAction.h" #include "Widgets/SNiagaraGraphNode.h" #include "Misc/SecureHash.h" #include "ToolMenuSection.h" #include "ToolMenu.h" #include "GraphEditorActions.h" #include "NiagaraConstants.h" #include "Serialization/PropertyLocalizationDataGathering.h" #include "NiagaraEditorUtilities.h" #include "ScopedTransaction.h" #include "Misc/Guid.h" #include "UObject/TextProperty.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(NiagaraNode) #define LOCTEXT_NAMESPACE "NiagaraNode" UNiagaraNode::UNiagaraNode(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { } void UNiagaraNode::PostLoad() { Super::PostLoad(); if (ChangeId.IsValid() == false) { ChangeId = FGuid::NewGuid(); } if (GIsEditor && HasAllFlags(RF_Transactional) == false) { SetFlags(RF_Transactional); } for (UEdGraphPin* Pin : Pins) { if(Pin == nullptr) { UNiagaraGraph* OwningGraph = GetNiagaraGraph(); FString AssetPath = OwningGraph ? OwningGraph->GetPathName() : "Undetermined"; FString NodeName = GetNodeTitle(ENodeTitleType::FullTitle).ToString(); UE_LOG(LogNiagaraEditor, Log, TEXT("Pin of node %s in asset %s is nullptr!"), *NodeName, *AssetPath); continue; } if(Pin->LinkedTo.Num() > 0) { for(UEdGraphPin* LinkedPin : Pin->LinkedTo) { if(LinkedPin == nullptr) { UNiagaraGraph* OwningGraph = GetNiagaraGraph(); FString AssetPath = OwningGraph ? OwningGraph->GetPathName() : "Undetermined"; FString PinName = Pin->PinName.ToString(); FString NodeName = Pin->GetOwningNode() ? Pin->GetOwningNode()->GetNodeTitle(ENodeTitleType::FullTitle).ToString() : "Undetermined"; UE_LOG(LogNiagaraEditor, Log, TEXT("Connected pin of Pin %s of node %s in asset %s is nullptr!"), *PinName, *NodeName, *AssetPath); } } } const UEdGraphSchema_Niagara* Schema = CastChecked(GetSchema()); check(Schema); UEdGraphSchema_Niagara::ConvertIllegalPinsInPlace(Pin); const FNiagaraTypeDefinition PinType = Schema->PinToTypeDefinition(Pin); if (PinType.GetEnum() == FNiagaraTypeDefinition::GetCoordinateSpaceEnum()) { int32 EnumIndex = PinType.GetEnum()->GetIndexByNameString(Pin->DefaultValue, EGetByNameFlags::None); if (EnumIndex != INDEX_NONE ) { Pin->DefaultValue = PinType.GetEnum()->GetNameStringByIndex(EnumIndex); } } } } bool UNiagaraNode::NestedPropertiesAppendCompileHash(const void* Container, const UStruct* Struct, EFieldIteratorFlags::SuperClassFlags IteratorFlags, const FString& BaseName, FNiagaraCompileHashVisitor* InVisitor) const { return FNiagaraEditorUtilities::NestedPropertiesAppendCompileHash(Container, Struct, IteratorFlags, BaseName, InVisitor); } bool UNiagaraNode::PODPropertyAppendCompileHash(const void* Container, FProperty* Property, const FString& PropertyName, FNiagaraCompileHashVisitor* InVisitor) const { return FNiagaraEditorUtilities::PODPropertyAppendCompileHash(Container, Property, PropertyName, InVisitor); } bool UNiagaraNode::GenerateCompileHashForClassMembers(const UClass* InClass, FNiagaraCompileHashVisitor* InVisitor) const { if (!NestedPropertiesAppendCompileHash(static_cast(this), InClass, EFieldIteratorFlags::ExcludeSuper, InClass->GetName(), InVisitor)) { return false; } return true; } bool UNiagaraNode::AppendCompileHash(FNiagaraCompileHashVisitor* InVisitor) const { if (NiagaraNodeAppendCompileHash(InVisitor)) { UClass* Class = GetClass(); UClass* NiagaraNodeClass = UNiagaraNode::StaticClass(); while (Class != NiagaraNodeClass && Class != nullptr) { if (!GenerateCompileHashForClassMembers(Class, InVisitor)) { return false; } Class = Class->GetSuperClass(); } return true; } return false; } bool UNiagaraNode::PinAppendCompileHash(const UEdGraphPin* InPin, FNiagaraCompileHashVisitor* InVisitor) const { #if WITH_EDITORONLY_DATA InVisitor->UpdateReference(TEXT("\tPinId"), InPin); InVisitor->UpdateString(TEXT("\tName"), InPin->GetName()); InVisitor->UpdatePOD(TEXT("\tDirection"), InPin->Direction); const UEdGraphSchema_Niagara* Schema = CastChecked(GetSchema()); check(Schema); FNiagaraTypeDefinition PinType = Schema->PinToTypeDefinition(InPin); PinType.AppendCompileHash(InVisitor); if (InPin->LinkedTo.Num() > 0) { for (int32 i = 0; i < InPin->LinkedTo.Num(); i++) { InVisitor->UpdateReference(TEXT("\tLinkedTo"), InPin->LinkedTo[i]); } } else { InVisitor->UpdateString(TEXT("\tDefaultValue"), InPin->DefaultValue); InVisitor->UpdateString(TEXT("\tAutogeneratedDefaultValue"), InPin->AutogeneratedDefaultValue); bool bDefaultObj = InPin->DefaultObject != nullptr; InVisitor->UpdatePOD(TEXT("\tDefaultObject"), bDefaultObj); if (InPin->DefaultObject) { UE_LOG(LogNiagaraEditor, Warning, TEXT("AppendCompileHash can't handle default objects properly, please investigate!")); } } for (int32 i = 0; i < InPin->SubPins.Num(); i++) { PinAppendCompileHash(InPin->SubPins[i], InVisitor); } if (InPin->ParentPin != nullptr) { InVisitor->UpdateReference(TEXT("\tParentPin"), InPin->ParentPin); } return true; #else return false; #endif } bool UNiagaraNode::NiagaraNodeAppendCompileHash(FNiagaraCompileHashVisitor* InVisitor) const { #if WITH_EDITORONLY_DATA UClass* Class = GetClass(); InVisitor->UpdateString(TEXT("Classname"), Class->GetName()); InVisitor->UpdateReference(TEXT("Node"), this); // DO NOT include the name in the hash as it makes copy/paste duplicates not work out b/c the names internally will be incremented by a digit. // The state and iteration occurrence should be enough to uniquely identify. //InVisitor->UpdateString(TEXT("NodeName"), GetName()); ENodeEnabledState NodeEnabledState = GetDesiredEnabledState(); InVisitor->UpdatePOD(TEXT("EnabledState"), NodeEnabledState); for (const UEdGraphPin* Pin : Pins) { if (!PinAppendCompileHash(Pin, InVisitor)) { return false; } } return true; #else return false; #endif } bool UNiagaraNode::SetPinDefaultToTypeDefaultIfUnset(UEdGraphPin* InPin) { //UE_LOG(LogNiagaraEditor, Warning, TEXT("SetPinDefaultToTypeDefaultIfUnset")); if (InPin->DefaultValue.Len() != 0) return true; const UEdGraphSchema_Niagara* Schema = GetDefault(); FNiagaraTypeDefinition NiagaraType = Schema->PinToTypeDefinition(InPin); bool bNeedsValue = NiagaraType.IsDataInterface() == false; FNiagaraVariable Var = Schema->PinToNiagaraVariable(InPin, bNeedsValue); FString PinDefaultValue; if (Schema->TryGetPinDefaultValueFromNiagaraVariable(Var, PinDefaultValue)) { InPin->DefaultValue = PinDefaultValue; InPin->AutogeneratedDefaultValue = PinDefaultValue; return true; } return bNeedsValue; } bool UNiagaraNode::ReallocatePins(bool bMarkNeedsResynchronizeOnChange) { Modify(); // Move the existing pins to a saved array TArray OldPins(Pins); Pins.Reset(); // Recreate the new pins AllocateDefaultPins(); // Determine if the pins are the same as they were previously... bool bAllSame = OldPins.Num() == Pins.Num(); const UEdGraphSchema_Niagara* Schema = CastChecked(GetSchema()); // Copy the old pin data and remove it. for (int32 OldPinIndex = 0; OldPinIndex < OldPins.Num(); ++OldPinIndex) { UEdGraphPin* OldPin = OldPins[OldPinIndex]; // When matching pins, use the pin id if either pin id is valid, otherwise match by name. // we typically don't allow orphaned pins to find a match, unless types match each other again auto PinMatchPredicateGuid = [&](UEdGraphPin* Pin) { // early out conditions for when matching up is not wanted if (Pin->Direction != OldPin->Direction) { return false; } bool bPinsHaveIds = Pin->PersistentGuid.IsValid() || OldPin->PersistentGuid.IsValid(); bool bAllowPin = OldPin->bOrphanedPin == false || OldPin->PinType == Pin->PinType; return bAllowPin && ((bPinsHaveIds && Pin->PersistentGuid == OldPin->PersistentGuid) || (bPinsHaveIds == false && Pin->PinName == OldPin->PinName)); }; auto PinMatchPredicateName = [&](UEdGraphPin* Pin) { // early out conditions for when matching up is not wanted if (Pin->Direction != OldPin->Direction || Pin->PinName != OldPin->PinName) { return false; } if (OldPin->PinType == Pin->PinType) { return true; } // Allow numeric pins and wildcard pins to match any type. FNiagaraTypeDefinition NewType = Schema->PinTypeToTypeDefinition(Pin->PinType); return NewType == FNiagaraTypeDefinition::GetGenericNumericDef() || NewType == FNiagaraTypeDefinition::GetWildcardDef(); }; OldPin->Modify(); UEdGraphPin** MatchingNewPin = Pins.FindByPredicate(PinMatchPredicateGuid); if (MatchingNewPin == nullptr) { MatchingNewPin = Pins.FindByPredicate(PinMatchPredicateName); } if (MatchingNewPin) { // If the pin types don't match, CopyPersistentDataFromOldPin could very well overwrite our Matching pin with bad data. // Let's cache it for now and reset it after copying the other relevant data off the old pin. FString DefaultValue; FString AutogeneratedDefaultValue; class UObject* DefaultObject = nullptr; FText DefaultTextValue; bool bTypeMismatch = (*MatchingNewPin)->PinType != OldPin->PinType; FNiagaraTypeDefinition OldPinNiagaraType = Schema->PinToTypeDefinition(OldPin); FNiagaraTypeDefinition NewPinNiagaraType = Schema->PinToTypeDefinition(*MatchingNewPin); bool bRetainOldTypeDueToNumerics = OldPinNiagaraType != FNiagaraTypeDefinition::GetGenericNumericDef() && NewPinNiagaraType == FNiagaraTypeDefinition::GetGenericNumericDef(); if (bTypeMismatch && !bRetainOldTypeDueToNumerics) { DefaultValue = (*MatchingNewPin)->DefaultValue; DefaultObject = (*MatchingNewPin)->DefaultObject; DefaultTextValue = (*MatchingNewPin)->DefaultTextValue; } // This copies the existing default values, pin linkages, advanced pin view, pin splitting, etc. (*MatchingNewPin)->MovePersistentDataFromOldPin(*OldPin); // Somehow this pin was considered orphaned before, but now exists, so it is orphaned no longer. if (OldPin->bOrphanedPin) { OldPin->bOrphanedPin = false; UE_LOG(LogNiagaraEditor, Log, TEXT("Pin \"%s\" in node \"%s\" was orphaned, but is now matched. De-orphaning."), *OldPin->GetName(), *GetFullName()); } // The prior call would have clobbered our default values, which causes a crash down the line when we attempt to compile. // This resets to the default values prior to copying over the persistent data. // @TODO Make this try to preserve as much of the old default values as possible. // @TODO Should we push this up to CopyPersistentDataFromOldPin globally? if (bTypeMismatch && !bRetainOldTypeDueToNumerics) { (*MatchingNewPin)->DefaultValue = DefaultValue; (*MatchingNewPin)->DefaultObject = DefaultObject; (*MatchingNewPin)->DefaultTextValue = DefaultTextValue; bAllSame = false; } else if (bTypeMismatch && bRetainOldTypeDueToNumerics) { (*MatchingNewPin)->PinType = OldPin->PinType; } } else { bAllSame = false; // if the pin wasn't already marked orphaned before (from type conversions etc.), check if we need to mark it as such if(OldPin->bOrphanedPin == false) { if ((OldPin->bDefaultValueIsIgnored == false && OldPin->DefaultValue != OldPin->AutogeneratedDefaultValue) || OldPin->LinkedTo.Num() > 0) { // If an old pin is not reused mark it as orphaned so that it's value and connections aren't lost. OldPin->bOrphanedPin = true; } } } } for (UEdGraphPin* OldPin : OldPins) { // we exclude pins from being added as orphaned pins if they were set to pending kill already if (OldPin->bOrphanedPin && !OldPin->IsPendingKill()) { // Add orphaned pins back in at the end so that the user can fix them. Pins.Add(OldPin); } else { OldPin->MarkAsGarbage(); } } OnPostSynchronizationInReallocatePins(); //GetGraph()->NotifyGraphChanged(); if (bMarkNeedsResynchronizeOnChange && !bAllSame) { MarkNodeRequiresSynchronization(__FUNCTION__, true); } else { // Even if we're not marking the graph as needing sychronization we still need to let other listeners, // such as the UI, know that the graph has changed. GetNiagaraGraph()->NotifyGraphNeedsRecompile(); VisualsChangedDelegate.Broadcast(this); } return bAllSame; } int32 UNiagaraNode::CompileInputPin(FTranslator* Translator, UEdGraphPin* Pin) const { return Translator->CompileInputPin(Pin); } bool UNiagaraNode::IsValidPinToCompile(UEdGraphPin* Pin) const { return Pin->bOrphanedPin == false; } bool UNiagaraNode::CompileInputPins(FTranslator* Translator, TArray& OutCompiledInputs) const { bool bError = false; FPinCollectorArray InputPins; GetInputPins(InputPins); for (int32 i = 0; i < InputPins.Num(); ++i) { UEdGraphPin* Pin = InputPins[i]; if (!IsValidPinToCompile(Pin)) { OutCompiledInputs.Add(INDEX_NONE); continue; } check(Pin->Direction == EGPD_Input); int32 Result = CompileInputPin(Translator, Pin); if (Result == INDEX_NONE) { bError = true; Translator->Error(FText::Format(LOCTEXT("CompileInputPinErrorFormat", "Error compiling Pin"), Pin->PinFriendlyName), this, Pin); } OutCompiledInputs.Add(Result); } return bError; } void UNiagaraNode::RequestNewPinType(UEdGraphPin* PinToChange, FNiagaraTypeDefinition NewType) { FScopedTransaction Transaction(LOCTEXT("ChangePin", "Changed Pin Type")); Modify(); PinToChange->Modify(); if (OnNewPinTypeRequested(PinToChange, NewType)) { PinTypeChanged(PinToChange); } else { Transaction.Cancel(); } } void UNiagaraNode::PostPlacedNewNode() { if (ChangeId.IsValid() == false) { MarkNodeRequiresSynchronization(__FUNCTION__, false); // The add will have notified us anyway } } void UNiagaraNode::AutowireNewNode(UEdGraphPin* FromPin) { Super::AutowireNewNode(FromPin); if (FromPin != nullptr) { const UEdGraphSchema_Niagara* Schema = CastChecked(GetSchema()); check(Schema); //ENiagaraCompoundType FromType = Schema->GetPinDataType(FromPin); //Find first of this nodes pins with the right type and direction. UEdGraphPin* FirstPinOfSameType = NULL; EEdGraphPinDirection DesiredDirection = FromPin->Direction == EGPD_Output ? EGPD_Input : EGPD_Output; for (UEdGraphPin* Pin : Pins) { //ENiagaraCompoundType ToType = Schema->GetPinDataType(Pin); if (Pin->Direction == DesiredDirection) { const FPinConnectionResponse Response = Schema->CanCreateConnection(FromPin, Pin); if (Response.Response != ECanCreateConnectionResponse::CONNECT_RESPONSE_DISALLOW) //-V1051 { FirstPinOfSameType = Pin; break; } } } if (FirstPinOfSameType && GetSchema()->TryCreateConnection(FromPin, FirstPinOfSameType)) { FromPin->GetOwningNode()->NodeConnectionListChanged(); } } } bool UNiagaraNode::ConvertNumericPinToType(UEdGraphPin* InGraphPin, FNiagaraTypeDefinition TypeDef) { int32 PinIndex = GetPinIndex(InGraphPin); if (PinIndex == -1) { return false; } const UEdGraphSchema_Niagara* Schema = CastChecked(GetSchema()); check(Schema); FEdGraphPinType PinType = Schema->TypeDefinitionToPinType(TypeDef); if (!Schema->PinTypesValidForNumericConversion(PinType, InGraphPin->PinType)) { return false; } InGraphPin->Modify(); InGraphPin->PinType = PinType; InGraphPin->ResetDefaultValue(); PinTypeChanged(InGraphPin); ReallocatePins(); return true; } bool UNiagaraNode::GetIsPinRenamePending(const UEdGraphPin* Pin) { return PinsGuidsWithRenamePending.Contains(Pin->PersistentGuid); } void UNiagaraNode::SetIsPinRenamePending(const UEdGraphPin* Pin, bool bInIsRenamePending) { if (bInIsRenamePending) { PinsGuidsWithRenamePending.AddUnique(Pin->PersistentGuid); } else { PinsGuidsWithRenamePending.Remove(Pin->PersistentGuid); } } bool UNiagaraNode::IsParameterMapPin(const UEdGraphPin* Pin) const { return UEdGraphSchema_Niagara::PinToTypeDefinition(Pin) == FNiagaraTypeDefinition::GetParameterMapDef(); } TSharedPtr UNiagaraNode::CreateVisualWidget() { return SNew(SNiagaraGraphNode, this); } void UNiagaraNode::GetPinHoverText(const UEdGraphPin& Pin, FString& HoverTextOut) const { FText Text; if (GetTooltipTextForKnownPin(Pin, Text)) { HoverTextOut = Text.ToString(); return; } const UNiagaraGraph* NiagaraGraph = Cast(GetGraph()); if (NiagaraGraph) { const UEdGraphSchema_Niagara* Schema = Cast(NiagaraGraph->GetSchema()); if (Schema) { FNiagaraTypeDefinition TypeDef = Schema->PinToTypeDefinition(&Pin); if (Pin.PinToolTip.IsEmpty()) { Text = FText::Format(LOCTEXT("PinHoverTooltip", "Name: \"{0}\"\nType: {1}"), FText::FromName(Pin.PinName), TypeDef.GetNameText()); } else { Text = FText::Format(LOCTEXT("PinHoverTooltipFromPin", "{0}\n\nType: {1}"), FText::FromString(Pin.PinToolTip), TypeDef.GetNameText()); } HoverTextOut = Text.ToString(); } } } void UNiagaraNode::AddWidgetsToInputBox(TSharedPtr InputBox) { } void UNiagaraNode::AddWidgetsToOutputBox(TSharedPtr OutputBox) { } void UNiagaraNode::GetNodeContextMenuActions(class UToolMenu* Menu, class UGraphNodeContextMenuContext* Context) const { { FToolMenuSection& Section = Menu->AddSection("Alignment"); Section.AddSubMenu( "Alignment", LOCTEXT("AlignmentHeader", "Alignment"), FText(), FNewToolMenuDelegate::CreateLambda([](UToolMenu* InMenu) { { FToolMenuSection& SubMenuSection = InMenu->AddSection("EdGraphSchemaAlignment", LOCTEXT("AlignHeader", "Align")); SubMenuSection.AddMenuEntry(FGraphEditorCommands::Get().AlignNodesTop); SubMenuSection.AddMenuEntry(FGraphEditorCommands::Get().AlignNodesMiddle); SubMenuSection.AddMenuEntry(FGraphEditorCommands::Get().AlignNodesBottom); SubMenuSection.AddMenuEntry(FGraphEditorCommands::Get().AlignNodesLeft); SubMenuSection.AddMenuEntry(FGraphEditorCommands::Get().AlignNodesCenter); SubMenuSection.AddMenuEntry(FGraphEditorCommands::Get().AlignNodesRight); SubMenuSection.AddMenuEntry(FGraphEditorCommands::Get().StraightenConnections); } { FToolMenuSection& SubMenuSection = InMenu->AddSection("EdGraphSchemaDistribution", LOCTEXT("DistributionHeader", "Distribution")); SubMenuSection.AddMenuEntry(FGraphEditorCommands::Get().DistributeNodesHorizontally); SubMenuSection.AddMenuEntry(FGraphEditorCommands::Get().DistributeNodesVertically); } })); } } bool UNiagaraNode::CanCreateUnderSpecifiedSchema(const UEdGraphSchema* Schema) const { return Schema->IsA(); } TSharedRef UNiagaraNode::CreateTitleRightWidget() { return SNullWidget::NullWidget; } void UNiagaraNode::MarkNodeRequiresSynchronization(FString Reason, bool bRaiseGraphNeedsRecompile) { Modify(); ChangeId = FGuid::NewGuid(); //UE_LOG(LogNiagaraEditor, Verbose, TEXT("Node %s was marked requires synchronization. Reason: %s"), *GetPathName(), *Reason); if (bRaiseGraphNeedsRecompile) { if (UNiagaraGraph* Graph = Cast(GetGraph())) { Graph->NotifyGraphNeedsRecompile(); } } VisualsChangedDelegate.Broadcast(this); } void UNiagaraNode::ForceChangeId(const FGuid& InId, bool bRaiseGraphNeedsRecompile) { Modify(); ChangeId = InId; if (bRaiseGraphNeedsRecompile) { UNiagaraGraph* Graph = GetNiagaraGraph(); Graph->NotifyGraphNeedsRecompile(); } } void UNiagaraNode::PinDefaultValueChanged(UEdGraphPin* Pin) { if (Pin->bOrphanedPin && Pin->DefaultValue == Pin->AutogeneratedDefaultValue) { RemovePin(Pin); } MarkNodeRequiresSynchronization(__FUNCTION__, true); Super::PinDefaultValueChanged(Pin); } void UNiagaraNode::OnRenameNode(const FString& NewName) { MarkNodeRequiresSynchronization(__FUNCTION__, true); Super::OnRenameNode(NewName); } void UNiagaraNode::OnPinRemoved(UEdGraphPin* InRemovedPin) { MarkNodeRequiresSynchronization(__FUNCTION__, true); Super::OnPinRemoved(InRemovedPin); } void UNiagaraNode::NodeConnectionListChanged() { // Cache the pins so that we're not modifying the collection while iterating. TArray CurrentPins = Pins; for (UEdGraphPin* Pin : CurrentPins) { if (Pin->bOrphanedPin && Pin->LinkedTo.Num() == 0 && Pin->DefaultValue == Pin->AutogeneratedDefaultValue) { RemovePin(Pin); } } MarkNodeRequiresSynchronization(__FUNCTION__, true); Super::NodeConnectionListChanged(); } void UNiagaraNode::PinConnectionListChanged(UEdGraphPin* Pin) { if (Pin->bOrphanedPin && Pin->LinkedTo.Num() == 0) { RemovePin(Pin); } MarkNodeRequiresSynchronization(__FUNCTION__, true); Super::PinConnectionListChanged(Pin); } void UNiagaraNode::PinTypeChanged(UEdGraphPin* Pin) { MarkNodeRequiresSynchronization(__FUNCTION__, true); } const UNiagaraGraph* UNiagaraNode::GetNiagaraGraph()const { return CastChecked(GetGraph()); } UNiagaraGraph* UNiagaraNode::GetNiagaraGraph() { return CastChecked(GetGraph()); } UNiagaraScriptSource* UNiagaraNode::GetSource()const { return GetNiagaraGraph()->GetSource(); } void UNiagaraNode::Compile(FTranslator* Translator, TArray& Outputs) const { Translator->Error(FText::FromString("Unimplemented Node!"), this, nullptr); } UEdGraphPin* UNiagaraNode::GetInputPin(int32 InputIndex) const { for (int32 PinIndex = 0, FoundInputs = 0; PinIndex < Pins.Num(); PinIndex++) { if (Pins[PinIndex]->Direction == EGPD_Input) { if (InputIndex == FoundInputs) { return Pins[PinIndex]; } else { FoundInputs++; } } } return NULL; } bool UNiagaraNode::CanAddToGraph(UNiagaraGraph* TargetGraph, FString& OutErrorMsg) const { if (TargetGraph == nullptr) { OutErrorMsg = LOCTEXT("NiagaraNodeInvalidGraph", "Target Graph is invalid.").ToString(); return false; } return true; } void UNiagaraNode::BuildParameterMapHistory(FNiagaraParameterMapHistoryBuilder& OutHistory, bool bRecursive /*= true*/, bool bFilterForCompilation /*= true*/) const { if (bRecursive) { OutHistory.VisitInputPins(this, bFilterForCompilation); } } UEdGraphPin* UNiagaraNode::GetOutputPin(int32 OutputIndex) const { for (int32 PinIndex = 0, FoundOutputs = 0; PinIndex < Pins.Num(); PinIndex++) { if (Pins[PinIndex]->Direction == EGPD_Output) { if (OutputIndex == FoundOutputs) { return Pins[PinIndex]; } else { FoundOutputs++; } } } return NULL; } UEdGraphPin* UNiagaraNode::GetPinByPersistentGuid(const FGuid& InPersistentGuid) const { for (UEdGraphPin* Pin : Pins) { if (InPersistentGuid == Pin->PersistentGuid) { return Pin; } } return nullptr; } void UNiagaraNode::NumericResolutionByPins(const UEdGraphSchema_Niagara* Schema, TArrayView InputPins, TArrayView OutputPins, bool bFixInline, TMap, FNiagaraTypeDefinition>* PinCache) { TArray InputTypes; TArray OutputTypes; TArray NonNumericInputs; for (UEdGraphPin* InputPin : InputPins) { FNiagaraTypeDefinition InputPinType = Schema->PinToTypeDefinition(InputPin); if (InputPin->PinType.PinCategory == UEdGraphSchema_Niagara::PinCategoryType || InputPin->PinType.PinCategory == UEdGraphSchema_Niagara::PinCategoryStaticType) { // If the input pin is the generic numeric type set it to the type of the linked output pin which should have been processed already. if (InputPinType == FNiagaraTypeDefinition::GetGenericNumericDef() && InputPin->LinkedTo.Num() == 1) { UEdGraphPin* InputPinLinkedPin = InputPin->LinkedTo[0]; FNiagaraTypeDefinition InputPinLinkedPinType = Schema->PinToTypeDefinition(InputPinLinkedPin); if (InputPinLinkedPinType.IsValid()) { if (InputPinLinkedPinType == FNiagaraTypeDefinition::GetGenericNumericDef() && PinCache) { FNiagaraTypeDefinition* FoundDef = PinCache->Find(TPair(InputPinLinkedPin->PinId, InputPinLinkedPin->GetOwningNode())); if (FoundDef && FoundDef->IsValid()) { InputPinLinkedPinType = *FoundDef; } } // Only update the input pin type if the linked pin type is valid. FEdGraphPinType PinType = Schema->TypeDefinitionToPinType(InputPinLinkedPinType); if (bFixInline) { InputPin->PinType = PinType; } InputPinType = InputPinLinkedPinType; } } if (InputPinType != FNiagaraTypeDefinition::GetGenericNumericDef()) { NonNumericInputs.Add(InputPinType); } } InputTypes.Add(InputPinType); } // Fix up numeric outputs based on the inputs. for (UEdGraphPin* OutputPin : OutputPins) { FNiagaraTypeDefinition OutputPinType = Schema->PinToTypeDefinition(OutputPin); ENiagaraNumericOutputTypeSelectionMode NumericMode = GetNumericOutputTypeSelectionMode(); if (NonNumericInputs.Num() > 0 && NumericMode != ENiagaraNumericOutputTypeSelectionMode::None) { FNiagaraTypeDefinition OutputNumericType; if (NumericMode == ENiagaraNumericOutputTypeSelectionMode::Custom) { OutputNumericType = ResolveCustomNumericType(NonNumericInputs); } else { OutputNumericType = FNiagaraTypeDefinition::GetNumericOutputType(NonNumericInputs, NumericMode); } if (OutputNumericType != FNiagaraTypeDefinition::GetGenericNumericDef()) { if (OutputPinType == FNiagaraTypeDefinition::GetGenericNumericDef()) { FEdGraphPinType PinType = Schema->TypeDefinitionToPinType(OutputNumericType); if (bFixInline) { OutputPin->PinType = PinType; } OutputPinType = OutputNumericType; } } } OutputTypes.Add(OutputPinType); } if (PinCache) { int32 j; for (j = 0; j < InputPins.Num(); j++) { PinCache->Add(TPair(InputPins[j]->PinId, InputPins[j]->GetOwningNode()), InputTypes[j]); } for (j = 0; j < OutputPins.Num(); j++) { PinCache->Add(TPair(OutputPins[j]->PinId, OutputPins[j]->GetOwningNode()), OutputTypes[j]); } } } FNiagaraTypeDefinition UNiagaraNode::ResolveCustomNumericType(const TArray&) const { checkf(false, TEXT("Not implemented for node type")); return FNiagaraTypeDefinition::GetFloatDef(); } void UNiagaraNode::ResolveNumerics(const UEdGraphSchema_Niagara* Schema, bool bSetInline, TMap, FNiagaraTypeDefinition>* PinCache) { // Fix up numeric input pins and keep track of numeric types to decide the output type. FPinCollectorArray InputPins; GetInputPins(InputPins); FPinCollectorArray OutputPins; GetOutputPins(OutputPins); NumericResolutionByPins(Schema, InputPins, OutputPins, bSetInline, PinCache); } void UNiagaraNode::GetWildcardPinHoverConnectionTextAddition(const UEdGraphPin* WildcardPin, const UEdGraphPin* OtherPin, ECanCreateConnectionResponse ConnectionResponse, FString& OutString) const { // OutString can be modified depending on use case and connection response } ENiagaraNumericOutputTypeSelectionMode UNiagaraNode::GetNumericOutputTypeSelectionMode() const { return ENiagaraNumericOutputTypeSelectionMode::None; } UEdGraphPin* UNiagaraNode::TraceOutputPin(UEdGraphPin* LocallyOwnedOutputPin, bool bFilterForCompilation, TArray* OutNodesVisitedDuringTrace) { if (LocallyOwnedOutputPin == nullptr) { return nullptr; } UNiagaraNode* LinkedNode = CastChecked(LocallyOwnedOutputPin->GetOwningNode()); return LinkedNode->GetTracedOutputPin(LocallyOwnedOutputPin, bFilterForCompilation, OutNodesVisitedDuringTrace); } UEdGraphPin* UNiagaraNode::GetTracedOutputPin(UEdGraphPin* LocallyOwnedOutputPin, bool bFilterForCompilation, TArray* OutNodesVisitedDuringTrace) const { if (OutNodesVisitedDuringTrace != nullptr) { OutNodesVisitedDuringTrace->Add(this); } return LocallyOwnedOutputPin; } bool UNiagaraNode::SubstituteCompiledPin(FTranslator* Translator, UEdGraphPin** LocallyOwnedPin) { return false; } void UNiagaraNode::RouteParameterMapAroundMe(FNiagaraParameterMapHistoryBuilder& OutHistory, bool bRecursive) const { const UEdGraphSchema_Niagara* Schema = CastChecked(GetSchema()); FPinCollectorArray InputPins; GetInputPins(InputPins); FPinCollectorArray OutputPins; GetOutputPins(OutputPins); const UEdGraphPin* InputPin = nullptr; const UEdGraphPin* OutputPin = nullptr; for (const UEdGraphPin* Pin : InputPins) { if (Schema->PinToTypeDefinition(Pin) == FNiagaraTypeDefinition::GetParameterMapDef()) { InputPin = Pin; break; } } for (const UEdGraphPin* Pin : OutputPins) { if (Schema->PinToTypeDefinition(Pin) == FNiagaraTypeDefinition::GetParameterMapDef()) { OutputPin = Pin; break; } } if (InputPin != nullptr && OutputPin != nullptr && InputPin->LinkedTo.Num() != 0) { int32 PMIdx = OutHistory.TraceParameterMapOutputPin(InputPin->LinkedTo[0]); OutHistory.RegisterParameterMapPin(PMIdx, OutputPin); } } void UNiagaraNode::RegisterPassthroughPin(FNiagaraParameterMapHistoryBuilder& OutHistory, UEdGraphPin* InputPin, UEdGraphPin* OutputPin, bool bFilterForCompilation, bool bVisitInputPin) const { const UEdGraphSchema_Niagara* Schema = CastChecked(GetSchema()); if (bVisitInputPin) OutHistory.VisitInputPin(InputPin, bFilterForCompilation); FNiagaraTypeDefinition InDef = Schema->PinToTypeDefinition(InputPin); FNiagaraTypeDefinition OutDef = Schema->PinToTypeDefinition(OutputPin); if (InputPin && InDef == FNiagaraTypeDefinition::GetParameterMapDef() && OutDef == FNiagaraTypeDefinition::GetParameterMapDef() && InputPin->LinkedTo.Num() > 0) { int32 PMIdx = OutHistory.TraceParameterMapOutputPin(InputPin->LinkedTo[0]); OutHistory.RegisterParameterMapPin(PMIdx, OutputPin); } else if (InputPin && InDef.IsStatic() && InputPin->LinkedTo.Num() > 0) { int32 ConstantIdx = OutHistory.GetConstantFromOutputPin(InputPin->LinkedTo[0]); OutHistory.RegisterConstantPin(ConstantIdx, InputPin); if (OutputPin && OutDef == InDef) { OutHistory.RegisterConstantPin(ConstantIdx, OutputPin); } } else if (InputPin && InDef.IsStatic() && InputPin->LinkedTo.Num() == 0) { int32 ConstantIdx = OutHistory.AddOrGetConstantFromValue(InputPin->DefaultValue); OutHistory.RegisterConstantPin(ConstantIdx, InputPin); if (OutputPin && OutDef == InDef) { OutHistory.RegisterConstantPin(ConstantIdx, OutputPin); } } } bool UNiagaraNode::GetTooltipTextForKnownPin(const UEdGraphPin& Pin, FText& OutTooltip) const { const UNiagaraGraph* NiagaraGraph = Cast(GetGraph()); const UEdGraphSchema_Niagara* Schema = Cast(NiagaraGraph->GetSchema()); if (Schema) { FNiagaraTypeDefinition TypeDef = Schema->PinToTypeDefinition(&Pin); FNiagaraVariable Var(TypeDef, Pin.PinName); // check for known engine constants if (const FNiagaraVariableMetaData* VariableMetaData = FNiagaraConstants::GetConstantMetaData(Var)) { FText Text = VariableMetaData->Description; if (Text.IsEmptyOrWhitespace() == false) { OutTooltip = FText::Format(LOCTEXT("KnownPinHoverTooltip", "{0}\nType: {1}"), Text, TypeDef.GetNameText()); return true; } } } return false; } UNiagaraNode::FOnNodeVisualsChanged& UNiagaraNode::OnVisualsChanged() { return VisualsChangedDelegate; } void UNiagaraNode::UpdateCompileHashForNode(FSHA1& HashState) const { HashState.Update((const uint8*)&ChangeId, sizeof(FGuid)); } #if WITH_EDITORONLY_DATA void UNiagaraNode::GatherForLocalization(FPropertyLocalizationDataGatherer& PropertyLocalizationDataGatherer, const EPropertyLocalizationGathererTextFlags GatherTextFlags) const { // Niagara nodes only contain editor-only text data Super::GatherForLocalization(PropertyLocalizationDataGatherer, GatherTextFlags | EPropertyLocalizationGathererTextFlags::ForceEditorOnly); } #endif void UNiagaraNode::GetCompilationInputPins(FPinCollectorArray& InputPins) const { for (UEdGraphPin* Pin : Pins) { if (Pin->Direction == EGPD_Input && !Pin->bOrphanedPin) { InputPins.Add(Pin); } } } void UNiagaraNode::GetCompilationOutputPins(FPinCollectorArray& OutputPins) const { for (UEdGraphPin* Pin : Pins) { if (Pin->Direction == EGPD_Output && !Pin->bOrphanedPin) { OutputPins.Add(Pin); } } } #undef LOCTEXT_NAMESPACE