2022-06-02 03:01:54 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "LevelSequence.h"
2023-04-25 00:54:15 -04:00
# include "IMovieSceneMetaData.h"
# include "MovieSceneMetaData.h"
2022-06-02 03:01:54 -04:00
# include "Engine/EngineTypes.h"
# include "HAL/IConsoleManager.h"
2023-11-27 09:05:23 -05:00
# include "UniversalObjectLocator.h"
# include "UniversalObjectLocatorFragmentType.h"
# include "UniversalObjectLocatorResolveParameterBuffer.inl"
# include "UniversalObjectLocators/ActorLocatorFragment.h"
# include "WorldPartition/IWorldPartitionObjectResolver.h"
# include "LegacyLazyObjectPtrFragment.h"
# include "SubObjectLocator.h"
2022-06-02 03:01:54 -04:00
# include "Components/ActorComponent.h"
2022-10-26 12:57:32 -04:00
# include "Components/SkeletalMeshComponent.h"
2022-06-02 03:01:54 -04:00
# include "GameFramework/Actor.h"
# include "LevelSequenceDirector.h"
# include "Engine/Engine.h"
# include "MovieScene.h"
# include "MovieSceneCommonHelpers.h"
2023-12-08 21:45:21 -05:00
# include "UObject/AssetRegistryTagsContext.h"
2022-06-02 03:01:54 -04:00
# include "UObject/Package.h"
# include "UObject/UObjectHash.h"
# include "Animation/AnimInstance.h"
# include "LevelSequenceModule.h"
# include "MovieSceneSpawnableAnnotation.h"
# include "Tracks/MovieScene3DAttachTrack.h"
# include "Tracks/MovieScene3DPathTrack.h"
# include "Tracks/MovieSceneAudioTrack.h"
# include "Tracks/MovieSceneCameraCutTrack.h"
# include "Tracks/MovieSceneCinematicShotTrack.h"
# include "Tracks/MovieSceneEventTrack.h"
# include "Tracks/MovieSceneFadeTrack.h"
# include "Tracks/MovieSceneLevelVisibilityTrack.h"
# include "Tracks/MovieSceneDataLayerTrack.h"
# include "Tracks/MovieSceneMaterialParameterCollectionTrack.h"
2022-11-09 21:03:34 -05:00
# include "Tracks/MovieSceneSkeletalAnimationTrack.h"
2022-06-02 03:01:54 -04:00
# include "Tracks/MovieSceneSlomoTrack.h"
# include "Tracks/MovieSceneSpawnTrack.h"
# include "Tracks/MovieSceneSubTrack.h"
# include "Tracks/MovieSceneCVarTrack.h"
2023-11-10 07:34:17 -05:00
# include "Tracks/MovieSceneBindingLifetimeTrack.h"
2022-06-02 03:01:54 -04:00
# include "Modules/ModuleManager.h"
# include "LevelSequencePlayer.h"
# include "Compilation/MovieSceneCompiledDataManager.h"
# include "Evaluation/MovieSceneEvaluationTemplateInstance.h"
2023-11-27 09:05:23 -05:00
# include "UniversalObjectLocators/AnimInstanceLocatorFragment.h"
2022-06-02 03:01:54 -04:00
# include "Engine/AssetUserData.h"
2023-04-25 00:54:15 -04:00
# include "Misc/App.h"
# include "Misc/DateTime.h"
2022-06-02 03:01:54 -04:00
2022-09-24 13:57:58 -04:00
# include UE_INLINE_GENERATED_CPP_BY_NAME(LevelSequence)
2022-06-02 03:01:54 -04:00
# if WITH_EDITOR
# include "UObject/SequencerObjectVersion.h"
# include "UObject/ObjectRedirector.h"
ULevelSequence : : FPostDuplicateEvent ULevelSequence : : PostDuplicateEvent ;
# endif
static TAutoConsoleVariable < int32 > CVarDefaultLockEngineToDisplayRate (
TEXT ( " LevelSequence.DefaultLockEngineToDisplayRate " ) ,
0 ,
TEXT ( " 0: Playback locked to playback frames \n 1: Unlocked playback with sub frame interpolation " ) ,
ECVF_Default ) ;
static TAutoConsoleVariable < FString > CVarDefaultTickResolution (
TEXT ( " LevelSequence.DefaultTickResolution " ) ,
TEXT ( " 24000fps " ) ,
TEXT ( " Specifies the default tick resolution for newly created level sequences. Examples: 30 fps, 120/1 (120 fps), 30000/1001 (29.97), 0.01s (10ms). " ) ,
ECVF_Default ) ;
static TAutoConsoleVariable < FString > CVarDefaultDisplayRate (
TEXT ( " LevelSequence.DefaultDisplayRate " ) ,
TEXT ( " 30fps " ) ,
TEXT ( " Specifies the default display frame rate for newly created level sequences; also defines frame locked frame rate where sequences are set to be frame locked. Examples: 30 fps, 120/1 (120 fps), 30000/1001 (29.97), 0.01s (10ms). " ) ,
ECVF_Default ) ;
static TAutoConsoleVariable < int32 > CVarDefaultClockSource (
TEXT ( " LevelSequence.DefaultClockSource " ) ,
0 ,
TEXT ( " Specifies the default clock source for newly created level sequences. 0: Tick, 1: Platform, 2: Audio, 3: RelativeTimecode, 4: Timecode, 5: Custom " ) ,
ECVF_Default ) ;
ULevelSequence : : ULevelSequence ( const FObjectInitializer & ObjectInitializer )
: Super ( ObjectInitializer )
, MovieScene ( nullptr )
{
bParentContextsAreSignificant = true ;
}
void ULevelSequence : : Initialize ( )
{
MovieScene = NewObject < UMovieScene > ( this , NAME_None , RF_Transactional ) ;
const bool bFrameLocked = CVarDefaultLockEngineToDisplayRate . GetValueOnGameThread ( ) ! = 0 ;
MovieScene - > SetEvaluationType ( bFrameLocked ? EMovieSceneEvaluationType : : FrameLocked : EMovieSceneEvaluationType : : WithSubFrames ) ;
FFrameRate TickResolution ( 60000 , 1 ) ;
TryParseString ( TickResolution , * CVarDefaultTickResolution . GetValueOnGameThread ( ) ) ;
MovieScene - > SetTickResolutionDirectly ( TickResolution ) ;
FFrameRate DisplayRate ( 30 , 1 ) ;
TryParseString ( DisplayRate , * CVarDefaultDisplayRate . GetValueOnGameThread ( ) ) ;
MovieScene - > SetDisplayRate ( DisplayRate ) ;
int32 ClockSource = CVarDefaultClockSource . GetValueOnGameThread ( ) ;
MovieScene - > SetClockSource ( ( EUpdateClockSource ) ClockSource ) ;
2023-04-25 00:54:15 -04:00
# if WITH_EDITOR
UMovieSceneMetaData * MetaData = FindOrAddMetaData < UMovieSceneMetaData > ( ) ;
MetaData - > SetCreated ( FDateTime : : UtcNow ( ) ) ;
MetaData - > SetAuthor ( FApp : : GetSessionOwner ( ) ) ;
# endif
2022-06-02 03:01:54 -04:00
}
UObject * ULevelSequence : : MakeSpawnableTemplateFromInstance ( UObject & InSourceObject , FName ObjectName )
{
return MovieSceneHelpers : : MakeSpawnableTemplateFromInstance ( InSourceObject , MovieScene , ObjectName ) ;
}
bool ULevelSequence : : CanAnimateObject ( UObject & InObject ) const
{
return InObject . IsA < AActor > ( ) | | InObject . IsA < UActorComponent > ( ) | | InObject . IsA < UAnimInstance > ( ) ;
}
# if WITH_EDITOR
ETrackSupport ULevelSequence : : IsTrackSupported ( TSubclassOf < class UMovieSceneTrack > InTrackClass ) const
{
if ( ! UMovieScene : : IsTrackClassAllowed ( InTrackClass ) )
{
return ETrackSupport : : NotSupported ;
}
if ( InTrackClass = = UMovieScene3DAttachTrack : : StaticClass ( ) | |
InTrackClass = = UMovieScene3DPathTrack : : StaticClass ( ) | |
InTrackClass = = UMovieSceneAudioTrack : : StaticClass ( ) | |
InTrackClass = = UMovieSceneCameraCutTrack : : StaticClass ( ) | |
InTrackClass = = UMovieSceneCinematicShotTrack : : StaticClass ( ) | |
InTrackClass = = UMovieSceneEventTrack : : StaticClass ( ) | |
InTrackClass = = UMovieSceneFadeTrack : : StaticClass ( ) | |
InTrackClass = = UMovieSceneLevelVisibilityTrack : : StaticClass ( ) | |
InTrackClass = = UMovieSceneDataLayerTrack : : StaticClass ( ) | |
InTrackClass = = UMovieSceneMaterialParameterCollectionTrack : : StaticClass ( ) | |
2022-11-09 21:03:34 -05:00
InTrackClass = = UMovieSceneSkeletalAnimationTrack : : StaticClass ( ) | |
2022-06-02 03:01:54 -04:00
InTrackClass = = UMovieSceneSlomoTrack : : StaticClass ( ) | |
InTrackClass = = UMovieSceneSpawnTrack : : StaticClass ( ) | |
InTrackClass = = UMovieSceneSubTrack : : StaticClass ( ) | |
2023-11-10 07:34:17 -05:00
InTrackClass = = UMovieSceneCVarTrack : : StaticClass ( ) | |
InTrackClass = = UMovieSceneBindingLifetimeTrack : : StaticClass ( ) )
2022-06-02 03:01:54 -04:00
{
return ETrackSupport : : Supported ;
}
return Super : : IsTrackSupported ( InTrackClass ) ;
}
void ULevelSequence : : GetAssetRegistryTags ( TArray < FAssetRegistryTag > & OutTags ) const
2023-12-08 21:45:21 -05:00
{
PRAGMA_DISABLE_DEPRECATION_WARNINGS ;
Super : : GetAssetRegistryTags ( OutTags ) ;
PRAGMA_ENABLE_DEPRECATION_WARNINGS ;
}
void ULevelSequence : : GetAssetRegistryTags ( FAssetRegistryTagsContext Context ) const
2022-06-02 03:01:54 -04:00
{
# if WITH_EDITORONLY_DATA
if ( DirectorBlueprint )
{
2023-12-08 21:45:21 -05:00
DirectorBlueprint - > GetAssetRegistryTags ( Context ) ;
2022-06-02 03:01:54 -04:00
}
# endif
for ( UObject * MetaData : MetaDataObjects )
{
2023-04-25 00:54:15 -04:00
IMovieSceneMetaDataInterface * MetaDataInterface = Cast < IMovieSceneMetaDataInterface > ( MetaData ) ;
2022-06-02 03:01:54 -04:00
if ( MetaDataInterface )
{
2023-12-08 21:45:21 -05:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS ;
TArray < UObject : : FAssetRegistryTag > DeprecatedFunctionTags ;
MetaDataInterface - > ExtendAssetRegistryTags ( DeprecatedFunctionTags ) ;
for ( UObject : : FAssetRegistryTag & Tag : DeprecatedFunctionTags )
{
Context . AddTag ( MoveTemp ( Tag ) ) ;
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS ;
MetaDataInterface - > ExtendAssetRegistryTags ( Context ) ;
2022-06-02 03:01:54 -04:00
}
}
2023-12-08 21:45:21 -05:00
Super : : GetAssetRegistryTags ( Context ) ;
2022-06-02 03:01:54 -04:00
}
void ULevelSequence : : GetAssetRegistryTagMetadata ( TMap < FName , FAssetRegistryTagMetadata > & OutMetadata ) const
{
for ( UObject * MetaData : MetaDataObjects )
{
2023-04-25 00:54:15 -04:00
IMovieSceneMetaDataInterface * MetaDataInterface = Cast < IMovieSceneMetaDataInterface > ( MetaData ) ;
2022-06-02 03:01:54 -04:00
if ( MetaDataInterface )
{
MetaDataInterface - > ExtendAssetRegistryTagMetaData ( OutMetadata ) ;
}
}
Super : : GetAssetRegistryTagMetadata ( OutMetadata ) ;
}
2024-03-20 20:13:47 -04:00
void ULevelSequence : : ThreadedPostLoadAssetRegistryTagsOverride ( FPostLoadAssetRegistryTagsContext & Context ) const
2022-06-02 03:01:54 -04:00
{
2024-03-20 20:13:47 -04:00
Super : : ThreadedPostLoadAssetRegistryTagsOverride ( Context ) ;
2022-06-02 03:01:54 -04:00
2024-03-20 20:13:47 -04:00
// GetAssetRegistryTags appends the DirectorBlueprint tags to the World's tags, so we also have to run the Blueprint ThreadedPostLoadAssetRegistryTagsOverride
UBlueprint : : PostLoadBlueprintAssetRegistryTags ( Context ) ;
2022-06-02 03:01:54 -04:00
}
void PurgeLegacyBlueprints ( UObject * InObject , UPackage * Package )
{
if ( UBlueprint * BP = Cast < UBlueprint > ( InObject ) )
{
UPackage * TransientPackage = GetTransientPackage ( ) ;
{
FString OldName = BP - > GetName ( ) ;
BP - > ClearFlags ( RF_Public ) ;
BP - > SetFlags ( RF_Transient ) ;
BP - > RemoveFromRoot ( ) ;
FName NewName = MakeUniqueObjectName ( TransientPackage , UBlueprint : : StaticClass ( ) , * FString : : Printf ( TEXT ( " DEAD_SPAWNABLE_BLUEPRINT_%s " ) , * BP - > GetName ( ) ) ) ;
BP - > Rename ( * NewName . ToString ( ) , GetTransientPackage ( ) , ( REN_NonTransactional | REN_ForceNoResetLoaders | REN_DoNotDirty ) ) ;
UE_LOG ( LogLevelSequence , Log , TEXT ( " Discarding blueprint '%s' from package '%s'. " ) , * OldName , * Package - > GetName ( ) ) ;
}
if ( BP - > GeneratedClass )
{
FName OldName = BP - > GeneratedClass - > GetFName ( ) ;
UObject * OldOuter = BP - > GeneratedClass - > GetOuter ( ) ;
UClass * SuperClass = BP - > GeneratedClass - > GetSuperClass ( ) ;
if ( BP - > GeneratedClass - > ClassDefaultObject )
{
BP - > GeneratedClass - > ClassDefaultObject - > ClearFlags ( RF_Public ) ;
BP - > GeneratedClass - > ClassDefaultObject - > SetFlags ( RF_Transient ) ;
BP - > GeneratedClass - > ClassDefaultObject - > RemoveFromRoot ( ) ;
}
BP - > GeneratedClass - > ClearFlags ( RF_Public ) ;
BP - > GeneratedClass - > SetFlags ( RF_Transient ) ;
BP - > GeneratedClass - > ClassFlags | = CLASS_Deprecated ;
BP - > GeneratedClass - > RemoveFromRoot ( ) ;
FName NewName = MakeUniqueObjectName ( TransientPackage , BP - > GeneratedClass , * FString : : Printf ( TEXT ( " DEAD_SPAWNABLE_BP_CLASS_%s_C " ) , * BP - > GeneratedClass - > ClassGeneratedBy - > GetName ( ) ) ) ;
BP - > GeneratedClass - > Rename ( * NewName . ToString ( ) , GetTransientPackage ( ) , ( REN_DoNotDirty | REN_NonTransactional | REN_ForceNoResetLoaders ) ) ;
if ( SuperClass )
{
UObjectRedirector * Redirector = NewObject < UObjectRedirector > ( OldOuter , OldName ) ;
Redirector - > DestinationObject = SuperClass ;
UE_LOG ( LogLevelSequence , Log , TEXT ( " Discarding generated class '%s' from package '%s'. Replacing with redirector to '%s' " ) , * OldName . ToString ( ) , * Package - > GetName ( ) , * SuperClass - > GetName ( ) ) ;
}
else
{
UE_LOG ( LogLevelSequence , Log , TEXT ( " Discarding generated class '%s' from package '%s'. Unable to create redirector due to no super class. " ) , * OldName . ToString ( ) , * Package - > GetName ( ) ) ;
}
}
}
}
# endif
void ULevelSequence : : PostDuplicate ( bool bDuplicateForPIE )
{
Super : : PostDuplicate ( bDuplicateForPIE ) ;
# if WITH_EDITORONLY_DATA
if ( DirectorBlueprint )
{
DirectorClass = DirectorBlueprint - > GeneratedClass . Get ( ) ;
// Remove the binding for the director blueprint recompilation and re-add it to be sure there is only one entry in the list
DirectorBlueprint - > OnCompiled ( ) . RemoveAll ( this ) ;
DirectorBlueprint - > OnCompiled ( ) . AddUObject ( this , & ULevelSequence : : OnDirectorRecompiled ) ;
}
else
{
DirectorClass = nullptr ;
}
# endif
# if WITH_EDITOR
if ( PostDuplicateEvent . IsBound ( ) )
{
PostDuplicateEvent . Execute ( this ) ;
}
# endif
2023-04-25 14:08:57 -04:00
# if WITH_EDITOR
UMovieSceneMetaData * MetaData = FindOrAddMetaData < UMovieSceneMetaData > ( ) ;
MetaData - > SetCreated ( FDateTime : : UtcNow ( ) ) ;
MetaData - > SetAuthor ( FApp : : GetSessionOwner ( ) ) ;
MetaData - > SetNotes ( FString ( ) ) ; // Intentionally clear the notes
# endif
2022-06-02 03:01:54 -04:00
}
void ULevelSequence : : PostLoad ( )
{
Super : : PostLoad ( ) ;
# if WITH_EDITOR
2024-01-26 21:35:37 -05:00
if ( MovieScene )
{
// Remove any invalid object bindings. This was moved from PostInitProperties
// because it has to happen after the asset has actually been serialized.
TSet < FGuid > ValidObjectBindings ;
for ( int32 Index = 0 ; Index < MovieScene - > GetSpawnableCount ( ) ; + + Index )
{
ValidObjectBindings . Add ( MovieScene - > GetSpawnable ( Index ) . GetGuid ( ) ) ;
}
for ( int32 Index = 0 ; Index < MovieScene - > GetPossessableCount ( ) ; + + Index )
{
ValidObjectBindings . Add ( MovieScene - > GetPossessable ( Index ) . GetGuid ( ) ) ;
}
BindingReferences . RemoveInvalidBindings ( ValidObjectBindings ) ;
}
2022-06-02 03:01:54 -04:00
if ( ! DirectorBlueprint )
{
UBlueprint * PhantomDirector = FindObject < UBlueprint > ( this , TEXT ( " SequenceDirector " ) ) ;
if ( ! ensureMsgf ( ! PhantomDirector , TEXT ( " Phantom sequence director found in sequence '%s' which has a nullptr DirectorBlueprint. Re-assigning to prevent future crash. " ) , * GetName ( ) ) )
{
DirectorBlueprint = PhantomDirector ;
}
}
if ( DirectorBlueprint )
{
DirectorBlueprint - > ClearFlags ( RF_Standalone ) ;
// Remove the binding for the director blueprint recompilation and re-add it to be sure there is only one entry in the list
DirectorBlueprint - > OnCompiled ( ) . RemoveAll ( this ) ;
DirectorBlueprint - > OnCompiled ( ) . AddUObject ( this , & ULevelSequence : : OnDirectorRecompiled ) ;
if ( DirectorBlueprint - > Rename ( * GetDirectorBlueprintName ( ) , nullptr , ( REN_NonTransactional | REN_ForceNoResetLoaders | REN_DoNotDirty | REN_Test ) ) )
{
DirectorBlueprint - > Rename ( * GetDirectorBlueprintName ( ) , nullptr , ( REN_NonTransactional | REN_ForceNoResetLoaders | REN_DoNotDirty ) ) ;
}
}
2023-05-16 19:09:17 -04:00
if ( MovieScene )
2022-06-02 03:01:54 -04:00
{
2023-05-16 19:09:17 -04:00
TSet < FGuid > InvalidSpawnables ;
2022-06-02 03:01:54 -04:00
2023-05-16 19:09:17 -04:00
for ( int32 Index = 0 ; Index < MovieScene - > GetSpawnableCount ( ) ; + + Index )
{
FMovieSceneSpawnable & Spawnable = MovieScene - > GetSpawnable ( Index ) ;
if ( ! Spawnable . GetObjectTemplate ( ) )
{
if ( Spawnable . GeneratedClass_DEPRECATED & & Spawnable . GeneratedClass_DEPRECATED - > ClassGeneratedBy )
2022-06-02 03:01:54 -04:00
{
2023-05-16 19:09:17 -04:00
const FName TemplateName = MakeUniqueObjectName ( MovieScene , UObject : : StaticClass ( ) , Spawnable . GeneratedClass_DEPRECATED - > ClassGeneratedBy - > GetFName ( ) ) ;
UObject * NewTemplate = NewObject < UObject > ( MovieScene , Spawnable . GeneratedClass_DEPRECATED - > GetSuperClass ( ) , TemplateName ) ;
if ( NewTemplate )
{
Spawnable . CopyObjectTemplate ( * NewTemplate , * this ) ;
}
2022-06-02 03:01:54 -04:00
}
}
2023-05-16 19:09:17 -04:00
if ( ! Spawnable . GetObjectTemplate ( ) )
{
InvalidSpawnables . Add ( Spawnable . GetGuid ( ) ) ;
UE_LOG ( LogLevelSequence , Warning , TEXT ( " Spawnable '%s' with ID '%s' does not have a valid object template " ) , * Spawnable . GetName ( ) , * Spawnable . GetGuid ( ) . ToString ( ) ) ;
}
2022-06-02 03:01:54 -04:00
}
}
if ( GetLinkerCustomVersion ( FSequencerObjectVersion : : GUID ) < FSequencerObjectVersion : : PurgeSpawnableBlueprints )
{
// Remove any old generated classes from the package that will have been left behind from when we used blueprints for spawnables
{
UPackage * Package = GetOutermost ( ) ;
TArray < UObject * > PackageSubobjects ;
GetObjectsWithOuter ( Package , PackageSubobjects , false ) ;
for ( UObject * ObjectInPackage : PackageSubobjects )
{
PurgeLegacyBlueprints ( ObjectInPackage , Package ) ;
}
}
}
2023-11-27 09:05:23 -05:00
for ( TPair < FGuid , FLevelSequenceLegacyObjectReference > & Pair : ObjectReferences_DEPRECATED . Map )
{
if ( Pair . Value . ObjectId . IsValid ( ) )
{
FUniversalObjectLocator NewLocator ;
NewLocator . AddFragment < FLegacyLazyObjectPtrFragment > ( Pair . Value . ObjectId . GetGuid ( ) ) ;
BindingReferences . FMovieSceneBindingReferences : : AddBinding ( Pair . Key , MoveTemp ( NewLocator ) ) ;
}
else if ( Pair . Value . ObjectPath . Len ( ) > 0 )
{
FUniversalObjectLocator NewLocator ;
NewLocator . AddFragment < FSubObjectLocator > ( Pair . Value . ObjectPath ) ;
BindingReferences . FMovieSceneBindingReferences : : AddBinding ( Pair . Key , MoveTemp ( NewLocator ) ) ;
}
}
ObjectReferences_DEPRECATED . Map . Empty ( ) ;
2022-06-02 03:01:54 -04:00
# endif
}
2022-10-14 12:42:43 -04:00
# if WITH_EDITORONLY_DATA
void ULevelSequence : : DeclareConstructClasses ( TArray < FTopLevelAssetPath > & OutConstructClasses , const UClass * SpecificSubclass )
{
Super : : DeclareConstructClasses ( OutConstructClasses , SpecificSubclass ) ;
OutConstructClasses . Add ( FTopLevelAssetPath ( UObjectRedirector : : StaticClass ( ) ) ) ;
}
# endif
2022-06-02 03:01:54 -04:00
void ULevelSequence : : PostInitProperties ( )
{
Super : : PostInitProperties ( ) ;
}
bool ULevelSequence : : Rename ( const TCHAR * NewName , UObject * NewOuter , ERenameFlags Flags )
{
bool bRetVal = Super : : Rename ( NewName , NewOuter , Flags ) ;
# if WITH_EDITOR
if ( DirectorBlueprint )
{
DirectorBlueprint - > Rename ( * GetDirectorBlueprintName ( ) , this , Flags ) ;
}
# endif
return bRetVal ;
}
void ULevelSequence : : BindPossessableObject ( const FGuid & ObjectId , UObject & PossessedObject , UObject * Context )
{
if ( Context )
{
BindingReferences . AddBinding ( ObjectId , & PossessedObject , Context ) ;
}
}
bool ULevelSequence : : CanPossessObject ( UObject & Object , UObject * InPlaybackContext ) const
{
2023-11-27 09:05:23 -05:00
return true ;
2022-06-02 03:01:54 -04:00
}
2023-04-28 16:43:32 -04:00
void ULevelSequence : : LocateBoundObjects ( const FGuid & ObjectId , UObject * Context , const FLevelSequenceBindingReference : : FResolveBindingParams & InResolveBindingParams , TArray < UObject * , TInlineAllocator < 1 > > & OutObjects ) const
2022-06-02 03:01:54 -04:00
{
2023-11-27 09:05:23 -05:00
using namespace UE : : UniversalObjectLocator ;
2022-06-02 03:01:54 -04:00
2023-11-27 09:05:23 -05:00
TResolveParamsWithBuffer < 128 > ResolveParams ;
ResolveParams . AddParameter ( FActorLocatorFragmentResolveParameter : : ParameterType ,
InResolveBindingParams . StreamingWorld ,
InResolveBindingParams . WorldPartitionResolveData ? InResolveBindingParams . WorldPartitionResolveData - > ContainerID : FActorContainerID ( ) ,
InResolveBindingParams . WorldPartitionResolveData ? InResolveBindingParams . WorldPartitionResolveData - > SourceWorldAssetPath : InResolveBindingParams . StreamedLevelAssetPath
) ;
LocateBoundObjects ( ObjectId , ResolveParams , OutObjects ) ;
2022-06-02 03:01:54 -04:00
}
FGuid ULevelSequence : : FindBindingFromObject ( UObject * InObject , UObject * Context ) const
{
return BindingReferences . FindBindingFromObject ( InObject , Context ) ;
}
void ULevelSequence : : GatherExpiredObjects ( const FMovieSceneObjectCache & InObjectCache , TArray < FGuid > & OutInvalidIDs ) const
{
2023-11-27 09:05:23 -05:00
using namespace UE : : UniversalObjectLocator ;
TArrayView < const FMovieSceneBindingReference > References = BindingReferences . GetAllReferences ( ) ;
for ( int32 Index = 0 ; Index < References . Num ( ) ; + + Index )
2022-06-02 03:01:54 -04:00
{
2023-11-27 09:05:23 -05:00
const FMovieSceneBindingReference & Reference = References [ Index ] ;
if ( Reference . Locator . GetLastFragmentTypeHandle ( ) = = FAnimInstanceLocatorFragment : : FragmentType )
2022-06-02 03:01:54 -04:00
{
2023-11-27 09:05:23 -05:00
for ( TWeakObjectPtr < > WeakObject : InObjectCache . IterateBoundObjects ( Reference . ID ) )
2022-06-02 03:01:54 -04:00
{
2023-11-27 09:05:23 -05:00
UAnimInstance * AnimInstance = Cast < UAnimInstance > ( WeakObject . Get ( ) ) ;
if ( ! AnimInstance | | ! AnimInstance - > GetOwningComponent ( ) | | AnimInstance - > GetOwningComponent ( ) - > GetAnimInstance ( ) ! = AnimInstance )
{
OutInvalidIDs . Add ( Reference . ID ) ;
}
}
// Skip over subsequent matched IDs
while ( Index < References . Num ( ) - 1 & & References [ Index + 1 ] . ID = = Reference . ID )
{
+ + Index ;
2022-06-02 03:01:54 -04:00
}
}
}
}
UMovieScene * ULevelSequence : : GetMovieScene ( ) const
{
return MovieScene ;
}
UObject * ULevelSequence : : GetParentObject ( UObject * Object ) const
{
if ( UActorComponent * Component = Cast < UActorComponent > ( Object ) )
{
return Component - > GetOwner ( ) ;
}
if ( UAnimInstance * AnimInstance = Cast < UAnimInstance > ( Object ) )
{
if ( AnimInstance - > GetWorld ( ) )
{
return AnimInstance - > GetOwningComponent ( ) ;
}
}
return nullptr ;
}
bool ULevelSequence : : AllowsSpawnableObjects ( ) const
{
# if WITH_EDITOR
if ( ! UMovieScene : : IsTrackClassAllowed ( UMovieSceneSpawnTrack : : StaticClass ( ) ) )
{
return false ;
}
# endif
return true ;
}
Sequencer- Refactor Bindings to support inherited Custom Bindings.
With this checkin, the binding system inside Level Sequences has been refactored to allow C++ custom binding classes inheriting from a new class, UMovieSceneCustomBinding. Custom bindings are held as instanced UObjects inside FMovieSceneBindingReference, alongside the Universal Object Locator. In the case where a custom binding has been specified, the UOL will be ignored.
The intention with this change is to allow developers to create their own arbitrary methods of binding any UObject to a track inside Sequencer. Examples of this include:
* Runtime dynamic bindings to spawned NPC or player characters (e.g. in UEFN) with a different spawned preview character
* Blueprint-specified dynamic bindings
* Spawnable bindings based on something other than a AActor template
* Scene Graph/Prefabs
Alongside the base UMovieSceneCustomBinding class, a new hierarchy of spawnable custom binding classes has been created as the basis for allowing spawnable bindings that don't require object templates, and may instead require other asset references, for example UEFN NPC character spawnables requiring an NPC Character Definition asset.
This hierarchy is currently. UMovieSceneCustomBinding <- UMovieSceneSpawnableBindingBase <- UMovieSceneSpawnableActorBindingBase <- UMovieSceneSpawnableActorBinding.
This hierarchy allows for spawnables of non-actor types, spawnables of actor types not based on templates, and finally UMovieSceneSpawnableActorBinding, which is a re-implementation of FMovieSceneSpawnable inside the custom binding system.
Going forward, we will be moving to deprecate FMovieSceneSpawnable in UE Sequencer in favor of the custom spawnable binding types. Sequences with FMovieSceneSpawnables will continue to function as normal, but new spawnables in new sequences will use UMovieSceneSpawnableActorBinding under the hood. These will technically have FMovieScenePossessable structs created for them for now rather than FMovieSceneSpawnable structs. This should be mostly invisible to the user.
In addition, I've added a separate hierarchy of custom bindings called 'Replaceable' bindings. A 'Replaceable' is a binding type that will expect in runtime to always be overridden by a different object that may not be available in editor. UMovieSceneReplaceableBinding will hold in EDITORONLY_DATA a UMovieSceneSpawnableBindingBase to provide a preview while within Sequencer. Included in this is a UMovieSceneReplaceableActorBinding, which uses the UMovieSceneSpawnableActorBinding internally to spawn its preview, and has no implementation to find the actor during runtime- it relies on the user overriding the actor binding using the existing BindingOverrides method on LevelSequenceActor.
Next Steps:
* UEFN custom bindings will be built upon this system, to allow UEFN users multiple ways to override bindings.
* UEFN custom spawnable bindings will be built upon this system, to allow UEFN users a way to spawn actors and objects within Sequencer with their lifetime managed by Sequencer
* With this change, we have a cleaner way of incorporating the 'dynamic binding' work that uses the Director Blueprint to create a spawnable or possessable. This should be incorporated into the UCustomBinding hierarchy.
* Once Verse supports it, we should add Verse custom binding logic
* We need to add a Universal Object Locator type that can reference any other binding in the sequence hierarchy. This will allow us to adapt the existing 'Spawnable Parent' possessable binding logic to reference any other type of binding, not just spawnable ones.
* Eventually we will fully deprecate FMovieSceneSpawnable
Further notes:
* A previous imagined version of this system used the cache as a way to allow locators to spawn actors and register them with Sequencer. This change removes this code.
* Custom Spawnable Bindings use the Binding Lifetime Track to determine their spawn lifetime rather than the old spawn track. This unifies the concept of the lifetime of an object binding within sequencer for both spawnables and possessables.
* As FMovieScenePossessable and FMovieSceneBindingReferences allows multiple bindings per guid, this change now allows multiple spawnables to be spawned and controlled by a single binding track. However, some of the editor tooling code doesn't currently handle this properly and at times assumes a single binding. This should be resolved one way or the other.
* The UX for this allows UCustomBindings to handle creating bindings from any dragged in object using a priority system- Sequencer will ask all classes in priority order if they support binding creation from that object.
* The UX supports unique overlay icons and tooltips for different types of bindings
* The UX allows each binding type to decide whether it can convert a current binding into its own type and then do so
* Currently there is some manual instanced detail customization for UMovieSceneCustomSpawnableActorBinding. We use instanced customization so that the UX has the sequencer context. This should both be improved (to allow ways to choose a class/template via this UI), as well as extended so that clients outside of the Movie Scene plugin can register instanced detail customizations for their own binding types.
* Where possible, Sequencer APIs around spawnables have been refactored to take guids rather than FMovieSceneSpawnable references. In particular, custom spawnables do still use the spawn register, but do not use actor spawners- instead the UMovieSceneCustomBinding class is given the responsiblity of spawning/destroying the object/actor. This created an awkward API for USD export, which had overridden the spawn register to unhide/hide actors rather than spawning/destroying them. This could use some more work. MetaHumanPerformanceExportUtils will also need to be refactored at some point to use the custom spawnable binding system.
* FSequencerUtilities::CreateBinding and CreateGenericBinding has been refactored to accept binding parameters and now supports creating custom bindings including spawnables, as well as replacing existing bindings and adding additional bindings to an existing guid.
[REVIEW] [at]ue-sequencer
#jira UE-199299, FORT-582815, UE-209594
#rb Andrew.Rodham, daniel.coelho, Max.Chen
[CL 32218187 by david bromberg in ue5-main branch]
2024-03-13 12:48:46 -04:00
bool ULevelSequence : : AllowsCustomBindings ( ) const
{
return true ;
}
2022-06-02 03:01:54 -04:00
bool ULevelSequence : : CanRebindPossessable ( const FMovieScenePossessable & InPossessable ) const
{
return ! InPossessable . GetParent ( ) . IsValid ( ) ;
}
void ULevelSequence : : UnbindPossessableObjects ( const FGuid & ObjectId )
{
BindingReferences . RemoveBinding ( ObjectId ) ;
}
void ULevelSequence : : UnbindObjects ( const FGuid & ObjectId , const TArray < UObject * > & InObjects , UObject * InContext )
{
BindingReferences . RemoveObjects ( ObjectId , InObjects , InContext ) ;
}
void ULevelSequence : : UnbindInvalidObjects ( const FGuid & ObjectId , UObject * InContext )
{
BindingReferences . RemoveInvalidObjects ( ObjectId , InContext ) ;
}
2023-11-27 09:05:23 -05:00
const FMovieSceneBindingReferences * ULevelSequence : : GetBindingReferences ( ) const
{
return & BindingReferences ;
}
2022-06-02 03:01:54 -04:00
# if WITH_EDITOR
UBlueprint * ULevelSequence : : GetDirectorBlueprint ( ) const
{
return DirectorBlueprint ;
}
FString ULevelSequence : : GetDirectorBlueprintName ( ) const
{
2022-12-01 11:02:37 -05:00
return GetDisplayName ( ) . ToString ( ) + " _DirectorBP " ;
2022-06-02 03:01:54 -04:00
}
void ULevelSequence : : SetDirectorBlueprint ( UBlueprint * NewDirectorBlueprint )
{
if ( DirectorBlueprint )
{
DirectorBlueprint - > OnCompiled ( ) . RemoveAll ( this ) ;
}
DirectorBlueprint = NewDirectorBlueprint ;
if ( DirectorBlueprint )
{
DirectorClass = NewDirectorBlueprint - > GeneratedClass . Get ( ) ;
DirectorBlueprint - > OnCompiled ( ) . AddUObject ( this , & ULevelSequence : : OnDirectorRecompiled ) ;
}
else
{
DirectorClass = nullptr ;
}
MarkAsChanged ( ) ;
}
void ULevelSequence : : OnDirectorRecompiled ( UBlueprint * InCompiledBlueprint )
{
ensure ( InCompiledBlueprint = = DirectorBlueprint ) ;
DirectorClass = DirectorBlueprint - > GeneratedClass . Get ( ) ;
MarkAsChanged ( ) ;
}
FGuid ULevelSequence : : FindOrAddBinding ( UObject * InObject )
{
2023-11-30 14:33:10 -05:00
using namespace UE : : MovieScene ;
2022-06-02 03:01:54 -04:00
UObject * PlaybackContext = InObject ? InObject - > GetWorld ( ) : nullptr ;
if ( ! InObject | | ! PlaybackContext )
{
return FGuid ( ) ;
}
AActor * Actor = Cast < AActor > ( InObject ) ;
if ( Actor & & Actor - > ActorHasTag ( " SequencerActor " ) )
{
TOptional < FMovieSceneSpawnableAnnotation > Annotation = FMovieSceneSpawnableAnnotation : : Find ( Actor ) ;
if ( Annotation . IsSet ( ) & & Annotation - > OriginatingSequence = = this )
{
return Annotation - > ObjectBindingID ;
}
2023-02-08 12:34:24 -05:00
// If this actor is a spawnable and is not in the same originating sequence, it's likely a spawnable that will be possessed.
// SetSpawnableObjectBindingID will need to be called on that possessable.
2022-06-02 03:01:54 -04:00
}
UObject * ParentObject = GetParentObject ( InObject ) ;
FGuid ParentGuid = ParentObject ? FindOrAddBinding ( ParentObject ) : FGuid ( ) ;
if ( ParentObject & & ! ParentGuid . IsValid ( ) )
{
UE_LOG ( LogLevelSequence , Error , TEXT ( " Unable to possess object '%s' because it's parent could not be bound. " ) , * InObject - > GetName ( ) ) ;
return FGuid ( ) ;
}
// Perform a potentially slow lookup of every possessable binding in the sequence to see if we already have this
{
2023-11-30 14:33:10 -05:00
FSharedPlaybackStateCreateParams CreateParams ;
CreateParams . PlaybackContext = PlaybackContext ;
TSharedRef < FSharedPlaybackState > TransientPlaybackState = MakeShared < FSharedPlaybackState > ( * this , CreateParams ) ;
2022-06-02 03:01:54 -04:00
2023-11-30 14:33:10 -05:00
FMovieSceneEvaluationState State ;
TransientPlaybackState - > AddCapabilityRaw ( & State ) ;
State . AssignSequence ( MovieSceneSequenceID : : Root , * this , TransientPlaybackState ) ;
2022-06-02 03:01:54 -04:00
2023-11-30 14:33:10 -05:00
FGuid ExistingID = State . FindObjectId ( * InObject , MovieSceneSequenceID : : Root , TransientPlaybackState ) ;
2022-06-02 03:01:54 -04:00
if ( ExistingID . IsValid ( ) )
{
return ExistingID ;
}
}
// We have to possess this object
if ( ! CanPossessObject ( * InObject , PlaybackContext ) )
{
return FGuid ( ) ;
}
FString NewName = Actor ? Actor - > GetActorLabel ( ) : InObject - > GetName ( ) ;
const FGuid NewGuid = MovieScene - > AddPossessable ( NewName , InObject - > GetClass ( ) ) ;
// Attempt to use the parent as a context if necessary
UObject * BindingContext = ParentObject & & AreParentContextsSignificant ( ) ? ParentObject : PlaybackContext ;
// Set up parent/child guids for possessables within spawnables
if ( ParentGuid . IsValid ( ) )
{
FMovieScenePossessable * ChildPossessable = MovieScene - > FindPossessable ( NewGuid ) ;
if ( ensure ( ChildPossessable ) )
{
ChildPossessable - > SetParent ( ParentGuid , MovieScene ) ;
}
FMovieSceneSpawnable * ParentSpawnable = MovieScene - > FindSpawnable ( ParentGuid ) ;
if ( ParentSpawnable )
{
ParentSpawnable - > AddChildPossessable ( NewGuid ) ;
}
}
BindPossessableObject ( NewGuid , * InObject , BindingContext ) ;
return NewGuid ;
}
FGuid ULevelSequence : : CreatePossessable ( UObject * ObjectToPossess )
{
return FindOrAddBinding ( ObjectToPossess ) ;
}
FGuid ULevelSequence : : CreateSpawnable ( UObject * ObjectToSpawn )
{
if ( ! MovieScene | | ! ObjectToSpawn )
{
return FGuid ( ) ;
}
TArray < TSharedRef < IMovieSceneObjectSpawner > > ObjectSpawners ;
// In order to create a spawnable, we have to instantiate all the relevant object spawners for level sequences, and try to create a spawnable from each
FLevelSequenceModule & LevelSequenceModule = FModuleManager : : LoadModuleChecked < FLevelSequenceModule > ( " LevelSequence " ) ;
LevelSequenceModule . GenerateObjectSpawners ( ObjectSpawners ) ;
// The first object spawner to return a valid result will win
for ( TSharedRef < IMovieSceneObjectSpawner > Spawner : ObjectSpawners )
{
TValueOrError < FNewSpawnable , FText > Result = Spawner - > CreateNewSpawnableType ( * ObjectToSpawn , * MovieScene , nullptr ) ;
if ( Result . IsValid ( ) )
{
FNewSpawnable & NewSpawnable = Result . GetValue ( ) ;
NewSpawnable . Name = MovieSceneHelpers : : MakeUniqueSpawnableName ( MovieScene , NewSpawnable . Name ) ;
FGuid NewGuid = MovieScene - > AddSpawnable ( NewSpawnable . Name , * NewSpawnable . ObjectTemplate ) ;
UMovieSceneSpawnTrack * NewSpawnTrack = MovieScene - > AddTrack < UMovieSceneSpawnTrack > ( NewGuid ) ;
if ( NewSpawnTrack )
{
2023-10-10 22:03:53 -04:00
NewSpawnTrack - > Modify ( ) ;
2022-06-02 03:01:54 -04:00
NewSpawnTrack - > AddSection ( * NewSpawnTrack - > CreateNewSection ( ) ) ;
}
return NewGuid ;
}
}
return FGuid ( ) ;
}
# endif // WITH_EDITOR
2024-01-23 16:09:37 -05:00
UObject * ULevelSequence : : CreateDirectorInstance ( TSharedRef < const FSharedPlaybackState > SharedPlaybackState , FMovieSceneSequenceID SequenceID )
2022-06-02 03:01:54 -04:00
{
2024-01-23 16:09:37 -05:00
UObject * DirectorOuter = SharedPlaybackState - > GetPlaybackContext ( ) ;
IMovieScenePlayer * OptionalPlayer = UE : : MovieScene : : FPlayerIndexPlaybackCapability : : GetPlayer ( SharedPlaybackState ) ;
2022-06-02 03:01:54 -04:00
2024-01-22 14:17:12 -05:00
# if WITH_EDITOR
if ( ! UMovieScene : : IsTrackClassAllowed ( ULevelSequenceDirector : : StaticClass ( ) ) )
{
return nullptr ;
}
# endif
2022-06-02 03:01:54 -04:00
if ( DirectorClass & & DirectorOuter & & DirectorClass - > IsChildOf ( ULevelSequenceDirector : : StaticClass ( ) ) )
{
FName DirectorName = NAME_None ;
# if WITH_EDITOR
// Give it a pretty name so it shows up in the debug instances drop down nicely
DirectorName = MakeUniqueObjectName ( DirectorOuter , DirectorClass , * ( GetFName ( ) . ToString ( ) + TEXT ( " _Director " ) ) ) ;
# endif
2024-01-23 16:09:37 -05:00
ULevelSequencePlayer * LevelSequencePlayer = nullptr ;
if ( OptionalPlayer )
{
LevelSequencePlayer = Cast < ULevelSequencePlayer > ( OptionalPlayer - > AsUObject ( ) ) ;
}
2022-06-02 03:01:54 -04:00
ULevelSequenceDirector * NewDirector = NewObject < ULevelSequenceDirector > ( DirectorOuter , DirectorClass , DirectorName , RF_Transient ) ;
NewDirector - > SubSequenceID = SequenceID . GetInternalValue ( ) ;
2024-01-23 16:09:37 -05:00
NewDirector - > WeakLinker = SharedPlaybackState - > GetLinker ( ) ;
NewDirector - > InstanceID = SharedPlaybackState - > GetRootInstanceHandle ( ) . InstanceID ;
NewDirector - > InstanceSerial = SharedPlaybackState - > GetRootInstanceHandle ( ) . InstanceSerial ;
NewDirector - > Player = LevelSequencePlayer ;
NewDirector - > MovieScenePlayerIndex = OptionalPlayer ? OptionalPlayer - > GetUniqueIndex ( ) : INDEX_NONE ;
2022-06-02 03:01:54 -04:00
NewDirector - > OnCreated ( ) ;
return NewDirector ;
}
return nullptr ;
}
void ULevelSequence : : AddAssetUserData ( UAssetUserData * InUserData )
{
if ( InUserData ! = NULL )
{
UAssetUserData * ExistingData = GetAssetUserDataOfClass ( InUserData - > GetClass ( ) ) ;
if ( ExistingData ! = NULL )
{
AssetUserData . Remove ( ExistingData ) ;
}
AssetUserData . Add ( InUserData ) ;
}
}
UAssetUserData * ULevelSequence : : GetAssetUserDataOfClass ( TSubclassOf < UAssetUserData > InUserDataClass )
{
for ( int32 DataIdx = 0 ; DataIdx < AssetUserData . Num ( ) ; DataIdx + + )
{
UAssetUserData * Datum = AssetUserData [ DataIdx ] ;
if ( Datum ! = NULL & & Datum - > IsA ( InUserDataClass ) )
{
return Datum ;
}
}
return NULL ;
}
void ULevelSequence : : RemoveUserDataOfClass ( TSubclassOf < UAssetUserData > InUserDataClass )
{
for ( int32 DataIdx = 0 ; DataIdx < AssetUserData . Num ( ) ; DataIdx + + )
{
UAssetUserData * Datum = AssetUserData [ DataIdx ] ;
if ( Datum ! = NULL & & Datum - > IsA ( InUserDataClass ) )
{
AssetUserData . RemoveAt ( DataIdx ) ;
return ;
}
}
}
const TArray < UAssetUserData * > * ULevelSequence : : GetAssetUserDataArray ( ) const
{
return & ToRawPtrTArrayUnsafe ( AssetUserData ) ;
}
2022-09-24 13:57:58 -04:00