void UActorRecording::StartRecordingNewComponents(ULevelSequence* CurrentSequence, float CurrentSequenceTime) { if (GetActorToRecord() != nullptr) { // find the new component(s) TInlineComponentArray<USceneComponent*> NewComponents; TArray<USceneComponent*> SceneComponents; GetSceneComponents(SceneComponents); for(USceneComponent* SceneComponent : SceneComponents) { if(ValidComponent(SceneComponent)) { TWeakObjectPtr<USceneComponent> WeakSceneComponent(SceneComponent); int32 FoundIndex = TrackedComponents.Find(WeakSceneComponent); if(FoundIndex == INDEX_NONE) { // new component! NewComponents.Add(SceneComponent); } } } ProcessNewComponentArray(NewComponents); UMovieScene* MovieScene = CurrentSequence->GetMovieScene(); check(MovieScene); FAnimationRecordingSettings ComponentAnimationSettings = AnimationSettings; ComponentAnimationSettings.bRemoveRootAnimation = false; ComponentAnimationSettings.bRecordInWorldSpace = false; const USequenceRecorderSettings* Settings = GetDefault<USequenceRecorderSettings>(); if (!bRecordToPossessable) { FMovieSceneSpawnable* Spawnable = MovieScene->FindSpawnable(Guid); check(Spawnable); AActor* ObjectTemplate = CastChecked<AActor>(Spawnable->GetObjectTemplate()); for (USceneComponent* SceneComponent : NewComponents) { // new component, so we need to add this to our BP if it didn't come from SCS FName NewName; if (SceneComponent->CreationMethod != EComponentCreationMethod::SimpleConstructionScript) { // Give this component a unique name within its parent NewName = *FString::Printf(TEXT("Dynamic%s"), *SceneComponent->GetFName().GetPlainNameString()); NewName.SetNumber(1); while (FindObjectFast<UObject>(ObjectTemplate, NewName)) { NewName.SetNumber(NewName.GetNumber() + 1); } USceneComponent* TemplateRoot = ObjectTemplate->GetRootComponent(); USceneComponent* AttachToComponent = nullptr; // look for a similar attach parent in the current structure USceneComponent* AttachParent = SceneComponent->GetAttachParent(); if(AttachParent != nullptr) { // First off, check if we're attached to a component that has already been duplicated into this object // If so, the name lookup will fail, so we use a direct reference if (TWeakObjectPtr<USceneComponent>* DuplicatedComponent = DuplicatedDynamicComponents.Find(AttachParent)) { AttachToComponent = DuplicatedComponent->Get(); } // If we don't have an attachment parent duplicated already, perform a name lookup if (!AttachToComponent) { FName AttachName = SceneComponent->GetAttachParent()->GetFName(); TInlineComponentArray<USceneComponent*> AllChildren; ObjectTemplate->GetComponents(AllChildren); for (USceneComponent* Child : AllChildren) { CA_SUPPRESS(28182); // Dereferencing NULL pointer. 'Child' contains the same NULL value as 'AttachToComponent' did. if (Child->GetFName() == AttachName) { AttachToComponent = Child; break; } } } } if (!AttachToComponent) { AttachToComponent = ObjectTemplate->GetRootComponent(); } USceneComponent* NewTemplateComponent = Cast<USceneComponent>(StaticDuplicateObject(SceneComponent, ObjectTemplate, NewName, RF_AllFlags & ~RF_Transient)); NewTemplateComponent->AttachToComponent(AttachToComponent, FAttachmentTransformRules::KeepRelativeTransform, SceneComponent->GetAttachSocketName()); ObjectTemplate->AddInstanceComponent(NewTemplateComponent); DuplicatedDynamicComponents.Add(SceneComponent, NewTemplateComponent); } else { NewName = SceneComponent->GetFName(); } StartRecordingComponentProperties(NewName, SceneComponent, GetActorToRecord(), CurrentSequence, CurrentSequenceTime, ComponentAnimationSettings); bNewComponentAddedWhileRecording = true; } SyncTrackedComponents(); } else { for (USceneComponent* SceneComponent : NewComponents) { // new component, start recording StartRecordingComponentProperties(SceneComponent->GetFName(), SceneComponent, GetActorToRecord(), CurrentSequence, CurrentSequenceTime, ComponentAnimationSettings); } SyncTrackedComponents(); } } }
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; }
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; }
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 FSequencerObjectBindingNode::BuildContextMenu(FMenuBuilder& MenuBuilder) { ISequencerModule& SequencerModule = FModuleManager::GetModuleChecked<ISequencerModule>("Sequencer"); UObject* BoundObject = GetSequencer().FindSpawnedObjectOrTemplate(ObjectBinding); TSharedRef<FUICommandList> CommandList(new FUICommandList); TSharedPtr<FExtender> Extender = SequencerModule.GetObjectBindingContextMenuExtensibilityManager()->GetAllExtenders(CommandList, TArrayBuilder<UObject*>().Add(BoundObject)); if (Extender.IsValid()) { MenuBuilder.PushExtender(Extender.ToSharedRef()); } if (GetSequencer().IsLevelEditorSequencer()) { UMovieScene* MovieScene = GetSequencer().GetFocusedMovieSceneSequence()->GetMovieScene(); FMovieSceneSpawnable* Spawnable = MovieScene->FindSpawnable(ObjectBinding); if (Spawnable) { MenuBuilder.AddSubMenu( LOCTEXT("OwnerLabel", "Spawned Object Owner"), LOCTEXT("OwnerTooltip", "Specifies how the spawned object is to be owned"), FNewMenuDelegate::CreateSP(this, &FSequencerObjectBindingNode::AddSpawnOwnershipMenu) ); MenuBuilder.AddMenuEntry( FSequencerCommands::Get().ConvertToPossessable ); } else { const UClass* ObjectClass = GetClassForObjectBinding(); if (ObjectClass->IsChildOf(AActor::StaticClass())) { FFormatNamedArguments Args; MenuBuilder.AddSubMenu( FText::Format( LOCTEXT("Assign Actor ", "Assign Actor"), Args), FText::Format( LOCTEXT("AssignActorTooltip", "Assign an actor to this track"), Args ), FNewMenuDelegate::CreateRaw(&GetSequencer(), &FSequencer::AssignActor, ObjectBinding)); } MenuBuilder.AddMenuEntry( FSequencerCommands::Get().ConvertToSpawnable ); } MenuBuilder.AddMenuEntry( LOCTEXT("Import FBX", "Import..."), LOCTEXT("ImportFBXTooltip", "Import FBX animation to this object"), FSlateIcon(), FUIAction( FExecuteAction::CreateLambda([=]{ GetSequencer().ImportFBX(); }) )); MenuBuilder.AddMenuEntry( LOCTEXT("Export FBX", "Export..."), LOCTEXT("ExportFBXTooltip", "Export FBX animation from this object"), FSlateIcon(), FUIAction( FExecuteAction::CreateLambda([=]{ GetSequencer().ExportFBX(); }) )); MenuBuilder.BeginSection("Organize", LOCTEXT("OrganizeContextMenuSectionName", "Organize")); { MenuBuilder.AddSubMenu( LOCTEXT("LabelsSubMenuText", "Labels"), LOCTEXT("LabelsSubMenuTip", "Add or remove labels on this track"), FNewMenuDelegate::CreateSP(this, &FSequencerObjectBindingNode::HandleLabelsSubMenuCreate) ); } MenuBuilder.EndSection(); } FSequencerDisplayNode::BuildContextMenu(MenuBuilder); }
TSharedPtr<FMovieSceneAnimationSectionRecorder> UActorRecording::StartRecordingComponentProperties(const FName& BindingName, USceneComponent* SceneComponent, UObject* BindingContext, ULevelSequence* CurrentSequence, float CurrentSequenceTime, const FAnimationRecordingSettings& InAnimationSettings) { // first create a possessable for this component to be controlled by UMovieScene* OwnerMovieScene = CurrentSequence->GetMovieScene(); const FGuid PossessableGuid = OwnerMovieScene->AddPossessable(BindingName.ToString(), SceneComponent->GetClass()); // Set up parent/child guids for possessables within spawnables FMovieScenePossessable* ChildPossessable = OwnerMovieScene->FindPossessable(PossessableGuid); if (ensure(ChildPossessable)) { ChildPossessable->SetParent(Guid); } FMovieSceneSpawnable* ParentSpawnable = OwnerMovieScene->FindSpawnable(Guid); if (ParentSpawnable) { ParentSpawnable->AddChildPossessable(PossessableGuid); } // BindingName must be the component's path relative to its owner Actor FLevelSequenceObjectReference ObjectReference(FUniqueObjectGuid(), BindingName.ToString()); CurrentSequence->BindPossessableObject(PossessableGuid, ObjectReference); // First try built-in animation recorder... TSharedPtr<FMovieSceneAnimationSectionRecorder> AnimationRecorder = nullptr; if (FSequenceRecorder::Get().GetAnimationRecorderFactory().CanRecordObject(SceneComponent)) { AnimationRecorder = FSequenceRecorder::Get().GetAnimationRecorderFactory().CreateSectionRecorder(this, InAnimationSettings); AnimationRecorder->CreateSection(SceneComponent, OwnerMovieScene, PossessableGuid, CurrentSequenceTime); AnimationRecorder->Record(CurrentSequenceTime); SectionRecorders.Add(AnimationRecorder); } // ...and transform... if (FSequenceRecorder::Get().GetTransformRecorderFactory().CanRecordObject(SceneComponent)) { TSharedPtr<IMovieSceneSectionRecorder> Recorder = FSequenceRecorder::Get().GetTransformRecorderFactory().CreateSectionRecorder(true, nullptr); if (Recorder.IsValid()) { Recorder->CreateSection(SceneComponent, OwnerMovieScene, PossessableGuid, CurrentSequenceTime); Recorder->Record(CurrentSequenceTime); SectionRecorders.Add(Recorder); } } // ...now any external recorders TArray<IMovieSceneSectionRecorderFactory*> ModularFeatures = IModularFeatures::Get().GetModularFeatureImplementations<IMovieSceneSectionRecorderFactory>(MovieSceneSectionRecorderFactoryName); for (IMovieSceneSectionRecorderFactory* Factory : ModularFeatures) { if (Factory->CanRecordObject(SceneComponent)) { TSharedPtr<IMovieSceneSectionRecorder> Recorder = Factory->CreateSectionRecorder(ActorSettings); if (Recorder.IsValid()) { Recorder->CreateSection(SceneComponent, OwnerMovieScene, PossessableGuid, CurrentSequenceTime); Recorder->Record(CurrentSequenceTime); SectionRecorders.Add(Recorder); } } } return AnimationRecorder; }