2014-03-14 14:13:41 -04:00
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
/*=============================================================================
AnimationGraphSchema . cpp
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
# include "AnimGraphPrivatePCH.h"
# include "BlueprintUtilities.h"
# include "GraphEditorActions.h"
# include "ScopedTransaction.h"
# include "AssetData.h"
# include "K2ActionMenuBuilder.h" // for FK2ActionMenuBuilder::AddNewNodeAction()
2014-04-24 08:49:31 -04:00
# include "AnimationGraphSchema.h"
# include "K2Node_TransitionRuleGetter.h"
# include "AnimStateNode.h"
# include "AnimGraphNode_BlendSpacePlayer.h"
# include "AnimGraphNode_ComponentToLocalSpace.h"
# include "AnimGraphNode_LocalToComponentSpace.h"
2014-04-24 14:34:01 -04:00
# include "AnimGraphNode_Root.h"
2014-04-24 08:49:31 -04:00
# include "AnimGraphNode_RotationOffsetBlendSpace.h"
# include "AnimGraphNode_SequencePlayer.h"
2014-03-14 14:13:41 -04:00
# define LOCTEXT_NAMESPACE "AnimationGraphSchema"
/////////////////////////////////////////////////////
// UAnimationGraphSchema
UAnimationGraphSchema : : UAnimationGraphSchema ( const class FPostConstructInitializeProperties & PCIP )
: Super ( PCIP )
{
PN_SequenceName = TEXT ( " Sequence " ) ;
NAME_NeverAsPin = TEXT ( " NeverAsPin " ) ;
NAME_PinHiddenByDefault = TEXT ( " PinHiddenByDefault " ) ;
NAME_PinShownByDefault = TEXT ( " PinShownByDefault " ) ;
NAME_AlwaysAsPin = TEXT ( " AlwaysAsPin " ) ;
NAME_OnEvaluate = TEXT ( " OnEvaluate " ) ;
2014-05-16 11:43:02 -04:00
NAME_CustomizeProperty = TEXT ( " CustomizeProperty " ) ;
2014-03-14 14:13:41 -04:00
DefaultEvaluationHandlerName = TEXT ( " EvaluateGraphExposedInputs " ) ;
}
void UAnimationGraphSchema : : GetStateMachineMenuItems ( FGraphContextMenuBuilder & ContextMenuBuilder ) const
{
// Show state machine helpers
if ( ( ContextMenuBuilder . FromPin = = NULL ) | | ( ( ContextMenuBuilder . FromPin - > Direction = = EGPD_Input ) & & ( ContextMenuBuilder . FromPin - > PinType . PinCategory = = PC_Float ) ) )
{
FString StateCategory ( TEXT ( " State Machines " ) ) ;
// Create the additional entries to get state information
TArray < UAnimStateNode * > States ;
UBlueprint * CurrentBlueprint = FBlueprintEditorUtils : : FindBlueprintForGraphChecked ( ContextMenuBuilder . CurrentGraph ) ;
FBlueprintEditorUtils : : GetAllNodesOfClass ( CurrentBlueprint , /*out*/ States ) ;
for ( auto StateIt = States . CreateIterator ( ) ; StateIt ; + + StateIt )
{
UAnimStateNode * StateNode = * StateIt ;
UK2Node_TransitionRuleGetter * NodeTemplate = ContextMenuBuilder . CreateTemplateNode < UK2Node_TransitionRuleGetter > ( ) ;
NodeTemplate - > AssociatedStateNode = StateNode ;
NodeTemplate - > GetterType = ETransitionGetter : : ArbitraryState_GetBlendWeight ;
const FString OwnerName = StateNode - > GetOuter ( ) - > GetName ( ) ;
2014-04-23 18:30:37 -04:00
const FString Title = FString : : Printf ( TEXT ( " Current %s for state '%s.%s' " ) , * UK2Node_TransitionRuleGetter : : GetFriendlyName ( NodeTemplate - > GetterType ) . ToString ( ) , * OwnerName , * StateNode - > GetStateName ( ) ) ;
2014-03-14 14:13:41 -04:00
2014-04-23 18:30:37 -04:00
TSharedPtr < FEdGraphSchemaAction_K2NewNode > Action = FK2ActionMenuBuilder : : AddNewNodeAction ( ContextMenuBuilder , StateCategory , FText : : FromString ( Title ) , NodeTemplate - > GetTooltip ( ) , 0 , NodeTemplate - > GetKeywords ( ) ) ;
2014-03-14 14:13:41 -04:00
Action - > NodeTemplate = NodeTemplate ;
}
}
}
void UAnimationGraphSchema : : GetGraphContextActions ( FGraphContextMenuBuilder & ContextMenuBuilder ) const
{
// Add state machine info items
GetStateMachineMenuItems ( ContextMenuBuilder ) ;
// Inherited abilities
return Super : : GetGraphContextActions ( ContextMenuBuilder ) ;
}
FLinearColor UAnimationGraphSchema : : GetPinTypeColor ( const FEdGraphPinType & PinType ) const
{
const bool bAdditive = PinType . PinSubCategory = = TEXT ( " Additive " ) ;
if ( UAnimationGraphSchema : : IsLocalSpacePosePin ( PinType ) )
{
if ( bAdditive )
{
return FLinearColor ( 0.12 , 0.60 , 0.10 ) ;
}
else
{
return FLinearColor : : White ;
}
}
else if ( UAnimationGraphSchema : : IsComponentSpacePosePin ( PinType ) )
{
//@TODO: Pick better colors
if ( bAdditive )
{
return FLinearColor ( 0.12 , 0.60 , 0.60 ) ;
}
else
{
return FLinearColor ( 0.20f , 0.50f , 1.00f ) ;
}
}
return Super : : GetPinTypeColor ( PinType ) ;
}
EGraphType UAnimationGraphSchema : : GetGraphType ( const UEdGraph * TestEdGraph ) const
{
return GT_Animation ;
}
void UAnimationGraphSchema : : CreateDefaultNodesForGraph ( UEdGraph & Graph ) const
{
// Create the result node
FGraphNodeCreator < UAnimGraphNode_Root > NodeCreator ( Graph ) ;
UAnimGraphNode_Root * ResultSinkNode = NodeCreator . CreateNode ( ) ;
NodeCreator . Finalize ( ) ;
}
void UAnimationGraphSchema : : HandleGraphBeingDeleted ( UEdGraph & GraphBeingRemoved ) const
{
if ( UBlueprint * Blueprint = FBlueprintEditorUtils : : FindBlueprintForGraph ( & GraphBeingRemoved ) )
{
// Look for state nodes that reference this graph
TArray < UAnimStateNode * > StateNodes ;
FBlueprintEditorUtils : : GetAllNodesOfClass < UAnimStateNode > ( Blueprint , /*out*/ StateNodes ) ;
TSet < UAnimStateNode * > NodesToDelete ;
for ( int32 i = 0 ; i < StateNodes . Num ( ) ; + + i )
{
UAnimStateNode * StateNode = StateNodes [ i ] ;
if ( StateNode - > BoundGraph = = & GraphBeingRemoved )
{
NodesToDelete . Add ( StateNode ) ;
}
}
// Delete the node that owns us
ensure ( NodesToDelete . Num ( ) < = 1 ) ;
for ( TSet < UAnimStateNode * > : : TIterator It ( NodesToDelete ) ; It ; + + It )
{
UAnimStateNode * NodeToDelete = * It ;
// Prevent re-entrancy here
NodeToDelete - > BoundGraph = NULL ;
NodeToDelete - > Modify ( ) ;
NodeToDelete - > DestroyNode ( ) ;
}
}
}
bool UAnimationGraphSchema : : IsPosePin ( const FEdGraphPinType & PinType )
{
return IsLocalSpacePosePin ( PinType ) | | IsComponentSpacePosePin ( PinType ) ;
}
bool UAnimationGraphSchema : : IsLocalSpacePosePin ( const FEdGraphPinType & PinType )
{
const UAnimationGraphSchema * Schema = GetDefault < UAnimationGraphSchema > ( ) ;
UScriptStruct * PoseLinkStruct = FPoseLink : : StaticStruct ( ) ;
return ( PinType . PinCategory = = Schema - > PC_Struct ) & & ( PinType . PinSubCategoryObject = = PoseLinkStruct ) ;
}
bool UAnimationGraphSchema : : IsComponentSpacePosePin ( const FEdGraphPinType & PinType )
{
const UAnimationGraphSchema * Schema = GetDefault < UAnimationGraphSchema > ( ) ;
UScriptStruct * ComponentSpacePoseLinkStruct = FComponentSpacePoseLink : : StaticStruct ( ) ;
return ( PinType . PinCategory = = Schema - > PC_Struct ) & & ( PinType . PinSubCategoryObject = = ComponentSpacePoseLinkStruct ) ;
}
const FPinConnectionResponse UAnimationGraphSchema : : DetermineConnectionResponseOfCompatibleTypedPins ( const UEdGraphPin * PinA , const UEdGraphPin * PinB , const UEdGraphPin * InputPin , const UEdGraphPin * OutputPin ) const
{
// Enforce a tree hierarchy; where poses can only have one output (parent) connection
if ( IsPosePin ( OutputPin - > PinType ) & & IsPosePin ( InputPin - > PinType ) )
{
if ( ( OutputPin - > LinkedTo . Num ( ) > 0 ) | | ( InputPin - > LinkedTo . Num ( ) > 0 ) )
{
const ECanCreateConnectionResponse ReplyBreakOutputs = CONNECT_RESPONSE_BREAK_OTHERS_AB ;
return FPinConnectionResponse ( ReplyBreakOutputs , TEXT ( " Replace existing connections " ) ) ;
}
}
// Fall back to standard K2 rules
return Super : : DetermineConnectionResponseOfCompatibleTypedPins ( PinA , PinB , InputPin , OutputPin ) ;
}
bool UAnimationGraphSchema : : ArePinsCompatible ( const UEdGraphPin * PinA , const UEdGraphPin * PinB , const UClass * CallingContext , bool bIgnoreArray ) const
{
// both are pose pin, but doesn't match type, then return false;
if ( IsPosePin ( PinA - > PinType ) & & IsPosePin ( PinB - > PinType ) & & IsLocalSpacePosePin ( PinA - > PinType ) ! = IsLocalSpacePosePin ( PinB - > PinType ) )
{
return false ;
}
return Super : : ArePinsCompatible ( PinA , PinB , CallingContext , bIgnoreArray ) ;
}
bool UAnimationGraphSchema : : DoesSupportAnimNotifyActions ( ) const
{
// Don't offer notify items in anim graph
return false ;
}
bool UAnimationGraphSchema : : SearchForAutocastFunction ( const UEdGraphPin * OutputPin , const UEdGraphPin * InputPin , FName & TargetFunction ) const
{
if ( IsComponentSpacePosePin ( OutputPin - > PinType ) & & IsLocalSpacePosePin ( InputPin - > PinType ) )
{
// Insert a Component To LocalSpace conversion
return true ;
}
else if ( IsLocalSpacePosePin ( OutputPin - > PinType ) & & IsComponentSpacePosePin ( InputPin - > PinType ) )
{
// Insert a Local To ComponentSpace conversion
return true ;
}
else
{
return Super : : SearchForAutocastFunction ( OutputPin , InputPin , TargetFunction ) ;
}
}
bool UAnimationGraphSchema : : CreateAutomaticConversionNodeAndConnections ( UEdGraphPin * PinA , UEdGraphPin * PinB ) const
{
// Determine which pin is an input and which pin is an output
UEdGraphPin * InputPin = NULL ;
UEdGraphPin * OutputPin = NULL ;
if ( ! CategorizePinsByDirection ( PinA , PinB , /*out*/ InputPin , /*out*/ OutputPin ) )
{
return false ;
}
// Look for animation specific conversion operations
UK2Node * TemplateNode = NULL ;
if ( IsComponentSpacePosePin ( OutputPin - > PinType ) & & IsLocalSpacePosePin ( InputPin - > PinType ) )
{
TemplateNode = NewObject < UAnimGraphNode_ComponentToLocalSpace > ( ) ;
}
else if ( IsLocalSpacePosePin ( OutputPin - > PinType ) & & IsComponentSpacePosePin ( InputPin - > PinType ) )
{
TemplateNode = NewObject < UAnimGraphNode_LocalToComponentSpace > ( ) ;
}
// Spawn the conversion node if it's specific to animation
if ( TemplateNode ! = NULL )
{
UEdGraph * Graph = InputPin - > GetOwningNode ( ) - > GetGraph ( ) ;
FVector2D AverageLocation = CalculateAveragePositionBetweenNodes ( InputPin , OutputPin ) ;
UK2Node * ConversionNode = FEdGraphSchemaAction_K2NewNode : : SpawnNodeFromTemplate < UK2Node > ( Graph , TemplateNode , AverageLocation ) ;
AutowireConversionNode ( InputPin , OutputPin , ConversionNode ) ;
return true ;
}
else
{
// Give the regular conversions a shot
return Super : : CreateAutomaticConversionNodeAndConnections ( PinA , PinB ) ;
}
}
bool IsAimOffsetBlendSpace ( UBlendSpaceBase * BlendSpace )
{
return BlendSpace - > IsA ( UAimOffsetBlendSpace : : StaticClass ( ) ) | |
BlendSpace - > IsA ( UAimOffsetBlendSpace1D : : StaticClass ( ) ) ;
}
void UAnimationGraphSchema : : SpawnNodeFromAsset ( UAnimationAsset * Asset , const FVector2D & GraphPosition , UEdGraph * Graph , UEdGraphPin * PinIfAvailable )
{
check ( Graph ) ;
check ( Graph - > GetSchema ( ) - > IsA ( UAnimationGraphSchema : : StaticClass ( ) ) ) ;
check ( Asset ) ;
UAnimBlueprint * AnimBlueprint = Cast < UAnimBlueprint > ( FBlueprintEditorUtils : : FindBlueprintForGraph ( Graph ) ) ;
const bool bSkelMatch = ( AnimBlueprint ! = NULL ) & & ( AnimBlueprint - > TargetSkeleton = = Asset - > GetSkeleton ( ) ) ;
const bool bTypeMatch = ( PinIfAvailable = = NULL ) | | UAnimationGraphSchema : : IsLocalSpacePosePin ( PinIfAvailable - > PinType ) ;
const bool bDirectionMatch = ( PinIfAvailable = = NULL ) | | ( PinIfAvailable - > Direction = = EGPD_Input ) ;
if ( bSkelMatch & & bTypeMatch & & bDirectionMatch )
{
FEdGraphSchemaAction_K2NewNode Action ;
if ( UAnimSequence * Sequence = Cast < UAnimSequence > ( Asset ) )
{
UAnimGraphNode_SequencePlayer * PlayerNode = NewObject < UAnimGraphNode_SequencePlayer > ( ) ;
PlayerNode - > Node . Sequence = Sequence ;
Action . NodeTemplate = PlayerNode ;
}
else if ( UBlendSpaceBase * BlendSpace = Cast < UBlendSpaceBase > ( Asset ) )
{
if ( IsAimOffsetBlendSpace ( BlendSpace ) )
{
UAnimGraphNode_RotationOffsetBlendSpace * PlayerNode = NewObject < UAnimGraphNode_RotationOffsetBlendSpace > ( ) ;
PlayerNode - > Node . BlendSpace = BlendSpace ;
Action . NodeTemplate = PlayerNode ;
}
else
{
UAnimGraphNode_BlendSpacePlayer * PlayerNode = NewObject < UAnimGraphNode_BlendSpacePlayer > ( ) ;
PlayerNode - > Node . BlendSpace = BlendSpace ;
Action . NodeTemplate = PlayerNode ;
}
}
else if ( UAnimComposite * Composite = Cast < UAnimComposite > ( Asset ) )
{
UAnimGraphNode_SequencePlayer * PlayerNode = NewObject < UAnimGraphNode_SequencePlayer > ( ) ;
PlayerNode - > Node . Sequence = Composite ;
Action . NodeTemplate = PlayerNode ;
}
else
{
//unknown type
return ;
}
Action . PerformAction ( Graph , PinIfAvailable , GraphPosition ) ;
}
}
void UAnimationGraphSchema : : UpdateNodeWithAsset ( UK2Node * K2Node , UAnimationAsset * Asset )
{
if ( Asset ! = NULL )
{
if ( UAnimGraphNode_SequencePlayer * SequencePlayerNode = Cast < UAnimGraphNode_SequencePlayer > ( K2Node ) )
{
if ( UAnimSequence * Sequence = Cast < UAnimSequence > ( Asset ) )
{
// Skeleton matches, and it's a sequence player; replace the existing sequence with the dragged one
SequencePlayerNode - > Node . Sequence = Sequence ;
}
}
else if ( UBlendSpace * BlendSpace = Cast < UBlendSpace > ( Asset ) )
{
if ( IsAimOffsetBlendSpace ( BlendSpace ) )
{
if ( UAnimGraphNode_RotationOffsetBlendSpace * RotationOffsetNode = Cast < UAnimGraphNode_RotationOffsetBlendSpace > ( K2Node ) )
{
// Skeleton matches, and it's a blendspace player; replace the existing blendspace with the dragged one
RotationOffsetNode - > Node . BlendSpace = BlendSpace ;
}
}
else
{
if ( UAnimGraphNode_BlendSpacePlayer * BlendSpacePlayerNode = Cast < UAnimGraphNode_BlendSpacePlayer > ( K2Node ) )
{
// Skeleton matches, and it's a blendspace player; replace the existing blendspace with the dragged one
BlendSpacePlayerNode - > Node . BlendSpace = BlendSpace ;
}
}
}
}
}
void UAnimationGraphSchema : : DroppedAssetsOnGraph ( const TArray < FAssetData > & Assets , const FVector2D & GraphPosition , UEdGraph * Graph ) const
{
UAnimationAsset * Asset = FAssetData : : GetFirstAsset < UAnimationAsset > ( Assets ) ;
if ( ( Asset ! = NULL ) & & ( Graph ! = NULL ) )
{
SpawnNodeFromAsset ( Asset , GraphPosition , Graph , NULL ) ;
}
}
void UAnimationGraphSchema : : DroppedAssetsOnNode ( const TArray < FAssetData > & Assets , const FVector2D & GraphPosition , UEdGraphNode * Node ) const
{
UAnimationAsset * Asset = FAssetData : : GetFirstAsset < UAnimationAsset > ( Assets ) ;
UK2Node * K2Node = Cast < UK2Node > ( Node ) ;
if ( ( Asset ! = NULL ) & & ( K2Node ! = NULL ) )
{
UpdateNodeWithAsset ( K2Node , Asset ) ;
}
}
void UAnimationGraphSchema : : DroppedAssetsOnPin ( const TArray < FAssetData > & Assets , const FVector2D & GraphPosition , UEdGraphPin * Pin ) const
{
UAnimationAsset * Asset = FAssetData : : GetFirstAsset < UAnimationAsset > ( Assets ) ;
if ( ( Asset ! = NULL ) & & ( Pin ! = NULL ) )
{
SpawnNodeFromAsset ( Asset , GraphPosition , Pin - > GetOwningNode ( ) - > GetGraph ( ) , Pin ) ;
}
}
void UAnimationGraphSchema : : GetAssetsNodeHoverMessage ( const TArray < FAssetData > & Assets , const UEdGraphNode * HoverNode , FString & OutTooltipText , bool & OutOkIcon ) const
{
UAnimationAsset * Asset = FAssetData : : GetFirstAsset < UAnimationAsset > ( Assets ) ;
if ( ( Asset = = NULL ) | | ( HoverNode = = NULL ) )
{
OutTooltipText = TEXT ( " " ) ;
OutOkIcon = false ;
return ;
}
const UK2Node * PlayerNodeUnderCursor = NULL ;
if ( Asset - > IsA ( UAnimSequence : : StaticClass ( ) ) )
{
PlayerNodeUnderCursor = Cast < const UAnimGraphNode_SequencePlayer > ( HoverNode ) ;
}
else if ( Asset - > IsA ( UBlendSpace : : StaticClass ( ) ) )
{
UBlendSpace * BlendSpace = CastChecked < UBlendSpace > ( Asset ) ;
if ( IsAimOffsetBlendSpace ( BlendSpace ) )
{
PlayerNodeUnderCursor = Cast < const UAnimGraphNode_RotationOffsetBlendSpace > ( HoverNode ) ;
}
else
{
PlayerNodeUnderCursor = Cast < const UAnimGraphNode_BlendSpacePlayer > ( HoverNode ) ;
}
}
// this one only should happen when there is an Anim Blueprint
UAnimBlueprint * AnimBlueprint = Cast < UAnimBlueprint > ( FBlueprintEditorUtils : : FindBlueprintForNode ( HoverNode ) ) ;
const bool bSkelMatch = ( AnimBlueprint ! = NULL ) & & ( AnimBlueprint - > TargetSkeleton = = Asset - > GetSkeleton ( ) ) ;
if ( ! bSkelMatch )
{
OutOkIcon = false ;
OutTooltipText = FString : : Printf ( TEXT ( " Skeletons are not compatible " ) ) ;
}
else if ( PlayerNodeUnderCursor ! = NULL )
{
OutOkIcon = true ;
OutTooltipText = FString : : Printf ( TEXT ( " Change node to play %s " ) , * ( Asset - > GetName ( ) ) ) ;
}
else
{
OutOkIcon = false ;
OutTooltipText = FString : : Printf ( TEXT ( " Cannot replace '%s' with a sequence player " ) , * ( HoverNode - > GetName ( ) ) ) ;
}
}
void UAnimationGraphSchema : : GetAssetsPinHoverMessage ( const TArray < FAssetData > & Assets , const UEdGraphPin * HoverPin , FString & OutTooltipText , bool & OutOkIcon ) const
{
UAnimationAsset * Asset = FAssetData : : GetFirstAsset < UAnimationAsset > ( Assets ) ;
if ( ( Asset = = NULL ) | | ( HoverPin = = NULL ) )
{
OutTooltipText = TEXT ( " " ) ;
OutOkIcon = false ;
return ;
}
// this one only should happen when there is an Anim Blueprint
UAnimBlueprint * AnimBlueprint = Cast < UAnimBlueprint > ( FBlueprintEditorUtils : : FindBlueprintForNode ( HoverPin - > GetOwningNode ( ) ) ) ;
const bool bSkelMatch = ( AnimBlueprint ! = NULL ) & & ( AnimBlueprint - > TargetSkeleton = = Asset - > GetSkeleton ( ) ) ;
const bool bTypeMatch = UAnimationGraphSchema : : IsLocalSpacePosePin ( HoverPin - > PinType ) ;
const bool bDirectionMatch = HoverPin - > Direction = = EGPD_Input ;
if ( bSkelMatch & & bTypeMatch & & bDirectionMatch )
{
OutOkIcon = true ;
OutTooltipText = FString : : Printf ( TEXT ( " Play %s and feed to %s " ) , * ( Asset - > GetName ( ) ) , * HoverPin - > PinName ) ;
}
else
{
OutOkIcon = false ;
OutTooltipText = FString : : Printf ( TEXT ( " Type or direction mismatch; must be wired to a pose input " ) ) ;
}
}
void UAnimationGraphSchema : : GetAssetsGraphHoverMessage ( const TArray < FAssetData > & Assets , const UEdGraph * HoverGraph , FString & OutTooltipText , bool & OutOkIcon ) const
{
UAnimationAsset * Asset = FAssetData : : GetFirstAsset < UAnimationAsset > ( Assets ) ;
if ( Asset )
{
UAnimBlueprint * AnimBlueprint = Cast < UAnimBlueprint > ( FBlueprintEditorUtils : : FindBlueprintForGraph ( HoverGraph ) ) ;
const bool bSkelMatch = ( AnimBlueprint ! = NULL ) & & ( AnimBlueprint - > TargetSkeleton = = Asset - > GetSkeleton ( ) ) ;
if ( ! bSkelMatch )
{
OutOkIcon = false ;
OutTooltipText = FString : : Printf ( TEXT ( " Skeletons are not compatible " ) ) ;
}
}
}
void UAnimationGraphSchema : : GetContextMenuActions ( const UEdGraph * CurrentGraph , const UEdGraphNode * InGraphNode , const UEdGraphPin * InGraphPin , FMenuBuilder * MenuBuilder , bool bIsDebugging ) const
{
Super : : GetContextMenuActions ( CurrentGraph , InGraphNode , InGraphPin , MenuBuilder , bIsDebugging ) ;
}
FString UAnimationGraphSchema : : GetPinDisplayName ( const UEdGraphPin * Pin ) const
{
check ( Pin ! = NULL ) ;
FString DisplayName = Super : : GetPinDisplayName ( Pin ) ;
if ( UAnimGraphNode_Base * Node = Cast < UAnimGraphNode_Base > ( Pin - > GetOwningNode ( ) ) )
{
Node - > PostProcessPinName ( Pin , DisplayName ) ;
}
return DisplayName ;
}
# undef LOCTEXT_NAMESPACE