bool UActorRecording::StopRecording(ULevelSequence* CurrentSequence) { FString ActorName; if(CurrentSequence) { UMovieScene* MovieScene = CurrentSequence->GetMovieScene(); check(MovieScene); FMovieSceneSpawnable* Spawnable = MovieScene->FindSpawnable(Guid); if(Spawnable) { ActorName = Spawnable->GetName(); } } FScopedSlowTask SlowTask((float)SectionRecorders.Num() + 1.0f, FText::Format(NSLOCTEXT("SequenceRecorder", "ProcessingActor", "Processing Actor {0}"), FText::FromString(ActorName))); // stop property recorders for(auto& SectionRecorder : SectionRecorders) { SlowTask.EnterProgressFrame(); SectionRecorder->FinalizeSection(); } SlowTask.EnterProgressFrame(); SectionRecorders.Empty(); return true; }
float UMovieSceneShotTrack::FindEndTimeForShot( float StartTime ) { float EndTime = 0; bool bFoundEndTime = false; for( UMovieSceneSection* Section : SubMovieSceneSections ) { if( Section->GetStartTime() >= StartTime ) { EndTime = Section->GetStartTime(); bFoundEndTime = true; break; } } if( !bFoundEndTime ) { UMovieScene* OwnerScene = GetTypedOuter<UMovieScene>(); // End time should just end where the movie scene ends. Ensure it is at least the same as start time (this should only happen when the movie scene has an initial time range smaller than the start time EndTime = FMath::Max( OwnerScene->GetTimeRange().GetUpperBoundValue(), StartTime ); } if( StartTime == EndTime ) { // Give the shot a reasonable length of time to start out with. A 0 time shot is not usable EndTime = StartTime + .5f; } return EndTime; }
void UUMGSequencePlayer::InitSequencePlayer( const UWidgetAnimation& InAnimation, UUserWidget& UserWidget ) { Animation = &InAnimation; UMovieScene* MovieScene = Animation->MovieScene; // Cache the time range of the sequence to determine when we stop TimeRange = MovieScene->GetTimeRange(); RuntimeBindings = NewObject<UMovieSceneBindings>(this); RuntimeBindings->SetRootMovieScene( MovieScene ); UWidgetTree* WidgetTree = UserWidget.WidgetTree; TMap<FGuid, TArray<UObject*> > GuidToRuntimeObjectMap; // Bind to Runtime Objects for (const FWidgetAnimationBinding& Binding : InAnimation.AnimationBindings) { UObject* FoundObject = Binding.FindRuntimeObject( *WidgetTree ); if( FoundObject ) { TArray<UObject*>& Objects = GuidToRuntimeObjectMap.FindOrAdd(Binding.AnimationGuid); Objects.Add(FoundObject); } } for( auto It = GuidToRuntimeObjectMap.CreateConstIterator(); It; ++It ) { RuntimeBindings->AddBinding( It.Key(), It.Value() ); } }
void FSequencerDragOperation::GetSectionSnapTimes(TArray<float>& OutSnapTimes, UMovieSceneSection* Section, TSharedPtr<FTrackNode> SequencerNode, bool bIgnoreOurSectionCustomSnaps) { // @todo Sequencer handle dilation snapping better // Collect all the potential snap times from other section borders const TArray< TSharedRef<ISequencerSection> >& Sections = SequencerNode->GetSections(); for (int32 SectionIndex = 0; SectionIndex < Sections.Num(); ++SectionIndex) { const UMovieSceneSection* InSection = Sections[SectionIndex]->GetSectionObject(); bool bIsThisSection = Section == InSection; if (!bIgnoreOurSectionCustomSnaps || !bIsThisSection) { InSection->GetSnapTimes(OutSnapTimes, Section != InSection); } } // snap to director track if it exists, and we are not the director track UMovieSceneTrack* OuterTrack = Cast<UMovieSceneTrack>(Section->GetOuter()); UMovieScene* MovieScene = Cast<UMovieScene>(OuterTrack->GetOuter()); UMovieSceneTrack* ShotTrack = MovieScene->FindMasterTrack(UMovieSceneShotTrack::StaticClass()); if (ShotTrack && OuterTrack != ShotTrack) { const TArray<UMovieSceneSection*>& ShotSections = ShotTrack->GetAllSections(); for (int32 SectionIndex = 0; SectionIndex < ShotSections.Num(); ++SectionIndex) { auto Shot = ShotSections[SectionIndex]; Shot->GetSnapTimes(OutSnapTimes, true); } } }
UObject* FMovieSceneSequenceInstance::FindObject(const FGuid& ObjectId, const IMovieScenePlayer& Player) const { if(MovieSceneSequence.IsValid()) { // Attempt to find a possessable first UMovieScene* MovieScene = MovieSceneSequence->GetMovieScene(); if (MovieScene != nullptr) { FMovieScenePossessable* Possessable = MovieScene->FindPossessable(ObjectId); if (Possessable) { UObject* ParentObject = Player.GetPlaybackContext(); if (Possessable->GetParent().IsValid()) { ParentObject = FindObject(Possessable->GetParent(), Player); } return MovieSceneSequence->FindPossessableObject(ObjectId, ParentObject); } else { return FindSpawnedObject(ObjectId); } } } return nullptr; }
void FSlomoTrackEditor::HandleAddSlomoTrackMenuEntryExecute() { UMovieScene* MovieScene = GetFocusedMovieScene(); if (MovieScene == nullptr) { return; } UMovieSceneTrack* SlomoTrack = MovieScene->FindMasterTrack<UMovieSceneSlomoTrack>(); if (SlomoTrack != nullptr) { return; } const FScopedTransaction Transaction(NSLOCTEXT("Sequencer", "AddSlomoTrack_Transaction", "Add Play Rate Track")); MovieScene->Modify(); SlomoTrack = FindOrCreateMasterTrack<UMovieSceneSlomoTrack>().Track; ensure(SlomoTrack); UMovieSceneSection* NewSection = SlomoTrack->CreateNewSection(); ensure(NewSection); SlomoTrack->AddSection(*NewSection); GetSequencer()->NotifyMovieSceneDataChanged(); }
void FSequencerNodeTree::Update() { // @todo Sequencer - This update pass is too aggressive. Some nodes may still be valid Empty(); UMovieScene* MovieScene = Sequencer.GetFocusedMovieScene(); // Get the master tracks so we can get sections from them const TArray<UMovieSceneTrack*>& MasterTracks = MovieScene->GetMasterTracks(); for( int32 TrackIndex = 0; TrackIndex < MasterTracks.Num(); ++TrackIndex ) { UMovieSceneTrack& Track = *MasterTracks[TrackIndex]; TSharedRef<FTrackNode> SectionNode = MakeShareable( new FTrackNode( Track.GetTrackName(), Track, NULL, *this ) ); RootNodes.Add( SectionNode ); MakeSectionInterfaces( Track, SectionNode ); SectionNode->PinNode(); } const TArray<FMovieSceneObjectBinding>& ObjectBindings = MovieScene->GetObjectBindings(); // Make nodes for all object bindings for( int32 BindingIndex = 0; BindingIndex < ObjectBindings.Num(); ++BindingIndex ) { TSharedRef<FObjectBindingNode> ObjectBindingNode = AddObjectBinding( ObjectBindings[BindingIndex].GetDisplayName(), ObjectBindings[BindingIndex].GetObjectGuid() ); const TArray<UMovieSceneTrack*>& Tracks = ObjectBindings[BindingIndex].GetTracks(); for( int32 TrackIndex = 0; TrackIndex < Tracks.Num(); ++TrackIndex ) { UMovieSceneTrack& Track = *Tracks[TrackIndex]; FName SectionName = Track.GetTrackName(); check( SectionName != NAME_None ); TSharedRef<FTrackNode> SectionAreaNode = ObjectBindingNode->AddSectionAreaNode( SectionName, Track ); MakeSectionInterfaces( Track, SectionAreaNode ); } } struct FRootNodeSorter { bool operator()( const TSharedRef<FSequencerDisplayNode>& A, const TSharedRef<FSequencerDisplayNode>& B ) const { return A->GetType() == ESequencerNode::Object ? false : true; } }; // Sort so that master tracks appear before object tracks RootNodes.Sort( FRootNodeSorter() ); // Re-filter the tree after updating // @todo Sequencer - Newly added sections may need to be visible even when there is a filter FilterNodes( FilterString ); }
void FSequencerObjectBindingNode::SetDisplayName(const FText& NewDisplayName) { UMovieScene* MovieScene = GetSequencer().GetFocusedMovieSceneSequence()->GetMovieScene(); if (MovieScene != nullptr) { MovieScene->SetObjectDisplayName(ObjectBinding, NewDisplayName); } }
void FSequencerNodeTree::SaveExpansionState( const FSequencerDisplayNode& Node, bool bExpanded ) { // @todo Sequencer - This should be moved to the sequence level UMovieScene* MovieScene = Sequencer.GetFocusedMovieSceneSequence()->GetMovieScene(); FMovieSceneEditorData& EditorData = MovieScene->GetEditorData(); EditorData.ExpansionStates.Add( Node.GetPathName(), FMovieSceneExpansionState(bExpanded) ); }
bool FSequencerNodeTree::GetSavedExpansionState( const FSequencerDisplayNode& Node ) const { // @todo Sequencer - This should be moved to the sequence level UMovieScene* MovieScene = Sequencer.GetFocusedMovieSceneSequence()->GetMovieScene(); FMovieSceneEditorData& EditorData = MovieScene->GetEditorData(); FMovieSceneExpansionState* ExpansionState = EditorData.ExpansionStates.Find( Node.GetPathName() ); return ExpansionState ? ExpansionState->bExpanded : GetDefaultExpansionState(Node); }
FGuid FSequencer::GetHandleToObject( UObject* Object ) { TSharedRef<FMovieSceneInstance> FocusedMovieSceneInstance = GetFocusedMovieSceneInstance(); UMovieScene* FocusedMovieScene = FocusedMovieSceneInstance->GetMovieScene(); FGuid ObjectGuid = ObjectBindingManager->FindGuidForObject( *FocusedMovieScene, *Object ); if (ObjectGuid.IsValid()) { // Make sure that the possessable is still valid, if it's not remove the binding so new one // can be created. This can happen due to undo. FMovieScenePossessable* Possessable = FocusedMovieScene->FindPossessable(ObjectGuid); if (Possessable == nullptr ) { ObjectBindingManager->UnbindPossessableObjects(ObjectGuid); ObjectGuid.Invalidate(); } } bool bPossessableAdded = false; // If the object guid was not found attempt to add it // Note: Only possessed actors can be added like this if( !ObjectGuid.IsValid() && ObjectBindingManager->CanPossessObject( *Object ) ) { // @todo sequencer: Undo doesn't seem to be working at all const FScopedTransaction Transaction( LOCTEXT("UndoPossessingObject", "Possess Object with MovieScene") ); // Possess the object! { // Create a new possessable FocusedMovieScene->Modify(); ObjectGuid = FocusedMovieScene->AddPossessable( Object->GetName(), Object->GetClass() ); if ( IsShotFilteringOn() ) { AddUnfilterableObject(ObjectGuid); } ObjectBindingManager->BindPossessableObject( ObjectGuid, *Object ); bPossessableAdded = true; } } if( bPossessableAdded ) { SpawnOrDestroyPuppetObjects( GetFocusedMovieSceneInstance() ); NotifyMovieSceneDataChanged(); } return ObjectGuid; }
bool FSequencerNodeTree::GetSavedExpansionState( const FSequencerDisplayNode& Node ) const { UMovieScene* MovieScene = Sequencer.GetFocusedMovieScene(); FMovieSceneEditorData& EditorData = MovieScene->GetEditorData(); // Collapsed nodes are stored instead of expanded so that new nodes are expanded by default bool bCollapsed = EditorData.CollapsedSequencerNodes.Contains( Node.GetPathName() ); return !bCollapsed; }
FText FSequencerObjectBindingNode::GetDisplayName() const { UMovieScene* MovieScene = GetSequencer().GetFocusedMovieSceneSequence()->GetMovieScene(); if (MovieScene != nullptr) { return MovieScene->GetObjectDisplayName(ObjectBinding); } return DefaultDisplayName; }
const UClass* FSequencerObjectBindingNode::GetClassForObjectBinding() const { UMovieScene* MovieScene = GetSequencer().GetFocusedMovieSceneSequence()->GetMovieScene(); FMovieSceneSpawnable* Spawnable = MovieScene->FindSpawnable(ObjectBinding); FMovieScenePossessable* Possessable = MovieScene->FindPossessable(ObjectBinding); // should exist, but also shouldn't be both a spawnable and a possessable check((Spawnable != nullptr) ^ (Possessable != nullptr)); check((nullptr == Spawnable) || (nullptr != Spawnable->GetObjectTemplate()) ); const UClass* ObjectClass = Spawnable ? Spawnable->GetObjectTemplate()->GetClass() : Possessable->GetPossessedObjectClass(); return ObjectClass; }
UMovieSceneCameraCutTrack* FCameraCutTrackEditor::FindOrCreateCameraCutTrack() { UMovieScene* FocusedMovieScene = GetFocusedMovieScene(); UMovieSceneTrack* CameraCutTrack = FocusedMovieScene->GetCameraCutTrack(); if (CameraCutTrack == nullptr) { const FScopedTransaction Transaction(LOCTEXT("AddCameraCutTrack_Transaction", "Add Camera Cut Track")); FocusedMovieScene->Modify(); CameraCutTrack = FocusedMovieScene->AddCameraCutTrack(UMovieSceneCameraCutTrack::StaticClass()); } return CastChecked<UMovieSceneCameraCutTrack>(CameraCutTrack); }
void FSequencerNodeTree::SaveExpansionState( const FSequencerDisplayNode& Node, bool bExpanded ) { UMovieScene* MovieScene = Sequencer.GetFocusedMovieScene(); FMovieSceneEditorData& EditorData = MovieScene->GetEditorData(); if( bExpanded ) { EditorData.CollapsedSequencerNodes.Remove( Node.GetPathName() ); } else { // Collapsed nodes are stored instead of expanded so that new nodes are expanded by default EditorData.CollapsedSequencerNodes.AddUnique( Node.GetPathName() ) ; } }
void FMovieSceneSequenceInstance::UpdatePassSingle( EMovieSceneUpdateData& UpdateData, class IMovieScenePlayer& Player ) { if(MovieSceneSequence.IsValid()) { // Refresh time range so that spawnables can be created if they fall within the playback range, or destroyed if not UMovieScene* MovieScene = MovieSceneSequence->GetMovieScene(); TimeRange = MovieScene->GetPlaybackRange(); TArray<TWeakObjectPtr<UObject>> NoObjects; // update each master track for( FMovieSceneInstanceMap::TIterator It( MasterTrackInstances ); It; ++It ) { if (It.Value()->HasUpdatePasses() & UpdateData.UpdatePass && It.Value() != CameraCutTrackInstance) { It.Value()->Update( UpdateData, NoObjects, Player, *this); } } // update tracks bound to objects TMap<FGuid, FMovieSceneObjectBindingInstance>::TIterator ObjectIt = ObjectBindingInstances.CreateIterator(); for(; ObjectIt; ++ObjectIt ) { FMovieSceneObjectBindingInstance& ObjectBindingInstance = ObjectIt.Value(); for( FMovieSceneInstanceMap::TIterator It = ObjectBindingInstance.TrackInstances.CreateIterator(); It; ++It ) { if (It.Value()->HasUpdatePasses() & UpdateData.UpdatePass && It.Value() != CameraCutTrackInstance) { It.Value()->Update( UpdateData, ObjectBindingInstance.RuntimeObjects, Player, *this ); } } } // update camera cut track last to make sure spawnable cameras are there, and to override sub-shots if (CameraCutTrackInstance.IsValid()) { if (CameraCutTrackInstance->HasUpdatePasses() & UpdateData.UpdatePass) { CameraCutTrackInstance->Update(UpdateData, NoObjects, Player, *this ); } } } }
FText UK2Node_PlayMovieScene::GetNodeTitle(ENodeTitleType::Type TitleType) const { UMovieScene* MovieScene = (MovieSceneBindings != nullptr) ? MovieSceneBindings->GetRootMovieScene() : nullptr; if (MovieScene == nullptr) { return NSLOCTEXT("PlayMovieSceneNode", "NodeTitleWithNoMovieScene", "Play Movie Scene (No Asset)"); } // @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 else //if (CachedNodeTitle.IsOutOfDate(this)) { FFormatNamedArguments Args; Args.Add(TEXT("SceneName"), FText::FromString(MovieScene->GetName())); // FText::Format() is slow, so we cache this to save on performance CachedNodeTitle.SetCachedText(FText::Format(NSLOCTEXT("PlayMovieSceneNode", "NodeTitle", "Play Movie Scene: {SceneName}"), Args), this); } return CachedNodeTitle; }
FSequencerObjectBindingNode::FSequencerObjectBindingNode(FName NodeName, const FText& InDisplayName, const FGuid& InObjectBinding, TSharedPtr<FSequencerDisplayNode> InParentNode, FSequencerNodeTree& InParentTree) : FSequencerDisplayNode(NodeName, InParentNode, InParentTree) , ObjectBinding(InObjectBinding) , DefaultDisplayName(InDisplayName) { UMovieScene* MovieScene = GetSequencer().GetFocusedMovieSceneSequence()->GetMovieScene(); if (MovieScene->FindPossessable(ObjectBinding)) { BindingType = EObjectBindingType::Possessable; } else if (MovieScene->FindSpawnable(ObjectBinding)) { BindingType = EObjectBindingType::Spawnable; } else { BindingType = EObjectBindingType::Unknown; } }
void FSequencer::OnActorsDropped( const TArray<TWeakObjectPtr<AActor> >& Actors ) { bool bPossessableAdded = false; for( TWeakObjectPtr<AActor> WeakActor : Actors ) { AActor* Actor = WeakActor.Get(); if( Actor != NULL ) { // Grab the MovieScene that is currently focused. We'll add our Blueprint as an inner of the // MovieScene asset. UMovieScene* OwnerMovieScene = GetFocusedMovieScene(); // @todo sequencer: Undo doesn't seem to be working at all const FScopedTransaction Transaction( LOCTEXT("UndoPossessingObject", "Possess Object with MovieScene") ); // Possess the object! { // Create a new possessable OwnerMovieScene->Modify(); const FGuid PossessableGuid = OwnerMovieScene->AddPossessable( Actor->GetActorLabel(), Actor->GetClass() ); if ( IsShotFilteringOn() ) { AddUnfilterableObject(PossessableGuid); } ObjectBindingManager->BindPossessableObject( PossessableGuid, *Actor ); bPossessableAdded = true; } } } if( bPossessableAdded ) { SpawnOrDestroyPuppetObjects( GetFocusedMovieSceneInstance() ); NotifyMovieSceneDataChanged(); } }
void FSequencer::AddSubMovieScene( UMovieScene* SubMovieScene ) { // @todo Sequencer - sub-moviescenes This should be moved to the sub-moviescene editor SubMovieScene->SetFlags( RF_Transactional ); // Grab the MovieScene that is currently focused. THis is the movie scene that will contain the sub-moviescene UMovieScene* OwnerMovieScene = GetFocusedMovieScene(); // @todo sequencer: Undo doesn't seem to be working at all const FScopedTransaction Transaction( LOCTEXT("UndoAddingObject", "Add Object to MovieScene") ); OwnerMovieScene->Modify(); UMovieSceneTrack* Type = OwnerMovieScene->FindMasterTrack( USubMovieSceneTrack::StaticClass() ) ; if( !Type ) { Type = OwnerMovieScene->AddMasterTrack( USubMovieSceneTrack::StaticClass() ); } USubMovieSceneTrack* SubMovieSceneType = CastChecked<USubMovieSceneTrack>( Type ); SubMovieSceneType->AddMovieSceneSection( SubMovieScene, ScrubPosition ); }
FGuid FSequencerActorBindingManager::FindGuidForObject( const UMovieScene& MovieScene, UObject& Object ) const { FGuid ObjectGuid = FindSpawnableGuidForPuppetObject( &Object ); if( !ObjectGuid.IsValid() ) { // Spawnable // Is this a game preview object? const bool bIsGamePreviewObject = !!( Object.GetOutermost()->PackageFlags & PKG_PlayInEditor ); if( bIsGamePreviewObject ) { // OK, so someone is asking for a handle to an object from a game preview session, probably because // they want to capture keys during live simulation. // Check to see if we already have a puppet that was generated from a recording of this game preview object // @todo sequencer livecapture: We could support recalling counterpart by full name instead of weak pointer, to allow "overdubbing" of previously recorded actors, when the new actors in the current play session have the same path name // @todo sequencer livecapture: Ideally we could capture from editor-world actors that are "puppeteered" as well (real time) const FMovieSceneSpawnable* FoundSpawnable = MovieScene.FindSpawnableForCounterpart( &Object ); if( FoundSpawnable != NULL ) { ObjectGuid = FoundSpawnable->GetGuid(); } } else { BindToPlayMovieSceneNode( false ); // Possessable // When editing within the level editor, make sure we're bound to the level script node which contains data about possessables. if( PlayMovieSceneNode.IsValid() ) { ObjectGuid = PlayMovieSceneNode->FindGuidForObject( &Object ); } } } return ObjectGuid; }
void FSequencer::ResetToNewRootMovieScene( UMovieScene& NewRoot, TSharedRef<ISequencerObjectBindingManager> NewObjectBindingManager ) { DestroySpawnablesForAllMovieScenes(); //@todo Sequencer - Encapsulate this better MovieSceneStack.Empty(); Selection.Empty(); FilteringShots.Empty(); UnfilterableSections.Empty(); UnfilterableObjects.Empty(); MovieSceneSectionToInstanceMap.Empty(); NewRoot.SetFlags(RF_Transactional); ObjectBindingManager = NewObjectBindingManager; // Focusing the initial movie scene needs to be done before the first time GetFocusedMovieSceneInstane or GetRootMovieSceneInstance is used RootMovieSceneInstance = MakeShareable(new FMovieSceneInstance(NewRoot)); MovieSceneStack.Add(RootMovieSceneInstance.ToSharedRef()); SequencerWidget->ResetBreadcrumbs(); NotifyMovieSceneDataChanged(); }
bool FCameraCutTrackEditor::HandleAddCameraCutTrackMenuEntryCanExecute() const { UMovieScene* FocusedMovieScene = GetFocusedMovieScene(); return ((FocusedMovieScene != nullptr) && (FocusedMovieScene->GetCameraCutTrack() == nullptr)); }
void FSequencerActorBindingManager::SpawnOrDestroyObjectsForInstance( 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 ) { FMovieSceneSpawnable& Spawnable = MovieScene->GetSpawnable( SpawnableIndex ); // Must have a valid world for us to be able to do this if( ActorWorld != 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 = ActorWorld->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? FActorLabelUtilities::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 UK2Node_PlayMovieScene::CreatePinForBoundObject( FMovieSceneBoundObject& BoundObject ) { const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>(); // We use the GUID as the pin name as this uniquely identifies it const FString PinName = BoundObject.GetPossessableGuid().ToString( EGuidFormats::DigitsWithHyphens ); // For the friendly name, we use the possessable name from the MovieScene asset that is associated with this node FText PinFriendlyName = FText::FromString(PinName); { UMovieScene* MovieScene = GetMovieScene(); if( MovieScene != NULL ) // @todo sequencer: Need to refresh the PinFriendlyName if the MovieScene asset changes, or if the possessable slot is renamed within Sequencer { for( auto PossessableIndex = 0; PossessableIndex < MovieScene->GetPossessableCount(); ++PossessableIndex ) { auto& Possessable = MovieScene->GetPossessable( PossessableIndex ); if( Possessable.GetGuid() == BoundObject.GetPossessableGuid() ) { // Found a name for this possessable PinFriendlyName = FText::FromString(Possessable.GetName()); break; } } } } const FString PinSubCategory(TEXT("")); UObject* PinSubCategoryObject = AActor::StaticClass(); const bool bIsArray = false; const bool bIsReference = false; UEdGraphPin* NewPin = CreatePin( EGPD_Input, K2Schema->PC_Object, PinSubCategory, PinSubCategoryObject, bIsArray, bIsReference, PinName ); check( NewPin != NULL ); // Set the friendly name for this pin NewPin->PinFriendlyName = PinFriendlyName; // 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? const TArray< UObject* >& Objects = BoundObject.GetObjects(); if( Objects.Num() > 0 ) { for( auto ObjectIter( Objects.CreateConstIterator() ); ObjectIter; ++ObjectIter ) { auto* Object = *ObjectIter; if( ensure( Object != NULL ) ) { // Check to see if we have a literal for this object already UK2Node_Literal* LiteralNode = NULL; { TArray< UK2Node_Literal* > LiteralNodes; GetGraph()->GetNodesOfClass( LiteralNodes ); for( auto NodeIt( LiteralNodes.CreateConstIterator() ); NodeIt; ++NodeIt ) { const auto CurLiteralNode = *NodeIt; if( CurLiteralNode->GetObjectRef() == Object ) { // Found one! LiteralNode = CurLiteralNode; break; } } } if( LiteralNode == NULL ) { // No literal for this object yet, so we'll make one now. UK2Node_Literal* LiteralNodeTemplate = NewObject<UK2Node_Literal>(); LiteralNodeTemplate->SetObjectRef( Object ); // 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(); LiteralNode = FEdGraphSchemaAction_K2NewNode::SpawnNodeFromTemplate<UK2Node_Literal>(GetGraph(), LiteralNodeTemplate, NewNodePos); } // Hook up the object reference literal to our pin LiteralNode->GetValuePin()->MakeLinkTo( NewPin ); } } } }
TOptional<EItemDropZone> FSequencerFolderNode::CanDrop( FSequencerDisplayNodeDragDropOp& DragDropOp, EItemDropZone ItemDropZone ) const { DragDropOp.ResetToDefaultToolTip(); if ( ItemDropZone == EItemDropZone::AboveItem ) { if ( GetParent().IsValid() ) { // When dropping above, only allow it for root level nodes. return TOptional<EItemDropZone>(); } else { // Make sure there are no folder name collisions with the root folders UMovieScene* FocusedMovieScene = GetParentTree().GetSequencer().GetFocusedMovieSceneSequence()->GetMovieScene(); TSet<FName> RootFolderNames; for ( UMovieSceneFolder* RootFolder : FocusedMovieScene->GetRootFolders() ) { RootFolderNames.Add( RootFolder->GetFolderName() ); } for ( TSharedRef<FSequencerDisplayNode> DraggedNode : DragDropOp.GetDraggedNodes() ) { if ( DraggedNode->GetType() == ESequencerNode::Folder ) { TSharedRef<FSequencerFolderNode> DraggedFolder = StaticCastSharedRef<FSequencerFolderNode>( DraggedNode ); if ( RootFolderNames.Contains( DraggedFolder->GetFolder().GetFolderName() ) ) { DragDropOp.CurrentHoverText = FText::Format( NSLOCTEXT( "SeqeuencerFolderNode", "DuplicateRootFolderDragErrorFormat", "Root folder with name '{0}' already exists." ), FText::FromName( DraggedFolder->GetFolder().GetFolderName() ) ); return TOptional<EItemDropZone>(); } } } } return TOptional<EItemDropZone>( EItemDropZone::AboveItem ); } else { // When dropping onto, don't allow dropping into the same folder, don't allow dropping // parents into children, and don't allow duplicate folder names. TSet<FName> ChildFolderNames; for ( UMovieSceneFolder* ChildFolder : GetFolder().GetChildFolders() ) { ChildFolderNames.Add( ChildFolder->GetFolderName() ); } for ( TSharedRef<FSequencerDisplayNode> DraggedNode : DragDropOp.GetDraggedNodes() ) { TSharedPtr<FSequencerDisplayNode> ParentSeqNode = DraggedNode->GetParent(); if ( ParentSeqNode.IsValid() && ParentSeqNode.Get() == this ) { DragDropOp.CurrentHoverText = NSLOCTEXT( "SeqeuencerFolderNode", "SameParentDragError", "Can't drag a node onto the same parent." ); return TOptional<EItemDropZone>(); } if ( DraggedNode->GetType() == ESequencerNode::Folder ) { TSharedRef<FSequencerFolderNode> DraggedFolder = StaticCastSharedRef<FSequencerFolderNode>( DraggedNode ); if ( ChildFolderNames.Contains( DraggedFolder->GetFolder().GetFolderName() ) ) { DragDropOp.CurrentHoverText = FText::Format( NSLOCTEXT( "SeqeuencerFolderNode", "DuplicateChildFolderDragErrorFormat", "Folder with name '{0}' already exists." ), FText::FromName( DraggedFolder->GetFolder().GetFolderName() ) ); return TOptional<EItemDropZone>(); } } } TSharedPtr<FSequencerDisplayNode> CurrentNode = SharedThis( ( FSequencerDisplayNode* )this ); while ( CurrentNode.IsValid() ) { if ( DragDropOp.GetDraggedNodes().Contains( CurrentNode ) ) { DragDropOp.CurrentHoverText = NSLOCTEXT( "SeqeuencerFolderNode", "ParentIntoChildDragError", "Can't drag a parent node into one of it's children." ); return TOptional<EItemDropZone>(); } CurrentNode = CurrentNode->GetParent(); } return TOptional<EItemDropZone>( EItemDropZone::OntoItem ); } }
void FSequencerFolderNode::Drop( const TArray<TSharedRef<FSequencerDisplayNode>>& DraggedNodes, EItemDropZone ItemDropZone ) { const FScopedTransaction Transaction( NSLOCTEXT( "SequencerFolderNode", "MoveIntoFolder", "Move items into folder." ) ); GetFolder().SetFlags(RF_Transactional); GetFolder().Modify(); for ( TSharedRef<FSequencerDisplayNode> DraggedNode : DraggedNodes ) { TSharedPtr<FSequencerDisplayNode> ParentSeqNode = DraggedNode->GetParent(); switch ( DraggedNode->GetType() ) { case ESequencerNode::Folder: { TSharedRef<FSequencerFolderNode> DraggedFolderNode = StaticCastSharedRef<FSequencerFolderNode>( DraggedNode ); UMovieScene* FocusedMovieScene = GetParentTree().GetSequencer().GetFocusedMovieSceneSequence()->GetMovieScene(); if ( ItemDropZone == EItemDropZone::OntoItem ) { GetFolder().AddChildFolder( &DraggedFolderNode->GetFolder() ); } else { FocusedMovieScene->Modify(); FocusedMovieScene->GetRootFolders().Add( &DraggedFolderNode->GetFolder() ); } if ( ParentSeqNode.IsValid() ) { checkf( ParentSeqNode->GetType() == ESequencerNode::Folder, TEXT( "Can not remove from unsupported parent node." ) ); TSharedPtr<FSequencerFolderNode> ParentFolder = StaticCastSharedPtr<FSequencerFolderNode>( ParentSeqNode ); ParentFolder->GetFolder().Modify(); ParentFolder->GetFolder().RemoveChildFolder( &DraggedFolderNode->GetFolder() ); } else { FocusedMovieScene->Modify(); FocusedMovieScene->GetRootFolders().Remove( &DraggedFolderNode->GetFolder() ); } break; } case ESequencerNode::Track: { TSharedRef<FSequencerTrackNode> DraggedTrackNode = StaticCastSharedRef<FSequencerTrackNode>( DraggedNode ); if( ItemDropZone == EItemDropZone::OntoItem ) { GetFolder().AddChildMasterTrack( DraggedTrackNode->GetTrack() ); } if ( ParentSeqNode.IsValid() ) { checkf( ParentSeqNode->GetType() == ESequencerNode::Folder, TEXT( "Can not remove from unsupported parent node." ) ); TSharedPtr<FSequencerFolderNode> ParentFolder = StaticCastSharedPtr<FSequencerFolderNode>( ParentSeqNode ); ParentFolder->GetFolder().Modify(); ParentFolder->GetFolder().RemoveChildMasterTrack( DraggedTrackNode->GetTrack() ); } break; } case ESequencerNode::Object: { TSharedRef<FSequencerObjectBindingNode> DraggedObjectBindingNode = StaticCastSharedRef<FSequencerObjectBindingNode>( DraggedNode ); if ( ItemDropZone == EItemDropZone::OntoItem ) { GetFolder().AddChildObjectBinding( DraggedObjectBindingNode->GetObjectBinding() ); } if ( ParentSeqNode.IsValid() ) { checkf( ParentSeqNode->GetType() == ESequencerNode::Folder, TEXT( "Can not remove from unsupported parent node." ) ); TSharedPtr<FSequencerFolderNode> ParentFolder = StaticCastSharedPtr<FSequencerFolderNode>( ParentSeqNode ); ParentFolder->GetFolder().Modify(); ParentFolder->GetFolder().RemoveChildObjectBinding( DraggedObjectBindingNode->GetObjectBinding() ); } break; } } } SetExpansionState( true ); ParentTree.GetSequencer().NotifyMovieSceneDataChanged( EMovieSceneDataChangeType::MovieSceneStructureItemsChanged ); }
void UActorAnimationPlayer::SpawnActorsForMovie(TSharedRef<FMovieSceneSequenceInstance> MovieSceneInstance) { UWorld* WorldPtr = World.Get(); if (WorldPtr == nullptr) { return; } UMovieScene* MovieScene = MovieSceneInstance->GetSequence()->GetMovieScene(); if (MovieScene == nullptr) { return; } TArray<FSpawnedActorInfo>* FoundSpawnedActors = InstanceToSpawnedActorMap.Find(MovieSceneInstance); if (FoundSpawnedActors != nullptr) { // Remove existing spawned actors for this movie DestroyActorsForMovie( MovieSceneInstance ); } TArray<FSpawnedActorInfo>& SpawnedActorList = InstanceToSpawnedActorMap.Add(MovieSceneInstance, TArray<FSpawnedActorInfo>()); for (auto SpawnableIndex = 0; SpawnableIndex < MovieScene->GetSpawnableCount(); ++SpawnableIndex) { auto& Spawnable = MovieScene->GetSpawnable(SpawnableIndex); UClass* GeneratedClass = Spawnable.GetClass(); if ((GeneratedClass == nullptr) || !GeneratedClass->IsChildOf(AActor::StaticClass())) { continue; } AActor* ActorCDO = CastChecked<AActor>(GeneratedClass->ClassDefaultObject); const FVector SpawnLocation = ActorCDO->GetRootComponent()->RelativeLocation; const FRotator SpawnRotation = ActorCDO->GetRootComponent()->RelativeRotation; FActorSpawnParameters SpawnInfo; { SpawnInfo.ObjectFlags = RF_NoFlags; } AActor* NewActor = WorldPtr->SpawnActor(GeneratedClass, &SpawnLocation, &SpawnRotation, SpawnInfo); if (NewActor) { // Actor was spawned OK! FSpawnedActorInfo NewInfo; { NewInfo.RuntimeGuid = Spawnable.GetGuid(); NewInfo.SpawnedActor = NewActor; } SpawnedActorList.Add(NewInfo); } } }
void URuntimeMovieScenePlayer::SpawnActorsForMovie( TSharedRef<FMovieSceneInstance> MovieSceneInstance ) { UWorld* WorldPtr = World.Get(); if( WorldPtr != NULL && MovieSceneBindings != NULL ) { UMovieScene* MovieScene = MovieSceneInstance->GetMovieScene(); if( MovieScene != NULL ) { TArray<FSpawnedActorInfo>* FoundSpawnedActors = InstanceToSpawnedActorMap.Find( MovieSceneInstance ); if( FoundSpawnedActors ) { // Remove existing spawned actors for this movie DestroyActorsForMovie( MovieSceneInstance ); } TArray<FSpawnedActorInfo>& SpawnedActorList = InstanceToSpawnedActorMap.Add( MovieSceneInstance, TArray<FSpawnedActorInfo>() ); for( auto SpawnableIndex = 0; SpawnableIndex < MovieScene->GetSpawnableCount(); ++SpawnableIndex ) { auto& Spawnable = MovieScene->GetSpawnable( SpawnableIndex ); UClass* GeneratedClass = Spawnable.GetClass(); if ( GeneratedClass != NULL ) { const bool bIsActorBlueprint = GeneratedClass->IsChildOf( AActor::StaticClass() ); if ( bIsActorBlueprint ) { AActor* ActorCDO = CastChecked< AActor >( GeneratedClass->ClassDefaultObject ); const FVector SpawnLocation = ActorCDO->GetRootComponent()->RelativeLocation; const FRotator SpawnRotation = ActorCDO->GetRootComponent()->RelativeRotation; FActorSpawnParameters SpawnInfo; SpawnInfo.ObjectFlags = RF_NoFlags; AActor* NewActor = WorldPtr->SpawnActor( GeneratedClass, &SpawnLocation, &SpawnRotation, SpawnInfo ); if( NewActor ) { // Actor was spawned OK! FSpawnedActorInfo NewInfo; NewInfo.RuntimeGuid = Spawnable.GetGuid(); NewInfo.SpawnedActor = NewActor; SpawnedActorList.Add( NewInfo ); } } } } } } }