// Copyright Epic Games, Inc. All Rights Reserved. #include "MetaHumanToolkitBase.h" #include "MetaHumanToolkitCommands.h" #include "MetaHumanToolkitStyle.h" #include "MetaHumanEditorViewportClient.h" #include "SMetaHumanEditorViewport.h" #include "EditorViewportTabContent.h" #include "MetaHumanSequence.h" #include "MetaHumanSequencerPlaybackContext.h" #include "MetaHumanMovieSceneMediaSection.h" #include "MetaHumanMovieSceneMediaTrack.h" #include "MetaHumanAudioTrack.h" #include "MetaHumanAudioSection.h" #include "ImageSequenceUtils.h" #include "MetaHumanDepthMeshComponent.h" #include "ImgMediaSource.h" #include "ISequencerModule.h" #include "SequencerSettings.h" #include "MediaTexture.h" #include "AdvancedPreviewScene.h" #include "AdvancedPreviewSceneModule.h" #include "Fonts/FontMeasure.h" #include "Editor.h" #include "Editor/Transactor.h" #include "LevelEditor.h" #include "Sections/MovieSceneAudioSection.h" #include "Tracks/MovieSceneAudioTrack.h" #include "MediaPlayer.h" #include "Widgets/Input/SComboBox.h" #include "PropertyEditorModule.h" #include "Widgets/Docking/SDockTab.h" #include "Materials/Material.h" #include "Materials/MaterialInterface.h" #define LOCTEXT_NAMESPACE "MetaHumanToolkitBase" class STimeDisplayCombo : public SCompoundWidget { public: SLATE_BEGIN_ARGS(STimeDisplayCombo) : _TimelineSequencer() { } SLATE_ATTRIBUTE(TWeakPtr, TimelineSequencer) SLATE_END_ARGS() typedef TSharedPtr FComboItemType; void Construct(const FArguments& InArgs) { if (InArgs._TimelineSequencer.IsSet()) { TimelineSequencer = InArgs._TimelineSequencer.Get(); } Options.Add(MakeShareable(new FString("Frames"))); // Do not change order unless you also change OnSelectionChanged Options.Add(MakeShareable(new FString("Seconds"))); Options.Add(MakeShareable(new FString("Timecode (NDF)"))); Options.Add(MakeShareable(new FString("Timecode (DF)"))); const FSlateFontInfo Font(FCoreStyle::GetDefaultFont(), 10, "Regular"); for (TSharedPtr Option : Options) { const float Width = FSlateApplication::Get().GetRenderer()->GetFontMeasureService()->Measure(*Option.Get(), Font, 1.0).X + 10; if (Width > MinWidth) { MinWidth = Width; } } CurrentItem = Options[0]; ChildSlot [ SNew(SComboBox) .OptionsSource(&Options) .OnSelectionChanged(this, &STimeDisplayCombo::OnSelectionChanged) .OnGenerateWidget(this, &STimeDisplayCombo::MakeWidgetForOption) .InitiallySelectedItem(CurrentItem) [ SNew(STextBlock) .Text(this, &STimeDisplayCombo::GetCurrentItemLabel) .MinDesiredWidth(MinWidth) ] ]; } TSharedRef MakeWidgetForOption(FComboItemType InOption) { return SNew(STextBlock).Text(FText::FromString(*InOption)); } void OnSelectionChanged(FComboItemType InNewValue, ESelectInfo::Type) { CurrentItem = InNewValue; if (TimelineSequencer.IsValid() && CurrentItem.IsValid()) { int32 Index = Options.Find(CurrentItem); if (Index == 0) { TimelineSequencer.Pin()->GetSequencerSettings()->SetTimeDisplayFormat(EFrameNumberDisplayFormats::Frames); } else if (Index == 1) { TimelineSequencer.Pin()->GetSequencerSettings()->SetTimeDisplayFormat(EFrameNumberDisplayFormats::Seconds); } else if (Index == 2) { TimelineSequencer.Pin()->GetSequencerSettings()->SetTimeDisplayFormat(EFrameNumberDisplayFormats::NonDropFrameTimecode); } else if (Index == 3) { TimelineSequencer.Pin()->GetSequencerSettings()->SetTimeDisplayFormat(EFrameNumberDisplayFormats::DropFrameTimecode); } } } FText GetCurrentItemLabel() const { if (CurrentItem.IsValid()) { return FText::FromString(*CurrentItem); } return LOCTEXT("InvalidComboEntryText", "<>"); } FComboItemType CurrentItem; TArray Options; TWeakPtr TimelineSequencer; float MinWidth = -1; }; const FName FMetaHumanToolkitBase::TimelineTabId(TEXT("Timeline")); const FName FMetaHumanToolkitBase::PreviewSettingsTabId(TEXT("PreviewSettings")); FMetaHumanToolkitBase::FMetaHumanToolkitBase(UAssetEditor* InOwningAssetEditor) : FBaseAssetToolkit{ InOwningAssetEditor } { CreateSequencerTimeline(); CreatePreviewScene(); } FMetaHumanToolkitBase::~FMetaHumanToolkitBase() { // Unregister Map Change Events if (FLevelEditorModule* LevelEditor = FModuleManager::GetModulePtr("LevelEditor")) { LevelEditor->OnMapChanged().RemoveAll(this); } } void FMetaHumanToolkitBase::AddReferencedObjects(FReferenceCollector& InCollector) { if (Sequence) { InCollector.AddReferencedObject(Sequence); } } FString FMetaHumanToolkitBase::GetReferencerName() const { return TEXT("FMetaHumanToolkitBase"); } bool FMetaHumanToolkitBase::IsPrimaryEditor() const { return true; } void FMetaHumanToolkitBase::CreateWidgets() { FBaseAssetToolkit::CreateWidgets(); // Replace the DetailsView widget with a custom one that has a notify hook set to this class FPropertyEditorModule& PropertyEditorModule = FModuleManager::GetModuleChecked(TEXT("PropertyEditor")); FDetailsViewArgs DetailsViewArgs; DetailsViewArgs.NameAreaSettings = FDetailsViewArgs::HideNameArea; DetailsViewArgs.bHideSelectionTip = true; DetailsViewArgs.NotifyHook = this; DetailsView = PropertyEditorModule.CreateDetailView(DetailsViewArgs); } void FMetaHumanToolkitBase::RegisterTabSpawners(const TSharedRef& InTabManager) { //the following part is copied from FBaseAssetToolkit::RegisterTabSpawners(InTabManager) //because we want to name our viewport differently //begin FBaseAssetToolkit::RegisterTabSpawners FAssetEditorToolkit::RegisterTabSpawners(InTabManager); InTabManager->RegisterTabSpawner(ViewportTabID, FOnSpawnTab::CreateSP(this, &FMetaHumanToolkitBase::SpawnTab_Viewport)) .SetDisplayName(LOCTEXT("ViewportTab", "A|B Viewport")) .SetGroup(AssetEditorTabsCategory.ToSharedRef()) .SetIcon(FSlateIcon(FMetaHumanToolkitStyle::Get().GetStyleSetName(), TEXT("MetaHuman Toolkit.Tabs.ABViewport"))); InTabManager->RegisterTabSpawner(DetailsTabID, FOnSpawnTab::CreateSP(this, &FMetaHumanToolkitBase::SpawnTab_Details)) .SetDisplayName(LOCTEXT("Details", "Details")) .SetGroup(AssetEditorTabsCategory.ToSharedRef()) .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), TEXT("LevelEditor.Tabs.Details"))); //end InTabManager->RegisterTabSpawner(TimelineTabId, FOnSpawnTab::CreateSP(this, &FMetaHumanToolkitBase::SpawnTab_Sequencer)) .SetDisplayName(LOCTEXT("TimelineTab", "Timeline")) .SetGroup(AssetEditorTabsCategory.ToSharedRef()) .SetIcon(FSlateIcon(FMetaHumanToolkitStyle::Get().GetStyleSetName(), TEXT("MetaHuman Toolkit.Tabs.Timeline"))); InTabManager->RegisterTabSpawner(PreviewSettingsTabId, FOnSpawnTab::CreateSP(this, &FMetaHumanToolkitBase::SpawnTab_PreviewSettings)) .SetDisplayName(LOCTEXT("PreviewSettingsTab", "Preview Scene Settings")) .SetGroup(AssetEditorTabsCategory.ToSharedRef()) .SetIcon(FSlateIcon(FAppStyle::GetAppStyleSetName(), "LevelEditor.Tabs.Details")); } void FMetaHumanToolkitBase::UnregisterTabSpawners(const TSharedRef& InTabManager) { FBaseAssetToolkit::UnregisterTabSpawners(InTabManager); } void FMetaHumanToolkitBase::PostInitAssetEditor() { BindCommands(); // Bind to depth data change delegate so we can update the depth view GetMetaHumanEditorViewportClient()->OnUpdateFootageDepthDataDelegate.BindSP(this, &FMetaHumanToolkitBase::HandleFootageDepthDataChanged); GetMetaHumanEditorViewportClient()->OnUpdateMeshDepthDataDelegate.BindSP(this, &FMetaHumanToolkitBase::HandleMeshDepthDataChanged); GetMetaHumanEditorViewportClient()->OnUpdateDepthMapVisibilityDelegate.BindSP(this, &FMetaHumanToolkitBase::HandleDepthMapVisibilityChanged); FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked("LevelEditor"); LevelEditorModule.OnMapChanged().AddSP(this, &FMetaHumanToolkitBase::HandleMapChanged); // Force the viewport tab to exist to prevent crashes when using the viewport client TabManager->TryInvokeTab(ViewportTabID); } void FMetaHumanToolkitBase::NotifyPostChange(const FPropertyChangedEvent& InPropertyChangedEvent, FProperty* InPropertyThatChanged) { GetMetaHumanEditorViewportClient()->UpdateABVisibility(); } void FMetaHumanToolkitBase::PostUndo(bool bInSuccess) { if (bInSuccess) { const FTransaction* Transaction = GEditor->Trans->GetTransaction(GEditor->Trans->GetQueueLength() - GEditor->Trans->GetUndoCount()); HandleUndoOrRedoTransaction(Transaction); } } void FMetaHumanToolkitBase::PostRedo(bool bInSuccess) { if (bInSuccess) { const FTransaction* Transaction = GEditor->Trans->GetTransaction(GEditor->Trans->GetQueueLength() - GEditor->Trans->GetUndoCount() - 1); HandleUndoOrRedoTransaction(Transaction); } } TSharedPtr FMetaHumanToolkitBase::CreateEditorViewportClient() const { check(PreviewScene.IsValid()); return MakeShared(PreviewScene.Get()); } AssetEditorViewportFactoryFunction FMetaHumanToolkitBase::GetViewportDelegate() { AssetEditorViewportFactoryFunction TempViewportDelegate = [this](const FAssetEditorViewportConstructionArgs& InArgs) -> TSharedRef { return SNew(SMetaHumanEditorViewport, InArgs) .ViewportClient(GetMetaHumanEditorViewportClient()) .ABCommandList(ABCommandList) .OnGetABViewMenuContents(this, &FMetaHumanToolkitBase::HandleGetViewABMenuContents) [ GetViewportExtraContentWidget() ]; }; return TempViewportDelegate; } TSharedRef FMetaHumanToolkitBase::SpawnTab_Viewport(const FSpawnTabArgs& Args) { TSharedRef< SDockTab > DockableTab = SNew(SDockTab) .Label(LOCTEXT("ABViewportTabTitle", "A|B Viewport")) .ToolTipText(LOCTEXT("ABViewportTabTooltip", "AB Viewport\nInspect 2D and 3D components of the scene by switching between Single, Wipe and Dual View Mix Mode.\nIn Single View Mix mode, use A|B button to toggle between A and B view.\nIn Wipe mode, drag the splitting line to adjust the wiper position and orientation, and use the lever gizmo\nto control the transparency of A over B view.\nUse A or B button and/or View Mode buttons in the viewport toolbar corners to adjust the lighting and\nvisualization settings for each view.\nNOTE: Tracking curves can be viewed and edited in Single View Mix mode only.")); const FString LayoutId = FString("BaseAssetViewport"); ViewportTabContent->Initialize(ViewportDelegate, DockableTab, LayoutId); return DockableTab; } TSharedRef FMetaHumanToolkitBase::SpawnTab_Details(const FSpawnTabArgs& Args) { TSharedPtr DetailsTab = SNew(SDockTab) .Label(LOCTEXT("BaseDetailsTabTitle", "Details")) .ToolTipText(LOCTEXT("BaseDetailsTabTooltip", "Details\nInspect and edit properties of the selected item")) [ DetailsView.ToSharedRef() ] ; return DetailsTab.ToSharedRef(); } TSharedRef FMetaHumanToolkitBase::SpawnTab_PreviewSettings(const FSpawnTabArgs& Args) { FAdvancedPreviewSceneModule& AdvancedPreviewSceneModule = FModuleManager::LoadModuleChecked(TEXT("AdvancedPreviewScene")); TSharedRef PreviewSceneSettingsWidget = SNullWidget::NullWidget; if (PreviewScene.IsValid()) { PreviewSceneSettingsWidget = AdvancedPreviewSceneModule.CreateAdvancedPreviewSceneSettingsWidget(PreviewScene.ToSharedRef()); } TSharedRef SpawnedTab = SNew(SDockTab) .Label(LOCTEXT("PreviewSceneSettingsTab", "Preview Scene Settings")) [ SNew(SBox) [ PreviewSceneSettingsWidget ] ]; return SpawnedTab; } TSharedRef FMetaHumanToolkitBase::SpawnTab_Sequencer(const FSpawnTabArgs& InArgs) { check(InArgs.GetTabId() == TimelineTabId); return SNew(SDockTab) .Label(LOCTEXT("TimelineTabTitle", "Footage Timeline")) .ToolTipText(LOCTEXT("TimelineTabTooltip", "Footage Timeline\n\nDrag the gizmo at the top of the vertical line to review frames in the footage\nand use A|B viewport to see how Components in the MetaHuman Identity Tree View behave in relation to them.")) .TabColorScale(GetTabColorScale()) [ SNew(SVerticalBox) .IsEnabled_Lambda([this]() { return IsTimelineEnabled(); }) + SVerticalBox::Slot() .Padding(0, 2) .AutoHeight() [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .FillWidth(1) + SHorizontalBox::Slot() .Padding(0, 0, 2, 0) .AutoWidth() [ SNew(STimeDisplayCombo) .TimelineSequencer(TimelineSequencer) ] ] + SVerticalBox::Slot() [ TimelineSequencer->GetSequencerWidget() ] ]; } TRange FMetaHumanToolkitBase::GetSequencerPlaybackRange() const { TRange PlaybackRange; if (Sequence) { if (UMovieScene* MovieScene = Sequence->GetMovieScene()) { const FFrameRate TickRate = MovieScene->GetTickResolution(); // TODO: Using the display rate might not be ideal here and we might need to query the actual image sequence frame rate to do the transformation properly const FFrameRate SourceRate = MovieScene->GetDisplayRate(); TRange RangeAsTime = MovieScene->GetPlaybackRange(); const FFrameTime TransformedLower = FFrameRate::TransformTime(FFrameTime{ RangeAsTime.GetLowerBoundValue().Value }, TickRate, SourceRate); const FFrameTime TransformedUpper = FFrameRate::TransformTime(FFrameTime{ RangeAsTime.GetUpperBoundValue().Value }, TickRate, SourceRate); PlaybackRange = TRange{ TransformedLower.FrameNumber.Value, TransformedUpper.FrameNumber.Value }; } } return PlaybackRange; } FFrameNumber FMetaHumanToolkitBase::GetCurrentFrameNumber() const { if (UMovieScene* MovieScene = Sequence->GetMovieScene()) { // TODO: Same as above, using display rate might not ideal because the user can change it at any point const FFrameRate FrameRate = MovieScene->GetDisplayRate(); // This will be the current frame number being displayed by sequencer const FFrameTime CurrentFrameTime = TimelineSequencer->GetGlobalTime().ConvertTo(FrameRate); return CurrentFrameTime.GetFrame(); } return FFrameNumber{}; } void FMetaHumanToolkitBase::SetMediaTrack(EMediaTrackType InTrackType, TSubclassOf InTrackClass, UImgMediaSource* InImageSequence, FTimecode InTimeCode, FFrameNumber InStartFrameOffset) { UMovieScene* MovieScene = Sequence->GetMovieScene(); check(MovieScene); UMediaTexture* MediaTexture = nullptr; UMetaHumanMovieSceneMediaTrack* MediaTrack = nullptr; switch (InTrackType) { case EMediaTrackType::Colour: { if (ColourMediaTrack == nullptr) { ColourMediaTrack = CastChecked(Sequence->GetMovieScene()->AddTrack(InTrackClass)); ColourMediaTrack->ClearFlags(RF_Transactional); ColourMediaTrack->SetDisplayName(LOCTEXT("VideoSequenceTrack", "Video")); } MediaTexture = NewObject(GetTransientPackage()); MediaTrack = ColourMediaTrack; ColourMediaTexture = MediaTexture; break; } case EMediaTrackType::Depth: { if (DepthMediaTrack == nullptr) { DepthMediaTrack = CastChecked(Sequence->GetMovieScene()->AddTrack(InTrackClass)); DepthMediaTrack->ClearFlags(RF_Transactional); DepthMediaTrack->SetDisplayName(LOCTEXT("DepthSequenceTrack", "Depth")); } MediaTexture = NewObject(GetTransientPackage()); MediaTrack = DepthMediaTrack; DepthMediaTexture = MediaTexture; break; } default: check(false); // TODO: Proper log break; } check(MediaTexture); check(MediaTrack); // New style output prevents texture from being set as external MediaTexture->NewStyleOutput = true; MediaTexture->UpdateResource(); // Add a new Section with the new ImgSequence UMetaHumanMovieSceneMediaSection* MediaSection = CastChecked(MediaTrack->AddNewMediaSource(*InImageSequence, 0)); MediaSection->MediaTexture = MediaTexture; MediaSection->TimecodeSource = InTimeCode; MediaSection->OnKeyAddedEventDelegate().AddSP(this, &FMetaHumanToolkitBase::HandleSequencerKeyAdded); MediaSection->OnKeyDeletedEventDelegate().AddSP(this, &FMetaHumanToolkitBase::HandleSequencerKeyRemoved); int32 NumFrames = 0; FIntVector2 ImageDimensions; bool bImageOK = FImageSequenceUtils::GetImageSequenceInfoFromAsset(InImageSequence, ImageDimensions, NumFrames); verify(bImageOK); // Set the range of the MediaSection based on the number of images in the ImgSequence const FFrameRate TickRate = MovieScene->GetTickResolution(); const FFrameRate SourceRate = InImageSequence->FrameRateOverride.IsValid() ? InImageSequence->FrameRateOverride : MovieScene->GetDisplayRate(); FFrameTime TransformedStartFrame = FFrameRate::TransformTime(FFrameTime{ 0 }, SourceRate, TickRate); FFrameTime TransformedEndFrame = FFrameRate::TransformTime(FFrameTime{ NumFrames }, SourceRate, TickRate); TransformedStartFrame += InStartFrameOffset; TransformedEndFrame += InStartFrameOffset; const TRange PlaybackRange{ TransformedStartFrame.GetFrame(), TransformedEndFrame.GetFrame() }; MediaSection->SetRange(PlaybackRange); RatchetMovieSceneDisplayRate(SourceRate); } void FMetaHumanToolkitBase::HandleDepthMapVisibilityChanged(bool bInDepthMapVisibility) { // automatically change whether the depth map track is muted in sequencer according to the visibility of the depthmap if (DepthMediaTrack != nullptr && DepthMediaTexture != nullptr) { UMovieScene* MovieScene = Sequence->GetMovieScene(); check(MovieScene); const TArray CurMuteNodes = MovieScene->GetMuteNodes(); bool bCurVisibility = CurMuteNodes.Find(DepthMediaTrack.GetName()) == INDEX_NONE; if (bInDepthMapVisibility != bCurVisibility) { MovieScene->Modify(); TArray& MuteNodes = MovieScene->GetMuteNodes(); if (bInDepthMapVisibility) { MuteNodes.Remove(DepthMediaTrack.GetName()); // this is a HACK to ensure that the image media cache for the depth map is updated when we turn the depth map back on // otherwise it will not be updated if we are currently outside the cache TimelineSequencer->SetLocalTime(TimelineSequencer->GetLastEvaluatedLocalTime().RoundToFrame()); } else { MuteNodes.AddUnique(DepthMediaTrack.GetName()); } TimelineSequencer->RefreshTree(); } } } void FMetaHumanToolkitBase::SetMediaTrack(TSubclassOf InTrackClass, class USoundBase* InAudio, FTimecode InTimecode, FFrameNumber InStartFrameOffset) { if (InAudio != nullptr) { UMovieScene* MovieScene = Sequence->GetMovieScene(); check(MovieScene); if (AudioMediaTrack == nullptr) { UMovieSceneTrack* Track = Sequence->GetMovieScene()->AddTrack(InTrackClass); AudioMediaTrack = CastChecked(Track); AudioMediaTrack->SetDisplayName(LOCTEXT("AudioSequenceTrack", "Audio")); } UMovieSceneAudioSection* AudioSection = CastChecked(AudioMediaTrack->AddNewSound(InAudio, InStartFrameOffset)); AudioSection->TimecodeSource = InTimecode; AudioSection->Modify(); // Audio tracks currently don't have a proper display rate associated with them, so we default to 30 fps const FFrameRate AssumedAudioDisplayRate(30'000, 1'000); RatchetMovieSceneDisplayRate(AssumedAudioDisplayRate); } } void FMetaHumanToolkitBase::ClearMediaTracks() { for (UMetaHumanMovieSceneMediaTrack* MediaTrack : { ColourMediaTrack, DepthMediaTrack }) { if (MediaTrack != nullptr) { for (UMovieSceneSection* Section : MediaTrack->GetAllSections()) { if (UMetaHumanMovieSceneMediaSection* MetaHumanSection = Cast(Section)) { MetaHumanSection->OnKeyAddedEventDelegate().RemoveAll(this); MetaHumanSection->OnKeyDeletedEventDelegate().RemoveAll(this); } } } } // Remove all tracks from the movie scene UMovieScene* MovieScene = Sequence->GetMovieScene(); TArray MasterTracks = MovieScene->GetTracks(); for (UMovieSceneTrack* MasterTrack : MasterTracks) { MovieScene->RemoveTrack(*MasterTrack); } ColourMediaTrack = nullptr; ColourMediaTexture = nullptr; DepthMediaTrack = nullptr; DepthMediaTexture = nullptr; if (AudioMediaTrack != nullptr) { Sequence->GetMovieScene()->RemoveTrack(*AudioMediaTrack); AudioMediaTrack = nullptr; } ResetMovieSceneDisplayRate(); } bool FMetaHumanToolkitBase::ChannelContainsKey(UMetaHumanMovieSceneMediaTrack* InMediaTrack, const FFrameNumber& InFrameTime) const { if (InMediaTrack != nullptr) { TArray OurKeyTimes; TArray OurKeyHandles; TRange CurrentFrameRange; CurrentFrameRange.SetLowerBound(TRangeBound(InFrameTime)); CurrentFrameRange.SetUpperBound(TRangeBound(InFrameTime)); UMovieSceneSection* Section = InMediaTrack->GetAllSections().Last(); TArrayView MediaTrackChannel = Section->GetChannelProxy().GetChannels(); TMovieSceneChannelData ChannelData = MediaTrackChannel.Last()->GetData(); ChannelData.GetKeys(CurrentFrameRange, &OurKeyTimes, &OurKeyHandles); return !OurKeyTimes.IsEmpty(); } return false; } void FMetaHumanToolkitBase::HandleSequencerGlobalTimeChanged() { if (ViewportClient) { GetMetaHumanEditorViewportClient()->UpdateSceneCaptureComponents(); } } TSharedRef FMetaHumanToolkitBase::GetViewportExtraContentWidget() { return SNullWidget::NullWidget; } void FMetaHumanToolkitBase::CreateDepthMeshComponent(UCameraCalibration* InCameraCalibration) { DestroyDepthMeshComponent(); if (InCameraCalibration != nullptr) { checkf(PreviewActor, TEXT("Preview Actor should have been created by CreatePreviewScene")); DepthMeshComponent = NewObject(PreviewActor); PreviewActor->AddInstanceComponent(DepthMeshComponent); DepthMeshComponent->AttachToComponent(PreviewActor->GetRootComponent(), FAttachmentTransformRules::KeepRelativeTransform); DepthMeshComponent->RegisterComponent(); DepthMeshComponent->SetCameraCalibration(InCameraCalibration); DepthMeshComponent->GetMaterial(0)->GetMaterial()->OnMaterialCompilationFinished().AddSP(this, &FMetaHumanToolkitBase::HandleDepthMeshMaterialCompiled); GetMetaHumanEditorViewportClient()->SetDepthMeshComponent(DepthMeshComponent); } } void FMetaHumanToolkitBase::HandleDepthMeshMaterialCompiled(UMaterialInterface*) { GetMetaHumanEditorViewportClient()->UpdateSceneCaptureComponents(); } void FMetaHumanToolkitBase::HandleMapChanged(UWorld* InNewWorld, EMapChangeType InMapChangeType) { if ((InMapChangeType == EMapChangeType::LoadMap || InMapChangeType == EMapChangeType::NewMap || InMapChangeType == EMapChangeType::TearDownWorld)) { TimelineSequencer->GetSpawnRegister().CleanUp(*TimelineSequencer); CloseWindow(EAssetEditorCloseReason::EditorRefreshRequested); } } void FMetaHumanToolkitBase::SetDepthMeshTexture(UTexture* InDepthTexture) { if (DepthMeshComponent != nullptr) { DepthMeshComponent->SetDepthTexture(InDepthTexture); } } void FMetaHumanToolkitBase::DestroyDepthMeshComponent() { if (DepthMeshComponent != nullptr) { DepthMeshComponent->DestroyComponent(); DepthMeshComponent = nullptr; } } void FMetaHumanToolkitBase::CreatePreviewScene() { constexpr float InitialFloorOffset = 250.0f; PreviewScene = MakeShared(FPreviewScene::ConstructionValues(), InitialFloorOffset); check(PreviewScene); FActorSpawnParameters SpawnInfo; SpawnInfo.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; SpawnInfo.bNoFail = true; SpawnInfo.ObjectFlags = RF_Transient; PreviewActor = PreviewScene->GetWorld()->SpawnActor(SpawnInfo); check(PreviewActor); // Create a root scene component for the preview actor // Automatic attachment means this will be the new root component const bool bManualAttachment = false; const bool bDeferredFinish = false; UActorComponent* RootComponent = PreviewActor->AddComponentByClass(USceneComponent::StaticClass(), bManualAttachment, FTransform{}, bDeferredFinish); check(RootComponent); } void FMetaHumanToolkitBase::CreateSequencerTimeline() { Sequence = NewObject(GetTransientPackage()); Sequence->MovieScene = NewObject(Sequence, NAME_None, RF_Transactional); // Setting the tick rate to 24000 to accommodate for audio/video timecode difference of 10+ hours Sequence->MovieScene->SetTickResolutionDirectly(FFrameRate(24000, 1)); ResetMovieSceneDisplayRate(); PlaybackContext = MakeShared(); FSequencerInitParams SequencerInitParams; SequencerInitParams.ViewParams.ScrubberStyle = ESequencerScrubberStyle::FrameBlock; SequencerInitParams.ViewParams.bShowPlaybackRangeInTimeSlider = true; SequencerInitParams.RootSequence = Sequence; SequencerInitParams.bEditWithinLevelEditor = false; SequencerInitParams.ToolkitHost = nullptr; SequencerInitParams.PlaybackContext.Bind(PlaybackContext.ToSharedRef(), &FMetaHumanSequencerPlaybackContext::GetPlaybackContext); ISequencerModule& SequencerModule = FModuleManager::LoadModuleChecked(TEXT("Sequencer")); TimelineSequencer = SequencerModule.CreateSequencer(SequencerInitParams); // Set default settings for the sequencer editor USequencerSettings* SequencerSettings = TimelineSequencer->GetSequencerSettings(); SequencerSettings->SetTimeDisplayFormat(EFrameNumberDisplayFormats::Frames); SequencerSettings->SetKeepPlayRangeInSectionBounds(false); SequencerSettings->SetIsSnapEnabled(true); SequencerSettings->SetAutoScrollEnabled(true); SequencerSettings->SetShowRangeSlider(true); SequencerSettings->SetShowInfoButton(false); SequencerSettings->SetShowTickLines(false); SequencerSettings->SetShowSequencerToolbar(false); TimelineSequencer->OnMovieSceneDataChanged().AddRaw(this, &FMetaHumanToolkitBase::HandleSequencerMovieSceneDataChanged); TimelineSequencer->OnGlobalTimeChanged().AddRaw(this, &FMetaHumanToolkitBase::HandleSequencerGlobalTimeChanged); } TSharedRef FMetaHumanToolkitBase::GetMetaHumanEditorViewportClient() const { return StaticCastSharedPtr(ViewportClient).ToSharedRef(); } void FMetaHumanToolkitBase::RatchetMovieSceneDisplayRate(const FFrameRate InFrameRate) { if (IsValid(Sequence)) { UMovieScene* MovieScene = Sequence->GetMovieScene(); if (IsValid(MovieScene)) { const FFrameRate CurrentDisplayRate = MovieScene->GetDisplayRate(); if (InFrameRate.AsDecimal() > CurrentDisplayRate.AsDecimal()) { MovieScene->SetDisplayRate(InFrameRate); } } } } void FMetaHumanToolkitBase::ResetMovieSceneDisplayRate() { if (IsValid(Sequence)) { UMovieScene* MovieScene = Sequence->GetMovieScene(); if (IsValid(MovieScene)) { const FFrameRate InitialDisplayRate(1'000, 1'000); MovieScene->SetDisplayRate(InitialDisplayRate); } } } #undef LOCTEXT_NAMESPACE