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
# include "MovieSceneToolsPrivatePCH.h"
# include "ScopedTransaction.h"
# include "K2Node_PlayMovieScene.h"
# include "BlueprintUtilities.h"
# include "MovieScene.h"
# include "Kismet2/BlueprintEditorUtils.h"
2015-06-30 19:01:05 -04:00
# include "Kismet/KismetSystemLibrary.h"
2014-03-14 14:13:41 -04:00
# include "MovieSceneBindings.h"
UEdGraphNode * FEdGraphSchemaAction_K2AddPlayMovieScene : : PerformAction ( class UEdGraph * ParentGraph , UEdGraphPin * FromPin , const FVector2D Location , bool bSelectNewNode /* = true*/ )
{
const FScopedTransaction Transaction ( NSLOCTEXT ( " PlayMovieSceneNode " , " UndoAddingNewNode " , " Add PlayMovieScene Node " ) ) ;
UBlueprint * Blueprint = FBlueprintEditorUtils : : FindBlueprintForGraphChecked ( ParentGraph ) ;
UEdGraphNode * NewNode = FEdGraphSchemaAction_K2NewNode : : PerformAction ( ParentGraph , FromPin , Location , bSelectNewNode ) ;
if ( NewNode ! = NULL & & Blueprint ! = NULL )
{
UK2Node_PlayMovieScene * PlayMovieSceneNode = CastChecked < UK2Node_PlayMovieScene > ( NewNode ) ;
// @todo sequencer: Perform any setup work on the new PlayMovieScene node here!
}
return NewNode ;
}
2014-10-14 10:29:11 -04:00
UK2Node_PlayMovieScene : : UK2Node_PlayMovieScene ( const FObjectInitializer & ObjectInitializer )
: Super ( ObjectInitializer )
2014-03-14 14:13:41 -04:00
{
}
namespace PlayMovieScenePinNames
{
static FString Play ( TEXT ( " Play " ) ) ;
static FString Pause ( TEXT ( " Pause " ) ) ;
}
void UK2Node_PlayMovieScene : : AllocateDefaultPins ( )
{
const UEdGraphSchema_K2 * K2Schema = GetDefault < UEdGraphSchema_K2 > ( ) ;
// Do not allow users to rename the node
// @todo sequencer: If we want to support renaming, this we'll need to update FNameValidatorFactory::MakeValidator() and perhaps other locations
bCanRenameNode = 0 ;
// "Play" starts or resumes playback from the current position
CreatePin ( EGPD_Input , K2Schema - > PC_Exec , TEXT ( " " ) , NULL , false , false , PlayMovieScenePinNames : : Play ) ;
// "Pause" stops playback, leaving the time cursor at its current position
CreatePin ( EGPD_Input , K2Schema - > PC_Exec , TEXT ( " " ) , NULL , false , false , PlayMovieScenePinNames : : Pause ) ;
// @todo sequencer: Add PlayFromStart?
// @todo sequencer: Add PlayReverse pin? PlayReverseFromEnd?
// @todo sequencer: Add "set time" input
// @todo sequencer: Needs output pin for "finished playing"
if ( MovieSceneBindings ! = NULL )
{
for ( auto BoundObjectIter ( MovieSceneBindings - > GetBoundObjects ( ) . CreateIterator ( ) ) ; BoundObjectIter ; + + BoundObjectIter )
{
auto & BoundObject = * BoundObjectIter ;
2015-06-30 14:49:26 -04:00
CreatePinsForBoundObject ( BoundObject ) ;
2014-03-14 14:13:41 -04:00
}
}
Super : : AllocateDefaultPins ( ) ;
}
FLinearColor UK2Node_PlayMovieScene : : GetNodeTitleColor ( ) const
{
return FLinearColor ( 0.8f , 0.05f , 0.05f ) ;
}
2014-04-23 18:30:37 -04:00
FText UK2Node_PlayMovieScene : : GetNodeTitle ( ENodeTitleType : : Type TitleType ) const
{
2014-09-04 11:25:05 -04:00
UMovieScene * MovieScene = ( MovieSceneBindings ! = nullptr ) ? MovieSceneBindings - > GetRootMovieScene ( ) : nullptr ;
if ( MovieScene = = nullptr )
{
return NSLOCTEXT ( " PlayMovieSceneNode " , " NodeTitleWithNoMovieScene " , " Play Movie Scene (No Asset) " ) ;
}
2014-09-24 14:15:13 -04:00
// @TODO: don't know enough about this node type to comfortably assert that
// the MovieScene won't change after the node has spawned... until
// then, we'll leave this optimization off
2015-04-02 11:16:23 -04:00
else //if (CachedNodeTitle.IsOutOfDate(this))
2014-09-04 11:25:05 -04:00
{
FFormatNamedArguments Args ;
Args . Add ( TEXT ( " SceneName " ) , FText : : FromString ( MovieScene - > GetName ( ) ) ) ;
// FText::Format() is slow, so we cache this to save on performance
2015-04-02 11:16:23 -04:00
CachedNodeTitle . SetCachedText ( FText : : Format ( NSLOCTEXT ( " PlayMovieSceneNode " , " NodeTitle " , " Play Movie Scene: {SceneName} " ) , Args ) , this ) ;
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
UEdGraphPin * UK2Node_PlayMovieScene : : GetPlayPin ( ) const
{
UEdGraphPin * Pin = FindPinChecked ( PlayMovieScenePinNames : : Play ) ;
check ( Pin - > Direction = = EGPD_Input ) ;
return Pin ;
}
UEdGraphPin * UK2Node_PlayMovieScene : : GetPausePin ( ) const
{
UEdGraphPin * Pin = FindPinChecked ( PlayMovieScenePinNames : : Pause ) ;
check ( Pin - > Direction = = EGPD_Input ) ;
return Pin ;
}
void UK2Node_PlayMovieScene : : PinConnectionListChanged ( UEdGraphPin * InPin )
{
// Call parent implementation
Super : : PostReconstructNode ( ) ;
// @todo sequencer: What about the "default" value for the pin? Right now we don't do anything with that. Might feel buggy.
// --> Actually, we can't use that yet because Actor references won't be fixed up for PIE except for literals
CreateBindingsIfNeeded ( ) ;
// Update the MovieScene bindings for any changes that were made to the node
// @todo sequencer: Only a single incoming Pin is changing, but we're refreshing all pin states here. Could be simplified
bool bAnyBindingsChanged = false ;
{
auto & BoundObjects = MovieSceneBindings - > GetBoundObjects ( ) ;
for ( auto BoundObjectIter ( BoundObjects . CreateIterator ( ) ) ; BoundObjectIter ; + + BoundObjectIter )
{
auto & BoundObject = * BoundObjectIter ;
2015-06-30 14:49:26 -04:00
TArray < FMovieSceneBoundObjectInfo > OtherObjectInfos ;
2014-03-14 14:13:41 -04:00
// Find the pin that goes with this object
2015-06-30 14:49:26 -04:00
UEdGraphPin * ActorPin = FindPinChecked ( BoundObject . GetPossessableGuid ( ) . ToString ( EGuidFormats : : DigitsWithHyphens ) ) ;
UEdGraphPin * ComponentNamePin = FindPinChecked ( BoundObject . GetPossessableGuid ( ) . ToString ( EGuidFormats : : DigitsWithHyphens ) + " _ComponentName " ) ;
2014-03-14 14:13:41 -04:00
// Is the pin even linked to anything?
2015-06-30 14:49:26 -04:00
for ( int32 i = 0 ; i < ActorPin - > LinkedTo . Num ( ) ; i + + )
2014-03-14 14:13:41 -04:00
{
2015-06-30 14:49:26 -04:00
UEdGraphPin * LinkedActorPin = ActorPin - > LinkedTo [ i ] ;
2015-06-30 19:01:05 -04:00
UEdGraphPin * LinkedComponentNamePin = ComponentNamePin - > LinkedTo . Num ( ) > i ? ComponentNamePin - > LinkedTo [ i ] : NULL ;
2015-06-30 14:49:26 -04:00
if ( LinkedActorPin ! = NULL )
2014-03-14 14:13:41 -04:00
{
2015-06-30 14:49:26 -04:00
// Look for an object bound to a literal. We can use these for scrub preview in the editor!
UK2Node_Literal * LinkedActorLiteralNode = Cast < UK2Node_Literal > ( LinkedActorPin - > GetOwningNode ( ) ) ;
if ( LinkedActorLiteralNode ! = NULL )
2014-03-14 14:13:41 -04:00
{
2015-06-30 14:49:26 -04:00
// @todo sequencer: Because we only recognize object literals, any dynamically bound actor won't be scrubable
// in the editor. Maybe we should support a "preview actor" that can be hooked up just for scrubbing and puppeting?
// ==> Maybe use the pin's default value as the preview actor, even when overridden with a dynamically spawned actor?
UObject * LinkedActor = LinkedActorLiteralNode - > GetObjectRef ( ) ;
if ( LinkedActor ! = NULL )
2014-03-14 14:13:41 -04:00
{
2015-06-30 14:49:26 -04:00
FString LinkedComponentName ;
2015-06-30 19:01:05 -04:00
if ( LinkedComponentNamePin ! = NULL )
2014-03-14 14:13:41 -04:00
{
2015-06-30 19:01:05 -04:00
UK2Node_CallFunction * LinkedComponentNameLiteralNode = Cast < UK2Node_CallFunction > ( LinkedComponentNamePin - > GetOwningNode ( ) ) ;
2015-06-30 14:49:26 -04:00
if ( LinkedComponentNameLiteralNode ! = NULL )
{
2015-06-30 19:01:05 -04:00
LinkedComponentName = LinkedComponentNameLiteralNode - > FindPinChecked ( " Value " ) - > DefaultValue ;
2015-06-30 14:49:26 -04:00
}
}
OtherObjectInfos . Add ( FMovieSceneBoundObjectInfo ( LinkedActor , LinkedComponentName ) ) ;
2014-03-14 14:13:41 -04:00
}
}
}
}
// Update our bindings to match the state of the node
2015-06-30 14:49:26 -04:00
if ( BoundObject . GetObjectInfos ( ) ! = OtherObjectInfos ) // @todo sequencer: Kind of weird to compare entire array (order change shouldn't cause us to invalidate). Change to a set compare?
2014-03-14 14:13:41 -04:00
{
// @todo sequencer: No type checking is happening here. Should we? (interactive during pin drag?)
Modify ( ) ;
2015-06-30 14:49:26 -04:00
BoundObject . SetObjectInfos ( OtherObjectInfos ) ;
2014-03-14 14:13:41 -04:00
bAnyBindingsChanged = true ;
}
}
}
if ( bAnyBindingsChanged )
{
OnBindingsChangedEvent . Broadcast ( ) ;
}
}
void UK2Node_PlayMovieScene : : CreateBindingsIfNeeded ( )
{
// NOTE: This function can be called on the transient copy of the UK2Node_PlayMovieScene before we've called
// SpawnNodeFromTemplate to create the actual node in the graph. Just something to be aware of if you go looking at
// Outers and find yourself in the transient package.
if ( MovieSceneBindings = = NULL )
{
Modify ( ) ;
// The new MovieSceneBindings object will be stored as an "inner" of this object
UObject * MovieSceneBindingsOuter = this ;
2015-02-03 05:40:57 -05:00
UMovieSceneBindings * NewMovieSceneBindings = NewObject < UMovieSceneBindings > ( MovieSceneBindingsOuter ) ;
2014-03-14 14:13:41 -04:00
check ( NewMovieSceneBindings ! = NULL ) ;
MovieSceneBindings = NewMovieSceneBindings ;
2014-09-04 11:25:05 -04:00
CachedNodeTitle . MarkDirty ( ) ;
2014-03-14 14:13:41 -04:00
}
}
UMovieScene * UK2Node_PlayMovieScene : : GetMovieScene ( )
{
return MovieSceneBindings ! = NULL ? MovieSceneBindings - > GetRootMovieScene ( ) : NULL ;
}
UMovieSceneBindings * UK2Node_PlayMovieScene : : GetMovieSceneBindings ( )
{
return MovieSceneBindings ;
}
void UK2Node_PlayMovieScene : : SetMovieScene ( class UMovieScene * NewMovieScene )
{
CreateBindingsIfNeeded ( ) ;
MovieSceneBindings - > SetRootMovieScene ( NewMovieScene ) ;
}
2015-06-30 14:49:26 -04:00
void UK2Node_PlayMovieScene : : BindPossessableToObjects ( const FGuid & PossessableGuid , const TArray < FMovieSceneBoundObjectInfo > & ObjectInfos )
2014-03-14 14:13:41 -04:00
{
CreateBindingsIfNeeded ( ) ;
// Bind the possessable from the MovieScene asset to the supplied object
2015-06-30 14:49:26 -04:00
FMovieSceneBoundObject & BoundObject = MovieSceneBindings - > AddBinding ( PossessableGuid , ObjectInfos ) ;
2014-03-14 14:13:41 -04:00
// Create a pin for the bound object
Modify ( ) ;
2015-06-30 14:49:26 -04:00
CreatePinsForBoundObject ( BoundObject ) ;
2014-03-14 14:13:41 -04:00
FBlueprintEditorUtils : : MarkBlueprintAsStructurallyModified ( this - > GetBlueprint ( ) ) ;
}
2015-06-30 14:49:26 -04:00
TArray < FMovieSceneBoundObjectInfo > UK2Node_PlayMovieScene : : FindBoundObjectInfos ( const FGuid & Guid )
2014-03-14 14:13:41 -04:00
{
2015-06-30 14:49:26 -04:00
TArray < UObject * > BoundObjects ;
2014-03-14 14:13:41 -04:00
if ( MovieSceneBindings ! = NULL )
{
2015-06-30 14:49:26 -04:00
return MovieSceneBindings - > FindBoundObjects ( Guid ) ;
2014-03-14 14:13:41 -04:00
}
2015-06-30 14:49:26 -04:00
return TArray < FMovieSceneBoundObjectInfo > ( ) ;
2014-03-14 14:13:41 -04:00
}
2015-06-30 14:49:26 -04:00
FGuid UK2Node_PlayMovieScene : : FindGuidForObjectInfo ( FMovieSceneBoundObjectInfo ObjectInfo ) const
2014-03-14 14:13:41 -04:00
{
if ( MovieSceneBindings ! = NULL )
{
// Search all bindings for the object
TArray < FMovieSceneBoundObject > & ObjectBindings = MovieSceneBindings - > GetBoundObjects ( ) ;
2015-06-30 14:49:26 -04:00
for ( FMovieSceneBoundObject & BoundObject : MovieSceneBindings - > GetBoundObjects ( ) )
2014-03-14 14:13:41 -04:00
{
2015-06-30 14:49:26 -04:00
if ( BoundObject . GetObjectInfos ( ) . Contains ( ObjectInfo ) )
2014-03-14 14:13:41 -04:00
{
2015-06-30 14:49:26 -04:00
return BoundObject . GetPossessableGuid ( ) ;
2014-03-14 14:13:41 -04:00
}
}
}
return FGuid ( ) ;
}
2015-06-30 14:49:26 -04:00
void UK2Node_PlayMovieScene : : CreatePinsForBoundObject ( FMovieSceneBoundObject & BoundObject )
2014-03-14 14:13:41 -04:00
{
const UEdGraphSchema_K2 * K2Schema = GetDefault < UEdGraphSchema_K2 > ( ) ;
// We use the GUID as the pin name as this uniquely identifies it
2015-06-30 14:49:26 -04:00
const FString ActorPinName = BoundObject . GetPossessableGuid ( ) . ToString ( EGuidFormats : : DigitsWithHyphens ) ;
const FString ComponentNamePinName = ActorPinName + " _ComponentName " ;
2014-03-14 14:13:41 -04:00
// For the friendly name, we use the possessable name from the MovieScene asset that is associated with this node
2015-06-30 14:49:26 -04:00
FText ActorPinFriendlyName = FText : : FromString ( ActorPinName ) ;
FText ComponentNamePinFriendlyName = FText : : FromString ( ComponentNamePinName ) ;
2014-03-14 14:13:41 -04:00
{
UMovieScene * MovieScene = GetMovieScene ( ) ;
2015-06-30 14:49:26 -04:00
if ( MovieScene ! = NULL ) // @todo sequencer: Need to refresh the ActorPinFriendlyName if the MovieScene asset changes, or if the possessable slot is renamed within Sequencer
2014-03-14 14:13:41 -04:00
{
for ( auto PossessableIndex = 0 ; PossessableIndex < MovieScene - > GetPossessableCount ( ) ; + + PossessableIndex )
{
auto & Possessable = MovieScene - > GetPossessable ( PossessableIndex ) ;
if ( Possessable . GetGuid ( ) = = BoundObject . GetPossessableGuid ( ) )
{
// Found a name for this possessable
2015-06-30 14:49:26 -04:00
ActorPinFriendlyName = FText : : FromString ( Possessable . GetName ( ) ) ;
ComponentNamePinFriendlyName = FText : : Format ( NSLOCTEXT ( " PlayMovieSceneNode " , " ComponentNamePinFormat " , " {0} Component Name " ) , FText : : FromString ( Possessable . GetName ( ) ) ) ;
2014-03-14 14:13:41 -04:00
break ;
}
}
}
}
const FString PinSubCategory ( TEXT ( " " ) ) ;
UObject * PinSubCategoryObject = AActor : : StaticClass ( ) ;
const bool bIsArray = false ;
const bool bIsReference = false ;
2015-06-30 14:49:26 -04:00
UEdGraphPin * NewActorPin = CreatePin ( EGPD_Input , K2Schema - > PC_Object , PinSubCategory , PinSubCategoryObject , bIsArray , bIsReference , ActorPinName ) ;
2015-06-30 19:01:05 -04:00
UEdGraphPin * NewComponentNamePin = CreatePin ( EGPD_Input , K2Schema - > PC_String , PinSubCategory , NULL , bIsArray , bIsReference , ComponentNamePinName ) ;
2015-06-30 14:49:26 -04:00
check ( NewActorPin ! = NULL & & NewComponentNamePin ! = NULL ) ;
2014-03-14 14:13:41 -04:00
// Set the friendly name for this pin
2015-06-30 14:49:26 -04:00
NewActorPin - > PinFriendlyName = ActorPinFriendlyName ;
NewComponentNamePin - > PinFriendlyName = ComponentNamePinFriendlyName ;
2014-03-14 14:13:41 -04:00
// Place a literal for the bound object and hook it up to the pin
// @todo sequencer: Should we instead set the default on the pin to the object instead?
2015-06-30 14:49:26 -04:00
const TArray < FMovieSceneBoundObjectInfo > & ObjectInfos = BoundObject . GetObjectInfos ( ) ;
if ( ObjectInfos . Num ( ) > 0 )
2014-03-14 14:13:41 -04:00
{
2015-06-30 14:49:26 -04:00
for ( const FMovieSceneBoundObjectInfo & BoundObjectInfo : ObjectInfos )
2014-03-14 14:13:41 -04:00
{
2015-06-30 14:49:26 -04:00
if ( ensure ( BoundObjectInfo . Object ! = NULL ) )
2014-03-14 14:13:41 -04:00
{
// Check to see if we have a literal for this object already
2015-06-30 14:49:26 -04:00
UK2Node_Literal * ActorLiteralNode = NULL ;
2015-06-30 19:01:05 -04:00
UK2Node_CallFunction * ComponentNameLiteralNode = NULL ;
2014-03-14 14:13:41 -04:00
{
TArray < UK2Node_Literal * > LiteralNodes ;
GetGraph ( ) - > GetNodesOfClass ( LiteralNodes ) ;
2015-06-30 19:01:05 -04:00
for ( UK2Node_Literal * LiteralNode : LiteralNodes )
2014-03-14 14:13:41 -04:00
{
2015-06-30 19:01:05 -04:00
if ( LiteralNode - > GetObjectRef ( ) = = BoundObjectInfo . Object )
2014-03-14 14:13:41 -04:00
{
// Found one!
2015-06-30 19:01:05 -04:00
ActorLiteralNode = LiteralNode ;
break ;
2015-06-30 14:49:26 -04:00
}
}
2015-06-30 19:01:05 -04:00
TArray < UK2Node_CallFunction * > CallFunctionNodes ;
GetGraph ( ) - > GetNodesOfClass ( CallFunctionNodes ) ;
for ( UK2Node_CallFunction * CallFunctionNode : CallFunctionNodes )
2015-06-30 14:49:26 -04:00
{
2015-06-30 19:01:05 -04:00
if ( CallFunctionNode - > FunctionReference . GetMemberName ( ) = = FName ( " MakeLiteralString " ) )
{
UEdGraphPin * ValuePin = CallFunctionNode - > FindPinChecked ( " Value " ) ;
if ( ValuePin - > DefaultValue = = BoundObjectInfo . Tag )
{
ComponentNameLiteralNode = CallFunctionNode ;
break ;
}
}
2014-03-14 14:13:41 -04:00
}
}
2015-06-30 14:49:26 -04:00
if ( ActorLiteralNode = = NULL )
2014-03-14 14:13:41 -04:00
{
// No literal for this object yet, so we'll make one now.
UK2Node_Literal * LiteralNodeTemplate = NewObject < UK2Node_Literal > ( ) ;
2015-06-30 14:49:26 -04:00
LiteralNodeTemplate - > SetObjectRef ( BoundObjectInfo . Object ) ;
2014-03-14 14:13:41 -04:00
// Figure out a decent place to stick the node
// @todo sequencer: The placement of these is really unacceptable. We should setup new logic specific for moviescene pin objects.
const FVector2D NewNodePos = GetGraph ( ) - > GetGoodPlaceForNewNode ( ) ;
2015-06-30 14:49:26 -04:00
ActorLiteralNode = FEdGraphSchemaAction_K2NewNode : : SpawnNodeFromTemplate < UK2Node_Literal > ( GetGraph ( ) , LiteralNodeTemplate , NewNodePos ) ;
2014-03-14 14:13:41 -04:00
}
// Hook up the object reference literal to our pin
2015-06-30 14:49:26 -04:00
ActorLiteralNode - > GetValuePin ( ) - > MakeLinkTo ( NewActorPin ) ;
if ( BoundObjectInfo . Tag . IsEmpty ( ) = = false )
{
if ( ComponentNameLiteralNode = = NULL )
{
2015-06-30 19:01:05 -04:00
UK2Node_CallFunction * MakeLiteralStringNodeTemplate = NewObject < UK2Node_CallFunction > ( ) ;
MakeLiteralStringNodeTemplate - > FunctionReference . SetExternalMember ( " MakeLiteralString " , UKismetSystemLibrary : : StaticClass ( ) ) ;
2015-06-30 14:49:26 -04:00
// @todo sequencer: The placement of these is really unacceptable. We should setup new logic specific for moviescene pin objects.
const FVector2D NewNodePos = GetGraph ( ) - > GetGoodPlaceForNewNode ( ) ;
2015-06-30 19:01:05 -04:00
ComponentNameLiteralNode = FEdGraphSchemaAction_K2NewNode : : SpawnNodeFromTemplate < UK2Node_CallFunction > ( GetGraph ( ) , MakeLiteralStringNodeTemplate , NewNodePos ) ;
UEdGraphPin * ValuePin = ComponentNameLiteralNode - > FindPinChecked ( FString ( " Value " ) ) ;
ValuePin - > DefaultValue = BoundObjectInfo . Tag ;
2015-06-30 14:49:26 -04:00
}
2015-06-30 19:01:05 -04:00
ComponentNameLiteralNode - > GetReturnValuePin ( ) - > MakeLinkTo ( NewComponentNamePin ) ;
2015-06-30 14:49:26 -04:00
}
2014-03-14 14:13:41 -04:00
}
}
}
}