// Copyright Epic Games, Inc. All Rights Reserved. #include "RigVMModel/Nodes/RigVMTemplateNode.h" #include "RigVMCore/RigVMTemplate.h" #include "RigVMModel/RigVMController.h" URigVMTemplateNode::URigVMTemplateNode() : Super() , TemplateNotation(NAME_None) , ResolvedFunctionName() , CachedTemplate(nullptr) , CachedFunction(nullptr) { } void URigVMTemplateNode::PostLoad() { Super::PostLoad(); // if there are brackets in the notation remove them const FString OriginalNotation = TemplateNotation.ToString(); const FString SanitizedNotation = OriginalNotation.Replace(TEXT("[]"), TEXT("")); if(OriginalNotation != SanitizedNotation) { TemplateNotation = *SanitizedNotation; } InvalidateCache(); } UScriptStruct* URigVMTemplateNode::GetScriptStruct() const { if(const FRigVMFunction* Function = GetResolvedFunction()) { return Function->Struct; } return nullptr; } FString URigVMTemplateNode::GetNodeTitle() const { if(!IsResolved()) { if(const FRigVMTemplate* Template = GetTemplate()) { return Template->GetName().ToString(); } } FString ResolvedNodeTitle = Super::GetNodeTitle(); const int32 BracePos = ResolvedNodeTitle.Find(TEXT(" (")); if(BracePos != INDEX_NONE) { ResolvedNodeTitle = ResolvedNodeTitle.Left(BracePos); } return ResolvedNodeTitle; } FName URigVMTemplateNode::GetMethodName() const { if(const FRigVMFunction* Function = GetResolvedFunction()) { return Function->GetMethodName(); } return NAME_None; } FText URigVMTemplateNode::GetToolTipText() const { if(const FRigVMTemplate* Template = GetTemplate()) { const TArray PermutationIndices = GetFilteredPermutationsIndices(); return Template->GetTooltipText(PermutationIndices); } return Super::GetToolTipText(); } FText URigVMTemplateNode::GetToolTipTextForPin(const URigVMPin* InPin) const { const FText SuperToolTip = Super::GetToolTipTextForPin(InPin); if (const FRigVMTemplate* Template = GetTemplate()) { const URigVMPin* RootPin = InPin->GetRootPin(); if (RootPin->IsWildCard()) { if (const FRigVMTemplateArgument* Arg = Template->FindArgument(RootPin->GetFName())) { FString Tooltip; const TArray PermutationIndices = GetFilteredPermutationsIndices(); if(PermutationIndices.Num() == GetTemplate()->NumPermutations()) { if(Arg->GetTypes().Num() > 100) { Tooltip = TEXT("Supports any type."); } } if(Tooltip.IsEmpty()) { FString SupportedTypesJoined; for (int32 Index : PermutationIndices) { FString Type = Arg->GetTypes()[Index].CPPType; if (!FilteredPermutations.Contains(Index)) { Type += TEXT(" : Breaks Connections"); } SupportedTypesJoined += Type + TEXT("\n"); } Tooltip = TEXT("Supported Types:\n\n") + SupportedTypesJoined; } if(!SuperToolTip.IsEmpty()) { Tooltip += TEXT("\n\n") + SuperToolTip.ToString(); } return FText::FromString(Tooltip); } } } return SuperToolTip; } FName URigVMTemplateNode::GetNotation() const { return TemplateNotation; } bool URigVMTemplateNode::IsSingleton() const { return GetTemplate() == nullptr; } bool URigVMTemplateNode::SupportsType(const URigVMPin* InPin, const FString& InCPPType, FString* OutCPPType) { static const FString WildCardCPPType = RigVMTypeUtils::GetWildCardCPPType(); static const FString WildCardArrayCPPType = RigVMTypeUtils::ArrayTypeFromBaseType(WildCardCPPType); const URigVMPin* RootPin = InPin->GetRootPin(); FString CPPType = InCPPType; if(InPin->GetParentPin() == RootPin && RootPin->IsArray()) { CPPType = RigVMTypeUtils::ArrayTypeFromBaseType(CPPType); } // we always support the unknown type if((InCPPType == WildCardCPPType) || (InCPPType == WildCardArrayCPPType)) { if(const FRigVMTemplate* Template = GetTemplate()) { if(const FRigVMTemplateArgument* Argument = Template->FindArgument(RootPin->GetFName())) { // support this only on non-singleton arguments if(Argument->IsSingleton()) { return false; } if(RigVMTypeUtils::IsArrayType(CPPType)) { if(Argument->GetArrayType() == FRigVMTemplateArgument::EArrayType_SingleValue) { return false; } } else { if(Argument->GetArrayType() == FRigVMTemplateArgument::EArrayType_ArrayValue) { return false; } } if(OutCPPType) { *OutCPPType = InCPPType; } return true; } } return false; } if (const FRigVMTemplate* Template = GetTemplate()) { const FString CacheKey = RootPin->GetName() + TEXT("|") + CPPType; if (const TPair* CachedResult = SupportedTypesCache.Find(CacheKey)) { if(OutCPPType) { *OutCPPType = CachedResult->Value.CPPType; } return CachedResult->Key; } FRigVMTemplateArgument::FType OutType; if (Template->ArgumentSupportsType(RootPin->GetFName(), CPPType, &OutType)) { if (OutCPPType) { (*OutCPPType) = OutType.CPPType; } return true; } return false; } if(RootPin->GetCPPType() == CPPType) { if(OutCPPType) { *OutCPPType = CPPType; } return true; } return false; } bool URigVMTemplateNode::FilteredSupportsType(const URigVMPin* InPin, const FString& InCPPType, FString* OutCPPType, bool bAllowFloatingPointCasts) { if (OutCPPType) { *OutCPPType = FString(); } const URigVMPin* RootPin = InPin; bool bIsArrayElement = false; bool bIsStructElement = false; if (URigVMPin* ParentPin = InPin->GetParentPin()) { RootPin = ParentPin; if (ParentPin->IsArray()) { bIsArrayElement = true; } else if (ParentPin->IsStruct()) { bIsStructElement = true; } } if (bIsStructElement) { return InPin->GetCPPType() == InCPPType; } const FRigVMTemplateArgument* Argument = GetTemplate()->FindArgument(RootPin->GetFName()); if (Argument == nullptr) { return false; } FString RootCPPType = InCPPType; if (bIsArrayElement) { RootCPPType = RigVMTypeUtils::ArrayTypeFromBaseType(InCPPType); } if (FilteredPermutations.Num() == GetTemplate()->NumPermutations()) { return GetTemplate()->ArgumentSupportsType(RootPin->GetFName(), InCPPType); } const TArray& Types = Argument->GetTypes(); for (int32 PermutationIndex : FilteredPermutations) { const FRigVMTemplateArgument::FType& FilteredType = Types[PermutationIndex]; if (FilteredType.Matches(RootCPPType, bAllowFloatingPointCasts)) { return true; } } return false; } TArray URigVMTemplateNode::GetResolvedPermutations() const { TArray Indices = GetFilteredPermutationsIndices(); TArray Functions; for(const int32 Index : Indices) { Functions.Add(GetTemplate()->GetPermutation(Index)); } return Functions; } const FRigVMTemplate* URigVMTemplateNode::GetTemplate() const { if(CachedTemplate == nullptr) { CachedTemplate = FRigVMRegistry::Get().FindTemplate(GetNotation()); } return CachedTemplate; } const FRigVMFunction* URigVMTemplateNode::GetResolvedFunction() const { if(CachedFunction == nullptr) { if(!ResolvedFunctionName.IsEmpty()) { CachedFunction = FRigVMRegistry::Get().FindFunction(*ResolvedFunctionName); } if(CachedFunction == nullptr) { TArray PermutationIndices = GetFilteredPermutationsIndices(); if(PermutationIndices.Num() == 1) { CachedFunction = GetTemplate()->GetPermutation(PermutationIndices[0]); } } } return CachedFunction; } bool URigVMTemplateNode::IsResolved() const { return GetScriptStruct() != nullptr; } bool URigVMTemplateNode::IsFullyUnresolved() const { check(GetTemplate()); // all permutations are available means we haven't resolved any wildcard pin return GetFilteredPermutationsIndices().Num() == GetTemplate()->NumPermutations(); } FString URigVMTemplateNode::GetInitialDefaultValueForPin(const FName& InRootPinName, const TArray& InPermutationIndices) const { if(GetTemplate() == nullptr) { return FString(); } TArray PermutationIndices = InPermutationIndices; if(PermutationIndices.IsEmpty()) { PermutationIndices = GetFilteredPermutationsIndices(); } FString DefaultValue; for(const int32 PermutationIndex : PermutationIndices) { FString NewDefaultValue; if(const FRigVMFunction* Permutation = GetTemplate()->GetPermutation(PermutationIndex)) { const TSharedPtr StructOnScope = MakeShareable(new FStructOnScope(Permutation->Struct)); const FRigVMStruct* DefaultStruct = (const FRigVMStruct*)StructOnScope->GetStructMemory(); NewDefaultValue = DefaultStruct->ExportToFullyQualifiedText( Cast(StructOnScope->GetStruct()), InRootPinName); } else { const FRigVMTemplate* Template = GetTemplate(); const FRigVMTemplateArgument* Argument = Template->FindArgument(InRootPinName); const FRigVMTemplateArgument::FType Type = Argument->GetTypes()[PermutationIndex]; if (Type.IsArray()) { NewDefaultValue = TEXT("()"); } else { if (UScriptStruct* ScriptStruct = Cast(Type.CPPTypeObject)) { TArray> TempBuffer; TempBuffer.AddUninitialized(ScriptStruct->GetStructureSize()); // call the struct constructor to initialize the struct ScriptStruct->InitializeDefaultValue(TempBuffer.GetData()); ScriptStruct->ExportText(NewDefaultValue, TempBuffer.GetData(), nullptr, nullptr, PPF_None, nullptr); ScriptStruct->DestroyStruct(TempBuffer.GetData()); } else if (UEnum* Enum = Cast(Type.CPPTypeObject)) { NewDefaultValue = Enum->GetNameStringByValue(0); } else if(UClass* Class = Cast(Type.CPPTypeObject)) { // not supporting objects yet ensure(false); } else if (Type.CPPType == RigVMTypeUtils::FloatType) { NewDefaultValue = TEXT("0.000000"); } else if (Type.CPPType == RigVMTypeUtils::DoubleType) { NewDefaultValue = TEXT("0.000000"); } else if (Type.CPPType == RigVMTypeUtils::Int32Type) { NewDefaultValue = TEXT("0"); } else if (Type.CPPType == RigVMTypeUtils::BoolType) { NewDefaultValue = TEXT("False"); } else if (Type.CPPType == RigVMTypeUtils::FStringType) { NewDefaultValue = TEXT(""); } else if (Type.CPPType == RigVMTypeUtils::FNameType) { NewDefaultValue = TEXT(""); } } } if(!NewDefaultValue.IsEmpty()) { if(DefaultValue.IsEmpty()) { DefaultValue = NewDefaultValue; } else if(!NewDefaultValue.Equals(DefaultValue)) { return FString(); } } } return DefaultValue; } FName URigVMTemplateNode::GetDisplayNameForPin(const FName& InRootPinName, const TArray& InPermutationIndices) const { #if WITH_EDITOR if(const FRigVMTemplate* Template = GetTemplate()) { const TArray* PermutationIndicesPtr = &InPermutationIndices; if(PermutationIndicesPtr->IsEmpty()) { PermutationIndicesPtr = &GetFilteredPermutationsIndices(); } const FText DisplayNameText = Template->GetDisplayNameForArgument(InRootPinName, *PermutationIndicesPtr); if(DisplayNameText.IsEmpty()) { return InRootPinName; } const FName DisplayName = *DisplayNameText.ToString(); if(DisplayName.IsEqual(InRootPinName)) { return NAME_None; } return DisplayName; } #endif return NAME_None; } const TArray& URigVMTemplateNode::GetFilteredPermutationsIndices() const { return FilteredPermutations; } TArray URigVMTemplateNode::GetFilteredTypesForPin(URigVMPin* InPin) const { ensureMsgf(InPin->GetNode() == this, TEXT("GetFilteredTypesForPin of %s with pin from another node %s"), *GetNodePath(), *InPin->GetPinPath(true)); TArray FilteredTypes; if (FilteredPermutations.IsEmpty()) { return FilteredTypes; } if (!PreferredPermutationTypes.IsEmpty()) { for (const FString& PreferredType : PreferredPermutationTypes) { FString ArgName, CPPType; PreferredType.Split(TEXT(":"), &ArgName, &CPPType); if (InPin->GetName() == ArgName) { const FRigVMTemplateArgument* Argument = GetTemplate()->FindArgument(*ArgName); for (const FRigVMTemplateArgument::FType& Type : Argument->GetTypes()) { if (Type.CPPType == CPPType) { return {Type}; } } } } } URigVMPin* RootPin = InPin; bool bIsArrayElement = false; if (URigVMPin* ParentPin = InPin->GetParentPin()) { RootPin = ParentPin; bIsArrayElement = true; } if (const FRigVMTemplateArgument* Argument = GetTemplate()->FindArgument(RootPin->GetFName())) { FilteredTypes = Argument->GetSupportedTypes(FilteredPermutations); if (bIsArrayElement) { for (FRigVMTemplateArgument::FType& ArrayType : FilteredTypes) { ArrayType.ConvertToBaseElement(); } } } return FilteredTypes; } TArray URigVMTemplateNode::GetNewFilteredPermutations(URigVMPin* InPin, URigVMPin* LinkedPin) { TArray NewFilteredPermutations; if (InPin->GetNode() != this) { return NewFilteredPermutations; } NewFilteredPermutations.Reserve(FilteredPermutations.Num()); bool bIsArrayElement = false; bool bIsStructElement = false; URigVMPin* RootPin = InPin; if (URigVMPin* ParentPin = InPin->GetParentPin()) { RootPin = ParentPin; bIsArrayElement = RootPin->IsArray(); bIsStructElement = RootPin->IsStruct(); } if (bIsStructElement) { if (RigVMTypeUtils::AreCompatible(InPin->GetCPPType(), InPin->GetCPPTypeObject(), LinkedPin->GetCPPType(), LinkedPin->GetCPPTypeObject())) { return FilteredPermutations; } } TArray PermutationsToTry = PreferredPermutationTypes.IsEmpty()? FilteredPermutations : TArray(FindPermutationsForTypes(PreferredPermutationTypes)); bool bLinkedIsTemplate = false; if (URigVMTemplateNode* LinkedTemplate = Cast(LinkedPin->GetNode())) { if (!LinkedTemplate->IsSingleton() && !LinkedPin->IsStructMember()) { bLinkedIsTemplate = true; if (const FRigVMTemplateArgument* Argument = GetTemplate()->FindArgument(RootPin->GetFName())) { for(int32 PermutationIndex : PermutationsToTry) { FRigVMTemplateArgument::FType Type = Argument->GetTypes()[PermutationIndex]; if (bIsArrayElement) { Type.ConvertToBaseElement(); } if (LinkedTemplate->FilteredSupportsType(LinkedPin, Type.CPPType)) { NewFilteredPermutations.Add(PermutationIndex); } } } } } if (!bLinkedIsTemplate) { if (const FRigVMTemplateArgument* Argument = GetTemplate()->FindArgument(RootPin->GetFName())) { const FString LinkedCPPType = LinkedPin->GetCPPType(); for(int32 PermIndex : PermutationsToTry) { FRigVMTemplateArgument::FType Type = Argument->GetTypes()[PermIndex]; if (bIsArrayElement) { Type.ConvertToBaseElement(); } if (Type.Matches(LinkedCPPType)) { NewFilteredPermutations.Add(PermIndex); } } } } return NewFilteredPermutations; } TArray URigVMTemplateNode::GetNewFilteredPermutations(URigVMPin* InPin, const TArray& InTypes) { TArray NewFilteredPermutations; NewFilteredPermutations.Reserve(FilteredPermutations.Num()); URigVMPin* RootPin = InPin; TArray RootTypes = InTypes; if (URigVMPin* ParentPin = InPin->GetParentPin()) { RootPin = ParentPin; for (FRigVMTemplateArgument::FType& Type : RootTypes) { Type.ConvertToArray(); } } TArray PermutationsToTry = PreferredPermutationTypes.IsEmpty()? FilteredPermutations : TArray(FindPermutationsForTypes(PreferredPermutationTypes)); if (const FRigVMTemplateArgument* Argument = GetTemplate()->FindArgument(RootPin->GetFName())) { const TArray& Types = Argument->GetTypes(); for(int32 PermIndex : PermutationsToTry) { for (FRigVMTemplateArgument::FType& RootType : RootTypes) { if (Types[PermIndex].Matches(RootType.CPPType)) { NewFilteredPermutations.Add(PermIndex); break; } } } } return NewFilteredPermutations; } TArray URigVMTemplateNode::FindPermutationsForTypes(const TArray& ArgumentTypes, bool bAllowCasting) { TArray Permutations; if (const FRigVMTemplate* Template = GetTemplate()) { TArray Args; TArray CPPTypes; for (const FString& TypePairString : ArgumentTypes) { FString ArgName, Type; if (!TypePairString.Split(TEXT(":"), &ArgName, &Type)) { return {}; } if (const FRigVMTemplateArgument* Argument = Template->FindArgument(*ArgName)) { Args.Add(Argument); CPPTypes.Add(Type); } else { return {}; } } for (int32 i=0; iNumPermutations(); ++i) { bool bAllArgsMatched = true; for (int32 ArgIndex = 0; ArgIndex < Args.Num(); ++ArgIndex) { const FRigVMTemplateArgument* Argument = Args[ArgIndex]; { if ((bAllowCasting && !Argument->GetTypes()[i].Matches(CPPTypes[ArgIndex])) || (!bAllowCasting && Argument->GetTypes()[i].CPPType != CPPTypes[ArgIndex])) { bAllArgsMatched = false; break; } } } if (bAllArgsMatched) { Permutations.Add(i); } } } return Permutations; } TArray URigVMTemplateNode::GetArgumentTypesForPermutation(const int32 InPermutationIndex) { TArray ArgTypes; if (const FRigVMTemplate* Template = GetTemplate()) { for (int32 ArgIndex = 0; ArgIndex < Template->NumArguments(); ++ArgIndex) { const FRigVMTemplateArgument* Argument = Template->GetArgument(ArgIndex); if (Argument->GetTypes().Num() > InPermutationIndex) { ArgTypes.Add(Argument->GetName().ToString() + TEXT(":") + Argument->GetTypes()[InPermutationIndex].CPPType); } else { ArgTypes.Reset(); return ArgTypes; } } } return ArgTypes; } bool URigVMTemplateNode::PinNeedsFilteredTypesUpdate(URigVMPin* InPin, const TArray& InTypes) { TArray NewFilteredPermutations = GetNewFilteredPermutations(InPin, InTypes); if (NewFilteredPermutations.Num() == FilteredPermutations.Num()) { return false; } return true; } bool URigVMTemplateNode::PinNeedsFilteredTypesUpdate(URigVMPin* InPin, URigVMPin* LinkedPin) { TArray NewFilteredPermutations = GetNewFilteredPermutations(InPin, LinkedPin); if (NewFilteredPermutations.Num() == FilteredPermutations.Num()) { return false; } return true; } bool URigVMTemplateNode::UpdateFilteredPermutations(URigVMPin* InPin, URigVMPin* LinkedPin) { ensureMsgf(InPin->GetNode() == this, TEXT("Updating filtered permutations of %s with pin from another node %s"), *GetNodePath(), *InPin->GetPinPath(true)); ensureMsgf(LinkedPin->GetNode() != this, TEXT("Updating filtered permutations of %s with linked pin from same node %s"), *GetNodePath(), *LinkedPin->GetPinPath(true)); TArray NewFilteredPermutations = GetNewFilteredPermutations(InPin, LinkedPin); if (NewFilteredPermutations.Num() == FilteredPermutations.Num()) { return false; } if (NewFilteredPermutations.IsEmpty()) { return false; } FilteredPermutations = NewFilteredPermutations; return true; } bool URigVMTemplateNode::UpdateFilteredPermutations(URigVMPin* InPin, const TArray& InTypes) { TArray NewFilteredPermutations = GetNewFilteredPermutations(InPin, InTypes); if (NewFilteredPermutations.Num() == FilteredPermutations.Num()) { return false; } if (NewFilteredPermutations.IsEmpty()) { return false; } FilteredPermutations = NewFilteredPermutations; return true; } void URigVMTemplateNode::InvalidateCache() { SupportedTypesCache.Reset(); CachedFunction = nullptr; CachedTemplate = nullptr; ResolvedPermutations.Reset(); for(URigVMPin* Pin : GetPins()) { if(Pin->IsWildCard()) { ResolvedFunctionName.Reset(); break; } } } void URigVMTemplateNode::InitializeFilteredPermutations() { if (const FRigVMTemplate* Template = GetTemplate()) { if (!PreferredPermutationTypes.IsEmpty()) { FilteredPermutations = FindPermutationsForTypes(PreferredPermutationTypes); } else { FilteredPermutations.SetNumUninitialized(Template->NumPermutations()); for (int32 i=0; i ArgTypes; for (int32 ArgIndex = 0; ArgIndex < Template->NumArguments(); ++ArgIndex) { const FRigVMTemplateArgument* Argument = Template->GetArgument(ArgIndex); if (URigVMPin* Pin = FindPin(Argument->GetName().ToString())) { if (!Pin->IsWildCard()) { ArgTypes.Add(Argument->GetName().ToString() + TEXT(":") + Pin->GetCPPType()); } } } TArray Permutations = FindPermutationsForTypes(ArgTypes); if (!Permutations.IsEmpty()) { FilteredPermutations = Permutations; PreferredPermutationTypes = ArgTypes; } else { InitializeFilteredPermutations(); } } }