2014-03-14 14:13:41 -04:00
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
# include "AnimGraphPrivatePCH.h"
# include "GraphEditorActions.h"
# include "ScopedTransaction.h"
# include "Kismet2NameValidators.h"
# include "K2ActionMenuBuilder.h" // for FK2ActionMenuBuilder::AddNewNodeAction()
2014-04-24 08:49:31 -04:00
# include "AnimGraphNode_SaveCachedPose.h"
2014-08-21 18:50:33 -04:00
# include "BlueprintNodeSpawner.h"
2014-08-23 20:16:29 -04:00
# include "BlueprintActionDatabaseRegistrar.h"
2014-03-14 14:13:41 -04:00
/////////////////////////////////////////////////////
// FCachedPoseNameValidator
class FCachedPoseNameValidator : public FStringSetNameValidator
{
public :
FCachedPoseNameValidator ( class UBlueprint * InBlueprint , const FString & InExistingName )
: FStringSetNameValidator ( InExistingName )
{
TArray < UAnimGraphNode_SaveCachedPose * > Nodes ;
FBlueprintEditorUtils : : GetAllNodesOfClass < UAnimGraphNode_SaveCachedPose > ( InBlueprint , Nodes ) ;
2014-04-24 08:49:31 -04:00
for ( auto Node : Nodes )
2014-03-14 14:13:41 -04:00
{
Names . Add ( Node - > CacheName ) ;
}
}
} ;
/////////////////////////////////////////////////////
// UAnimGraphNode_ComponentToLocalSpace
2014-04-23 18:30:37 -04:00
# define LOCTEXT_NAMESPACE "A3Nodes"
2014-03-14 14:13:41 -04:00
UAnimGraphNode_SaveCachedPose : : UAnimGraphNode_SaveCachedPose ( const FPostConstructInitializeProperties & PCIP )
: Super ( PCIP )
{
bCanRenameNode = true ;
}
2014-09-03 18:14:09 -04:00
FText UAnimGraphNode_SaveCachedPose : : GetTooltipText ( ) const
2014-03-14 14:13:41 -04:00
{
2014-09-03 18:14:09 -04:00
return LOCTEXT ( " SaveCachedPose_Tooltip " , " Denotes an animation tree that can be referenced elsewhere in the blueprint, which will be evaluated at most once per frame and then cached. " ) ;
2014-03-14 14:13:41 -04:00
}
2014-04-23 18:30:37 -04:00
FText UAnimGraphNode_SaveCachedPose : : GetNodeTitle ( ENodeTitleType : : Type TitleType ) const
2014-03-14 14:13:41 -04:00
{
2014-04-23 18:30:37 -04:00
if ( TitleType = = ENodeTitleType : : EditableTitle )
{
return FText : : FromString ( CacheName ) ;
}
2014-09-16 15:01:38 -04:00
else if ( ( TitleType = = ENodeTitleType : : MenuTitle ) & & CacheName . IsEmpty ( ) )
2014-08-21 18:50:33 -04:00
{
return LOCTEXT ( " NewSaveCachedPose " , " New Save cached pose... " ) ;
}
2014-09-24 14:15:13 -04:00
// @TODO: don't know enough about this node type to comfortably assert that
// the CacheName won't change after the node has spawned... until
// then, we'll leave this optimization off
else //if (CachedNodeTitle.IsOutOfDate())
2014-04-23 18:30:37 -04:00
{
FFormatNamedArguments Args ;
Args . Add ( TEXT ( " NodeTitle " ) , FText : : FromString ( CacheName ) ) ;
2014-09-04 11:25:05 -04:00
CachedNodeTitle = FText : : Format ( LOCTEXT ( " AnimGraphNode_SaveCachedPose_Title " , " Save cached pose '{NodeTitle}' " ) , Args ) ;
2014-04-23 18:30:37 -04:00
}
2014-09-04 11:25:05 -04:00
return CachedNodeTitle ;
2014-04-23 18:30:37 -04:00
}
2014-03-14 14:13:41 -04:00
FString UAnimGraphNode_SaveCachedPose : : GetNodeCategory ( ) const
{
return TEXT ( " Cached Poses " ) ;
}
void UAnimGraphNode_SaveCachedPose : : GetMenuEntries ( FGraphContextMenuBuilder & ContextMenuBuilder ) const
{
//@TODO: Check the type of the from pin to make sure it's a pose
if ( ( ContextMenuBuilder . FromPin = = NULL ) | | ( ContextMenuBuilder . FromPin - > Direction = = EGPD_Output ) )
{
// Disallow the graph being cached from residing in a state for safety and sanity; the tree is in effect it's own graph entirely and should be treated that way
const bool bNotInStateMachine = ContextMenuBuilder . CurrentGraph - > GetOuter ( ) - > IsA ( UAnimBlueprint : : StaticClass ( ) ) ;
if ( bNotInStateMachine )
{
// Offer save cached pose
UAnimGraphNode_SaveCachedPose * SaveCachedPose = NewObject < UAnimGraphNode_SaveCachedPose > ( ) ;
2014-08-21 18:50:33 -04:00
SaveCachedPose - > CacheName = TEXT ( " SavedPose " ) + FString : : FromInt ( FMath : : Rand ( ) ) ;
2014-03-14 14:13:41 -04:00
2014-09-03 18:14:09 -04:00
TSharedPtr < FEdGraphSchemaAction_K2NewNode > SaveCachedPoseAction = FK2ActionMenuBuilder : : AddNewNodeAction ( ContextMenuBuilder , GetNodeCategory ( ) , LOCTEXT ( " NewSaveCachedPose " , " New Save cached pose... " ) , SaveCachedPose - > GetTooltipText ( ) . ToString ( ) , 0 , SaveCachedPose - > GetKeywords ( ) ) ;
2014-03-14 14:13:41 -04:00
SaveCachedPoseAction - > NodeTemplate = SaveCachedPose ;
}
}
}
2014-08-23 20:16:29 -04:00
void UAnimGraphNode_SaveCachedPose : : GetMenuActions ( FBlueprintActionDatabaseRegistrar & ActionRegistrar ) const
2014-08-21 18:50:33 -04:00
{
auto PostSpawnSetupLambda = [ ] ( UEdGraphNode * NewNode , bool bIsTemplateNode )
{
UAnimGraphNode_SaveCachedPose * CachedPoseNode = CastChecked < UAnimGraphNode_SaveCachedPose > ( NewNode ) ;
// we use an empty CacheName in GetNodeTitle() to relay the proper menu title
if ( ! bIsTemplateNode )
{
// @TODO: is the idea that this name is unique? what if Rand() hit twice? why not MakeUniqueObjectName()?
CachedPoseNode - > CacheName = TEXT ( " SavedPose " ) + FString : : FromInt ( FMath : : Rand ( ) ) ;
}
} ;
2014-09-10 17:09:26 -04:00
// actions get registered under specific object-keys; the idea is that
// actions might have to be updated (or deleted) if their object-key is
// mutated (or removed)... here we use the node's class (so if the node
// type disappears, then the action should go with it)
UClass * ActionKey = GetClass ( ) ;
// to keep from needlessly instantiating a UBlueprintNodeSpawner, first
// check to make sure that the registrar is looking for actions of this type
// (could be regenerating actions for a specific asset, and therefore the
// registrar would only accept actions corresponding to that asset)
if ( ActionRegistrar . IsOpenForRegistration ( ActionKey ) )
{
UBlueprintNodeSpawner * NodeSpawner = UBlueprintNodeSpawner : : Create ( GetClass ( ) ) ;
NodeSpawner - > CustomizeNodeDelegate = UBlueprintNodeSpawner : : FCustomizeNodeDelegate : : CreateStatic ( PostSpawnSetupLambda ) ;
2014-08-21 18:50:33 -04:00
2014-09-10 17:09:26 -04:00
ActionRegistrar . AddBlueprintAction ( ActionKey , NodeSpawner ) ;
}
2014-08-21 18:50:33 -04:00
}
bool UAnimGraphNode_SaveCachedPose : : IsCompatibleWithGraph ( const UEdGraph * TargetGraph ) const
{
//EGraphType GraphType = TargetGraph->GetSchema()->GetGraphType(TargetGraph);
//bool const bIsNotStateMachine = (GraphType != GT_StateMachine);
bool const bIsNotStateMachine = TargetGraph - > GetOuter ( ) - > IsA ( UAnimBlueprint : : StaticClass ( ) ) ;
return bIsNotStateMachine & & Super : : IsCompatibleWithGraph ( TargetGraph ) ;
}
2014-03-14 14:13:41 -04:00
void UAnimGraphNode_SaveCachedPose : : OnRenameNode ( const FString & NewName )
{
CacheName = NewName ;
FBlueprintEditorUtils : : MarkBlueprintAsStructurallyModified ( GetBlueprint ( ) ) ;
}
TSharedPtr < class INameValidatorInterface > UAnimGraphNode_SaveCachedPose : : MakeNameValidator ( ) const
{
return MakeShareable ( new FCachedPoseNameValidator ( GetBlueprint ( ) , CacheName ) ) ;
2014-04-23 18:30:37 -04:00
}
# undef LOCTEXT_NAMESPACE