2014-12-07 19:09:38 -05:00
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
2014-03-14 14:13:41 -04:00
/*=============================================================================
AnimationStateMachineSchema . cpp
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
# include "AnimGraphPrivatePCH.h"
2014-04-24 14:34:01 -04:00
# include "AnimationStateMachineGraph.h"
2014-04-24 08:49:31 -04:00
# include "AnimationStateMachineSchema.h"
2014-04-24 14:34:01 -04:00
# include "AnimationGraphSchema.h"
2014-04-24 08:49:31 -04:00
# include "AnimStateEntryNode.h"
2014-04-24 14:34:01 -04:00
# include "AnimStateNode.h"
# include "AnimStateTransitionNode.h"
# include "AnimStateConduitNode.h"
# include "AnimStateNodeBase.h"
2014-04-24 08:49:31 -04:00
2014-03-14 14:13:41 -04:00
# include "ScopedTransaction.h"
# include "GraphEditorActions.h"
# include "EdGraphUtilities.h"
# include "AssetData.h"
# include "Kismet2/KismetEditorUtilities.h"
2014-10-14 22:50:06 -04:00
# include "GenericCommands.h"
2014-03-14 14:13:41 -04:00
# define LOCTEXT_NAMESPACE "AnimationStateMachineSchema"
/////////////////////////////////////////////////////
2014-04-23 18:30:37 -04:00
TSharedPtr < FEdGraphSchemaAction_NewStateNode > AddNewStateNodeAction ( FGraphContextMenuBuilder & ContextMenuBuilder , const FString & Category , const FText & MenuDesc , const FString & Tooltip , const int32 Grouping = 0 )
2014-03-14 14:13:41 -04:00
{
TSharedPtr < FEdGraphSchemaAction_NewStateNode > NewStateNode ( new FEdGraphSchemaAction_NewStateNode ( Category , MenuDesc , Tooltip , Grouping ) ) ;
ContextMenuBuilder . AddAction ( NewStateNode ) ;
return NewStateNode ;
}
/////////////////////////////////////////////////////
// FEdGraphSchemaAction_NewStateNode
UEdGraphNode * FEdGraphSchemaAction_NewStateNode : : PerformAction ( class UEdGraph * ParentGraph , UEdGraphPin * FromPin , const FVector2D Location , bool bSelectNewNode /* = true*/ )
{
UEdGraphNode * ResultNode = NULL ;
// If there is a template, we actually use it
if ( NodeTemplate ! = NULL )
{
const FScopedTransaction Transaction ( NSLOCTEXT ( " UnrealEd " , " K2_AddNode " , " Add Node " ) ) ;
ParentGraph - > Modify ( ) ;
if ( FromPin )
{
FromPin - > Modify ( ) ;
}
NodeTemplate - > SetFlags ( RF_Transactional ) ;
// set outer to be the graph so it doesn't go away
NodeTemplate - > Rename ( NULL , ParentGraph ) ;
2015-01-20 14:04:44 -05:00
ParentGraph - > AddNode ( NodeTemplate , false , bSelectNewNode ) ;
2014-03-14 14:13:41 -04:00
NodeTemplate - > CreateNewGuid ( ) ;
NodeTemplate - > PostPlacedNewNode ( ) ;
NodeTemplate - > AllocateDefaultPins ( ) ;
NodeTemplate - > AutowireNewNode ( FromPin ) ;
NodeTemplate - > NodePosX = Location . X ;
NodeTemplate - > NodePosY = Location . Y ;
//@TODO: ANIM: SNAP_GRID isn't centralized or exposed - NodeTemplate->SnapToGrid(SNAP_GRID);
ResultNode = NodeTemplate ;
UBlueprint * Blueprint = FBlueprintEditorUtils : : FindBlueprintForGraphChecked ( ParentGraph ) ;
FBlueprintEditorUtils : : MarkBlueprintAsStructurallyModified ( Blueprint ) ;
}
return ResultNode ;
}
void FEdGraphSchemaAction_NewStateNode : : AddReferencedObjects ( FReferenceCollector & Collector )
{
FEdGraphSchemaAction : : AddReferencedObjects ( Collector ) ;
// These don't get saved to disk, but we want to make sure the objects don't get GC'd while the action array is around
Collector . AddReferencedObject ( NodeTemplate ) ;
}
/////////////////////////////////////////////////////
// FEdGraphSchemaAction_NewStateComment
UEdGraphNode * FEdGraphSchemaAction_NewStateComment : : PerformAction ( class UEdGraph * ParentGraph , UEdGraphPin * FromPin , const FVector2D Location , bool bSelectNewNode /* = true*/ )
{
// Add menu item for creating comment boxes
UEdGraphNode_Comment * CommentTemplate = NewObject < UEdGraphNode_Comment > ( ) ;
UBlueprint * Blueprint = FBlueprintEditorUtils : : FindBlueprintForGraph ( ParentGraph ) ;
FVector2D SpawnLocation = Location ;
FSlateRect Bounds ;
if ( ( Blueprint ! = NULL ) & & FKismetEditorUtilities : : GetBoundsForSelectedNodes ( Blueprint , Bounds , 50.0f ) )
{
CommentTemplate - > SetBounds ( Bounds ) ;
SpawnLocation . X = CommentTemplate - > NodePosX ;
SpawnLocation . Y = CommentTemplate - > NodePosY ;
}
return FEdGraphSchemaAction_NewNode : : SpawnNodeFromTemplate < UEdGraphNode_Comment > ( ParentGraph , CommentTemplate , SpawnLocation ) ;
}
/////////////////////////////////////////////////////
// UAnimationStateMachineSchema
2014-10-14 10:29:11 -04:00
UAnimationStateMachineSchema : : UAnimationStateMachineSchema ( const FObjectInitializer & ObjectInitializer )
: Super ( ObjectInitializer )
2014-03-14 14:13:41 -04:00
{
PC_Exec = TEXT ( " exec " ) ;
}
void UAnimationStateMachineSchema : : CreateDefaultNodesForGraph ( UEdGraph & Graph ) const
{
// Create the entry/exit tunnels
FGraphNodeCreator < UAnimStateEntryNode > NodeCreator ( Graph ) ;
UAnimStateEntryNode * EntryNode = NodeCreator . CreateNode ( ) ;
NodeCreator . Finalize ( ) ;
2014-09-12 18:37:04 -04:00
SetNodeMetaData ( EntryNode , FNodeMetadata : : DefaultGraphNode ) ;
2014-03-14 14:13:41 -04:00
if ( UAnimationStateMachineGraph * StateMachineGraph = CastChecked < UAnimationStateMachineGraph > ( & Graph ) )
{
StateMachineGraph - > EntryNode = EntryNode ;
}
}
const FPinConnectionResponse UAnimationStateMachineSchema : : CanCreateConnection ( const UEdGraphPin * PinA , const UEdGraphPin * PinB ) const
{
// Make sure the pins are not on the same node
if ( PinA - > GetOwningNode ( ) = = PinB - > GetOwningNode ( ) )
{
return FPinConnectionResponse ( CONNECT_RESPONSE_DISALLOW , TEXT ( " Both are on the same node " ) ) ;
}
// Connect entry node to a state is OK
const bool bPinAIsEntry = PinA - > GetOwningNode ( ) - > IsA ( UAnimStateEntryNode : : StaticClass ( ) ) ;
const bool bPinBIsEntry = PinB - > GetOwningNode ( ) - > IsA ( UAnimStateEntryNode : : StaticClass ( ) ) ;
const bool bPinAIsStateNode = PinA - > GetOwningNode ( ) - > IsA ( UAnimStateNodeBase : : StaticClass ( ) ) ;
const bool bPinBIsStateNode = PinB - > GetOwningNode ( ) - > IsA ( UAnimStateNodeBase : : StaticClass ( ) ) ;
if ( bPinAIsEntry | | bPinBIsEntry )
{
if ( bPinAIsEntry & & bPinBIsStateNode )
{
return FPinConnectionResponse ( CONNECT_RESPONSE_BREAK_OTHERS_A , TEXT ( " " ) ) ;
}
if ( bPinBIsEntry & & bPinAIsStateNode )
{
return FPinConnectionResponse ( CONNECT_RESPONSE_BREAK_OTHERS_B , TEXT ( " " ) ) ;
}
return FPinConnectionResponse ( CONNECT_RESPONSE_DISALLOW , TEXT ( " Entry must connect to a state node " ) ) ;
}
const bool bPinAIsTransition = PinA - > GetOwningNode ( ) - > IsA ( UAnimStateTransitionNode : : StaticClass ( ) ) ;
const bool bPinBIsTransition = PinB - > GetOwningNode ( ) - > IsA ( UAnimStateTransitionNode : : StaticClass ( ) ) ;
if ( bPinAIsTransition & & bPinBIsTransition )
{
return FPinConnectionResponse ( CONNECT_RESPONSE_DISALLOW , TEXT ( " Cannot wire a transition to a transition " ) ) ;
}
// Compare the directions
bool bDirectionsOK = false ;
if ( ( PinA - > Direction = = EGPD_Input ) & & ( PinB - > Direction = = EGPD_Output ) )
{
bDirectionsOK = true ;
}
else if ( ( PinB - > Direction = = EGPD_Input ) & & ( PinA - > Direction = = EGPD_Output ) )
{
bDirectionsOK = true ;
}
/*
if ( ! bDirectionsOK )
{
return FPinConnectionResponse ( CONNECT_RESPONSE_DISALLOW , TEXT ( " Directions are not compatible " ) ) ;
}
*/
// Transitions are exclusive (both input and output), but states are not
if ( bPinAIsTransition )
{
return FPinConnectionResponse ( CONNECT_RESPONSE_BREAK_OTHERS_A , TEXT ( " " ) ) ;
}
else if ( bPinBIsTransition )
{
return FPinConnectionResponse ( CONNECT_RESPONSE_BREAK_OTHERS_B , TEXT ( " " ) ) ;
}
else if ( ! bPinAIsTransition & & ! bPinBIsTransition )
{
return FPinConnectionResponse ( CONNECT_RESPONSE_MAKE_WITH_CONVERSION_NODE , TEXT ( " Create a transition " ) ) ;
}
else
{
return FPinConnectionResponse ( CONNECT_RESPONSE_MAKE , TEXT ( " " ) ) ;
}
}
bool UAnimationStateMachineSchema : : TryCreateConnection ( UEdGraphPin * PinA , UEdGraphPin * PinB ) const
{
if ( PinB - > Direction = = PinA - > Direction )
{
2014-07-08 11:13:59 -04:00
if ( UAnimStateNodeBase * Node = Cast < UAnimStateNodeBase > ( PinB - > GetOwningNode ( ) ) )
2014-03-14 14:13:41 -04:00
{
2014-07-08 11:13:59 -04:00
if ( PinA - > Direction = = EGPD_Input )
2014-03-14 14:13:41 -04:00
{
2014-07-08 11:13:59 -04:00
PinB = Node - > GetOutputPin ( ) ;
2014-03-14 14:13:41 -04:00
}
else
{
2014-07-08 11:13:59 -04:00
PinB = Node - > GetInputPin ( ) ;
2014-03-14 14:13:41 -04:00
}
}
}
const bool bModified = UEdGraphSchema : : TryCreateConnection ( PinA , PinB ) ;
if ( bModified )
{
UBlueprint * Blueprint = FBlueprintEditorUtils : : FindBlueprintForNodeChecked ( PinA - > GetOwningNode ( ) ) ;
FBlueprintEditorUtils : : MarkBlueprintAsModified ( Blueprint ) ;
}
return bModified ;
}
bool UAnimationStateMachineSchema : : CreateAutomaticConversionNodeAndConnections ( UEdGraphPin * PinA , UEdGraphPin * PinB ) const
{
UAnimStateNodeBase * NodeA = Cast < UAnimStateNodeBase > ( PinA - > GetOwningNode ( ) ) ;
UAnimStateNodeBase * NodeB = Cast < UAnimStateNodeBase > ( PinB - > GetOwningNode ( ) ) ;
if ( ( NodeA ! = NULL ) & & ( NodeB ! = NULL )
& & ( NodeA - > GetInputPin ( ) ! = NULL ) & & ( NodeA - > GetOutputPin ( ) ! = NULL )
& & ( NodeB - > GetInputPin ( ) ! = NULL ) & & ( NodeB - > GetOutputPin ( ) ! = NULL ) )
{
2014-09-17 13:26:44 -04:00
UAnimStateTransitionNode * TransitionNode = FEdGraphSchemaAction_NewStateNode : : SpawnNodeFromTemplate < UAnimStateTransitionNode > ( NodeA - > GetGraph ( ) , NewObject < UAnimStateTransitionNode > ( ) , FVector2D ( 0.0f , 0.0f ) , false ) ;
2014-03-14 14:13:41 -04:00
if ( PinA - > Direction = = EGPD_Output )
{
TransitionNode - > CreateConnections ( NodeA , NodeB ) ;
}
else
{
TransitionNode - > CreateConnections ( NodeB , NodeA ) ;
}
UBlueprint * Blueprint = FBlueprintEditorUtils : : FindBlueprintForGraphChecked ( TransitionNode - > GetBoundGraph ( ) ) ;
FBlueprintEditorUtils : : MarkBlueprintAsStructurallyModified ( Blueprint ) ;
return true ;
}
return false ;
}
void UAnimationStateMachineSchema : : GetGraphContextActions ( FGraphContextMenuBuilder & ContextMenuBuilder ) const
{
// Add state node
{
2014-04-23 18:30:37 -04:00
TSharedPtr < FEdGraphSchemaAction_NewStateNode > Action = AddNewStateNodeAction ( ContextMenuBuilder , TEXT ( " " ) , LOCTEXT ( " AddState " , " Add State... " ) , TEXT ( " A new state " ) ) ;
2014-03-14 14:13:41 -04:00
Action - > NodeTemplate = NewObject < UAnimStateNode > ( ContextMenuBuilder . OwnerOfTemporaries ) ;
}
// Add conduit node
{
2014-04-23 18:30:37 -04:00
TSharedPtr < FEdGraphSchemaAction_NewStateNode > Action = AddNewStateNodeAction ( ContextMenuBuilder , TEXT ( " " ) , LOCTEXT ( " AddConduit " , " Add Conduit... " ) , TEXT ( " A new conduit state " ) ) ;
2014-03-14 14:13:41 -04:00
Action - > NodeTemplate = NewObject < UAnimStateConduitNode > ( ContextMenuBuilder . OwnerOfTemporaries ) ;
}
// Entry point (only if doesn't already exist)
{
bool bHasEntry = false ;
for ( auto NodeIt = ContextMenuBuilder . CurrentGraph - > Nodes . CreateConstIterator ( ) ; NodeIt ; + + NodeIt )
{
UEdGraphNode * Node = * NodeIt ;
if ( const UAnimStateEntryNode * StateNode = Cast < UAnimStateEntryNode > ( Node ) )
{
bHasEntry = true ;
break ;
}
}
if ( ! bHasEntry )
{
2014-04-23 18:30:37 -04:00
TSharedPtr < FEdGraphSchemaAction_NewStateNode > Action = AddNewStateNodeAction ( ContextMenuBuilder , TEXT ( " " ) , LOCTEXT ( " AddEntryPoint " , " Add Entry Point... " ) , TEXT ( " Define State Machine's Entry Point " ) ) ;
2014-03-14 14:13:41 -04:00
Action - > NodeTemplate = NewObject < UAnimStateEntryNode > ( ContextMenuBuilder . OwnerOfTemporaries ) ;
}
}
// Add Comment
if ( ! ContextMenuBuilder . FromPin )
{
UBlueprint * OwnerBlueprint = FBlueprintEditorUtils : : FindBlueprintForGraphChecked ( ContextMenuBuilder . CurrentGraph ) ;
const bool bIsManyNodesSelected = ( FKismetEditorUtilities : : GetNumberOfSelectedNodes ( OwnerBlueprint ) > 0 ) ;
2014-04-23 18:30:37 -04:00
const FText MenuDescription = bIsManyNodesSelected ? LOCTEXT ( " CreateCommentSelection " , " Create Comment from Selection " ) : LOCTEXT ( " AddComment " , " Add Comment... " ) ;
2014-03-14 14:13:41 -04:00
const FString ToolTip = TEXT ( " Create a resizeable comment box around selected nodes. " ) ;
TSharedPtr < FEdGraphSchemaAction_NewStateComment > NewComment ( new FEdGraphSchemaAction_NewStateComment ( TEXT ( " " ) , MenuDescription , ToolTip , 0 ) ) ;
ContextMenuBuilder . AddAction ( NewComment ) ;
}
}
EGraphType UAnimationStateMachineSchema : : GetGraphType ( const UEdGraph * TestEdGraph ) const
{
return GT_StateMachine ;
}
void UAnimationStateMachineSchema : : GetContextMenuActions ( const UEdGraph * CurrentGraph , const UEdGraphNode * InGraphNode , const UEdGraphPin * InGraphPin , FMenuBuilder * MenuBuilder , bool bIsDebugging ) const
{
check ( CurrentGraph ) ;
UBlueprint * OwnerBlueprint = FBlueprintEditorUtils : : FindBlueprintForGraphChecked ( CurrentGraph ) ;
if ( InGraphNode ! = NULL )
{
MenuBuilder - > BeginSection ( " AnimationStateMachineNodeActions " , LOCTEXT ( " NodeActionsMenuHeader " , " Node Actions " ) ) ;
{
if ( ! bIsDebugging )
{
// Node contextual actions
MenuBuilder - > AddMenuEntry ( FGenericCommands : : Get ( ) . Delete ) ;
MenuBuilder - > AddMenuEntry ( FGenericCommands : : Get ( ) . Cut ) ;
MenuBuilder - > AddMenuEntry ( FGenericCommands : : Get ( ) . Copy ) ;
MenuBuilder - > AddMenuEntry ( FGenericCommands : : Get ( ) . Duplicate ) ;
MenuBuilder - > AddMenuEntry ( FGraphEditorCommands : : Get ( ) . ReconstructNodes ) ;
MenuBuilder - > AddMenuEntry ( FGraphEditorCommands : : Get ( ) . BreakNodeLinks ) ;
if ( InGraphNode - > bCanRenameNode )
{
MenuBuilder - > AddMenuEntry ( FGenericCommands : : Get ( ) . Rename ) ;
}
}
}
MenuBuilder - > EndSection ( ) ;
}
Super : : GetContextMenuActions ( CurrentGraph , InGraphNode , InGraphPin , MenuBuilder , bIsDebugging ) ;
}
FLinearColor UAnimationStateMachineSchema : : GetPinTypeColor ( const FEdGraphPinType & PinType ) const
{
return FLinearColor : : White ;
}
void UAnimationStateMachineSchema : : GetGraphDisplayInformation ( const UEdGraph & Graph , /*out*/ FGraphDisplayInfo & DisplayInfo ) const
{
2014-04-23 18:30:37 -04:00
DisplayInfo . PlainName = FText : : FromString ( Graph . GetName ( ) ) ;
DisplayInfo . DisplayName = DisplayInfo . PlainName ;
2014-03-14 14:13:41 -04:00
}
void UAnimationStateMachineSchema : : DroppedAssetsOnGraph ( const TArray < FAssetData > & Assets , const FVector2D & GraphPosition , UEdGraph * Graph ) const
{
UAnimationAsset * Asset = FAssetData : : GetFirstAsset < UAnimationAsset > ( Assets ) ;
if ( Asset ! = NULL )
{
// Spawn new state
UAnimStateNode * NewStateNode = FEdGraphSchemaAction_NewStateNode : : SpawnNodeFromTemplate < UAnimStateNode > ( Graph , NewObject < UAnimStateNode > ( ) , GraphPosition ) ;
// Try to name the state close to the asset
FEdGraphUtilities : : RenameGraphToNameOrCloseToName ( NewStateNode - > BoundGraph , Asset - > GetName ( ) ) ;
// Change the current graph context to the inner graph, so that the rest of the drag drop happens inside it
FVector2D NewGraphPosition = FVector2D ( - 300.0f , 0.0f ) ;
UAnimationGraphSchema : : SpawnNodeFromAsset ( Asset , NewGraphPosition , NewStateNode - > BoundGraph , NewStateNode - > GetPoseSinkPinInsideState ( ) ) ;
}
}
void UAnimationStateMachineSchema : : DroppedAssetsOnNode ( const TArray < FAssetData > & Assets , const FVector2D & GraphPosition , UEdGraphNode * Node ) const
{
UAnimationAsset * Asset = FAssetData : : GetFirstAsset < UAnimationAsset > ( Assets ) ;
UAnimStateNode * StateNodeUnderCursor = Cast < UAnimStateNode > ( Node ) ;
if ( Asset ! = NULL & & StateNodeUnderCursor ! = NULL )
{
// Dropped onto a state machine state; try some user friendly resposnes
if ( UEdGraphPin * PosePin = StateNodeUnderCursor - > GetPoseSinkPinInsideState ( ) )
{
if ( PosePin - > LinkedTo . Num ( ) > 0 )
{
//@TODO: A2REMOVAL: This doesn't do anything
/*
// Try dropping in onto the node attached to the sink inside the state
check ( PosePin - > LinkedTo [ 0 ] ! = NULL ) ;
UA2Node * A2Node = Cast < UA2Node > ( PosePin - > LinkedTo [ 0 ] - > GetOwningNode ( ) ) ;
if ( A2Node ! = NULL )
{
UAnimationGraphSchema : : UpdateNodeWithAsset ( A2Node , Asset ) ;
}
*/
}
else
{
// Try dropping onto the pose pin, since nothing is currently connected
const FVector2D NewGraphPosition ( - 300.0f , 0.0f ) ;
UAnimationGraphSchema : : SpawnNodeFromAsset ( Asset , NewGraphPosition , StateNodeUnderCursor - > BoundGraph , PosePin ) ;
}
}
}
}
void UAnimationStateMachineSchema : : DroppedAssetsOnPin ( const TArray < FAssetData > & Assets , const FVector2D & GraphPosition , UEdGraphPin * Pin ) const
{
// unused for state machines?
}
void UAnimationStateMachineSchema : : 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 UAnimStateNode * StateNodeUnderCursor = Cast < const UAnimStateNode > ( HoverNode ) ;
if ( StateNodeUnderCursor ! = NULL )
{
OutOkIcon = true ;
OutTooltipText = FString : : Printf ( TEXT ( " Change node to play %s " ) , * ( Asset - > GetName ( ) ) ) ;
}
else
{
OutTooltipText = TEXT ( " " ) ;
OutOkIcon = false ;
}
}
void UAnimationStateMachineSchema : : GetAssetsPinHoverMessage ( const TArray < FAssetData > & Assets , const UEdGraphPin * HoverPin , FString & OutTooltipText , bool & OutOkIcon ) const
{
// unused for state machines?
OutTooltipText = TEXT ( " " ) ;
OutOkIcon = false ;
}
# undef LOCTEXT_NAMESPACE