You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
Removed ISequencerInternals interface as it was not necessary and confusing. [CL 2082317 by Matt Kuhlenschmidt in Main branch]
366 lines
12 KiB
C++
366 lines
12 KiB
C++
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "SequencerPrivatePCH.h"
|
|
#include "SequencerObjectSpawner.h"
|
|
#include "Sequencer.h"
|
|
#include "MovieScene.h"
|
|
#include "Toolkits/IToolkitHost.h"
|
|
#include "Kismet2/BlueprintEditorUtils.h"
|
|
#include "K2Node_PlayMovieScene.h"
|
|
#include "MovieSceneInstance.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "Sequencer"
|
|
|
|
FSequencerActorObjectSpawner::FSequencerActorObjectSpawner( FSequencer& InitSequencer )
|
|
: Sequencer( &InitSequencer )
|
|
{
|
|
// Register to be notified when object changes should be propagated
|
|
ISequencerObjectChangeListener& ObjectChangeListener = InitSequencer.GetObjectChangeListener();
|
|
ObjectChangeListener.GetOnPropagateObjectChanges().AddRaw( this, &FSequencerActorObjectSpawner::OnPropagateObjectChanges );
|
|
}
|
|
|
|
|
|
FSequencerActorObjectSpawner::~FSequencerActorObjectSpawner()
|
|
{
|
|
ISequencerObjectChangeListener& ObjectChangeListener = Sequencer->GetObjectChangeListener();
|
|
ObjectChangeListener.GetOnPropagateObjectChanges().RemoveAll( this );
|
|
}
|
|
|
|
|
|
void FSequencerActorObjectSpawner::SpawnOrDestroyPuppetObjects( TSharedRef<FMovieSceneInstance> MovieSceneInstance, const bool bDestroyAll )
|
|
{
|
|
bool bAnyLevelActorsChanged = false;
|
|
|
|
// Get the list of puppet objects for the movie scene
|
|
TArray< TSharedRef<FPuppetActorInfo> >& PuppetObjects = InstanceToPuppetObjectsMap.FindOrAdd( MovieSceneInstance );
|
|
|
|
UMovieScene* MovieScene = MovieSceneInstance->GetMovieScene();
|
|
|
|
// Remove any puppet objects that we no longer need
|
|
{
|
|
for( auto PuppetObjectIndex = 0; PuppetObjectIndex < PuppetObjects.Num(); ++PuppetObjectIndex )
|
|
{
|
|
if( PuppetObjects[ PuppetObjectIndex ]->GetType() == EPuppetObjectType::Actor )
|
|
{
|
|
TSharedRef< FPuppetActorInfo > PuppetActorInfo = StaticCastSharedRef< FPuppetActorInfo >( PuppetObjects[ PuppetObjectIndex ] );
|
|
|
|
// Figure out if we still need this puppet actor
|
|
bool bShouldDestroyActor = true;
|
|
if( !bDestroyAll )
|
|
{
|
|
for( auto SpawnableIndex = 0; SpawnableIndex < MovieScene->GetSpawnableCount(); ++SpawnableIndex )
|
|
{
|
|
auto& Spawnable = MovieScene->GetSpawnable( SpawnableIndex );
|
|
if( Spawnable.GetGuid() == PuppetActorInfo->SpawnableGuid )
|
|
{
|
|
bShouldDestroyActor = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( bShouldDestroyActor )
|
|
{
|
|
AActor* PuppetActor = PuppetActorInfo->PuppetActor.Get();
|
|
if( PuppetActor != NULL )
|
|
{
|
|
UWorld* PuppetWorld = PuppetActor->GetWorld();
|
|
if( ensure( PuppetWorld != NULL ) )
|
|
{
|
|
// Destroy this actor
|
|
{
|
|
// Ignored unless called while game is running
|
|
const bool bNetForce = false;
|
|
|
|
// We don't want to dirty the level for puppet actor changes
|
|
const bool bShouldModifyLevel = false;
|
|
|
|
if( !bAnyLevelActorsChanged )
|
|
{
|
|
DeselectAllPuppetObjects();
|
|
}
|
|
|
|
// Actor should never be selected in the editor at this point. We took care of that up above.
|
|
ensure( !PuppetActor->IsSelected() );
|
|
|
|
const bool bWasDestroyed = PuppetWorld->DestroyActor( PuppetActor, bNetForce, bShouldModifyLevel );
|
|
|
|
if( bWasDestroyed )
|
|
{
|
|
bAnyLevelActorsChanged = true;
|
|
PuppetObjects.RemoveAt( PuppetObjectIndex-- );
|
|
}
|
|
else
|
|
{
|
|
// @todo sequencer: At least one puppet couldn't be cleaned up!
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Actor is no longer valid (probably the world was destroyed)
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
check(0); // Unhandled type
|
|
}
|
|
}
|
|
}
|
|
|
|
if( !bDestroyAll )
|
|
{
|
|
for( auto SpawnableIndex = 0; SpawnableIndex < MovieScene->GetSpawnableCount(); ++SpawnableIndex )
|
|
{
|
|
auto& Spawnable = MovieScene->GetSpawnable( SpawnableIndex );
|
|
|
|
// Must have a valid world for us to be able to do this
|
|
UWorld* World = Sequencer->GetToolkitHost()->GetWorld();
|
|
if( World != NULL )
|
|
{
|
|
// Do we already have a puppet for this spawnable?
|
|
bool bIsAlreadySpawned = false;
|
|
for( auto PuppetIndex = 0; PuppetIndex < PuppetObjects.Num(); ++PuppetIndex )
|
|
{
|
|
auto& PuppetObject = PuppetObjects[ PuppetIndex ];
|
|
if( PuppetObject->SpawnableGuid == Spawnable.GetGuid() )
|
|
{
|
|
bIsAlreadySpawned = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( !bIsAlreadySpawned )
|
|
{
|
|
UClass* GeneratedClass = Spawnable.GetClass();
|
|
if ( GeneratedClass != NULL && GeneratedClass->IsChildOf(AActor::StaticClass()))
|
|
{
|
|
AActor* ActorCDO = CastChecked< AActor >( GeneratedClass->ClassDefaultObject );
|
|
|
|
const FVector SpawnLocation = ActorCDO->GetRootComponent()->RelativeLocation;
|
|
const FRotator SpawnRotation = ActorCDO->GetRootComponent()->RelativeRotation;
|
|
|
|
// @todo sequencer: We should probably spawn these in a specific sub-level!
|
|
// World->CurrentLevel = ???;
|
|
|
|
const FName PuppetActorName = NAME_None;
|
|
|
|
// Override the object flags so that RF_Transactional is not set. Puppet actors are never transactional
|
|
// @todo sequencer: These actors need to avoid any transaction history. However, RF_Transactional can currently be set on objects on the fly!
|
|
const EObjectFlags ObjectFlags = RF_Transient; // NOTE: We are omitting RF_Transactional intentionally
|
|
|
|
// @todo sequencer livecapture: Consider using SetPlayInEditorWorld() and RestoreEditorWorld() here instead
|
|
|
|
// @todo sequencer actors: We need to make sure puppet objects aren't copied into PIE/SIE sessions! They should be omitted from that duplication!
|
|
|
|
// Spawn the puppet actor
|
|
FActorSpawnParameters SpawnInfo;
|
|
SpawnInfo.Name = PuppetActorName;
|
|
SpawnInfo.ObjectFlags = ObjectFlags;
|
|
AActor* NewActor = World->SpawnActor( GeneratedClass, &SpawnLocation, &SpawnRotation, SpawnInfo );
|
|
|
|
if( NewActor )
|
|
{
|
|
// @todo sequencer: We're naming the actor based off of the spawnable's name. Is that really what we want?
|
|
GEditor->SetActorLabelUnique( NewActor, Spawnable.GetName() );
|
|
|
|
// Actor was spawned OK!
|
|
|
|
// Keep track of this actor
|
|
TSharedRef< FPuppetActorInfo > NewPuppetInfo( new FPuppetActorInfo() );
|
|
NewPuppetInfo->SpawnableGuid = Spawnable.GetGuid();
|
|
NewPuppetInfo->PuppetActor = NewActor;
|
|
PuppetObjects.Add( NewPuppetInfo );
|
|
}
|
|
else
|
|
{
|
|
// Actor failed to spawn
|
|
// @todo sequencer: What should we do when this happens to one or more actors?
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FSequencerActorObjectSpawner::DestroyAllPuppetObjects()
|
|
{
|
|
const bool bDestroyAll = true;
|
|
for( auto MovieSceneIter = InstanceToPuppetObjectsMap.CreateConstIterator(); MovieSceneIter; ++MovieSceneIter )
|
|
{
|
|
SpawnOrDestroyPuppetObjects( MovieSceneIter.Key().Pin().ToSharedRef(), bDestroyAll );
|
|
}
|
|
|
|
InstanceToPuppetObjectsMap.Empty();
|
|
}
|
|
|
|
void FSequencerActorObjectSpawner::DeselectAllPuppetObjects()
|
|
{
|
|
TArray< AActor* > ActorsToDeselect;
|
|
|
|
for( auto MovieSceneIter( InstanceToPuppetObjectsMap.CreateConstIterator() ); MovieSceneIter; ++MovieSceneIter )
|
|
{
|
|
const TArray< TSharedRef<FPuppetActorInfo> >& PuppetObjects = MovieSceneIter.Value();
|
|
|
|
for( auto PuppetObjectIter( PuppetObjects.CreateConstIterator() ); PuppetObjectIter; ++PuppetObjectIter )
|
|
{
|
|
if( PuppetObjectIter->Get().GetType() == EPuppetObjectType::Actor )
|
|
{
|
|
TSharedRef< FPuppetActorInfo > PuppetActorInfo = StaticCastSharedRef< FPuppetActorInfo >( *PuppetObjectIter );
|
|
AActor* PuppetActor = PuppetActorInfo->PuppetActor.Get();
|
|
if( PuppetActor != NULL )
|
|
{
|
|
ActorsToDeselect.Add( PuppetActor );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( ActorsToDeselect.Num() > 0 )
|
|
{
|
|
GEditor->GetSelectedActors()->BeginBatchSelectOperation();
|
|
GEditor->GetSelectedActors()->MarkBatchDirty();
|
|
for( auto ActorIt( ActorsToDeselect.CreateIterator() ); ActorIt; ++ActorIt )
|
|
{
|
|
GEditor->GetSelectedActors()->Deselect( *ActorIt );
|
|
}
|
|
GEditor->GetSelectedActors()->EndBatchSelectOperation();
|
|
}
|
|
}
|
|
|
|
|
|
void FSequencerActorObjectSpawner::OnPropagateObjectChanges( UObject* Object )
|
|
{
|
|
// @todo sequencer major: Many other types of changes to the puppet actor may occur which should be propagated (or disallowed?):
|
|
// - Deleting the puppet actor (Del key) should probably delete the reference actor and remove it from the MovieScene
|
|
// - Attachment changes
|
|
// - Replace/Convert operations?
|
|
|
|
// We only care about actor objects for this type of spawner. Note that sometimes we can get component objects passed in, but we'll
|
|
// rely on the caller also calling OnObjectPropertyChange for the outer Actor if a component property changes.
|
|
if( Object->IsA( AActor::StaticClass() ) )
|
|
{
|
|
for( auto MovieSceneIter( InstanceToPuppetObjectsMap.CreateConstIterator() ); MovieSceneIter; ++MovieSceneIter )
|
|
{
|
|
const TArray< TSharedRef<FPuppetActorInfo> >& PuppetObjects = MovieSceneIter.Value();
|
|
|
|
// Is this an object that we care about?
|
|
for( auto PuppetObjectIter( PuppetObjects.CreateConstIterator() ); PuppetObjectIter; ++PuppetObjectIter )
|
|
{
|
|
if( PuppetObjectIter->Get().GetType() == EPuppetObjectType::Actor )
|
|
{
|
|
TSharedRef< FPuppetActorInfo > PuppetActorInfo = StaticCastSharedRef< FPuppetActorInfo >( *PuppetObjectIter );
|
|
AActor* PuppetActor = PuppetActorInfo->PuppetActor.Get();
|
|
if( PuppetActor != NULL )
|
|
{
|
|
if( PuppetActor == Object )
|
|
{
|
|
// @todo sequencer propagation: Don't propagate changes that are being auto-keyed or key-adjusted!
|
|
|
|
// Our puppet actor was modified.
|
|
PropagatePuppetActorChanges( PuppetActorInfo );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void FSequencerActorObjectSpawner::PropagatePuppetActorChanges( const TSharedRef< FPuppetActorInfo > PuppetActorInfo )
|
|
{
|
|
AActor* PuppetActor = PuppetActorInfo->PuppetActor.Get();
|
|
AActor* TargetActor = NULL;
|
|
|
|
{
|
|
// Find the spawnable for this puppet actor
|
|
FMovieSceneSpawnable* FoundSpawnable = NULL;
|
|
TArray< UMovieScene* > MovieScenesBeingEdited = Sequencer->GetMovieScenesBeingEdited();
|
|
for( auto CurMovieSceneIt( MovieScenesBeingEdited.CreateIterator() ); CurMovieSceneIt; ++CurMovieSceneIt )
|
|
{
|
|
auto CurMovieScene = *CurMovieSceneIt;
|
|
FoundSpawnable = CurMovieScene->FindSpawnable( PuppetActorInfo->SpawnableGuid );
|
|
if( FoundSpawnable != NULL )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( ensure( FoundSpawnable != NULL ) )
|
|
{
|
|
UClass* ActorClass = FoundSpawnable->GetClass();
|
|
|
|
// The puppet actor's class should always be the blueprint that it was spawned from!
|
|
UClass* SpawnedActorClass = PuppetActor->GetClass();
|
|
check( ActorClass == SpawnedActorClass );
|
|
|
|
// We'll be copying properties into the class default object of the Blueprint's generated class
|
|
TargetActor = CastChecked<AActor>( ActorClass->GetDefaultObject() );
|
|
}
|
|
}
|
|
|
|
|
|
if( PuppetActor != NULL && TargetActor != NULL )
|
|
{
|
|
Sequencer->CopyActorProperties( PuppetActor, TargetActor );
|
|
}
|
|
}
|
|
|
|
|
|
FGuid FSequencerActorObjectSpawner::FindSpawnableGuidForPuppetObject( UObject* Object )
|
|
{
|
|
for( auto MovieSceneIter( InstanceToPuppetObjectsMap.CreateConstIterator() ); MovieSceneIter; ++MovieSceneIter )
|
|
{
|
|
const TArray< TSharedRef<FPuppetActorInfo> >& PuppetObjects = MovieSceneIter.Value();
|
|
|
|
// Is this an object that we care about?
|
|
for( auto PuppetObjectIter( PuppetObjects.CreateConstIterator() ); PuppetObjectIter; ++PuppetObjectIter )
|
|
{
|
|
if( PuppetObjectIter->Get().GetType() == EPuppetObjectType::Actor )
|
|
{
|
|
TSharedRef< FPuppetActorInfo > PuppetActorInfo = StaticCastSharedRef< FPuppetActorInfo >( *PuppetObjectIter );
|
|
AActor* PuppetActor = PuppetActorInfo->PuppetActor.Get();
|
|
if( PuppetActor != NULL )
|
|
{
|
|
if( PuppetActor == Object )
|
|
{
|
|
// Found it!
|
|
return PuppetActorInfo->SpawnableGuid;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Not found
|
|
return FGuid();
|
|
}
|
|
|
|
|
|
UObject* FSequencerActorObjectSpawner::FindPuppetObjectForSpawnableGuid( TSharedRef<FMovieSceneInstance>& MovieSceneInstance, const FGuid& Guid )
|
|
{
|
|
TArray< TSharedRef<FPuppetActorInfo> >& PuppetObjects = InstanceToPuppetObjectsMap.FindChecked( MovieSceneInstance );
|
|
|
|
for( auto PuppetObjectIter( PuppetObjects.CreateConstIterator() ); PuppetObjectIter; ++PuppetObjectIter )
|
|
{
|
|
if( PuppetObjectIter->Get().GetType() == EPuppetObjectType::Actor )
|
|
{
|
|
TSharedRef< FPuppetActorInfo > PuppetActorInfo = StaticCastSharedRef< FPuppetActorInfo >( *PuppetObjectIter );
|
|
if( PuppetActorInfo->SpawnableGuid == Guid )
|
|
{
|
|
return PuppetActorInfo->PuppetActor.Get();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Not found
|
|
return NULL;
|
|
}
|
|
|
|
|
|
#undef LOCTEXT_NAMESPACE |