You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
Due to reconstruction on edit, the member reference GUID was always conforming back to the original layer node once it was set. This fix ensures that the GUID is appropriately set each time the member reference is updated. #jira UE-168246 #preflight 6385ff577b4bd3f057796ae4 #rb Jurre.deBaare [CL 23312441 by thomas sarkanen in ue5-main branch]
617 lines
20 KiB
C++
617 lines
20 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "AnimGraphNode_LinkedAnimGraphBase.h"
|
|
#include "Widgets/Text/STextBlock.h"
|
|
#include "Widgets/Input/SCheckBox.h"
|
|
#include "EdGraphSchema_K2.h"
|
|
#include "Kismet2/CompilerResultsLog.h"
|
|
#include "Kismet2/BlueprintEditorUtils.h"
|
|
#include "PropertyHandle.h"
|
|
#include "DetailLayoutBuilder.h"
|
|
#include "DetailCategoryBuilder.h"
|
|
#include "Animation/AnimNode_LinkedInputPose.h"
|
|
#include "PropertyCustomizationHelpers.h"
|
|
#include "ScopedTransaction.h"
|
|
#include "AnimationGraphSchema.h"
|
|
#include "Widgets/Layout/SBox.h"
|
|
#include "UObject/CoreRedirects.h"
|
|
#include "Animation/AnimNode_LinkedAnimGraph.h"
|
|
#include "AnimGraphAttributes.h"
|
|
#include "IAnimBlueprintCopyTermDefaultsContext.h"
|
|
#include "AnimBlueprintExtension_LinkedAnimGraph.h"
|
|
#include "AnimGraphNodeCustomizationInterface.h"
|
|
#include "BlueprintNodeTemplateCache.h"
|
|
#include "EdGraphSchema_K2_Actions.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "LinkedAnimGraph"
|
|
|
|
namespace LinkedAnimGraphGraphNodeConstants
|
|
{
|
|
FLinearColor TitleColor(0.2f, 0.2f, 0.8f);
|
|
}
|
|
|
|
|
|
void UAnimGraphNode_LinkedAnimGraphBase::Serialize(FArchive& Ar)
|
|
{
|
|
Super::Serialize(Ar);
|
|
|
|
Ar.UsingCustomVersion(FUE5ReleaseStreamObjectVersion::GUID);
|
|
|
|
if(Ar.CustomVer(FUE5ReleaseStreamObjectVersion::GUID) < FUE5ReleaseStreamObjectVersion::LinkedAnimGraphMemberReference)
|
|
{
|
|
// Upgrade name to member reference.
|
|
// Note that if the name has changed underneath us, we still cant recover the GUID
|
|
// until this asset has its node refreshed & the node is resaved
|
|
const FAnimNode_LinkedAnimGraph& RuntimeNode = *GetLinkedAnimGraphNode();
|
|
const FName FunctionName = RuntimeNode.GetDynamicLinkFunctionName();
|
|
UClass* TargetClass = GetTargetSkeletonClass();
|
|
if(FunctionName != NAME_None)
|
|
{
|
|
if(TargetClass != nullptr)
|
|
{
|
|
FunctionReference.SetExternalMember(FunctionName, TargetClass);
|
|
}
|
|
else
|
|
{
|
|
FunctionReference.SetSelfMember(FunctionName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UAnimGraphNode_LinkedAnimGraphBase::AllocatePoseLinks()
|
|
{
|
|
FAnimNode_LinkedAnimGraph& RuntimeNode = *GetLinkedAnimGraphNode();
|
|
|
|
RuntimeNode.InputPoses.Empty();
|
|
RuntimeNode.InputPoseNames.Empty();
|
|
|
|
for(UEdGraphPin* Pin : Pins)
|
|
{
|
|
if(!Pin->bOrphanedPin)
|
|
{
|
|
if (UAnimationGraphSchema::IsPosePin(Pin->PinType))
|
|
{
|
|
if(Pin->Direction == EGPD_Input)
|
|
{
|
|
RuntimeNode.InputPoses.AddDefaulted();
|
|
RuntimeNode.InputPoseNames.Add(Pin->GetFName());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FLinearColor UAnimGraphNode_LinkedAnimGraphBase::GetNodeTitleColor() const
|
|
{
|
|
TOptional<FLinearColor> Color;
|
|
|
|
UClass* TargetClass = GetTargetClass();
|
|
if(TargetClass && TargetClass->ImplementsInterface(UAnimGraphNodeCustomizationInterface::StaticClass()))
|
|
{
|
|
FEditorScriptExecutionGuard AllowScripts;
|
|
Color = IAnimGraphNodeCustomizationInterface::Execute_GetTitleColor(TargetClass->GetDefaultObject());
|
|
}
|
|
|
|
if(!Color.IsSet())
|
|
{
|
|
return GetDefaultNodeTitleColor();
|
|
}
|
|
|
|
return Color.GetValue();
|
|
}
|
|
|
|
FSlateIcon UAnimGraphNode_LinkedAnimGraphBase::GetIconAndTint(FLinearColor& OutColor) const
|
|
{
|
|
return FSlateIcon(FAppStyle::GetAppStyleSetName(), "ClassIcon.AnimBlueprint");
|
|
}
|
|
|
|
FText UAnimGraphNode_LinkedAnimGraphBase::GetTooltipText() const
|
|
{
|
|
TOptional<FText> TooltipText;
|
|
UClass* TargetClass = GetTargetClass();
|
|
|
|
// With a null class, template nodes return an empty string to let the metadata take priority
|
|
if(TargetClass == nullptr && FBlueprintNodeTemplateCache::IsTemplateOuter(GetGraph()))
|
|
{
|
|
return FText::GetEmpty();
|
|
}
|
|
|
|
static const FName NAME_Tooltip(TEXT("Tooltip"));
|
|
if(TargetClass && TargetClass->HasMetaData(NAME_Tooltip))
|
|
{
|
|
TooltipText = TargetClass->GetMetaDataText(NAME_Tooltip);
|
|
}
|
|
|
|
if(!TooltipText.IsSet() || TooltipText.GetValue().IsEmpty())
|
|
{
|
|
return LOCTEXT("ToolTip", "Runs a linked anim graph in another instance to process animation");
|
|
}
|
|
|
|
return TooltipText.GetValue();
|
|
}
|
|
|
|
FText UAnimGraphNode_LinkedAnimGraphBase::GetMenuCategory() const
|
|
{
|
|
TOptional<FText> Category;
|
|
UClass* TargetClass = GetTargetClass();
|
|
static const FName NAME_Category(TEXT("Category"));
|
|
if(TargetClass && TargetClass->HasMetaData(NAME_Category))
|
|
{
|
|
Category = TargetClass->GetMetaDataText(NAME_Category);
|
|
}
|
|
|
|
if(!Category.IsSet() || Category.GetValue().IsEmpty())
|
|
{
|
|
return LOCTEXT("LinkedAnimGraphCategory", "Animation|Linked Anim Graphs");
|
|
}
|
|
|
|
return Category.GetValue();
|
|
}
|
|
|
|
FText UAnimGraphNode_LinkedAnimGraphBase::GetNodeTitle(ENodeTitleType::Type TitleType) const
|
|
{
|
|
TOptional<FText> Title;
|
|
UClass* TargetClass = GetTargetClass();
|
|
static const FName NAME_DisplayName(TEXT("DisplayName"));
|
|
if(TargetClass && TargetClass->HasMetaData(NAME_DisplayName))
|
|
{
|
|
Title = TargetClass->GetMetaDataText(NAME_DisplayName);
|
|
}
|
|
|
|
if(!Title.IsSet() || Title.GetValue().IsEmpty())
|
|
{
|
|
UAnimBlueprint* TargetAnimBlueprint = TargetClass ? CastChecked<UAnimBlueprint>(TargetClass->ClassGeneratedBy) : nullptr;
|
|
const FName TargetAnimBlueprintName = TargetAnimBlueprint ? TargetAnimBlueprint->GetFName() : NAME_None;
|
|
|
|
const FAnimNode_LinkedAnimGraph& Node = *GetLinkedAnimGraphNode();
|
|
|
|
FFormatNamedArguments Args;
|
|
Args.Add(TEXT("NodeType"), LOCTEXT("Title", "Linked Anim Graph"));
|
|
Args.Add(TEXT("TargetClass"), FText::FromName(TargetAnimBlueprintName));
|
|
|
|
if(TitleType == ENodeTitleType::MenuTitle)
|
|
{
|
|
Title = LOCTEXT("Title", "Linked Anim Graph");
|
|
}
|
|
else if(TitleType == ENodeTitleType::ListView)
|
|
{
|
|
if(TargetClass)
|
|
{
|
|
Title = FText::Format(LOCTEXT("TitleListFormat", "{TargetClass}"), Args);
|
|
}
|
|
else
|
|
{
|
|
Title = LOCTEXT("Title", "Linked Anim Graph");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(TargetClass)
|
|
{
|
|
Title = FText::Format(LOCTEXT("TitleFormat", "{TargetClass}\n{NodeType}"), Args);
|
|
}
|
|
else
|
|
{
|
|
Title = LOCTEXT("Title", "Linked Anim Graph");
|
|
}
|
|
}
|
|
}
|
|
|
|
return Title.GetValue();
|
|
}
|
|
|
|
void UAnimGraphNode_LinkedAnimGraphBase::ValidateAnimNodeDuringCompilation(USkeleton* ForSkeleton, FCompilerResultsLog& MessageLog)
|
|
{
|
|
Super::ValidateAnimNodeDuringCompilation(ForSkeleton, MessageLog);
|
|
|
|
UAnimBlueprint* AnimBP = CastChecked<UAnimBlueprint>(GetBlueprint());
|
|
|
|
UObject* OriginalNode = MessageLog.FindSourceObject(this);
|
|
|
|
if(HasInstanceLoop())
|
|
{
|
|
MessageLog.Error(TEXT("Detected loop in linked instance chain starting at @@ inside class @@"), this, AnimBP->GetAnimBlueprintGeneratedClass());
|
|
}
|
|
|
|
// Check for cycles from other linked instance nodes
|
|
TArray<UEdGraph*> Graphs;
|
|
AnimBP->GetAllGraphs(Graphs);
|
|
|
|
const FAnimNode_LinkedAnimGraph& Node = *GetLinkedAnimGraphNode();
|
|
|
|
// Check we don't try to spawn our own blueprint
|
|
if(GetTargetClass() == AnimBP->GetAnimBlueprintGeneratedClass())
|
|
{
|
|
MessageLog.Error(TEXT("Linked instance node @@ targets instance class @@ which it is inside, this would cause a loop."), this, AnimBP->GetAnimBlueprintGeneratedClass());
|
|
}
|
|
|
|
// Check for compatibility
|
|
if(UAnimBlueprint* TargetAnimBP = Cast<UAnimBlueprint>(UBlueprint::GetBlueprintFromClass(GetTargetClass())))
|
|
{
|
|
if(!AnimBP->IsCompatible(TargetAnimBP))
|
|
{
|
|
MessageLog.Error(TEXT("Linked instance node @@ targets instance class @@ which is incompatible."), this, GetTargetClass());
|
|
}
|
|
}
|
|
}
|
|
|
|
void UAnimGraphNode_LinkedAnimGraphBase::CreateOutputPins()
|
|
{
|
|
Super::CreateOutputPins();
|
|
|
|
// Grab the SKELETON class here as when we are reconstructed during during BP compilation
|
|
// the full generated class is not yet present built.
|
|
if(UClass* TargetClass = GetTargetSkeletonClass())
|
|
{
|
|
if(UFunction* Function = FunctionReference.ResolveMember<UFunction>(TargetClass))
|
|
{
|
|
// Could have re-resolved to a new member name via GUID
|
|
const FAnimNode_LinkedAnimGraph& Node = *GetLinkedAnimGraphNode();
|
|
if(Node.GetDynamicLinkFunctionName() != FunctionReference.GetMemberName())
|
|
{
|
|
HandleFunctionReferenceChanged(FunctionReference.GetMemberName());
|
|
}
|
|
|
|
Function = FBlueprintEditorUtils::GetMostUpToDateFunction(Function);
|
|
|
|
IterateFunctionParameters(Function, [this](FName InName, FEdGraphPinType InPinType)
|
|
{
|
|
if(UAnimationGraphSchema::IsPosePin(InPinType))
|
|
{
|
|
UEdGraphPin* NewPin = CreatePin(EEdGraphPinDirection::EGPD_Input, UAnimationGraphSchema::MakeLocalSpacePosePin(), InName);
|
|
NewPin->PinFriendlyName = FText::FromName(InName);
|
|
CustomizePinData(NewPin, InName, INDEX_NONE);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
void UAnimGraphNode_LinkedAnimGraphBase::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
|
|
{
|
|
Super::PostEditChangeProperty(PropertyChangedEvent);
|
|
|
|
bool bRequiresNodeReconstruct = false;
|
|
FProperty* ChangedProperty = PropertyChangedEvent.Property;
|
|
|
|
if(ChangedProperty)
|
|
{
|
|
if (IsStructuralProperty(ChangedProperty))
|
|
{
|
|
// Set function reference if node structure changes
|
|
const FAnimNode_LinkedAnimGraph& Node = *GetLinkedAnimGraphNode();
|
|
UClass* TargetClass = GetTargetSkeletonClass();
|
|
FName FunctionName = Node.GetDynamicLinkFunctionName();
|
|
if(FunctionName != NAME_None)
|
|
{
|
|
if(TargetClass != nullptr)
|
|
{
|
|
FGuid FunctionGuid;
|
|
FBlueprintEditorUtils::GetFunctionGuidFromClassByFieldName(FBlueprintEditorUtils::GetMostUpToDateClass(TargetClass), FunctionName, FunctionGuid);
|
|
FunctionReference.SetExternalMember(FunctionName, TargetClass, FunctionGuid);
|
|
}
|
|
else
|
|
{
|
|
FunctionReference.SetSelfMember(FunctionName);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FunctionReference = FMemberReference();
|
|
}
|
|
|
|
bRequiresNodeReconstruct = true;
|
|
}
|
|
}
|
|
|
|
if(bRequiresNodeReconstruct)
|
|
{
|
|
ReconstructNode();
|
|
}
|
|
}
|
|
|
|
bool UAnimGraphNode_LinkedAnimGraphBase::HasInstanceLoop()
|
|
{
|
|
TArray<FGuid> VisitedList;
|
|
TArray<FGuid> CurrentStack;
|
|
return HasInstanceLoop_Recursive(this, VisitedList, CurrentStack);
|
|
}
|
|
|
|
bool UAnimGraphNode_LinkedAnimGraphBase::HasInstanceLoop_Recursive(UAnimGraphNode_LinkedAnimGraphBase* CurrNode, TArray<FGuid>& VisitedNodes, TArray<FGuid>& NodeStack)
|
|
{
|
|
if(!VisitedNodes.Contains(CurrNode->NodeGuid))
|
|
{
|
|
VisitedNodes.Add(CurrNode->NodeGuid);
|
|
NodeStack.Add(CurrNode->NodeGuid);
|
|
|
|
if(UAnimBlueprint* AnimBP = Cast<UAnimBlueprint>(UBlueprint::GetBlueprintFromClass(CurrNode->GetTargetClass())))
|
|
{
|
|
// Check for cycles from other linked instance nodes
|
|
TArray<UEdGraph*> Graphs;
|
|
AnimBP->GetAllGraphs(Graphs);
|
|
|
|
for(UEdGraph* Graph : Graphs)
|
|
{
|
|
TArray<UAnimGraphNode_LinkedAnimGraphBase*> LinkedInstanceNodes;
|
|
Graph->GetNodesOfClass(LinkedInstanceNodes);
|
|
|
|
for(UAnimGraphNode_LinkedAnimGraphBase* LinkedInstanceNode : LinkedInstanceNodes)
|
|
{
|
|
// If we haven't visited this node, then check it for loops, otherwise if we're pointing to a previously visited node that is in the current instance stack we have a loop
|
|
if((!VisitedNodes.Contains(LinkedInstanceNode->NodeGuid) && HasInstanceLoop_Recursive(LinkedInstanceNode, VisitedNodes, NodeStack)) || NodeStack.Contains(LinkedInstanceNode->NodeGuid))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
NodeStack.Remove(CurrNode->NodeGuid);
|
|
return false;
|
|
}
|
|
|
|
void UAnimGraphNode_LinkedAnimGraphBase::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
|
|
{
|
|
Super::CustomizeDetails(DetailBuilder);
|
|
|
|
GenerateExposedPinsDetails(DetailBuilder);
|
|
|
|
IDetailCategoryBuilder& CategoryBuilder = DetailBuilder.EditCategory(FName(TEXT("Settings")));
|
|
|
|
// Customize InstanceClass
|
|
{
|
|
TSharedRef<IPropertyHandle> ClassHandle = DetailBuilder.GetProperty(TEXT("Node.InstanceClass"), GetClass());
|
|
ClassHandle->MarkHiddenByCustomization();
|
|
|
|
FDetailWidgetRow& ClassWidgetRow = CategoryBuilder.AddCustomRow(LOCTEXT("FilterStringInstanceClass", "Instance Class"));
|
|
ClassWidgetRow.NameContent()
|
|
[
|
|
ClassHandle->CreatePropertyNameWidget()
|
|
]
|
|
.ValueContent()
|
|
.MinDesiredWidth(250.0f)
|
|
[
|
|
SNew(SObjectPropertyEntryBox)
|
|
.ObjectPath_UObject(this, &UAnimGraphNode_LinkedAnimGraphBase::GetCurrentInstanceBlueprintPath)
|
|
.AllowedClass(UAnimBlueprint::StaticClass())
|
|
.NewAssetFactories(TArray<UFactory*>())
|
|
.OnShouldFilterAsset(FOnShouldFilterAsset::CreateUObject(this, &UAnimGraphNode_LinkedAnimGraphBase::OnShouldFilterInstanceBlueprint))
|
|
.OnObjectChanged(FOnSetObject::CreateUObject(this, &UAnimGraphNode_LinkedAnimGraphBase::OnSetInstanceBlueprint, &DetailBuilder))
|
|
];
|
|
}
|
|
}
|
|
|
|
void UAnimGraphNode_LinkedAnimGraphBase::GenerateExposedPinsDetails(IDetailLayoutBuilder &DetailBuilder)
|
|
{
|
|
// We dont allow multi-select here
|
|
if(DetailBuilder.GetSelectedObjects().Num() > 1)
|
|
{
|
|
DetailBuilder.HideCategory(TEXT("Settings"));
|
|
return;
|
|
}
|
|
|
|
const FStructProperty* NodeProperty = GetFNodeProperty();
|
|
if(NodeProperty == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(CustomPinProperties.Num() > 0)
|
|
{
|
|
TSharedRef<IPropertyHandle> NodePropertyHandle = DetailBuilder.GetProperty(NodeProperty->GetFName(), GetClass());
|
|
|
|
IDetailCategoryBuilder& CategoryBuilder = DetailBuilder.EditCategory(FName(TEXT("Exposable Properties")));
|
|
|
|
for(int32 OptionalPinIndex = 0; OptionalPinIndex < CustomPinProperties.Num(); ++OptionalPinIndex)
|
|
{
|
|
const FOptionalPinFromProperty& OptionalProperty = CustomPinProperties[OptionalPinIndex];
|
|
|
|
// Find the property of our inner class
|
|
FProperty* Property = GetPinProperty(OptionalProperty.PropertyName);
|
|
|
|
if(Property == nullptr)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
FDetailWidgetRow& PropertyWidgetRow = CategoryBuilder.AddCustomRow(FText::FromString(Property->GetName()));
|
|
|
|
FName PropertyName = Property->GetFName();
|
|
FText PropertyTypeText = GetPropertyTypeText(Property);
|
|
|
|
FFormatNamedArguments Args;
|
|
Args.Add(TEXT("PropertyName"), FText::FromName(PropertyName));
|
|
Args.Add(TEXT("PropertyType"), PropertyTypeText);
|
|
|
|
FText TooltipText = FText::Format(LOCTEXT("PropertyTooltipText", "{PropertyName}\nType: {PropertyType}"), Args);
|
|
|
|
PropertyWidgetRow.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(FText::FromString(Property->GetName()))
|
|
.ToolTipText(TooltipText)
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
];
|
|
|
|
TSharedPtr<SWidget> BindingWidget = UAnimationGraphSchema::MakeBindingWidgetForPin({ this }, PropertyName, false, true);
|
|
if(BindingWidget.IsValid())
|
|
{
|
|
PropertyWidgetRow.ExtensionContent()
|
|
[
|
|
BindingWidget.ToSharedRef()
|
|
];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool UAnimGraphNode_LinkedAnimGraphBase::IsStructuralProperty(FProperty* InProperty) const
|
|
{
|
|
return InProperty->GetFName() == GET_MEMBER_NAME_CHECKED(FAnimNode_LinkedAnimGraph, InstanceClass);
|
|
}
|
|
|
|
FString UAnimGraphNode_LinkedAnimGraphBase::GetCurrentInstanceBlueprintPath() const
|
|
{
|
|
UClass* InstanceClass = GetTargetClass();
|
|
|
|
if(InstanceClass)
|
|
{
|
|
UBlueprint* ActualBlueprint = UBlueprint::GetBlueprintFromClass(InstanceClass);
|
|
|
|
if(ActualBlueprint)
|
|
{
|
|
return ActualBlueprint->GetPathName();
|
|
}
|
|
}
|
|
|
|
return FString();
|
|
}
|
|
|
|
bool UAnimGraphNode_LinkedAnimGraphBase::OnShouldFilterInstanceBlueprint(const FAssetData& AssetData) const
|
|
{
|
|
// Check recursion
|
|
if(AssetData.IsAssetLoaded() && Cast<UBlueprint>(AssetData.GetAsset()) == GetBlueprint())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Check skeleton & flags
|
|
FAssetDataTagMapSharedView::FFindTagResult TargetSkeletonResult = AssetData.TagsAndValues.FindTag("TargetSkeleton");
|
|
FAssetDataTagMapSharedView::FFindTagResult IsTemplateResult = AssetData.TagsAndValues.FindTag("bIsTemplate");
|
|
FAssetDataTagMapSharedView::FFindTagResult BlueprintTypeResult = AssetData.TagsAndValues.FindTag("BlueprintType");
|
|
if (TargetSkeletonResult.IsSet())
|
|
{
|
|
const bool bIsTemplate = IsTemplateResult.IsSet() && IsTemplateResult.Equals(TEXT("True"));
|
|
const bool bIsInterface = BlueprintTypeResult.IsSet() && BlueprintTypeResult.Equals(TEXT("BPTYPE_Interface"));
|
|
if (UAnimBlueprint* CurrentBlueprint = Cast<UAnimBlueprint>(GetBlueprint()))
|
|
{
|
|
if (!CurrentBlueprint->IsCompatibleByAssetString(TargetSkeletonResult.GetValue(), bIsTemplate, bIsInterface))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void UAnimGraphNode_LinkedAnimGraphBase::OnSetInstanceBlueprint(const FAssetData& AssetData, IDetailLayoutBuilder* InDetailBuilder)
|
|
{
|
|
FScopedTransaction Transaction(LOCTEXT("SetInstanceBlueprint", "Set Linked Blueprint"));
|
|
|
|
Modify();
|
|
|
|
TSharedRef<IPropertyHandle> ClassHandle = InDetailBuilder->GetProperty(TEXT("Node.InstanceClass"), GetClass());
|
|
if(UAnimBlueprint* Blueprint = Cast<UAnimBlueprint>(AssetData.GetAsset()))
|
|
{
|
|
ClassHandle->SetValue(Blueprint->GetAnimBlueprintGeneratedClass());
|
|
}
|
|
else
|
|
{
|
|
ClassHandle->SetValue((UObject*)nullptr);
|
|
}
|
|
}
|
|
|
|
FLinearColor UAnimGraphNode_LinkedAnimGraphBase::GetDefaultNodeTitleColor() const
|
|
{
|
|
return LinkedAnimGraphGraphNodeConstants::TitleColor;
|
|
}
|
|
|
|
FPoseLinkMappingRecord UAnimGraphNode_LinkedAnimGraphBase::GetLinkIDLocation(const UScriptStruct* NodeType, UEdGraphPin* SourcePin)
|
|
{
|
|
FPoseLinkMappingRecord Record = Super::GetLinkIDLocation(NodeType, SourcePin);
|
|
if(Record.IsValid())
|
|
{
|
|
return Record;
|
|
}
|
|
else if(SourcePin->LinkedTo.Num() > 0 && SourcePin->Direction == EGPD_Input)
|
|
{
|
|
const FAnimNode_LinkedAnimGraph& Node = *GetLinkedAnimGraphNode();
|
|
|
|
check(Node.InputPoses.Num() == Node.InputPoseNames.Num());
|
|
|
|
// perform name-based logic for input pose pins
|
|
if (UAnimGraphNode_Base* LinkedNode = Cast<UAnimGraphNode_Base>(FBlueprintEditorUtils::FindFirstCompilerRelevantNode(SourcePin->LinkedTo[0])))
|
|
{
|
|
FArrayProperty* ArrayProperty = FindFieldChecked<FArrayProperty>(NodeType, GET_MEMBER_NAME_CHECKED(FAnimNode_LinkedAnimGraph, InputPoses));
|
|
int32 ArrayIndex = INDEX_NONE;
|
|
if(Node.InputPoseNames.Find(SourcePin->GetFName(), ArrayIndex))
|
|
{
|
|
check(Node.InputPoses.IsValidIndex(ArrayIndex));
|
|
return FPoseLinkMappingRecord::MakeFromArrayEntry(this, LinkedNode, ArrayProperty, ArrayIndex);
|
|
}
|
|
}
|
|
}
|
|
|
|
return FPoseLinkMappingRecord::MakeInvalid();
|
|
}
|
|
|
|
void UAnimGraphNode_LinkedAnimGraphBase::GetOutputLinkAttributes(FNodeAttributeArray& OutAttributes) const
|
|
{
|
|
// We have the potential to output ALL registered attributes as we can contain any dynamically-linked graph
|
|
const UAnimGraphAttributes* AnimGraphAttributes = GetDefault<UAnimGraphAttributes>();
|
|
AnimGraphAttributes->ForEachAttribute([&OutAttributes](const FAnimGraphAttributeDesc& InDesc)
|
|
{
|
|
OutAttributes.Add(InDesc.Name);
|
|
});
|
|
}
|
|
|
|
void UAnimGraphNode_LinkedAnimGraphBase::OnCopyTermDefaultsToDefaultObject(IAnimBlueprintCopyTermDefaultsContext& InCompilationContext, IAnimBlueprintNodeCopyTermDefaultsContext& InPerNodeContext, IAnimBlueprintGeneratedClassCompiledData& OutCompiledData)
|
|
{
|
|
Super::OnCopyTermDefaultsToDefaultObject(InCompilationContext, InPerNodeContext, OutCompiledData);
|
|
|
|
UAnimGraphNode_LinkedAnimGraphBase* TrueNode = InCompilationContext.GetMessageLog().FindSourceObjectTypeChecked<UAnimGraphNode_LinkedAnimGraphBase>(this);
|
|
|
|
FAnimNode_LinkedAnimGraph* DestinationNode = reinterpret_cast<FAnimNode_LinkedAnimGraph*>(InPerNodeContext.GetDestinationPtr());
|
|
DestinationNode->NodeIndex = InPerNodeContext.GetNodePropertyIndex();
|
|
}
|
|
|
|
void UAnimGraphNode_LinkedAnimGraphBase::GetRequiredExtensions(TArray<TSubclassOf<UAnimBlueprintExtension>>& OutExtensions) const
|
|
{
|
|
OutExtensions.Add(UAnimBlueprintExtension_LinkedAnimGraph::StaticClass());
|
|
}
|
|
|
|
TSharedPtr<FEdGraphSchemaAction> UAnimGraphNode_LinkedAnimGraphBase::GetEventNodeAction(const FText& ActionCategory)
|
|
{
|
|
TSharedPtr<FEdGraphSchemaAction_K2Event> NodeAction = MakeShareable(new FEdGraphSchemaAction_K2Event(ActionCategory, GetNodeTitle(ENodeTitleType::ListView), GetTooltipText(), 0));
|
|
NodeAction->NodeTemplate = this;
|
|
return NodeAction;
|
|
}
|
|
|
|
|
|
void UAnimGraphNode_LinkedAnimGraphBase::IterateFunctionParameters(UFunction* InFunction, TFunctionRef<void(const FName&, const FEdGraphPinType&)> InFunc) const
|
|
{
|
|
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
|
|
|
|
auto IterateParam = [K2Schema, &InFunc](FProperty* InParam, bool bInPoses)
|
|
{
|
|
const bool bIsFunctionInput = !InParam->HasAnyPropertyFlags(CPF_OutParm) || InParam->HasAnyPropertyFlags(CPF_ReferenceParm);
|
|
if (bIsFunctionInput)
|
|
{
|
|
FEdGraphPinType PinType;
|
|
if(K2Schema->ConvertPropertyToPinType(InParam, PinType))
|
|
{
|
|
if(UAnimationGraphSchema::IsPosePin(PinType) == bInPoses)
|
|
{
|
|
InFunc(InParam->GetFName(), PinType);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// Iterate poses first, then params
|
|
for (TFieldIterator<FProperty> PropIt(InFunction); PropIt && (PropIt->PropertyFlags & CPF_Parm); ++PropIt)
|
|
{
|
|
IterateParam(*PropIt, true);
|
|
}
|
|
|
|
for (TFieldIterator<FProperty> PropIt(InFunction); PropIt && (PropIt->PropertyFlags & CPF_Parm); ++PropIt)
|
|
{
|
|
IterateParam(*PropIt, false);
|
|
}
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|