2019-12-27 09:26:59 -05:00
|
|
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
2018-05-29 14:30:28 -04:00
|
|
|
|
2018-05-14 17:38:22 -04:00
|
|
|
#include "SequencerTools.h"
|
2018-10-25 23:23:56 -04:00
|
|
|
#include "SequencerScriptingEditor.h"
|
2018-05-14 17:38:22 -04:00
|
|
|
#include "MovieSceneCapture.h"
|
|
|
|
|
#include "MovieSceneCaptureDialogModule.h"
|
|
|
|
|
#include "AutomatedLevelSequenceCapture.h"
|
|
|
|
|
#include "MovieSceneTimeHelpers.h"
|
|
|
|
|
#include "UObject/Stack.h"
|
|
|
|
|
#include "UObject/Package.h"
|
2020-01-27 20:11:15 -05:00
|
|
|
#include "LevelSequenceActor.h"
|
2019-05-30 15:52:55 -04:00
|
|
|
#include "LevelSequencePlayer.h"
|
|
|
|
|
#include "FbxExporter.h"
|
|
|
|
|
#include "FbxImporter.h"
|
|
|
|
|
#include "MovieSceneToolsUserSettings.h"
|
|
|
|
|
#include "MovieSceneToolHelpers.h"
|
2020-09-24 00:43:27 -04:00
|
|
|
#include "MovieSceneEventUtils.h"
|
|
|
|
|
#include "MovieSceneSequenceEditor.h"
|
|
|
|
|
#include "Sections/MovieSceneEventSectionBase.h"
|
2019-05-30 15:52:55 -04:00
|
|
|
#include "CineCameraActor.h"
|
|
|
|
|
#include "CineCameraComponent.h"
|
|
|
|
|
#include "MovieSceneCommonHelpers.h"
|
2020-01-22 17:58:55 -05:00
|
|
|
#include "Components/SkeletalMeshComponent.h"
|
|
|
|
|
#include "Animation/AnimSequenceBase.h"
|
2020-02-14 11:53:25 -05:00
|
|
|
#include "ScopedTransaction.h"
|
2020-09-24 00:43:27 -04:00
|
|
|
#include "Exporters/AnimSeqExportOption.h"
|
|
|
|
|
|
|
|
|
|
#include "K2Node_CustomEvent.h"
|
|
|
|
|
#include "BlueprintFunctionNodeSpawner.h"
|
|
|
|
|
#include "BlueprintActionMenuItem.h"
|
|
|
|
|
#include "EdGraphSchema_K2.h"
|
2018-05-14 17:38:22 -04:00
|
|
|
|
2022-05-02 18:59:38 -04:00
|
|
|
#include "AssetRegistry/AssetData.h"
|
2022-04-21 13:12:26 -04:00
|
|
|
#include "LevelSequenceAnimSequenceLink.h"
|
|
|
|
|
#include "AnimSequenceLevelSequenceLink.h"
|
|
|
|
|
|
|
|
|
|
|
2018-05-14 17:38:22 -04:00
|
|
|
#define LOCTEXT_NAMESPACE "SequencerTools"
|
|
|
|
|
|
2019-01-10 17:26:53 -05:00
|
|
|
bool USequencerToolsFunctionLibrary::RenderMovie(UMovieSceneCapture* InCaptureSettings, FOnRenderMovieStopped OnFinishedCallback)
|
2018-05-14 17:38:22 -04:00
|
|
|
{
|
|
|
|
|
IMovieSceneCaptureDialogModule& MovieSceneCaptureModule = FModuleManager::Get().LoadModuleChecked<IMovieSceneCaptureDialogModule>("MovieSceneCaptureDialog");
|
|
|
|
|
|
|
|
|
|
// Because this comes from the Python/BP layer we need to soft-validate the state before we pass it onto functions that do a assert-based validation.
|
|
|
|
|
if (!InCaptureSettings)
|
|
|
|
|
{
|
|
|
|
|
FFrame::KismetExecutionMessage(TEXT("Cannot start Render Sequence to Movie with null capture settings."), ELogVerbosity::Error);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (IsRenderingMovie())
|
|
|
|
|
{
|
|
|
|
|
FFrame::KismetExecutionMessage(TEXT("Capture already in progress."), ELogVerbosity::Error);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If they're capturing a level sequence we'll do some additional checking as there are more parameters on the Automated Level Sequence capture.
|
|
|
|
|
UAutomatedLevelSequenceCapture* LevelSequenceCapture = Cast<UAutomatedLevelSequenceCapture>(InCaptureSettings);
|
|
|
|
|
if (LevelSequenceCapture)
|
|
|
|
|
{
|
|
|
|
|
if (!LevelSequenceCapture->LevelSequenceAsset.IsValid())
|
|
|
|
|
{
|
|
|
|
|
// UE_LOG(LogTemp, Warning, TEXT("No Level Sequence Asset specified in UAutomatedLevelSequenceCapture."));
|
|
|
|
|
FFrame::KismetExecutionMessage(TEXT("No Level Sequence Asset specified in UAutomatedLevelSequenceCapture."), ELogVerbosity::Error);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!LevelSequenceCapture->bUseCustomStartFrame && !LevelSequenceCapture->bUseCustomEndFrame)
|
|
|
|
|
{
|
|
|
|
|
// If they don't want to use a custom start/end frame we override the default values to be the length of the sequence, as the default is [0,1)
|
|
|
|
|
ULevelSequence* LevelSequence = Cast<ULevelSequence>(LevelSequenceCapture->LevelSequenceAsset.TryLoad());
|
|
|
|
|
if (!LevelSequence)
|
|
|
|
|
{
|
|
|
|
|
const FString ErrorMessage = FString::Printf(TEXT("Specified Level Sequence Asset failed to load. Specified Asset Path: %s"), *LevelSequenceCapture->LevelSequenceAsset.GetAssetPathString());
|
|
|
|
|
FFrame::KismetExecutionMessage(*ErrorMessage, ELogVerbosity::Error);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FFrameRate DisplayRate = LevelSequence->GetMovieScene()->GetDisplayRate();
|
|
|
|
|
FFrameRate TickResolution = LevelSequence->GetMovieScene()->GetTickResolution();
|
|
|
|
|
|
2019-04-19 12:53:47 -04:00
|
|
|
LevelSequenceCapture->Settings.CustomFrameRate = DisplayRate;
|
|
|
|
|
LevelSequenceCapture->Settings.bUseCustomFrameRate = true;
|
2018-05-14 17:38:22 -04:00
|
|
|
LevelSequenceCapture->Settings.bUseRelativeFrameNumbers = false;
|
|
|
|
|
TRange<FFrameNumber> Range = LevelSequence->GetMovieScene()->GetPlaybackRange();
|
|
|
|
|
|
2020-08-11 01:36:57 -04:00
|
|
|
FFrameNumber StartFrame = UE::MovieScene::DiscreteInclusiveLower(Range);
|
|
|
|
|
FFrameNumber EndFrame = UE::MovieScene::DiscreteExclusiveUpper(Range);
|
2018-05-14 17:38:22 -04:00
|
|
|
|
|
|
|
|
FFrameNumber RoundedStartFrame = FFrameRate::TransformTime(StartFrame, TickResolution, DisplayRate).CeilToFrame();
|
|
|
|
|
FFrameNumber RoundedEndFrame = FFrameRate::TransformTime(EndFrame, TickResolution, DisplayRate).CeilToFrame();
|
|
|
|
|
|
|
|
|
|
LevelSequenceCapture->CustomStartFrame = RoundedStartFrame;
|
|
|
|
|
LevelSequenceCapture->CustomEndFrame = RoundedEndFrame;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-10 17:26:53 -05:00
|
|
|
auto LocalCaptureStoppedCallback = [OnFinishedCallback](bool bSuccess)
|
|
|
|
|
{
|
|
|
|
|
OnFinishedCallback.ExecuteIfBound(bSuccess);
|
|
|
|
|
};
|
|
|
|
|
|
2018-05-14 17:38:22 -04:00
|
|
|
MovieSceneCaptureModule.StartCapture(InCaptureSettings);
|
2019-01-10 17:26:53 -05:00
|
|
|
MovieSceneCaptureModule.GetCurrentCapture()->CaptureStoppedDelegate.AddLambda(LocalCaptureStoppedCallback);
|
2018-05-14 17:38:22 -04:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void USequencerToolsFunctionLibrary::CancelMovieRender()
|
|
|
|
|
{
|
|
|
|
|
IMovieSceneCaptureDialogModule& MovieSceneCaptureModule = FModuleManager::Get().LoadModuleChecked<IMovieSceneCaptureDialogModule>("MovieSceneCaptureDialog");
|
|
|
|
|
TSharedPtr<FMovieSceneCaptureBase> CurrentCapture = MovieSceneCaptureModule.GetCurrentCapture();
|
|
|
|
|
if (CurrentCapture.IsValid())
|
|
|
|
|
{
|
|
|
|
|
// We just invoke the capture's Cancel function. This will cause a shut-down of the capture (the same as the UI)
|
|
|
|
|
// which will invoke all of the necessary callbacks as well. We don't null out CurrentCapture because that is done
|
|
|
|
|
// as the result of its shutdown callbacks.
|
|
|
|
|
CurrentCapture->Cancel();
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-05-30 15:52:55 -04:00
|
|
|
|
2020-01-27 20:11:15 -05:00
|
|
|
TArray<FSequencerBoundObjects> USequencerToolsFunctionLibrary::GetBoundObjects(UWorld* InWorld, ULevelSequence* InSequence, const TArray<FSequencerBindingProxy>& InBindings, const FSequencerScriptingRange& InRange)
|
|
|
|
|
{
|
|
|
|
|
ALevelSequenceActor* OutActor;
|
|
|
|
|
FMovieSceneSequencePlaybackSettings Settings;
|
|
|
|
|
FLevelSequenceCameraSettings CameraSettings;
|
|
|
|
|
|
|
|
|
|
ULevelSequencePlayer* Player = ULevelSequencePlayer::CreateLevelSequencePlayer(InWorld, InSequence, Settings, OutActor);
|
|
|
|
|
|
|
|
|
|
Player->Initialize(InSequence, InWorld->PersistentLevel, Settings, CameraSettings);
|
|
|
|
|
Player->State.AssignSequence(MovieSceneSequenceID::Root, *InSequence, *Player);
|
|
|
|
|
|
|
|
|
|
// Evaluation needs to occur in order to obtain spawnables
|
|
|
|
|
FFrameRate Resolution = InSequence->GetMovieScene()->GetTickResolution();
|
|
|
|
|
TRange<FFrameNumber> SpecifiedRange = InRange.ToNative(Resolution);
|
2021-04-08 14:32:07 -04:00
|
|
|
Player->SetPlaybackPosition(FMovieSceneSequencePlaybackParams(SpecifiedRange.GetLowerBoundValue().Value, EUpdatePositionMethod::Play));
|
2020-01-27 20:11:15 -05:00
|
|
|
|
|
|
|
|
FMovieSceneSequenceID SequenceId = Player->State.FindSequenceId(InSequence);
|
|
|
|
|
|
|
|
|
|
TArray<FSequencerBoundObjects> BoundObjects;
|
|
|
|
|
for (FSequencerBindingProxy Binding : InBindings)
|
|
|
|
|
{
|
2020-12-14 14:36:17 -04:00
|
|
|
FMovieSceneObjectBindingID ObjectBinding = UE::MovieScene::FFixedObjectBindingID(Binding.BindingID, SequenceId);
|
2020-01-27 20:11:15 -05:00
|
|
|
BoundObjects.Add(FSequencerBoundObjects(Binding, Player->GetBoundObjects(ObjectBinding)));
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-14 11:53:25 -05:00
|
|
|
Player->Stop();
|
2020-01-27 20:11:15 -05:00
|
|
|
InWorld->DestroyActor(OutActor);
|
|
|
|
|
return BoundObjects;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TArray<FSequencerBoundObjects> USequencerToolsFunctionLibrary::GetObjectBindings(UWorld* InWorld, ULevelSequence* InSequence, const TArray<UObject*>& InObjects, const FSequencerScriptingRange& InRange)
|
|
|
|
|
{
|
|
|
|
|
ALevelSequenceActor* OutActor;
|
|
|
|
|
FMovieSceneSequencePlaybackSettings Settings;
|
|
|
|
|
FLevelSequenceCameraSettings CameraSettings;
|
|
|
|
|
|
|
|
|
|
ULevelSequencePlayer* Player = ULevelSequencePlayer::CreateLevelSequencePlayer(InWorld, InSequence, Settings, OutActor);
|
|
|
|
|
|
|
|
|
|
Player->Initialize(InSequence, InWorld->PersistentLevel, Settings, CameraSettings);
|
|
|
|
|
Player->State.AssignSequence(MovieSceneSequenceID::Root, *InSequence, *Player);
|
|
|
|
|
|
|
|
|
|
FFrameRate Resolution = InSequence->GetMovieScene()->GetTickResolution();
|
|
|
|
|
TRange<FFrameNumber> SpecifiedRange = InRange.ToNative(Resolution);
|
2021-04-08 14:32:07 -04:00
|
|
|
Player->SetPlaybackPosition(FMovieSceneSequencePlaybackParams(SpecifiedRange.GetLowerBoundValue().Value, EUpdatePositionMethod::Play));
|
2020-01-27 20:11:15 -05:00
|
|
|
|
|
|
|
|
TArray<FSequencerBoundObjects> BoundObjects;
|
|
|
|
|
|
|
|
|
|
for (UObject* Object : InObjects)
|
|
|
|
|
{
|
|
|
|
|
TArray<FMovieSceneObjectBindingID> ObjectBindings = Player->GetObjectBindings(Object);
|
|
|
|
|
for (FMovieSceneObjectBindingID ObjectBinding : ObjectBindings)
|
|
|
|
|
{
|
2020-12-14 14:36:17 -04:00
|
|
|
FMovieSceneSequenceID SequenceID = ObjectBinding.ResolveSequenceID(MovieSceneSequenceID::Root, *Player);
|
|
|
|
|
FSequencerBindingProxy Binding(ObjectBinding.GetGuid(), Player->State.FindSequence(SequenceID));
|
2020-01-27 20:11:15 -05:00
|
|
|
BoundObjects.Add(FSequencerBoundObjects(Binding, TArray<UObject*>({ Object })));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-14 11:53:25 -05:00
|
|
|
Player->Stop();
|
2020-01-27 20:11:15 -05:00
|
|
|
InWorld->DestroyActor(OutActor);
|
|
|
|
|
return BoundObjects;
|
|
|
|
|
}
|
2019-05-30 15:52:55 -04:00
|
|
|
|
2022-02-10 15:23:50 -05:00
|
|
|
void GatherDescendantBindings(const FSequencerBindingProxy& Binding, UMovieSceneSequence* Sequence, TArray<FSequencerBindingProxy>& AllBindings)
|
|
|
|
|
{
|
|
|
|
|
UMovieScene* MovieScene = Sequence->GetMovieScene();
|
|
|
|
|
|
|
|
|
|
for (int32 Index = 0; Index < MovieScene->GetPossessableCount(); ++Index)
|
|
|
|
|
{
|
|
|
|
|
FMovieScenePossessable& Possessable = MovieScene->GetPossessable(Index);
|
|
|
|
|
if (Possessable.GetParent() == Binding.BindingID)
|
|
|
|
|
{
|
|
|
|
|
FSequencerBindingProxy ChildBinding(Possessable.GetGuid(), Sequence);
|
|
|
|
|
AllBindings.Add(ChildBinding);
|
|
|
|
|
GatherDescendantBindings(ChildBinding, Sequence, AllBindings);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-07 23:43:01 -05:00
|
|
|
bool ExportFBXInternal(UWorld* World, UMovieSceneSequence* Sequence, const TArray<FSequencerBindingProxy>& InBindings, const TArray<UMovieSceneTrack*>& MasterTracks, UFbxExportOption* OverrideOptions, const FString& InFBXFileName, UMovieSceneSequencePlayer* Player)
|
2019-05-30 15:52:55 -04:00
|
|
|
{
|
|
|
|
|
UnFbx::FFbxExporter* Exporter = UnFbx::FFbxExporter::GetInstance();
|
|
|
|
|
//Show the fbx export dialog options
|
|
|
|
|
Exporter->SetExportOptionsOverride(OverrideOptions);
|
|
|
|
|
|
|
|
|
|
UMovieScene* MovieScene = Sequence->GetMovieScene();
|
2022-02-10 15:23:50 -05:00
|
|
|
|
|
|
|
|
TArray<FSequencerBindingProxy> AllBindings;
|
|
|
|
|
for (const FSequencerBindingProxy& Binding : InBindings)
|
|
|
|
|
{
|
|
|
|
|
AllBindings.Add(Binding);
|
|
|
|
|
|
|
|
|
|
GatherDescendantBindings(Binding, Sequence, AllBindings);
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-30 15:52:55 -04:00
|
|
|
TArray<FGuid> Bindings;
|
2022-02-10 15:23:50 -05:00
|
|
|
for (const FSequencerBindingProxy& Proxy : AllBindings)
|
2019-05-30 15:52:55 -04:00
|
|
|
{
|
|
|
|
|
if (Proxy.Sequence == Sequence)
|
|
|
|
|
{
|
|
|
|
|
Bindings.Add(Proxy.BindingID);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
INodeNameAdapter NodeNameAdapter;
|
|
|
|
|
Player->State.AssignSequence(MovieSceneSequenceID::Root, *Sequence, *Player);
|
|
|
|
|
FMovieSceneSequenceIDRef Template = MovieSceneSequenceID::Root;
|
2020-02-14 11:53:25 -05:00
|
|
|
bool bDidExport = false;
|
2019-10-01 20:41:42 -04:00
|
|
|
FMovieSceneSequenceTransform RootToLocalTransform;
|
2020-09-24 00:43:27 -04:00
|
|
|
|
2020-02-14 11:53:25 -05:00
|
|
|
{
|
|
|
|
|
FSpawnableRestoreState SpawnableRestoreState(MovieScene);
|
|
|
|
|
|
|
|
|
|
if (SpawnableRestoreState.bWasChanged)
|
|
|
|
|
{
|
|
|
|
|
// Evaluate at the beginning of the subscene time to ensure that spawnables are created before export
|
2021-04-08 14:32:07 -04:00
|
|
|
Player->SetPlaybackPosition(FMovieSceneSequencePlaybackParams(UE::MovieScene::DiscreteInclusiveLower(MovieScene->GetPlaybackRange()).Value, EUpdatePositionMethod::Play));
|
2020-02-14 11:53:25 -05:00
|
|
|
}
|
|
|
|
|
|
2021-11-07 23:43:01 -05:00
|
|
|
bDidExport = MovieSceneToolHelpers::ExportFBX(World, MovieScene, Player, Bindings, MasterTracks, NodeNameAdapter, Template, InFBXFileName, RootToLocalTransform);
|
2020-02-14 11:53:25 -05:00
|
|
|
}
|
2020-09-24 00:43:27 -04:00
|
|
|
|
2020-02-14 11:53:25 -05:00
|
|
|
Player->Stop();
|
2019-05-30 15:52:55 -04:00
|
|
|
Exporter->SetExportOptionsOverride(nullptr);
|
2020-02-14 11:53:25 -05:00
|
|
|
|
2019-05-30 15:52:55 -04:00
|
|
|
return bDidExport;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-07 23:43:01 -05:00
|
|
|
bool USequencerToolsFunctionLibrary::ExportLevelSequenceFBX(UWorld* World, ULevelSequence* Sequence, const TArray<FSequencerBindingProxy>& InBindings, const TArray<UMovieSceneTrack*>& InMasterTracks, UFbxExportOption* OverrideOptions, const FString& InFBXFileName)
|
2021-03-05 19:27:14 -04:00
|
|
|
{
|
|
|
|
|
ALevelSequenceActor* OutActor;
|
|
|
|
|
FMovieSceneSequencePlaybackSettings Settings;
|
|
|
|
|
FLevelSequenceCameraSettings CameraSettings;
|
|
|
|
|
ULevelSequencePlayer* Player = ULevelSequencePlayer::CreateLevelSequencePlayer(World, Sequence, Settings, OutActor);
|
|
|
|
|
Player->Initialize(Sequence, World->PersistentLevel, Settings, CameraSettings);
|
|
|
|
|
|
2021-11-07 23:43:01 -05:00
|
|
|
bool bSuccess = ExportFBXInternal(World, Sequence, InBindings, InMasterTracks, OverrideOptions, InFBXFileName, Player);
|
2021-03-05 19:27:14 -04:00
|
|
|
World->DestroyActor(OutActor);
|
|
|
|
|
|
|
|
|
|
return bSuccess;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-22 17:58:55 -05:00
|
|
|
static USkeletalMeshComponent* GetSkelMeshComponent(IMovieScenePlayer* Player, const FSequencerBindingProxy& Binding)
|
|
|
|
|
{
|
|
|
|
|
FMovieSceneSequenceIDRef Template = MovieSceneSequenceID::Root;
|
|
|
|
|
for (TWeakObjectPtr<UObject> RuntimeObject : Player->FindBoundObjects(Binding.BindingID, Template))
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
if (AActor* Actor = Cast<AActor>(RuntimeObject.Get()))
|
|
|
|
|
{
|
|
|
|
|
for (UActorComponent* Component : Actor->GetComponents())
|
|
|
|
|
{
|
|
|
|
|
if (USkeletalMeshComponent* SkeletalMeshComp = Cast<USkeletalMeshComponent>(Component))
|
|
|
|
|
{
|
|
|
|
|
return SkeletalMeshComp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (USkeletalMeshComponent* SkeletalMeshComponent = Cast<USkeletalMeshComponent>(RuntimeObject.Get()))
|
|
|
|
|
{
|
|
|
|
|
if (SkeletalMeshComponent->SkeletalMesh)
|
|
|
|
|
{
|
|
|
|
|
return SkeletalMeshComponent;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-21 13:12:26 -04:00
|
|
|
bool USequencerToolsFunctionLibrary::ExportAnimSequence(UWorld* World, ULevelSequence* Sequence, UAnimSequence* AnimSequence, UAnimSeqExportOption* ExportOptions,const FSequencerBindingProxy& Binding, bool bCreateLink)
|
2020-01-22 17:58:55 -05:00
|
|
|
{
|
|
|
|
|
UMovieScene* MovieScene = Sequence->GetMovieScene();
|
|
|
|
|
if (Binding.Sequence != Sequence || !AnimSequence)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
INodeNameAdapter NodeNameAdapter;
|
|
|
|
|
ALevelSequenceActor* OutActor;
|
|
|
|
|
FMovieSceneSequencePlaybackSettings Settings;
|
|
|
|
|
FLevelSequenceCameraSettings CameraSettings;
|
|
|
|
|
FMovieSceneSequenceIDRef Template = MovieSceneSequenceID::Root;
|
|
|
|
|
FMovieSceneSequenceTransform RootToLocalTransform;
|
|
|
|
|
ULevelSequencePlayer* Player = ULevelSequencePlayer::CreateLevelSequencePlayer(World, Sequence, Settings, OutActor);
|
|
|
|
|
Player->Initialize(Sequence, World->PersistentLevel, Settings, CameraSettings);
|
|
|
|
|
Player->State.AssignSequence(MovieSceneSequenceID::Root, *Sequence, *Player);
|
|
|
|
|
|
|
|
|
|
bool bResult = false;
|
2021-06-22 00:27:54 -04:00
|
|
|
|
2020-01-22 17:58:55 -05:00
|
|
|
{
|
2020-08-11 01:36:57 -04:00
|
|
|
FSpawnableRestoreState SpawnableRestoreState(MovieScene);
|
|
|
|
|
|
|
|
|
|
if (SpawnableRestoreState.bWasChanged)
|
|
|
|
|
{
|
|
|
|
|
// Evaluate at the beginning of the subscene time to ensure that spawnables are created before export
|
2021-04-08 14:32:07 -04:00
|
|
|
Player->SetPlaybackPosition(FMovieSceneSequencePlaybackParams(UE::MovieScene::DiscreteInclusiveLower(MovieScene->GetPlaybackRange()).Value, EUpdatePositionMethod::Play));
|
2020-08-11 01:36:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
USkeletalMeshComponent* SkeletalMeshComp = GetSkelMeshComponent(Player, Binding);
|
2020-11-25 11:17:08 -04:00
|
|
|
if (SkeletalMeshComp && SkeletalMeshComp->SkeletalMesh && SkeletalMeshComp->SkeletalMesh->GetSkeleton())
|
2020-08-11 01:36:57 -04:00
|
|
|
{
|
2020-11-25 11:17:08 -04:00
|
|
|
AnimSequence->SetSkeleton(SkeletalMeshComp->SkeletalMesh->GetSkeleton());
|
2020-09-24 00:43:27 -04:00
|
|
|
bResult = MovieSceneToolHelpers::ExportToAnimSequence(AnimSequence,ExportOptions, MovieScene, Player, SkeletalMeshComp, Template, RootToLocalTransform);
|
2020-08-11 01:36:57 -04:00
|
|
|
}
|
2020-01-22 17:58:55 -05:00
|
|
|
}
|
2020-08-11 01:36:57 -04:00
|
|
|
|
|
|
|
|
Player->Stop();
|
|
|
|
|
World->DestroyActor(OutActor);
|
|
|
|
|
|
2022-04-21 13:12:26 -04:00
|
|
|
//create the link to the anim sequence
|
|
|
|
|
if (bResult && bCreateLink)
|
|
|
|
|
{
|
|
|
|
|
//create from anim sequence back to level sequence
|
|
|
|
|
if (IInterface_AssetUserData* AnimAssetUserData = Cast< IInterface_AssetUserData >(AnimSequence))
|
|
|
|
|
{
|
|
|
|
|
UAnimSequenceLevelSequenceLink* AnimLevelLink = AnimAssetUserData->GetAssetUserData< UAnimSequenceLevelSequenceLink >();
|
|
|
|
|
if (!AnimLevelLink)
|
|
|
|
|
{
|
|
|
|
|
AnimLevelLink = NewObject<UAnimSequenceLevelSequenceLink>(AnimSequence, NAME_None, RF_Public | RF_Transactional);
|
|
|
|
|
AnimAssetUserData->AddAssetUserData(AnimLevelLink);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AnimLevelLink->SetLevelSequence(Sequence);
|
|
|
|
|
AnimLevelLink->SkelTrackGuid = Binding.BindingID;
|
|
|
|
|
}
|
|
|
|
|
//create from level sequence to anim sequence, trickier since we can have multiples here.
|
|
|
|
|
if (IInterface_AssetUserData* AssetUserDataInterface = Cast< IInterface_AssetUserData >(Sequence))
|
|
|
|
|
{
|
|
|
|
|
bool bAddItem = true;
|
|
|
|
|
ULevelSequenceAnimSequenceLink* LevelAnimLink = AssetUserDataInterface->GetAssetUserData< ULevelSequenceAnimSequenceLink >();
|
|
|
|
|
if (LevelAnimLink)
|
|
|
|
|
{
|
|
|
|
|
for (FLevelSequenceAnimSequenceLinkItem& LevelAnimLinkItem : LevelAnimLink->AnimSequenceLinks)
|
|
|
|
|
{
|
|
|
|
|
if (LevelAnimLinkItem.SkelTrackGuid == Binding.BindingID)
|
|
|
|
|
{
|
|
|
|
|
bAddItem = false;
|
|
|
|
|
UAnimSequence* OtherAnimSequence = LevelAnimLinkItem.ResolveAnimSequence();
|
|
|
|
|
|
|
|
|
|
if (OtherAnimSequence != AnimSequence)
|
|
|
|
|
{
|
|
|
|
|
if (IInterface_AssetUserData* OtherAnimAssetUserData = Cast< IInterface_AssetUserData >(OtherAnimSequence))
|
|
|
|
|
{
|
|
|
|
|
UAnimSequenceLevelSequenceLink* OtherAnimLevelLink = OtherAnimAssetUserData->GetAssetUserData< UAnimSequenceLevelSequenceLink >();
|
|
|
|
|
if (OtherAnimLevelLink)
|
|
|
|
|
{
|
|
|
|
|
OtherAnimAssetUserData->RemoveUserDataOfClass(UAnimSequenceLevelSequenceLink::StaticClass());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
LevelAnimLinkItem.PathToAnimSequence = FSoftObjectPath(AnimSequence);
|
|
|
|
|
LevelAnimLinkItem.bExportMorphTargets = ExportOptions->bExportMorphTargets;
|
|
|
|
|
LevelAnimLinkItem.bExportAttributeCurves = ExportOptions->bExportAttributeCurves;
|
|
|
|
|
LevelAnimLinkItem.bExportMaterialCurves = ExportOptions->bExportMaterialCurves;
|
|
|
|
|
LevelAnimLinkItem.bExportTransforms = ExportOptions->bExportTransforms;
|
|
|
|
|
LevelAnimLinkItem.bRecordInWorldSpace = ExportOptions->bRecordInWorldSpace;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
LevelAnimLink = NewObject<ULevelSequenceAnimSequenceLink>(Sequence, NAME_None, RF_Public | RF_Transactional);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
if (bAddItem == true)
|
|
|
|
|
{
|
|
|
|
|
FLevelSequenceAnimSequenceLinkItem LevelAnimLinkItem;
|
|
|
|
|
LevelAnimLinkItem.SkelTrackGuid = Binding.BindingID;
|
|
|
|
|
LevelAnimLinkItem.PathToAnimSequence = FSoftObjectPath(AnimSequence);
|
|
|
|
|
LevelAnimLinkItem.bExportMorphTargets = ExportOptions->bExportMorphTargets;
|
|
|
|
|
LevelAnimLinkItem.bExportAttributeCurves = ExportOptions->bExportAttributeCurves;
|
|
|
|
|
LevelAnimLinkItem.bExportMaterialCurves = ExportOptions->bExportMaterialCurves;
|
|
|
|
|
LevelAnimLinkItem.bExportTransforms = ExportOptions->bExportTransforms;
|
|
|
|
|
LevelAnimLinkItem.bRecordInWorldSpace = ExportOptions->bRecordInWorldSpace;
|
|
|
|
|
|
|
|
|
|
LevelAnimLink->AnimSequenceLinks.Add(LevelAnimLinkItem);
|
|
|
|
|
AssetUserDataInterface->AddAssetUserData(LevelAnimLink);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-01-22 17:58:55 -05:00
|
|
|
return bResult;
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-21 13:12:26 -04:00
|
|
|
UAnimSequenceLevelSequenceLink* USequencerToolsFunctionLibrary::GetLevelSequenceLinkFromAnimSequence(UAnimSequence* InAnimSequence)
|
|
|
|
|
{
|
|
|
|
|
if (IInterface_AssetUserData* AnimAssetUserData = Cast< IInterface_AssetUserData >(InAnimSequence))
|
|
|
|
|
{
|
|
|
|
|
UAnimSequenceLevelSequenceLink* AnimLevelLink = AnimAssetUserData->GetAssetUserData< UAnimSequenceLevelSequenceLink >();
|
|
|
|
|
return AnimLevelLink;
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2019-05-30 15:52:55 -04:00
|
|
|
|
2022-04-21 13:12:26 -04:00
|
|
|
ULevelSequenceAnimSequenceLink* USequencerToolsFunctionLibrary::GetAnimSequenceLinkFromLevelSequence(ULevelSequence* InLevelSequence)
|
|
|
|
|
{
|
|
|
|
|
if (IInterface_AssetUserData* AssetUserDataInterface = Cast< IInterface_AssetUserData >(InLevelSequence))
|
|
|
|
|
{
|
|
|
|
|
ULevelSequenceAnimSequenceLink* LevelAnimLink = AssetUserDataInterface->GetAssetUserData< ULevelSequenceAnimSequenceLink >();
|
|
|
|
|
return LevelAnimLink;
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2019-05-30 15:52:55 -04:00
|
|
|
|
2019-05-31 15:16:06 -04:00
|
|
|
TArray<FGuid> AddActors(UWorld* World, UMovieSceneSequence* InSequence, UMovieScene* InMovieScene, IMovieScenePlayer* Player, FMovieSceneSequenceIDRef TemplateID,const TArray<TWeakObjectPtr<AActor> >& InActors)
|
2019-05-30 15:52:55 -04:00
|
|
|
{
|
|
|
|
|
TArray<FGuid> PossessableGuids;
|
|
|
|
|
|
|
|
|
|
if (InMovieScene->IsReadOnly())
|
|
|
|
|
{
|
|
|
|
|
return PossessableGuids;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (TWeakObjectPtr<AActor> WeakActor : InActors)
|
|
|
|
|
{
|
|
|
|
|
if (AActor* Actor = WeakActor.Get())
|
|
|
|
|
{
|
|
|
|
|
FGuid ExistingGuid = Player->FindObjectId(*Actor, TemplateID);
|
|
|
|
|
if (!ExistingGuid.IsValid())
|
|
|
|
|
{
|
|
|
|
|
InMovieScene->Modify();
|
|
|
|
|
const FGuid PossessableGuid = InMovieScene->AddPossessable(Actor->GetActorLabel(), Actor->GetClass());
|
|
|
|
|
PossessableGuids.Add(PossessableGuid);
|
|
|
|
|
InSequence->BindPossessableObject(PossessableGuid, *Actor, World);
|
|
|
|
|
|
|
|
|
|
//TODO New to figure way to call void FLevelSequenceEditorToolkit::AddDefaultTracksForActor(AActor& Actor, const FGuid Binding)
|
|
|
|
|
|
|
|
|
|
if (Actor->IsA<ACameraActor>())
|
|
|
|
|
{
|
2021-06-07 20:09:45 -04:00
|
|
|
MovieSceneToolHelpers::CreateCameraCutSectionForCamera(InMovieScene, PossessableGuid, 0);
|
2019-05-30 15:52:55 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return PossessableGuids;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2019-05-31 15:16:06 -04:00
|
|
|
void ImportFBXCamera(UnFbx::FFbxImporter* FbxImporter, UWorld* World, UMovieSceneSequence* Sequence, UMovieScene* InMovieScene, IMovieScenePlayer* Player, FMovieSceneSequenceIDRef TemplateID, TMap<FGuid, FString>& InObjectBindingMap, bool bMatchByNameOnly, bool bCreateCameras)
|
2019-05-30 15:52:55 -04:00
|
|
|
{
|
|
|
|
|
if (bCreateCameras)
|
|
|
|
|
{
|
|
|
|
|
TArray<FbxCamera*> AllCameras;
|
2019-06-07 12:19:21 -04:00
|
|
|
MovieSceneToolHelpers::GetCameras(FbxImporter->Scene->GetRootNode(), AllCameras);
|
2019-05-30 15:52:55 -04:00
|
|
|
|
|
|
|
|
// Find unmatched cameras
|
|
|
|
|
TArray<FbxCamera*> UnmatchedCameras;
|
|
|
|
|
for (auto Camera : AllCameras)
|
|
|
|
|
{
|
2019-06-07 12:19:21 -04:00
|
|
|
FString NodeName = MovieSceneToolHelpers::GetCameraName(Camera);
|
2019-05-30 15:52:55 -04:00
|
|
|
|
|
|
|
|
bool bMatched = false;
|
|
|
|
|
for (auto InObjectBinding : InObjectBindingMap)
|
|
|
|
|
{
|
|
|
|
|
FString ObjectName = InObjectBinding.Value;
|
|
|
|
|
if (ObjectName == NodeName)
|
|
|
|
|
{
|
|
|
|
|
// Look for a valid bound object, otherwise need to create a new camera and assign this binding to it
|
|
|
|
|
bool bFoundBoundObject = false;
|
|
|
|
|
TArrayView<TWeakObjectPtr<>> BoundObjects = Player->FindBoundObjects(InObjectBinding.Key, TemplateID);
|
|
|
|
|
for (auto BoundObject : BoundObjects)
|
|
|
|
|
{
|
|
|
|
|
if (BoundObject.IsValid())
|
|
|
|
|
{
|
|
|
|
|
bFoundBoundObject = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!bMatched)
|
|
|
|
|
{
|
|
|
|
|
UnmatchedCameras.Add(Camera);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If there are new cameras, clear the object binding map so that we're only assigning values to the newly created cameras
|
|
|
|
|
if (UnmatchedCameras.Num() != 0)
|
|
|
|
|
{
|
|
|
|
|
InObjectBindingMap.Reset();
|
|
|
|
|
bMatchByNameOnly = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add any unmatched cameras
|
|
|
|
|
for (auto UnmatchedCamera : UnmatchedCameras)
|
|
|
|
|
{
|
2019-06-07 12:19:21 -04:00
|
|
|
FString CameraName = MovieSceneToolHelpers::GetCameraName(UnmatchedCamera);
|
2019-05-30 15:52:55 -04:00
|
|
|
|
|
|
|
|
AActor* NewCamera = nullptr;
|
|
|
|
|
if (UnmatchedCamera->GetApertureMode() == FbxCamera::eFocalLength)
|
|
|
|
|
{
|
|
|
|
|
FActorSpawnParameters SpawnParams;
|
|
|
|
|
NewCamera = World->SpawnActor<ACineCameraActor>(SpawnParams);
|
|
|
|
|
NewCamera->SetActorLabel(*CameraName);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
FActorSpawnParameters SpawnParams;
|
|
|
|
|
NewCamera = World->SpawnActor<ACameraActor>(SpawnParams);
|
|
|
|
|
NewCamera->SetActorLabel(*CameraName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Copy camera properties before adding default tracks so that initial camera properties match and can be restored after sequencer finishes
|
|
|
|
|
MovieSceneToolHelpers::CopyCameraProperties(UnmatchedCamera, NewCamera);
|
|
|
|
|
|
|
|
|
|
TArray<TWeakObjectPtr<AActor> > NewCameras;
|
|
|
|
|
NewCameras.Add(NewCamera);
|
|
|
|
|
TArray<FGuid> NewCameraGuids = AddActors(World, Sequence,InMovieScene, Player, TemplateID,NewCameras);
|
|
|
|
|
|
|
|
|
|
if (NewCameraGuids.Num())
|
|
|
|
|
{
|
|
|
|
|
InObjectBindingMap.Add(NewCameraGuids[0]);
|
|
|
|
|
InObjectBindingMap[NewCameraGuids[0]] = CameraName;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//everything created now import it in.
|
2020-09-01 14:07:48 -04:00
|
|
|
MovieSceneToolHelpers::ImportFBXCameraToExisting(FbxImporter, Sequence, Player, TemplateID, InObjectBindingMap, bMatchByNameOnly, true);
|
2019-05-30 15:52:55 -04:00
|
|
|
}
|
|
|
|
|
|
2021-03-05 19:27:14 -04:00
|
|
|
bool ImportFBXInternal(UWorld* World, UMovieSceneSequence* Sequence, const TArray<FSequencerBindingProxy>& InBindings, UMovieSceneUserImportFBXSettings* ImportFBXSettings, const FString& ImportFilename, UMovieSceneSequencePlayer* Player)
|
2019-05-30 15:52:55 -04:00
|
|
|
{
|
|
|
|
|
UMovieScene* MovieScene = Sequence->GetMovieScene();
|
|
|
|
|
if (!MovieScene || MovieScene->IsReadOnly())
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-10 15:23:50 -05:00
|
|
|
TArray<FSequencerBindingProxy> AllBindings;
|
2019-05-30 15:52:55 -04:00
|
|
|
for (const FSequencerBindingProxy& Binding : InBindings)
|
2022-02-10 15:23:50 -05:00
|
|
|
{
|
|
|
|
|
AllBindings.Add(Binding);
|
|
|
|
|
|
|
|
|
|
GatherDescendantBindings(Binding, Sequence, AllBindings);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TMap<FGuid, FString> ObjectBindingMap;
|
|
|
|
|
for (const FSequencerBindingProxy& Binding : AllBindings)
|
2019-05-30 15:52:55 -04:00
|
|
|
{
|
|
|
|
|
FString Name = MovieScene->GetObjectDisplayName(Binding.BindingID).ToString();
|
|
|
|
|
ObjectBindingMap.Add(Binding.BindingID, Name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FFBXInOutParameters InOutParams;
|
|
|
|
|
if (!MovieSceneToolHelpers::ReadyFBXForImport(ImportFilename, ImportFBXSettings, InOutParams))
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const bool bMatchByNameOnly = ImportFBXSettings->bMatchByNameOnly;
|
|
|
|
|
Player->State.AssignSequence(MovieSceneSequenceID::Root, *Sequence, *Player);
|
|
|
|
|
|
|
|
|
|
UnFbx::FFbxImporter* FbxImporter = UnFbx::FFbxImporter::GetInstance();
|
2020-08-11 01:36:57 -04:00
|
|
|
|
|
|
|
|
bool bResult = false;
|
|
|
|
|
FScopedTransaction ImportFBXTransaction(NSLOCTEXT("Sequencer", "ImportFBX", "Import FBX"));
|
|
|
|
|
{
|
|
|
|
|
FSpawnableRestoreState SpawnableRestoreState(MovieScene);
|
|
|
|
|
|
|
|
|
|
if (SpawnableRestoreState.bWasChanged)
|
|
|
|
|
{
|
|
|
|
|
// Evaluate at the beginning of the subscene time to ensure that spawnables are created before export
|
2021-04-08 14:32:07 -04:00
|
|
|
Player->SetPlaybackPosition(FMovieSceneSequencePlaybackParams(UE::MovieScene::DiscreteInclusiveLower(MovieScene->GetPlaybackRange()).Value, EUpdatePositionMethod::Play));
|
2020-08-11 01:36:57 -04:00
|
|
|
}
|
2019-05-30 15:52:55 -04:00
|
|
|
|
2020-08-11 01:36:57 -04:00
|
|
|
ImportFBXCamera(FbxImporter, World, Sequence, MovieScene, Player, MovieSceneSequenceID::Root, ObjectBindingMap, bMatchByNameOnly, ImportFBXSettings->bCreateCameras);
|
2019-05-30 15:52:55 -04:00
|
|
|
|
2020-09-01 14:07:48 -04:00
|
|
|
bResult = MovieSceneToolHelpers::ImportFBXIfReady(World, Sequence, Player, MovieSceneSequenceID::Root, ObjectBindingMap, ImportFBXSettings, InOutParams);
|
2020-08-11 01:36:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Player->Stop();
|
|
|
|
|
return bResult;
|
2019-05-30 15:52:55 -04:00
|
|
|
}
|
|
|
|
|
|
2021-03-05 19:27:14 -04:00
|
|
|
bool USequencerToolsFunctionLibrary::ImportLevelSequenceFBX(UWorld* World, ULevelSequence* Sequence, const TArray<FSequencerBindingProxy>& InBindings, UMovieSceneUserImportFBXSettings* ImportFBXSettings, const FString& ImportFilename)
|
|
|
|
|
{
|
|
|
|
|
ALevelSequenceActor* OutActor;
|
|
|
|
|
FMovieSceneSequencePlaybackSettings Settings;
|
|
|
|
|
FLevelSequenceCameraSettings CameraSettings;
|
|
|
|
|
ULevelSequencePlayer* Player = ULevelSequencePlayer::CreateLevelSequencePlayer(World, Sequence, Settings, OutActor);
|
|
|
|
|
Player->Initialize(Sequence, World->GetLevel(0), Settings, CameraSettings);
|
|
|
|
|
|
|
|
|
|
bool bSuccess = ImportFBXInternal(World, Sequence, InBindings, ImportFBXSettings, ImportFilename, Player);
|
|
|
|
|
World->DestroyActor(OutActor);
|
|
|
|
|
|
|
|
|
|
return bSuccess;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-24 00:43:27 -04:00
|
|
|
bool USequencerToolsFunctionLibrary::ImportFBXToControlRig(UWorld* World, ULevelSequence* Sequence, const FString& ControlRigTrackName, const TArray<FString>& ControlRigNames,
|
|
|
|
|
UMovieSceneUserImportFBXControlRigSettings* ImportFBXControlRigSettings,
|
|
|
|
|
const FString& ImportFilename)
|
|
|
|
|
{
|
|
|
|
|
UMovieScene* MovieScene = Sequence->GetMovieScene();
|
|
|
|
|
if (!MovieScene || MovieScene->IsReadOnly())
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2019-05-30 15:52:55 -04:00
|
|
|
|
2020-09-24 00:43:27 -04:00
|
|
|
bool bValid = false;
|
|
|
|
|
|
|
|
|
|
const TArray<FMovieSceneBinding>& Bindings = MovieScene->GetBindings();
|
|
|
|
|
for (const FMovieSceneBinding& Binding : Bindings)
|
|
|
|
|
{
|
|
|
|
|
if (Binding.GetName() == ControlRigTrackName)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
ALevelSequenceActor* OutActor;
|
|
|
|
|
FMovieSceneSequencePlaybackSettings Settings;
|
|
|
|
|
FLevelSequenceCameraSettings CameraSettings;
|
|
|
|
|
ULevelSequencePlayer* Player = ULevelSequencePlayer::CreateLevelSequencePlayer(World, Sequence, Settings, OutActor);
|
|
|
|
|
Player->Initialize(Sequence, World->GetLevel(0), Settings, CameraSettings);
|
|
|
|
|
Player->State.AssignSequence(MovieSceneSequenceID::Root, *Sequence, *Player);
|
|
|
|
|
|
|
|
|
|
const TArray<UMovieSceneTrack*>& Tracks = Binding.GetTracks();
|
|
|
|
|
TArray<FName> SelectedControls;
|
|
|
|
|
for (UMovieSceneTrack* Track : Tracks)
|
|
|
|
|
{
|
|
|
|
|
INodeAndChannelMappings* ChannelMapping = Cast<INodeAndChannelMappings>(Track);
|
|
|
|
|
if (ChannelMapping)
|
|
|
|
|
{
|
2021-11-18 14:37:34 -05:00
|
|
|
TArray<FFBXNodeAndChannels>* NodeAndChannels = ChannelMapping->GetNodeAndChannelMappings(nullptr);
|
2020-09-24 00:43:27 -04:00
|
|
|
//use passed in controls for selected, actually selected controls should almost be empty anyway since we just loaded/set everything up.
|
|
|
|
|
for (const FString& StringName : ControlRigNames)
|
|
|
|
|
{
|
|
|
|
|
FName Name(*StringName);
|
|
|
|
|
SelectedControls.Add(Name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bValid = MovieSceneToolHelpers::ImportFBXIntoControlRigChannels(MovieScene,ImportFilename, ImportFBXControlRigSettings,
|
|
|
|
|
NodeAndChannels, SelectedControls, MovieScene->GetTickResolution());
|
|
|
|
|
|
|
|
|
|
if (NodeAndChannels)
|
|
|
|
|
{
|
|
|
|
|
delete NodeAndChannels;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return bValid;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FMovieSceneEvent USequencerToolsFunctionLibrary::CreateEvent(UMovieSceneSequence* InSequence, UMovieSceneEventSectionBase* InSection, const FSequencerQuickBindingResult& InEndpoint, const TArray<FString>& InPayload)
|
|
|
|
|
{
|
|
|
|
|
FMovieSceneEvent Event;
|
|
|
|
|
|
|
|
|
|
if (InEndpoint.EventEndpoint == nullptr)
|
|
|
|
|
{
|
|
|
|
|
FFrame::KismetExecutionMessage(TEXT("Invalid endpoint, event will not be initialized"), ELogVerbosity::Warning);
|
|
|
|
|
return Event;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UMovieScene* MovieScene = InSequence->GetMovieScene();
|
|
|
|
|
FGuid ObjectBindingID;
|
|
|
|
|
MovieScene->FindTrackBinding(*InSection->GetTypedOuter<UMovieSceneTrack>(), ObjectBindingID);
|
|
|
|
|
UClass* BoundObjectPinClass = nullptr;
|
|
|
|
|
if (FMovieScenePossessable* Possessable = MovieScene->FindPossessable(ObjectBindingID))
|
|
|
|
|
{
|
|
|
|
|
BoundObjectPinClass = const_cast<UClass*>(Possessable->GetPossessedObjectClass());
|
|
|
|
|
}
|
|
|
|
|
else if (FMovieSceneSpawnable* Spawnable = MovieScene->FindSpawnable(ObjectBindingID))
|
|
|
|
|
{
|
|
|
|
|
BoundObjectPinClass = Spawnable->GetObjectTemplate()->GetClass();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
InSection->Modify();
|
|
|
|
|
FMovieSceneEventUtils::BindEventSectionToBlueprint(InSection, InEndpoint.EventEndpoint->GetBlueprint());
|
|
|
|
|
|
|
|
|
|
UEdGraphPin* BoundObjectPin = FMovieSceneEventUtils::FindBoundObjectPin(InEndpoint.EventEndpoint, BoundObjectPinClass);
|
|
|
|
|
FMovieSceneEventUtils::SetEndpoint(&Event, InSection, InEndpoint.EventEndpoint, BoundObjectPin);
|
|
|
|
|
|
|
|
|
|
if (InEndpoint.PayloadNames.Num() != InPayload.Num())
|
|
|
|
|
{
|
|
|
|
|
const FString Message = FString::Printf(TEXT("Wrong number of payload values, expecting %i got %i"), InEndpoint.PayloadNames.Num(), InPayload.Num());
|
|
|
|
|
FFrame::KismetExecutionMessage(*Message, ELogVerbosity::Warning);
|
|
|
|
|
return Event;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int32 Index = 0; Index < InEndpoint.PayloadNames.Num(); Index++)
|
|
|
|
|
{
|
|
|
|
|
const FName PayloadName = FName(InEndpoint.PayloadNames[Index]);
|
|
|
|
|
if (!Event.PayloadVariables.Contains(PayloadName))
|
|
|
|
|
{
|
|
|
|
|
Event.PayloadVariables.Add(PayloadName);
|
|
|
|
|
Event.PayloadVariables[PayloadName].Value = InPayload[Index];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Event;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool USequencerToolsFunctionLibrary::IsEventEndpointValid(const FSequencerQuickBindingResult& InEndpoint)
|
|
|
|
|
{
|
|
|
|
|
return InEndpoint.EventEndpoint != nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FSequencerQuickBindingResult USequencerToolsFunctionLibrary::CreateQuickBinding(UMovieSceneSequence* InSequence, UObject* InObject, const FString& InFunctionName, bool bCallInEditor)
|
|
|
|
|
{
|
|
|
|
|
FSequencerQuickBindingResult Result;
|
|
|
|
|
|
|
|
|
|
FMovieSceneSequenceEditor* SequenceEditor = FMovieSceneSequenceEditor::Find(InSequence);
|
|
|
|
|
if (!SequenceEditor)
|
|
|
|
|
{
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UBlueprint* Blueprint = SequenceEditor->GetOrCreateDirectorBlueprint(InSequence);
|
|
|
|
|
if (!Blueprint)
|
|
|
|
|
{
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UMovieScene* MovieScene = InSequence->GetMovieScene();
|
|
|
|
|
|
|
|
|
|
FMovieSceneEventEndpointParameters Params;
|
|
|
|
|
Params.SanitizedObjectName = InObject->GetName();
|
|
|
|
|
Params.SanitizedEventName = InFunctionName;
|
|
|
|
|
Params.BoundObjectPinClass = InObject->GetClass();
|
|
|
|
|
UFunction* Function = InObject->GetClass()->FindFunctionByName(FName(InFunctionName));
|
|
|
|
|
if (Function == nullptr)
|
|
|
|
|
{
|
|
|
|
|
const FString Message = FString::Printf(TEXT("Cannot find function %s in class %s"), *(InFunctionName), *(InObject->GetClass()->GetName()));
|
|
|
|
|
FFrame::KismetExecutionMessage(*Message, ELogVerbosity::Warning);
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UBlueprintFunctionNodeSpawner* BlueprintFunctionNodeSpawner = UBlueprintFunctionNodeSpawner::Create(Function);
|
|
|
|
|
FBlueprintActionMenuItem Action(BlueprintFunctionNodeSpawner);
|
|
|
|
|
|
|
|
|
|
UK2Node_CustomEvent* NewEventEndpoint = FMovieSceneEventUtils::CreateUserFacingEvent(Blueprint, Params);
|
|
|
|
|
NewEventEndpoint->bCallInEditor = bCallInEditor;
|
|
|
|
|
Result.EventEndpoint = NewEventEndpoint;
|
|
|
|
|
|
|
|
|
|
UEdGraphPin* ThenPin = NewEventEndpoint->FindPin(UEdGraphSchema_K2::PN_Then, EGPD_Output);
|
|
|
|
|
UEdGraphPin* BoundObjectPin = FMovieSceneEventUtils::FindBoundObjectPin(NewEventEndpoint, Params.BoundObjectPinClass);
|
|
|
|
|
|
|
|
|
|
FVector2D NodePosition(NewEventEndpoint->NodePosX + 400.f, NewEventEndpoint->NodePosY);
|
|
|
|
|
UEdGraphNode* NewNode = Action.PerformAction(NewEventEndpoint->GetGraph(), BoundObjectPin ? BoundObjectPin : ThenPin, NodePosition);
|
|
|
|
|
|
|
|
|
|
if (NewNode == nullptr)
|
|
|
|
|
{
|
|
|
|
|
const FString Message = FString::Printf(TEXT("Failed creating blueprint event node for function %s"), *InFunctionName);
|
|
|
|
|
FFrame::KismetExecutionMessage(*Message, ELogVerbosity::Warning);
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Link execution pins
|
|
|
|
|
UEdGraphPin* ExecPin = NewNode->FindPin(UEdGraphSchema_K2::PN_Execute, EGPD_Input);
|
|
|
|
|
if (ensure(ThenPin && ExecPin))
|
|
|
|
|
{
|
|
|
|
|
ThenPin->MakeLinkTo(ExecPin);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Link payload parameters' pins
|
|
|
|
|
UK2Node_EditablePinBase* EditableNode = Cast<UK2Node_EditablePinBase>(NewEventEndpoint);
|
|
|
|
|
if (EditableNode)
|
|
|
|
|
{
|
|
|
|
|
for (UEdGraphPin* PayloadPin : NewNode->Pins)
|
|
|
|
|
{
|
|
|
|
|
if (PayloadPin != BoundObjectPin && PayloadPin->Direction == EGPD_Input && PayloadPin->PinType.PinCategory != UEdGraphSchema_K2::PC_Exec && PayloadPin->LinkedTo.Num() == 0)
|
|
|
|
|
{
|
|
|
|
|
Result.PayloadNames.Add(PayloadPin->PinName.ToString());
|
|
|
|
|
|
|
|
|
|
UEdGraphPin* NewPin = EditableNode->CreateUserDefinedPin(PayloadPin->PinName, PayloadPin->PinType, EGPD_Output);
|
|
|
|
|
if (NewNode != NewEventEndpoint && NewPin)
|
|
|
|
|
{
|
|
|
|
|
NewPin->MakeLinkTo(PayloadPin);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
2019-05-30 15:52:55 -04:00
|
|
|
|
|
|
|
|
|
2018-05-14 17:38:22 -04:00
|
|
|
#undef LOCTEXT_NAMESPACE // "SequencerTools"
|