// Copyright Epic Games, Inc. All Rights Reserved. #include "SAnimationEditorViewport.h" #include "AdvancedPreviewSceneMenus.h" #include "Widgets/Layout/SBorder.h" #include "Widgets/Text/STextBlock.h" #include "Styling/AppStyle.h" #include "Styling/CoreStyle.h" #include "Framework/Application/SlateApplication.h" #include "Widgets/Input/SEditableTextBox.h" #include "Widgets/Input/SButton.h" #include "Animation/AnimMontage.h" #include "Preferences/PersonaOptions.h" #include "Rendering/SkeletalMeshRenderData.h" #include "SAnimationScrubPanel.h" #include "SAnimMontageScrubPanel.h" #include "SAnimViewportToolBar.h" #include "AnimViewportMenuCommands.h" #include "AnimViewportShowCommands.h" #include "AnimViewportLODCommands.h" #include "AnimViewportPlaybackCommands.h" #include "AnimPreviewInstance.h" #include "AssetViewerSettings.h" #include "Widgets/Input/STextComboBox.h" #include "IEditableSkeleton.h" #include "EditorAxisDisplayInfo.h" #include "EditorViewportCommands.h" #include "TabSpawners.h" #include "ShowFlagMenuCommands.h" #include "BufferVisualizationMenuCommands.h" #include "UICommandList_Pinnable.h" #include "IPersonaEditorModeManager.h" #include "PreviewProfileController.h" #include "Materials/Material.h" #include "EditorFontGlyphs.h" #include "Tools/EdModeInteractiveToolsContext.h" #include "ContextObjectStore.h" #include "IPersonaEditMode.h" #include "IPersonaToolkit.h" #include "IPinnedCommandList.h" #include "IPinnedCommandListModule.h" #include "Subsystems/AssetEditorSubsystem.h" #include "SNameComboBox.h" #include "ToolMenus.h" #include "Viewports.h" #include "ViewportToolbar/AnimViewportContext.h" #include "ViewportToolbar/AnimationEditorMenus.h" #include "ViewportToolbar/UnrealEdViewportToolbar.h" #include "Widgets/Text/SRichTextBlock.h" #define LOCTEXT_NAMESPACE "PersonaViewportToolbar" ////////////////////////////////////////////////////////////////////////// // SAnimationEditorViewport void SAnimationEditorViewport::Construct(const FArguments& InArgs, const FAnimationEditorViewportRequiredArgs& InRequiredArgs) { PreviewScenePtr = InRequiredArgs.PreviewScene; TabBodyPtr = InRequiredArgs.TabBody; AssetEditorToolkitPtr = InRequiredArgs.AssetEditorToolkit; Extenders = InArgs._Extenders; ContextName = InArgs._ContextName; bShowShowMenu = InArgs._ShowShowMenu; bShowLODMenu = InArgs._ShowLODMenu; bShowPlaySpeedMenu = InArgs._ShowPlaySpeedMenu; bShowStats = InArgs._ShowStats; bShowFloorOptions = InArgs._ShowFloorOptions; bShowTurnTable = InArgs._ShowTurnTable; bShowPhysicsMenu = InArgs._ShowPhysicsMenu; ViewportIndex = InRequiredArgs.ViewportIndex; SEditorViewport::Construct( SEditorViewport::FArguments() .IsEnabled(FSlateApplication::Get().GetNormalExecutionAttribute()) .AddMetaData(TEXT("Persona.Viewport")) ); Client->VisibilityDelegate.BindSP(this, &SAnimationEditorViewport::IsVisible); // restore last used feature level auto ScenePtr = PreviewScenePtr.Pin(); if (ScenePtr.IsValid()) { UWorld* World = ScenePtr->GetWorld(); if (World != nullptr) { World->ChangeFeatureLevel(GWorld->GetFeatureLevel()); } } UEditorEngine* Editor = (UEditorEngine*)GEngine; PreviewFeatureLevelChangedHandle = Editor->OnPreviewFeatureLevelChanged().AddLambda( [PreviewScenePtrWeak = PreviewScenePtr](ERHIFeatureLevel::Type NewFeatureLevel) { if (TSharedPtr ScenePtr = PreviewScenePtrWeak.Pin()) { UWorld* World = ScenePtr->GetWorld(); if (World != nullptr) { World->ChangeFeatureLevel(NewFeatureLevel); } } }); } SAnimationEditorViewport::~SAnimationEditorViewport() { UEditorEngine* Editor = (UEditorEngine*)GEngine; Editor->OnPreviewFeatureLevelChanged().Remove(PreviewFeatureLevelChangedHandle); } void SAnimationEditorViewport::PopulateViewportOverlays(TSharedRef Overlay) { SEditorViewport::PopulateViewportOverlays(Overlay); // add the feature level display widget Overlay->AddSlot() .VAlign(VAlign_Bottom) .HAlign(HAlign_Right) .Padding(5.0f) [ BuildFeatureLevelWidget() ]; if (TSharedPtr ViewportTab = TabBodyPtr.Pin()) { TSharedRef VerticalBox = SNew(SVerticalBox); const TAttribute Padding = TAttribute::CreateLambda( []() -> FMargin { // The old viewport toolbar draws inside the viewport, so we need to pad to not overlap it. const float TopPadding = UE::UnrealEd::ShowOldViewportToolbars() ? 29.0f : 0; return FMargin(0, TopPadding, 0, 0); } ); // clang-format off Overlay->AddSlot() .VAlign(VAlign_Top) .HAlign(HAlign_Fill) .Padding(Padding) [ VerticalBox ]; // clang-format on if (TSharedPtr PinnedCommands = ViewportTab->GetPinnedCommands()) { // clang-format off VerticalBox->AddSlot() .AutoHeight() [ PinnedCommands.ToSharedRef() ]; // clang-format on } // clang-format off VerticalBox->AddSlot() .AutoHeight() .Padding(FMargin(4.0f, 3.0f, 0.0f, 0.0f)) [ // Display text (e.g., item being previewed) SNew(SRichTextBlock) .Visibility(EVisibility::SelfHitTestInvisible) .DecoratorStyleSet(&FAppStyle::Get()) .Text(ViewportTab.Get(), &SAnimationEditorViewportTabBody::GetDisplayString) .TextStyle(&FAppStyle::Get().GetWidgetStyle("AnimViewport.MessageText")) ]; // clang-format on } } TSharedRef SAnimationEditorViewport::MakeEditorViewportClient() { using namespace EditorViewportDefs; // Create an animation viewport client LevelViewportClient = MakeShareable(new FAnimationViewportClient(PreviewScenePtr.Pin().ToSharedRef(), SharedThis(this), AssetEditorToolkitPtr.Pin().ToSharedRef(), ViewportIndex, bShowStats)); // Done after constructor, as the delegates require the shared pointer to be assigned LevelViewportClient->Initialize(); LevelViewportClient->ViewportType = LVT_Perspective; LevelViewportClient->bSetListenerPosition = false; const bool bUsingLUFCoordinateSysem = AxisDisplayInfo::GetAxisDisplayCoordinateSystem() == EAxisList::LeftUpForward; FVector LocalViewLocation = FVector(DefaultPerspectiveViewLocation.X, (bUsingLUFCoordinateSysem ? -DefaultPerspectiveViewLocation.Y : DefaultPerspectiveViewLocation.Y), DefaultPerspectiveViewLocation.Z); FRotator LocalViewRotation = DefaultPerspectiveViewRotation + (bUsingLUFCoordinateSysem ? FRotator(0, -90, 0) : FRotator(0, 0, 0)); LevelViewportClient->SetInitialViewTransform(LVT_Perspective, LocalViewLocation, LocalViewRotation, DEFAULT_ORTHOZOOM); return LevelViewportClient.ToSharedRef(); } TSharedPtr SAnimationEditorViewport::MakeViewportToolbar() { // clang-format off TSharedRef ToolbarWidget = SNew(SVerticalBox) + SVerticalBox::Slot() [ SAssignNew(ViewportToolbar, SAnimViewportToolBar, TabBodyPtr.Pin(), SharedThis(this)) .Visibility(EVisibility::SelfHitTestInvisible) .Cursor(EMouseCursor::Default) .Extenders(Extenders) .ContextName(ContextName) .ShowShowMenu(bShowShowMenu) .ShowLODMenu(bShowLODMenu) .ShowPlaySpeedMenu(bShowPlaySpeedMenu) .ShowFloorOptions(bShowFloorOptions) .ShowTurnTable(bShowTurnTable) .ShowPhysicsMenu(bShowPhysicsMenu) ]; // clang-format on return ToolbarWidget; } TSharedPtr SAnimationEditorViewport::BuildViewportToolbar() { // Register the viewport toolbar if another viewport hasn't already (it's shared). const FName ViewportToolbarName = "AnimationEditor.ViewportToolbar"; if (!UToolMenus::Get()->IsMenuRegistered(ViewportToolbarName)) { UToolMenu* const ViewportToolbarMenu = UToolMenus::Get()->RegisterMenu( ViewportToolbarName, NAME_None /* parent */, EMultiBoxType::SlimHorizontalToolBar ); ViewportToolbarMenu->StyleName = "ViewportToolbar"; // Add the left-aligned part of the viewport toolbar. { FToolMenuSection& LeftSection = ViewportToolbarMenu->AddSection("Left"); LeftSection.AddEntry(UE::UnrealEd::CreateTransformsSubmenu()); // TODO: Needs specific Select menu for Skel Mesh. LeftSection.AddEntry(UE::UnrealEd::CreateSnappingSubmenu()); } // Add the right-aligned part of the viewport toolbar. { FToolMenuSection& RightSection = ViewportToolbarMenu->AddSection("Right"); RightSection.Alignment = EToolMenuSectionAlign::Last; // Add the "Camera" submenu. { // Build the menu name our Camera menu will be using so we can extend it. RightSection.AddEntry(UE::UnrealEd::CreateCameraSubmenu(UE::UnrealEd::FViewportCameraMenuOptions().ShowAll())); const FName SubmenuName = UToolMenus::JoinMenuPaths(ViewportToolbarName, "Camera"); UE::AnimationEditor::ExtendCameraMenu(SubmenuName); } // Add the "View Modes" sub menu. { const FName ViewModesMenuName = "AnimationEditor.ViewportToolbar.ViewModes"; // Stay backward-compatible with the old viewport toolbar. { const FName ParentSubmenuName = "UnrealEd.ViewportToolbar.View"; // Create our parent menu. if (!UToolMenus::Get()->IsMenuRegistered(ParentSubmenuName)) { UToolMenus::Get()->RegisterMenu(ParentSubmenuName); } // Register our ToolMenu here first, before we create the submenu, so we can set our parent. UToolMenus::Get()->RegisterMenu(ViewModesMenuName, ParentSubmenuName); } UE::AnimationEditor::ExtendViewModesSubmenu(ViewModesMenuName); RightSection.AddEntry(UE::UnrealEd::CreateViewModesSubmenu()); } RightSection.AddEntry(UE::AnimationEditor::CreateShowSubmenu()); RightSection.AddEntry(UE::AnimationEditor::CreateLODSubmenu()); // Add Preview Scene Submenu { const FName AssetViewerProfileMenuName = "AnimationEditor.ViewportToolbar.AssetViewerProfile"; RightSection.AddEntry(UE::UnrealEd::CreateAssetViewerProfileSubmenu()); UE::AdvancedPreviewScene::Menus::ExtendAdvancedPreviewSceneSettings(AssetViewerProfileMenuName); UE::AnimationEditor::ExtendPreviewSceneSettingsSubmenu(AssetViewerProfileMenuName); UE::UnrealEd::ExtendPreviewSceneSettingsWithTabEntry(AssetViewerProfileMenuName); } // Add the "Physics" sub menu (only shown when bShowPhysicsMenu is true) { FToolMenuInsert PhysicsSubmenuInsert("LOD", EToolMenuInsertType::Before); UE::AnimationEditor::AddPhysicsMenu(ViewportToolbarName, PhysicsSubmenuInsert); } } } FToolMenuContext ViewportToolbarContext; { if (TSharedPtr PreviewScene = PreviewScenePtr.Pin()) { ViewportToolbarContext.AppendCommandList(PreviewScene->GetCommandList()); } if (TSharedPtr TabBody = TabBodyPtr.Pin()) { ViewportToolbarContext.AppendCommandList(TabBody->GetCommandList()); } ViewportToolbarContext.AppendCommandList(GetCommandList()); // Add extenders { Extenders.Add(UE::AnimationEditor::GetViewModesLegacyExtenders(TabBodyPtr)); ViewportToolbarContext.AddExtender(FExtender::Combine(Extenders)); } // Add the UnrealEd viewport toolbar context. { UUnrealEdViewportToolbarContext* const ContextObject = UE::UnrealEd::CreateViewportToolbarDefaultContext(SharedThis(this)); ContextObject->AssetEditorToolkit = AssetEditorToolkitPtr; ContextObject->PreviewSettingsTabId = FPersonaTabs::AdvancedPreviewSceneSettingsID; ViewportToolbarContext.AddObject(ContextObject); } // Add the Anim viewport toolbar context. { UAnimViewportContext* const ContextObject = NewObject(); ContextObject->ViewportTabBody = TabBodyPtr.Pin(); ViewportToolbarContext.AddObject(ContextObject); } // Give the asset editor a chance to extend the context. if (TSharedPtr Tab = TabBodyPtr.Pin()) { Tab->GetAssetEditorToolkit()->InitToolMenuContext(ViewportToolbarContext); } } return UToolMenus::Get()->GenerateWidget(ViewportToolbarName, ViewportToolbarContext); } TSharedPtr SAnimationEditorViewport::CreatePreviewProfileController() { return MakeShared(); } void SAnimationEditorViewport::PostUndo( bool bSuccess ) { LevelViewportClient->Invalidate(); } void SAnimationEditorViewport::PostRedo( bool bSuccess ) { LevelViewportClient->Invalidate(); } void SAnimationEditorViewport::OnFocusViewportToSelection() { if (LevelViewportClient) { TSharedRef AnimViewportClient = StaticCastSharedRef(LevelViewportClient.ToSharedRef()); AnimViewportClient->OnFocusViewportToSelection(); } } void SAnimationEditorViewport::BindCommands() { SEditorViewport::BindCommands(); FShowFlagMenuCommands::Get().BindCommands(*CommandList, Client); FBufferVisualizationMenuCommands::Get().BindCommands(*CommandList, Client); if (TSharedPtr TabBody = TabBodyPtr.Pin()) { if (TSharedPtr ParentAssetEditor = TabBody->GetAssetEditorToolkit()) { CommandList->Append(ParentAssetEditor->GetToolkitCommands()); } } } void SAnimationEditorViewport::OnDragEnter(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent) { SEditorViewport::OnDragEnter(MyGeometry, DragDropEvent); if(AssetEditorToolkitPtr.IsValid()) { AssetEditorToolkitPtr.Pin()->OnViewportDragEnter(MyGeometry, DragDropEvent); } } void SAnimationEditorViewport::OnDragLeave(const FDragDropEvent& DragDropEvent) { SEditorViewport::OnDragLeave(DragDropEvent); if(AssetEditorToolkitPtr.IsValid()) { AssetEditorToolkitPtr.Pin()->OnViewportDragLeave(DragDropEvent); } } FReply SAnimationEditorViewport::OnDrop(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent) { if(AssetEditorToolkitPtr.IsValid()) { const FReply ReplyFromToolkit = AssetEditorToolkitPtr.Pin()->OnViewportDrop(MyGeometry, DragDropEvent); if(ReplyFromToolkit.IsEventHandled()) { return ReplyFromToolkit; } } return SEditorViewport::OnDrop(MyGeometry, DragDropEvent); } ////////////////////////////////////////////////////////////////////////// // SAnimationEditorViewportTabBody SAnimationEditorViewportTabBody::SAnimationEditorViewportTabBody() : SelectedTurnTableSpeed(EAnimationPlaybackSpeeds::Normal) , SelectedTurnTableMode(EPersonaTurnTableMode::Stopped) , SectionsDisplayMode(ESectionDisplayMode::None) { CreatePinnedCommands(); } SAnimationEditorViewportTabBody::~SAnimationEditorViewportTabBody() { // Close viewport if (LevelViewportClient.IsValid()) { LevelViewportClient->Viewport = nullptr; } // Release our reference to the viewport client LevelViewportClient.Reset(); PendingTransaction.Reset(); } bool SAnimationEditorViewportTabBody::CanUseGizmos() const { if (bAlwaysShowTransformToolbar) { return true; } class UDebugSkelMeshComponent* Component = GetPreviewScene()->GetPreviewMeshComponent(); if (Component != NULL) { if (Component->bForceRefpose) { return false; } else if (Component->IsPreviewOn()) { return true; } } if (LevelViewportClient.IsValid()) { if(const FEditorModeTools* ModeTools = LevelViewportClient->GetModeTools()) { if(ModeTools->UsesTransformWidget()) { return true; } } } return false; } FText ConcatenateLine(const FText& InText, const FText& InNewLine) { if(InText.IsEmpty()) { return InNewLine; } return FText::Format(LOCTEXT("ViewportTextNewlineFormatter", "{0}\n{1}"), InText, InNewLine); } FText SAnimationEditorViewportTabBody::GetDisplayString() const { class UDebugSkelMeshComponent* Component = GetPreviewScene()->GetPreviewMeshComponent(); TSharedPtr EditableSkeleton = GetPreviewScene()->GetPersonaToolkit()->GetEditableSkeleton(); FName TargetSkeletonName = (EditableSkeleton.IsValid() && EditableSkeleton->IsSkeletonValid()) ? EditableSkeleton->GetSkeleton().GetFName() : NAME_None; FText DefaultText; if (Component != NULL) { if (Component->bForceRefpose) { DefaultText = LOCTEXT("ReferencePose", "Reference pose"); } else if (Component->IsPreviewOn()) { DefaultText = FText::Format(LOCTEXT("Previewing", "Previewing {0}"), FText::FromString(Component->GetPreviewText())); } else if (Component->AnimClass != NULL) { TSharedPtr BPEditor = BlueprintEditorPtr.Pin(); const bool bWarnAboutBoneManip = BPEditor.IsValid() && BPEditor->IsModeCurrent(FPersonaModes::AnimBlueprintEditMode); if (bWarnAboutBoneManip) { DefaultText = FText::Format(LOCTEXT("PreviewingAnimBP_WarnDisabled", "Previewing {0}. \nBone manipulation is disabled in this mode. "), FText::FromString(Component->AnimClass->GetName())); } else { DefaultText = FText::Format(LOCTEXT("PreviewingAnimBP", "Previewing {0}"), FText::FromString(Component->AnimClass->GetName())); } } else if (Component->GetSkeletalMeshAsset() == NULL && TargetSkeletonName != NAME_None) { DefaultText = FText::Format(LOCTEXT("NoMeshFound", "No skeletal mesh found for skeleton '{0}'"), FText::FromName(TargetSkeletonName)); } } if(OnGetViewportText.IsBound()) { DefaultText = ConcatenateLine(DefaultText, OnGetViewportText.Execute(EViewportCorner::TopLeft)); } TSharedPtr AnimViewportClient = StaticCastSharedPtr(LevelViewportClient); if(AnimViewportClient->IsShowingMeshStats()) { DefaultText = ConcatenateLine(DefaultText, AnimViewportClient->GetDisplayInfo(AnimViewportClient->IsDetailedMeshStats())); } else if(AnimViewportClient->IsShowingSelectedNodeStats()) { // Allow edit modes (inc. skeletal control modes) to draw with the canvas, and collect on screen strings to draw later if (IAnimationEditContext* PersonaContext = AnimViewportClient->GetModeTools()->GetInteractiveToolsContext()->ContextObjectStore->FindContext()) { TArray EditModeDebugText; PersonaContext->GetOnScreenDebugInfo(EditModeDebugText); for(FText& Text : EditModeDebugText) { DefaultText = ConcatenateLine(DefaultText, Text); } } } if(Component) { for(const FGetExtendedViewportText& TextDelegate : Component->GetExtendedViewportTextDelegates()) { DefaultText = ConcatenateLine(DefaultText, TextDelegate.Execute()); } } return DefaultText; } TSharedRef SAnimationEditorViewportTabBody::SaveState() const { TSharedRef State = MakeShareable(new(FPersonaModeSharedData)); if (const TSharedPtr& AnimViewportClient = GetAnimationViewportClient()) { State->Save(AnimViewportClient.ToSharedRef()); } return State; } void SAnimationEditorViewportTabBody::RestoreState(TSharedRef InState) { if (const TSharedPtr& AnimViewportClient = GetAnimationViewportClient()) { TSharedRef State = StaticCastSharedRef(InState); State->Restore(AnimViewportClient.ToSharedRef()); } } FEditorViewportClient& SAnimationEditorViewportTabBody::GetViewportClient() const { return *LevelViewportClient; } TSharedRef SAnimationEditorViewportTabBody::GetPinnedCommandList() const { return PinnedCommands.ToSharedRef(); } const TSharedPtr& SAnimationEditorViewportTabBody::GetPinnedCommands() { if (!PinnedCommands) { CreatePinnedCommands(); } return PinnedCommands; } TWeakPtr SAnimationEditorViewportTabBody::AddNotification(TAttribute InSeverity, TAttribute InCanBeDismissed, const TSharedRef& InNotificationWidget, FPersonaViewportNotificationOptions InOptions) { TSharedPtr ContainingWidget = nullptr; TWeakPtr WeakNotificationWidget = InNotificationWidget; auto GetPadding = [WeakNotificationWidget]() { if(WeakNotificationWidget.IsValid()) { return WeakNotificationWidget.Pin()->GetVisibility() == EVisibility::Visible ? FMargin(2.0f) : FMargin(0.0f); } return FMargin(0.0f); }; TAttribute GetVisibility(EVisibility::Visible); if (InOptions.OnGetVisibility.IsSet()) { GetVisibility = InOptions.OnGetVisibility; } TAttribute GetBrushForSeverity = TAttribute::Create([InSeverity]() { switch(InSeverity.Get()) { case EMessageSeverity::Error: return FAppStyle::GetBrush("AnimViewport.Notification.Error"); case EMessageSeverity::PerformanceWarning: case EMessageSeverity::Warning: return FAppStyle::GetBrush("AnimViewport.Notification.Warning"); default: case EMessageSeverity::Info: return FAppStyle::GetBrush("AnimViewport.Notification.Message"); } }); if (InOptions.OnGetBrushOverride.IsSet()) { GetBrushForSeverity = InOptions.OnGetBrushOverride; } TSharedPtr BodyBox = nullptr; ViewportNotificationsContainer->AddSlot() .HAlign(HAlign_Right) .AutoHeight() .Padding(MakeAttributeLambda(GetPadding)) [ SAssignNew(ContainingWidget, SBorder) .Visibility(GetVisibility) .BorderImage(GetBrushForSeverity) [ SAssignNew(BodyBox, SHorizontalBox) +SHorizontalBox::Slot() .FillWidth(1.0f) [ InNotificationWidget ] ] ]; TWeakPtr WeakContainingWidget = ContainingWidget; auto DismissNotification = [this, WeakContainingWidget]() { if(WeakContainingWidget.IsValid()) { RemoveNotification(WeakContainingWidget.Pin().ToSharedRef()); } return FReply::Handled(); }; auto GetDismissButtonVisibility = [InCanBeDismissed]() { return InCanBeDismissed.Get() ? EVisibility::Visible : EVisibility::Collapsed; }; // add dismiss button BodyBox->InsertSlot(0) .AutoWidth() .HAlign(HAlign_Center) .VAlign(VAlign_Top) [ SNew(SButton) .Visibility_Lambda(GetDismissButtonVisibility) .ButtonStyle(FAppStyle::Get(), "AnimViewport.Notification.CloseButton") .ToolTipText(LOCTEXT("DismissNotificationToolTip", "Dismiss this notification.")) .OnClicked_Lambda(DismissNotification) ]; return ContainingWidget; } void SAnimationEditorViewportTabBody::RemoveNotification(const TWeakPtr& InContainingWidget) { if(InContainingWidget.IsValid()) { ViewportNotificationsContainer->RemoveSlot(InContainingWidget.Pin().ToSharedRef()); } } void SAnimationEditorViewportTabBody::AddToolbarExtender(FName MenuToExtend, FMenuExtensionDelegate MenuBuilderDelegate) { return ViewportWidget->ViewportToolbar->AddMenuExtender(MenuToExtend, MenuBuilderDelegate); } void SAnimationEditorViewportTabBody::AddOverlayWidget(TSharedRef InOverlaidWidget, int32 ZOrder) { ViewportWidget->ViewportOverlay->AddSlot(ZOrder) [ InOverlaidWidget ]; } void SAnimationEditorViewportTabBody::RemoveOverlayWidget(TSharedRef InOverlaidWidget) { ViewportWidget->ViewportOverlay->RemoveSlot(InOverlaidWidget); } void SAnimationEditorViewportTabBody::RefreshViewport() { LevelViewportClient->Invalidate(); } TSharedPtr SAnimationEditorViewportTabBody::GetAssetEditorToolkit() const { return AssetEditorToolkitPtr.Pin(); } bool SAnimationEditorViewportTabBody::IsVisible() const { return ViewportWidget.IsValid(); } FReply SAnimationEditorViewportTabBody::OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent) { if (UICommandList.IsValid() && UICommandList->ProcessCommandBindings(InKeyEvent)) { return FReply::Handled(); } if (OnKeyDownDelegate.IsBound()) { return OnKeyDownDelegate.Execute(MyGeometry, InKeyEvent); } return FReply::Unhandled(); } void SAnimationEditorViewportTabBody::Construct(const FArguments& InArgs, const TSharedRef& InPreviewScene, const TSharedRef& InAssetEditorToolkit, int32 InViewportIndex) { PreviewScenePtr = StaticCastSharedRef(InPreviewScene); AssetEditorToolkitPtr = InAssetEditorToolkit; BlueprintEditorPtr = InArgs._BlueprintEditor; bShowTimeline = InArgs._ShowTimeline; bAlwaysShowTransformToolbar = InArgs._AlwaysShowTransformToolbar; OnInvokeTab = InArgs._OnInvokeTab; OnGetViewportText = InArgs._OnGetViewportText; ContextName = InArgs._ContextName; TimelineDelegates = InArgs._TimelineDelegates; // register delegates for change notifications InPreviewScene->RegisterOnAnimChanged(FOnAnimChanged::CreateSP(this, &SAnimationEditorViewportTabBody::AnimChanged)); InPreviewScene->RegisterOnPreviewMeshChanged(FOnPreviewMeshChanged::CreateSP(this, &SAnimationEditorViewportTabBody::HandlePreviewMeshChanged)); const FSlateFontInfo SmallLayoutFont = FCoreStyle::GetDefaultFontStyle("Regular", 9); FAnimViewportMenuCommands::Register(); FAnimViewportShowCommands::Register(); FAnimViewportLODCommands::Register(); FAnimViewportPlaybackCommands::Register(); // Build toolbar widgets UVChannelCombo = SNew(STextComboBox) .OptionsSource(&UVChannels) .Font(SmallLayoutFont) .OnSelectionChanged(this, &SAnimationEditorViewportTabBody::ComboBoxSelectionChanged); PopulateSkinWeightProfileNames(); SkinWeightCombo = SNew(SNameComboBox) .OptionsSource(&SkinWeightProfileNames) .InitiallySelectedItem(SkinWeightProfileNames.Num() > 0 ? SkinWeightProfileNames[0] : nullptr) .OnComboBoxOpening(FOnComboBoxOpening::CreateLambda([this]() { // Retrieve currently selected value, and check whether or not it is still valid, it could be that a profile has been renamed or removed without updating the entries FName Name = SkinWeightCombo->GetSelectedItem().IsValid() ? *SkinWeightCombo->GetSelectedItem().Get() : NAME_None; PopulateSkinWeightProfileNames(); const int32 Index = SkinWeightProfileNames.IndexOfByPredicate([Name](TSharedPtr SearchName) { return Name == *SearchName; }); if (Index != INDEX_NONE) { SkinWeightCombo->SetSelectedItem(SkinWeightProfileNames[Index]); } })) .OnSelectionChanged(SNameComboBox::FOnNameSelectionChanged::CreateLambda([WeakScenePtr = PreviewScenePtr](TSharedPtr SelectedProfile, ESelectInfo::Type SelectInfo) { // Apply the skin weight profile to the component, according to the selected the name, if (WeakScenePtr.IsValid() && SelectedProfile.IsValid()) { UDebugSkelMeshComponent* MeshComponent = WeakScenePtr.Pin()->GetPreviewMeshComponent(); if (MeshComponent) { MeshComponent->ClearSkinWeightProfile(); if (*SelectedProfile != NAME_None) { MeshComponent->SetSkinWeightProfile(*SelectedProfile); } } } })); FAnimationEditorViewportRequiredArgs ViewportArgs(InPreviewScene, SharedThis(this), InAssetEditorToolkit, InViewportIndex); ViewportWidget = SNew(SAnimationEditorViewport, ViewportArgs) .Extenders(InArgs._Extenders) .ContextName(InArgs._ContextName) .ShowShowMenu(InArgs._ShowShowMenu) .ShowLODMenu(InArgs._ShowLODMenu) .ShowPlaySpeedMenu(InArgs._ShowPlaySpeedMenu) .ShowStats(InArgs._ShowStats) .ShowFloorOptions(InArgs._ShowFloorOptions) .ShowTurnTable(InArgs._ShowTurnTable) .ShowPhysicsMenu(InArgs._ShowPhysicsMenu); TSharedPtr ViewportContainer = nullptr; this->ChildSlot [ SAssignNew(ViewportContainer, SVerticalBox) // Build our toolbar level toolbar +SVerticalBox::Slot() .FillHeight(1) [ SNew(SOverlay) // The viewport +SOverlay::Slot() [ ViewportWidget.ToSharedRef() ] // The 'dirty/in-error' indicator text in the bottom-right corner +SOverlay::Slot() .Padding(8) .VAlign(VAlign_Bottom) .HAlign(HAlign_Right) [ SAssignNew(ViewportNotificationsContainer, SVerticalBox) ] ] ]; if(bShowTimeline && ViewportContainer.IsValid()) { ViewportContainer->AddSlot() .AutoHeight() [ SAssignNew(ScrubPanelContainer, SVerticalBox) +SVerticalBox::Slot() .AutoHeight() [ SNew(SAnimationScrubPanel, GetPreviewScene()) .ViewInputMin(this, &SAnimationEditorViewportTabBody::GetViewMinInput) .ViewInputMax(this, &SAnimationEditorViewportTabBody::GetViewMaxInput) .bAllowZoom(true) .TimelineDelegates(TimelineDelegates) ] ]; UpdateScrubPanel(InPreviewScene->GetPreviewAnimationAsset()); } LevelViewportClient = ViewportWidget->GetViewportClient(); if (const TSharedPtr& AnimViewportClient = GetAnimationViewportClient()) { // Load the view mode from config AnimViewportClient->SetViewMode(AnimViewportClient->ConfigOption ->GetAssetEditorOptions(AssetEditorToolkitPtr.Pin()->GetEditorName()) .ViewportConfigs[InViewportIndex] .ViewModeIndex); } UpdateShowFlagForMeshEdges(); OnSetTurnTableMode(SelectedTurnTableMode); OnSetTurnTableSpeed(SelectedTurnTableSpeed); BindCommands(); PopulateNumUVChannels(); PopulateSkinWeightProfileNames(); GetPreviewScene()->OnRecordingStateChanged().AddSP(this, &SAnimationEditorViewportTabBody::AddRecordingNotification); if (GetPreviewScene()->GetPreviewMesh()) { GetPreviewScene()->GetPreviewMesh()->OnPostMeshCached().AddSP(this, &SAnimationEditorViewportTabBody::UpdateSkinWeightSelection); } AddPostProcessNotification(); AddMinLODNotification(); AddSkinWeightProfileNotification(); if (TSharedPtr PreviewScene = PreviewScenePtr.Pin()) { UE::AdvancedPreviewScene::BindDefaultOnSettingsChangedHandler(PreviewScene, LevelViewportClient); } } void SAnimationEditorViewportTabBody::BindCommands() { FUICommandList_Pinnable& CommandList = *UICommandList; //Bind menu commands const FAnimViewportMenuCommands& MenuActions = FAnimViewportMenuCommands::Get(); CommandList.MapAction( MenuActions.TogglePauseAnimationOnCameraMove, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::TogglePauseAnimationOnCameraMove), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::GetShouldPauseAnimationOnCameraMove)); CommandList.MapAction( MenuActions.CameraFollowNone, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::SetCameraFollowMode, EAnimationViewportCameraFollowMode::None, FName()), FCanExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::CanChangeCameraMode), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsCameraFollowEnabled, EAnimationViewportCameraFollowMode::None)); CommandList.MapAction( MenuActions.CameraFollowBounds, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::SetCameraFollowMode, EAnimationViewportCameraFollowMode::Bounds, FName()), FCanExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::CanChangeCameraMode), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsCameraFollowEnabled, EAnimationViewportCameraFollowMode::Bounds)); CommandList.MapAction( MenuActions.CameraFollowRoot, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::SetCameraFollowMode, EAnimationViewportCameraFollowMode::Root, FName()), FCanExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::CanChangeCameraMode), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsCameraFollowEnabled, EAnimationViewportCameraFollowMode::Root)); CommandList.MapAction( MenuActions.JumpToDefaultCamera, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::JumpToDefaultCamera), FCanExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::HasDefaultCameraSet)); CommandList.MapAction( MenuActions.SaveCameraAsDefault, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::SaveCameraAsDefault), FCanExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::CanSaveCameraAsDefault)); CommandList.MapAction( MenuActions.ClearDefaultCamera, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::ClearDefaultCamera), FCanExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::HasDefaultCameraSet)); CommandList.MapAction( MenuActions.PreviewSceneSettings, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OpenPreviewSceneSettings)); if (const TSharedPtr& AnimationViewportClientPtr = GetAnimationViewportClient()) { if (FAnimationViewportClient* AnimViewportClient = AnimationViewportClientPtr.Get()) { CommandList.MapAction( MenuActions.SetCPUSkinning, FExecuteAction::CreateSP(AnimViewportClient, &FAnimationViewportClient::ToggleCPUSkinning), FCanExecuteAction(), FIsActionChecked::CreateSP(AnimViewportClient, &FAnimationViewportClient::IsSetCPUSkinningChecked) ); CommandList.MapAction( MenuActions.SetShowNormals, FExecuteAction::CreateSP(AnimViewportClient, &FAnimationViewportClient::ToggleShowNormals), FCanExecuteAction(), FIsActionChecked::CreateSP(AnimViewportClient, &FAnimationViewportClient::IsSetShowNormalsChecked) ); CommandList.MapAction( MenuActions.SetShowTangents, FExecuteAction::CreateSP(AnimViewportClient, &FAnimationViewportClient::ToggleShowTangents), FCanExecuteAction(), FIsActionChecked::CreateSP(AnimViewportClient, &FAnimationViewportClient::IsSetShowTangentsChecked) ); CommandList.MapAction( MenuActions.SetShowBinormals, FExecuteAction::CreateSP(AnimViewportClient, &FAnimationViewportClient::ToggleShowBinormals), FCanExecuteAction(), FIsActionChecked::CreateSP(AnimViewportClient, &FAnimationViewportClient::IsSetShowBinormalsChecked) ); } } //Bind Show commands const FAnimViewportShowCommands& ViewportShowMenuCommands = FAnimViewportShowCommands::Get(); CommandList.MapAction( ViewportShowMenuCommands.ShowBound, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::ShowBound), FCanExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::CanShowBound), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsShowBoundEnabled)); CommandList.MapAction( ViewportShowMenuCommands.UseInGameBound, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::UseInGameBound), FCanExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::CanUseInGameBound), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsUsingInGameBound)); CommandList.MapAction( ViewportShowMenuCommands.UseFixedBounds, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::UseFixedBounds), FCanExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::CanUseFixedBounds), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsUsingFixedBounds)); CommandList.MapAction( ViewportShowMenuCommands.UsePreSkinnedBounds, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::UsePreSkinnedBounds), FCanExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::CanUsePreSkinnedBounds), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsUsingPreSkinnedBounds)); CommandList.MapAction( ViewportShowMenuCommands.ShowPreviewMesh, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::ToggleShowPreviewMesh), FCanExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::CanShowPreviewMesh), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsShowPreviewMeshEnabled)); CommandList.MapAction( ViewportShowMenuCommands.ShowMorphTargets, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnShowMorphTargets), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsShowingMorphTargets)); CommandList.MapAction( ViewportShowMenuCommands.ShowBoneNames, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnShowBoneNames), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsShowingBoneNames)); CommandList.MapAction( ViewportShowMenuCommands.ShowBoneColors, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnShowBoneColors), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsShowingBoneColors)); CommandList.MapAction( ViewportShowMenuCommands.ShowRawAnimation, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnShowRawAnimation), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsShowingRawAnimation)); CommandList.MapAction( ViewportShowMenuCommands.ShowNonRetargetedAnimation, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnShowNonRetargetedAnimation), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsShowingNonRetargetedPose)); CommandList.MapAction( ViewportShowMenuCommands.ShowAdditiveBaseBones, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnShowAdditiveBase), FCanExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::IsPreviewingAnimation), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsShowingAdditiveBase)); CommandList.MapAction( ViewportShowMenuCommands.ShowSourceRawAnimation, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnShowSourceRawAnimation), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsShowingSourceRawAnimation)); CommandList.MapAction( ViewportShowMenuCommands.ShowBakedAnimation, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnShowBakedAnimation), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsShowingBakedAnimation)); //Display info CommandList.BeginGroup(TEXT("MeshDisplayInfo")); CommandList.MapAction( ViewportShowMenuCommands.ShowDisplayInfoBasic, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnShowDisplayInfo, (int32)EDisplayInfoMode::Basic), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsShowingMeshInfo, (int32)EDisplayInfoMode::Basic)); CommandList.MapAction( ViewportShowMenuCommands.ShowDisplayInfoDetailed, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnShowDisplayInfo, (int32)EDisplayInfoMode::Detailed), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsShowingMeshInfo, (int32)EDisplayInfoMode::Detailed)); CommandList.MapAction( ViewportShowMenuCommands.ShowDisplayInfoSkelControls, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnShowDisplayInfo, (int32)EDisplayInfoMode::SkeletalControls), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsShowingMeshInfo, (int32)EDisplayInfoMode::SkeletalControls)); CommandList.MapAction( ViewportShowMenuCommands.HideDisplayInfo, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnShowDisplayInfo, (int32)EDisplayInfoMode::None), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsShowingMeshInfo, (int32)EDisplayInfoMode::None)); CommandList.EndGroup(); //Material overlay option CommandList.BeginGroup(TEXT("MaterialOverlay")); CommandList.MapAction( ViewportShowMenuCommands.ShowOverlayNone, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnShowOverlayNone), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsShowingOverlayNone)); CommandList.MapAction( ViewportShowMenuCommands.ShowBoneWeight, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnShowOverlayBoneWeight), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsShowingOverlayBoneWeight)); CommandList.MapAction( ViewportShowMenuCommands.ShowMorphTargetVerts, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnShowOverlayMorphTargetVert), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsShowingOverlayMorphTargetVerts)); CommandList.EndGroup(); // Show sockets CommandList.MapAction( ViewportShowMenuCommands.ShowSockets, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnShowSockets), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsShowingSockets)); // Show transform attributes CommandList.MapAction( ViewportShowMenuCommands.ShowAttributes, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnShowAttributes), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsShowingAttributes)); // Set bone drawing mode CommandList.BeginGroup(TEXT("BoneDrawingMode")); CommandList.MapAction( ViewportShowMenuCommands.ShowBoneDrawNone, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnSetBoneDrawMode, (int32)EBoneDrawMode::None), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsBoneDrawModeSet, (int32)EBoneDrawMode::None)); CommandList.MapAction( ViewportShowMenuCommands.ShowBoneDrawSelected, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnSetBoneDrawMode, (int32)EBoneDrawMode::Selected), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsBoneDrawModeSet, (int32)EBoneDrawMode::Selected)); CommandList.MapAction( ViewportShowMenuCommands.ShowBoneDrawSelectedAndParents, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnSetBoneDrawMode, (int32)EBoneDrawMode::SelectedAndParents), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsBoneDrawModeSet, (int32)EBoneDrawMode::SelectedAndParents)); CommandList.MapAction( ViewportShowMenuCommands.ShowBoneDrawSelectedAndChildren, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnSetBoneDrawMode, (int32)EBoneDrawMode::SelectedAndChildren), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsBoneDrawModeSet, (int32)EBoneDrawMode::SelectedAndChildren)); CommandList.MapAction( ViewportShowMenuCommands.ShowBoneDrawSelectedAndParentsAndChildren, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnSetBoneDrawMode, (int32)EBoneDrawMode::SelectedAndParentsAndChildren), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsBoneDrawModeSet, (int32)EBoneDrawMode::SelectedAndParentsAndChildren)); CommandList.MapAction( ViewportShowMenuCommands.ShowBoneDrawAll, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnSetBoneDrawMode, (int32)EBoneDrawMode::All), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsBoneDrawModeSet, (int32)EBoneDrawMode::All)); CommandList.EndGroup(); // Set bone local axes mode CommandList.BeginGroup(TEXT("BoneLocalAxesMode")); CommandList.MapAction( ViewportShowMenuCommands.ShowLocalAxesNone, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnSetLocalAxesMode, (int32)ELocalAxesMode::None), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsLocalAxesModeSet, (int32)ELocalAxesMode::None)); CommandList.MapAction( ViewportShowMenuCommands.ShowLocalAxesSelected, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnSetLocalAxesMode, (int32)ELocalAxesMode::Selected), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsLocalAxesModeSet, (int32)ELocalAxesMode::Selected)); CommandList.MapAction( ViewportShowMenuCommands.ShowLocalAxesAll, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnSetLocalAxesMode, (int32)ELocalAxesMode::All), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsLocalAxesModeSet, (int32)ELocalAxesMode::All)); CommandList.EndGroup(); //Clothing show options CommandList.MapAction( ViewportShowMenuCommands.EnableClothSimulation, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnEnableClothSimulation), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsClothSimulationEnabled)); CommandList.MapAction( ViewportShowMenuCommands.ResetClothSimulation, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnResetClothSimulation), FCanExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::IsClothSimulationEnabled)); CommandList.MapAction( ViewportShowMenuCommands.EnableCollisionWithAttachedClothChildren, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnEnableCollisionWithAttachedClothChildren), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsEnablingCollisionWithAttachedClothChildren)); CommandList.MapAction( ViewportShowMenuCommands.PauseClothWithAnim, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnPauseClothingSimWithAnim), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsPausingClothingSimWithAnim)); CommandList.BeginGroup(TEXT("ClothSectionDisplayMode")); CommandList.MapAction( ViewportShowMenuCommands.ShowAllSections, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnSetSectionsDisplayMode, ESectionDisplayMode::ShowAll), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsSectionsDisplayMode, ESectionDisplayMode::ShowAll)); CommandList.MapAction( ViewportShowMenuCommands.ShowOnlyClothSections, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnSetSectionsDisplayMode, ESectionDisplayMode::ShowOnlyClothSections), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsSectionsDisplayMode, ESectionDisplayMode::ShowOnlyClothSections)); CommandList.MapAction( ViewportShowMenuCommands.HideOnlyClothSections, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnSetSectionsDisplayMode, ESectionDisplayMode::HideOnlyClothSections), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsSectionsDisplayMode, ESectionDisplayMode::HideOnlyClothSections)); CommandList.EndGroup(); CommandList.BeginGroup(TEXT("TimecodeSettings")); CommandList.MapAction( ViewportShowMenuCommands.ShowTimecode, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnToggleShowTimecode), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsShowTimecode)); CommandList.EndGroup(); GetPreviewScene()->RegisterOnSelectedLODChanged(FOnSelectedLODChanged::CreateSP(this, &SAnimationEditorViewportTabBody::OnLODModelChanged)); //Bind LOD preview menu commands const FAnimViewportLODCommands& ViewportLODMenuCommands = FAnimViewportLODCommands::Get(); CommandList.BeginGroup(TEXT("LOD")); UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); if (PreviewComponent) { //LOD Debug CommandList.MapAction( ViewportLODMenuCommands.LODDebug, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnSetLODTrackDebuggedInstance), FCanExecuteAction::CreateLambda([PreviewComponent]() { return PreviewComponent->PreviewInstance ? (bool)PreviewComponent->PreviewInstance->GetDebugSkeletalMeshComponent() : false; }), FIsActionChecked::CreateLambda([PreviewComponent]() { return PreviewComponent->IsTrackingAttachedLOD(); }), FIsActionButtonVisible::CreateLambda([PreviewComponent]() { return PreviewComponent->PreviewInstance ? (bool)PreviewComponent->PreviewInstance->GetDebugSkeletalMeshComponent() : false; })); PreviewComponent->RegisterOnDebugForceLODChangedDelegate(FOnDebugForceLODChanged::CreateSP(this, &SAnimationEditorViewportTabBody::OnDebugForcedLODChanged)); } //LOD Auto CommandList.MapAction( ViewportLODMenuCommands.LODAuto, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnSetLODModel, 0), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsLODModelSelected, 0)); // LOD 0 CommandList.MapAction( ViewportLODMenuCommands.LOD0, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnSetLODModel, 1), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsLODModelSelected, 1)); // all other LODs will be added dynamically CommandList.EndGroup(); CommandList.MapAction( ViewportShowMenuCommands.AutoAlignFloorToMesh, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnToggleAutoAlignFloor), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsAutoAlignFloor)); //Bind LOD preview menu commands const FAnimViewportPlaybackCommands& ViewportPlaybackCommands = FAnimViewportPlaybackCommands::Get(); CommandList.MapAction( ViewportShowMenuCommands.MuteAudio, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnToggleMuteAudio), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsAudioMuted)); CommandList.MapAction( ViewportShowMenuCommands.UseAudioAttenuation, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnToggleUseAudioAttenuation), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsAudioAttenuationEnabled)); CommandList.BeginGroup(TEXT("RootMotion")); CommandList.MapAction( ViewportShowMenuCommands.DoNotProcessRootMotion, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::SetProcessRootMotionMode, EProcessRootMotionMode::Ignore), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::CanUseProcessRootMotionMode, EProcessRootMotionMode::Ignore), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsProcessRootMotionModeSet, EProcessRootMotionMode::Ignore)); CommandList.MapAction( ViewportShowMenuCommands.ProcessRootMotionLoopAndReset, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::SetProcessRootMotionMode, EProcessRootMotionMode::LoopAndReset), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::CanUseProcessRootMotionMode, EProcessRootMotionMode::LoopAndReset), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsProcessRootMotionModeSet, EProcessRootMotionMode::LoopAndReset)); CommandList.MapAction( ViewportShowMenuCommands.ProcessRootMotionLoop, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::SetProcessRootMotionMode, EProcessRootMotionMode::Loop), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::CanUseProcessRootMotionMode, EProcessRootMotionMode::Loop), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsProcessRootMotionModeSet, EProcessRootMotionMode::Loop)); CommandList.MapAction( ViewportShowMenuCommands.DoNotVisualizeRootMotion, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::SetVisualizeRootMotionMode, EVisualizeRootMotionMode::None), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::CanVisualizeRootMotion), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsVisualizeRootMotionModeSet, EVisualizeRootMotionMode::None)); CommandList.MapAction( ViewportShowMenuCommands.VisualizeRootMotionTrajectory, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::SetVisualizeRootMotionMode, EVisualizeRootMotionMode::Trajectory), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::CanVisualizeRootMotion), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsVisualizeRootMotionModeSet, EVisualizeRootMotionMode::Trajectory)); CommandList.MapAction( ViewportShowMenuCommands.VisualizeRootMotionTrajectoryAndOrientation, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::SetVisualizeRootMotionMode, EVisualizeRootMotionMode::TrajectoryAndOrientation), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::CanVisualizeRootMotion), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsVisualizeRootMotionModeSet, EVisualizeRootMotionMode::TrajectoryAndOrientation)); CommandList.MapAction( ViewportShowMenuCommands.ShowNotificationVisualizations, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::ToggleNotificationVisualizations), FIsActionChecked(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsNotificationVisualizationsEnabled)); CommandList.MapAction( ViewportShowMenuCommands.ShowAssetUserDataVisualizations, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::ToggleAssetUserDataVisualizations), FIsActionChecked(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsAssetUserDataVisualizationsEnabled)); CommandList.EndGroup(); CommandList.MapAction( ViewportShowMenuCommands.DisablePostProcessBlueprint, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnToggleDisablePostProcess), FCanExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::CanDisablePostProcess), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsDisablePostProcessChecked)); CommandList.BeginGroup(TEXT("TurnTableSpeeds")); // Turn Table Controls for (int32 i = 0; i < int(EAnimationPlaybackSpeeds::NumPlaybackSpeeds); ++i) { CommandList.MapAction( ViewportPlaybackCommands.TurnTableSpeeds[i], FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnSetTurnTableSpeed, i), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsTurnTableSpeedSelected, i)); } CommandList.EndGroup(); CommandList.BeginGroup(TEXT("TurnTableMode")); CommandList.MapAction( ViewportPlaybackCommands.PersonaTurnTablePlay, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnSetTurnTableMode, int32(EPersonaTurnTableMode::Playing)), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsTurnTableModeSelected, int32(EPersonaTurnTableMode::Playing))); CommandList.MapAction( ViewportPlaybackCommands.PersonaTurnTablePause, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnSetTurnTableMode, int32(EPersonaTurnTableMode::Paused)), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsTurnTableModeSelected, int32(EPersonaTurnTableMode::Paused))); CommandList.MapAction( ViewportPlaybackCommands.PersonaTurnTableStop, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::OnSetTurnTableMode, int32(EPersonaTurnTableMode::Stopped)), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsTurnTableModeSelected, int32(EPersonaTurnTableMode::Stopped))); CommandList.EndGroup(); CommandList.MapAction( FEditorViewportCommands::Get().FocusViewportToSelection, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::HandleFocusCamera)); if (const TSharedPtr& AssetEditorToolkit = GetAssetEditorToolkit()) { TSharedPtr ToolkitCommandList = ConstCastSharedRef(AssetEditorToolkit->GetToolkitCommands()); ToolkitCommandList->Append(UICommandList->AsShared()); } PinnedCommands->BindCommandList(UICommandList.ToSharedRef()); if (TSharedPtr AnimationEditorPreviewScene = PreviewScenePtr.Pin()) { PinnedCommands->BindCommandList(AnimationEditorPreviewScene->GetPinnedCommandList().ToSharedRef()); } } void SAnimationEditorViewportTabBody::OnSetTurnTableSpeed(int32 SpeedIndex) { SelectedTurnTableSpeed = (EAnimationPlaybackSpeeds::Type)SpeedIndex; UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); if (PreviewComponent) { const float TurnTableSpeed = (SelectedTurnTableSpeed == EAnimationPlaybackSpeeds::Custom) ? GetCustomTurnTableSpeed() : EAnimationPlaybackSpeeds::Values[SelectedTurnTableSpeed]; PreviewComponent->TurnTableSpeedScaling = TurnTableSpeed; } } bool SAnimationEditorViewportTabBody::IsTurnTableSpeedSelected(int32 SpeedIndex) const { return (SelectedTurnTableSpeed == SpeedIndex); } void SAnimationEditorViewportTabBody::OnSetTurnTableMode(int32 ModeIndex) { SelectedTurnTableMode = (EPersonaTurnTableMode::Type)ModeIndex; UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); if (PreviewComponent) { PreviewComponent->SetTurnTableMode(SelectedTurnTableMode); } } bool SAnimationEditorViewportTabBody::IsTurnTableModeSelected(int32 ModeIndex) const { return (SelectedTurnTableMode == ModeIndex); } int32 SAnimationEditorViewportTabBody::GetLODModelCount() const { UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); if( PreviewComponent && PreviewComponent->GetSkeletalMeshAsset()) { return PreviewComponent->GetSkeletalMeshAsset()->GetResourceForRendering()->LODRenderData.Num(); } return 0; } void SAnimationEditorViewportTabBody::OnShowMorphTargets() { ForEachDebugMesh([](UDebugSkelMeshComponent* InMesh) { InMesh->bDisableMorphTarget = !InMesh->bDisableMorphTarget; InMesh->MarkRenderStateDirty(); }); RefreshViewport(); } void SAnimationEditorViewportTabBody::OnShowBoneNames() { ForEachDebugMesh([](UDebugSkelMeshComponent* InMesh) { InMesh->bShowBoneNames = !InMesh->bShowBoneNames; InMesh->MarkRenderStateDirty(); }); RefreshViewport(); if (const TSharedPtr& AnimViewportClient = GetAnimationViewportClient()) { AnimViewportClient->UpdateBonesToDraw(); } } void SAnimationEditorViewportTabBody::OnShowRawAnimation() { ForEachDebugMesh([](UDebugSkelMeshComponent* InMesh) { InMesh->bDisplayRawAnimation = !InMesh->bDisplayRawAnimation; InMesh->MarkRenderStateDirty(); }); RefreshViewport(); } void SAnimationEditorViewportTabBody::OnShowNonRetargetedAnimation() { ForEachDebugMesh([](UDebugSkelMeshComponent* InMesh) { InMesh->bDisplayNonRetargetedPose = !InMesh->bDisplayNonRetargetedPose; InMesh->MarkRenderStateDirty(); }); RefreshViewport(); } void SAnimationEditorViewportTabBody::OnShowSourceRawAnimation() { ForEachDebugMesh([](UDebugSkelMeshComponent* InMesh) { InMesh->bDisplaySourceAnimation = !InMesh->bDisplaySourceAnimation; InMesh->MarkRenderStateDirty(); }); RefreshViewport(); } void SAnimationEditorViewportTabBody::OnShowBakedAnimation() { ForEachDebugMesh([](UDebugSkelMeshComponent* InMesh) { InMesh->bDisplayBakedAnimation = !InMesh->bDisplayBakedAnimation; InMesh->MarkRenderStateDirty(); }); RefreshViewport(); } void SAnimationEditorViewportTabBody::OnShowAdditiveBase() { ForEachDebugMesh([](UDebugSkelMeshComponent* InMesh) { InMesh->bDisplayAdditiveBasePose = !InMesh->bDisplayAdditiveBasePose; InMesh->MarkRenderStateDirty(); }); RefreshViewport(); } bool SAnimationEditorViewportTabBody::IsPreviewingAnimation() const { UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); return (PreviewComponent && PreviewComponent->PreviewInstance && (PreviewComponent->PreviewInstance == PreviewComponent->GetAnimInstance())); } bool SAnimationEditorViewportTabBody::IsShowingMorphTargets() const { UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); return PreviewComponent != NULL && PreviewComponent->bDisableMorphTarget == false; } bool SAnimationEditorViewportTabBody::IsShowingBoneNames() const { UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); return PreviewComponent != NULL && PreviewComponent->bShowBoneNames; } void SAnimationEditorViewportTabBody::OnShowBoneColors() { ForEachDebugMesh([](UDebugSkelMeshComponent* InMesh) { UPersonaOptions* Settings = GetMutableDefault(); Settings->bShowBoneColors = !Settings->bShowBoneColors; InMesh->MarkRenderStateDirty(); }); RefreshViewport(); } bool SAnimationEditorViewportTabBody::IsShowingBoneColors() const { const UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); return PreviewComponent && GetDefault()->bShowBoneColors; } bool SAnimationEditorViewportTabBody::IsShowingRawAnimation() const { UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); return PreviewComponent != NULL && PreviewComponent->bDisplayRawAnimation; } void SAnimationEditorViewportTabBody::OnToggleDisablePostProcess() { ForEachDebugMesh([](UDebugSkelMeshComponent* InMesh) { InMesh->ToggleDisablePostProcessBlueprint(); }); AddPostProcessNotification(); } bool SAnimationEditorViewportTabBody::CanDisablePostProcess() const { const TArray PreviewMeshComponents = GetPreviewScene()->GetAllPreviewMeshComponents(); for (const UDebugSkelMeshComponent* PreviewMeshComponent : PreviewMeshComponents) { if (PreviewMeshComponent->PostProcessAnimInstance && PreviewMeshComponent->IsVisible()) { return true; } } return false; } bool SAnimationEditorViewportTabBody::IsDisablePostProcessChecked() { TArray PreviewMeshComponents = GetPreviewScene()->GetAllPreviewMeshComponents(); for (UDebugSkelMeshComponent* PreviewMeshComponent : PreviewMeshComponents) { if (PreviewMeshComponent->GetDisablePostProcessBlueprint()) { return true; } } return false; } bool SAnimationEditorViewportTabBody::IsShowingNonRetargetedPose() const { UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); return PreviewComponent != NULL && PreviewComponent->bDisplayNonRetargetedPose; } bool SAnimationEditorViewportTabBody::IsShowingAdditiveBase() const { UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); return PreviewComponent != NULL && PreviewComponent->bDisplayAdditiveBasePose; } bool SAnimationEditorViewportTabBody::IsShowingSourceRawAnimation() const { UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); return PreviewComponent != NULL && PreviewComponent->bDisplaySourceAnimation; } bool SAnimationEditorViewportTabBody::IsShowingBakedAnimation() const { UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); return PreviewComponent != NULL && PreviewComponent->bDisplayBakedAnimation; } void SAnimationEditorViewportTabBody::OnShowDisplayInfo(int32 DisplayInfoMode) { if (const TSharedPtr& AnimViewportClient = GetAnimationViewportClient()) { AnimViewportClient->OnSetShowMeshStats(DisplayInfoMode); } } bool SAnimationEditorViewportTabBody::IsShowingMeshInfo(int32 DisplayInfoMode) const { if (const TSharedPtr& AnimViewportClient = GetAnimationViewportClient()) { return AnimViewportClient->GetShowMeshStats() == DisplayInfoMode; } return false; } void SAnimationEditorViewportTabBody::OnShowOverlayNone() { ForEachDebugMesh([](UDebugSkelMeshComponent* PreviewMeshComponent) { PreviewMeshComponent->SetShowBoneWeight(false); PreviewMeshComponent->SetShowMorphTargetVerts(false); PreviewMeshComponent->MarkRenderStateDirty(); }); UpdateShowFlagForMeshEdges(); RefreshViewport(); } bool SAnimationEditorViewportTabBody::IsShowingOverlayNone() const { UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); return PreviewComponent != NULL && !PreviewComponent->bDrawBoneInfluences && !PreviewComponent->bDrawMorphTargetVerts; } void SAnimationEditorViewportTabBody::OnShowOverlayBoneWeight() { ForEachDebugMesh([](UDebugSkelMeshComponent* PreviewMeshComponent) { PreviewMeshComponent->SetShowBoneWeight( !PreviewMeshComponent->bDrawBoneInfluences ); PreviewMeshComponent->MarkRenderStateDirty(); }); UpdateShowFlagForMeshEdges(); RefreshViewport(); } bool SAnimationEditorViewportTabBody::IsShowingOverlayBoneWeight() const { UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); return PreviewComponent != NULL && PreviewComponent->bDrawBoneInfluences; } void SAnimationEditorViewportTabBody::OnShowOverlayMorphTargetVert() { ForEachDebugMesh([](UDebugSkelMeshComponent* PreviewMeshComponent) { PreviewMeshComponent->SetShowMorphTargetVerts(!PreviewMeshComponent->bDrawMorphTargetVerts); PreviewMeshComponent->MarkRenderStateDirty(); }); UpdateShowFlagForMeshEdges(); RefreshViewport(); } bool SAnimationEditorViewportTabBody::IsShowingOverlayMorphTargetVerts() const { UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); return PreviewComponent != NULL && PreviewComponent->bDrawMorphTargetVerts; } void SAnimationEditorViewportTabBody::SetBoneDrawSize(float BoneDrawSize) { if (const TSharedPtr& AnimViewportClient = GetAnimationViewportClient()) { AnimViewportClient->SetBoneDrawSize(BoneDrawSize); } } float SAnimationEditorViewportTabBody::GetBoneDrawSize() const { if (const TSharedPtr& AnimViewportClient = GetAnimationViewportClient()) { return AnimViewportClient->GetBoneDrawSize(); } return 0.0f; } void SAnimationEditorViewportTabBody::SetCustomTurnTableSpeed(float InCustomTurnTableSpeed) { CustomTurnTableSpeed = InCustomTurnTableSpeed; OnSetTurnTableSpeed(EAnimationPlaybackSpeeds::Custom); } float SAnimationEditorViewportTabBody::GetCustomTurnTableSpeed() const { return CustomTurnTableSpeed; } void SAnimationEditorViewportTabBody::OnSetBoneDrawMode(int32 BoneDrawMode) { if (const TSharedPtr& AnimViewportClient = GetAnimationViewportClient()) { AnimViewportClient->SetBoneDrawMode((EBoneDrawMode::Type)BoneDrawMode); } } bool SAnimationEditorViewportTabBody::IsBoneDrawModeSet(int32 BoneDrawMode) const { if (const TSharedPtr& AnimViewportClient = GetAnimationViewportClient()) { return AnimViewportClient->IsBoneDrawModeSet((EBoneDrawMode::Type)BoneDrawMode); } return false; } void SAnimationEditorViewportTabBody::OnSetLocalAxesMode(int32 LocalAxesMode) { if (const TSharedPtr& AnimViewportClient = GetAnimationViewportClient()) { AnimViewportClient->SetLocalAxesMode((ELocalAxesMode::Type)LocalAxesMode); } } bool SAnimationEditorViewportTabBody::IsLocalAxesModeSet(int32 LocalAxesMode) const { if (const TSharedPtr& AnimViewportClient = GetAnimationViewportClient()) { return AnimViewportClient->IsLocalAxesModeSet((ELocalAxesMode::Type)LocalAxesMode); } return false; } void SAnimationEditorViewportTabBody::OnShowSockets() { ForEachDebugMesh([](UDebugSkelMeshComponent* PreviewMeshComponent) { PreviewMeshComponent->bDrawSockets = !PreviewMeshComponent->bDrawSockets; PreviewMeshComponent->MarkRenderStateDirty(); }); RefreshViewport(); } bool SAnimationEditorViewportTabBody::IsShowingSockets() const { UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); return PreviewComponent != NULL && PreviewComponent->bDrawSockets; } void SAnimationEditorViewportTabBody::OnShowAttributes() { ForEachDebugMesh([](UDebugSkelMeshComponent* PreviewMeshComponent) { PreviewMeshComponent->bDrawAttributes = !PreviewMeshComponent->bDrawAttributes; PreviewMeshComponent->MarkRenderStateDirty(); }); RefreshViewport(); } bool SAnimationEditorViewportTabBody::IsShowingAttributes() const { UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); return PreviewComponent != NULL && PreviewComponent->bDrawAttributes; } void SAnimationEditorViewportTabBody::OnToggleAutoAlignFloor() { if (const TSharedPtr& AnimViewportClient = GetAnimationViewportClient()) { AnimViewportClient->OnToggleAutoAlignFloor(); } } bool SAnimationEditorViewportTabBody::IsAutoAlignFloor() const { if (const TSharedPtr& AnimViewportClient = GetAnimationViewportClient()) { return AnimViewportClient->IsAutoAlignFloor(); } return false; } void SAnimationEditorViewportTabBody::ShowBound() { if (const TSharedPtr& AnimViewportClient = GetAnimationViewportClient()) { AnimViewportClient->ToggleShowBounds(); ForEachDebugMesh( [AnimViewportClientWeak = AnimViewportClient.ToWeakPtr()](UDebugSkelMeshComponent* PreviewMeshComponent) { if (const TSharedPtr& AnimViewportClient = AnimViewportClientWeak.Pin()) { PreviewMeshComponent->bDisplayBound = AnimViewportClient->EngineShowFlags.Bounds; PreviewMeshComponent->RecreateRenderState_Concurrent(); } } ); } } bool SAnimationEditorViewportTabBody::CanShowBound() const { return !GetPreviewScene()->GetAllPreviewMeshComponents().IsEmpty(); } bool SAnimationEditorViewportTabBody::IsShowBoundEnabled() const { if (const TSharedPtr& AnimViewportClient = GetAnimationViewportClient()) { return AnimViewportClient->IsSetShowBoundsChecked(); } return false; } void SAnimationEditorViewportTabBody::ToggleShowPreviewMesh() { const bool bCurrentlyVisible = IsShowPreviewMeshEnabled(); ForEachDebugMesh([bCurrentlyVisible](UDebugSkelMeshComponent* PreviewMeshComponent) { PreviewMeshComponent->SetVisibility(!bCurrentlyVisible); }); } bool SAnimationEditorViewportTabBody::CanShowPreviewMesh() const { return !GetPreviewScene()->GetAllPreviewMeshComponents().IsEmpty(); } bool SAnimationEditorViewportTabBody::IsShowPreviewMeshEnabled() const { TArray PreviewMeshComponents = GetPreviewScene()->GetAllPreviewMeshComponents(); for (UDebugSkelMeshComponent* PreviewMeshComponent : PreviewMeshComponents) { if(PreviewMeshComponent && PreviewMeshComponent->IsVisible()) { return true; } } return false; } void SAnimationEditorViewportTabBody::OnToggleShowTimecode() { GetPreviewScene()->ToggleShowTimecode(); } bool SAnimationEditorViewportTabBody::IsShowTimecode() const { return GetPreviewScene()->IsShowTimecode(); } void SAnimationEditorViewportTabBody::UseInGameBound() { ForEachDebugMesh([](UDebugSkelMeshComponent* PreviewMeshComponent) { PreviewMeshComponent->UseInGameBounds(! PreviewMeshComponent->IsUsingInGameBounds()); }); } bool SAnimationEditorViewportTabBody::CanUseInGameBound() const { TArray PreviewMeshComponents = GetPreviewScene()->GetAllPreviewMeshComponents(); for (UDebugSkelMeshComponent* PreviewMeshComponent : PreviewMeshComponents) { if(IsShowBoundEnabled()) { return true; } } return false; } bool SAnimationEditorViewportTabBody::IsUsingInGameBound() const { TArray PreviewMeshComponents = GetPreviewScene()->GetAllPreviewMeshComponents(); for (UDebugSkelMeshComponent* PreviewMeshComponent : PreviewMeshComponents) { if(PreviewMeshComponent->IsUsingInGameBounds()) { return true; } } return false; } void SAnimationEditorViewportTabBody::UseFixedBounds() { ForEachDebugMesh([](UDebugSkelMeshComponent* PreviewMeshComponent) { PreviewMeshComponent->bComponentUseFixedSkelBounds = !PreviewMeshComponent->bComponentUseFixedSkelBounds; }); } bool SAnimationEditorViewportTabBody::CanUseFixedBounds() const { UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); return PreviewComponent != NULL && IsShowBoundEnabled(); } bool SAnimationEditorViewportTabBody::IsUsingFixedBounds() const { UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); return PreviewComponent != NULL && PreviewComponent->bComponentUseFixedSkelBounds; } void SAnimationEditorViewportTabBody::UsePreSkinnedBounds() { GetPreviewScene()->ForEachPreviewMesh([](UDebugSkelMeshComponent* PreviewMeshComponent) { PreviewMeshComponent->UsePreSkinnedBounds(!PreviewMeshComponent->IsUsingPreSkinnedBounds()); }); } bool SAnimationEditorViewportTabBody::CanUsePreSkinnedBounds() const { UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); return PreviewComponent != NULL && IsShowBoundEnabled(); } bool SAnimationEditorViewportTabBody::IsUsingPreSkinnedBounds() const { UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); return PreviewComponent != NULL && PreviewComponent->IsUsingPreSkinnedBounds(); } void SAnimationEditorViewportTabBody::HandlePreviewMeshChanged(class USkeletalMesh* OldSkeletalMesh, class USkeletalMesh* NewSkeletalMesh) { PopulateNumUVChannels(); PopulateSkinWeightProfileNames(); if (OldSkeletalMesh) { OldSkeletalMesh->OnPostMeshCached().RemoveAll(this); } if (NewSkeletalMesh) { NewSkeletalMesh->OnPostMeshCached().AddSP(this, &SAnimationEditorViewportTabBody::UpdateSkinWeightSelection); } } void SAnimationEditorViewportTabBody::AnimChanged(UAnimationAsset* AnimAsset) { UpdateScrubPanel(AnimAsset); } void SAnimationEditorViewportTabBody::ComboBoxSelectionChanged(TSharedPtr NewSelection, ESelectInfo::Type SelectInfo) { const TSharedPtr& AnimViewportClient = GetAnimationViewportClient(); if (!AnimViewportClient) { return; } int32 NewUVSelection = UVChannels.Find(NewSelection) - 1; // "None" is index -1 here. if ( NewUVSelection < 0 ) { AnimViewportClient->SetDrawUVOverlay(false); return; } AnimViewportClient->SetDrawUVOverlay(true); AnimViewportClient->SetUVChannelToDraw(NewUVSelection); RefreshViewport(); } void SAnimationEditorViewportTabBody::PopulateNumUVChannels() { NumUVChannels.Empty(); if (UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent()) { if (FSkeletalMeshRenderData* MeshResource = PreviewComponent->GetSkeletalMeshRenderData()) { int32 NumLods = MeshResource->LODRenderData.Num(); NumUVChannels.AddZeroed(NumLods); for(int32 LOD = 0; LOD < NumLods; ++LOD) { NumUVChannels[LOD] = MeshResource->LODRenderData[LOD].StaticVertexBuffers.StaticMeshVertexBuffer.GetNumTexCoords(); } } } PopulateUVChoices(); } void SAnimationEditorViewportTabBody::PopulateUVChoices() { const TSharedPtr& AnimViewportClient = GetAnimationViewportClient(); if (!AnimViewportClient) { return; } // Fill out the UV channels combo. UVChannels.Empty(); UVChannels.Add(MakeShareable(new FString(NSLOCTEXT("AnimationEditorViewport", "NoUVChannel", "None").ToString()))); if (UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent()) { int32 CurrentLOD = FMath::Clamp(PreviewComponent->GetForcedLOD() - 1, 0, NumUVChannels.Num() - 1); if (NumUVChannels.IsValidIndex(CurrentLOD)) { for (int32 UVChannelID = 0; UVChannelID < NumUVChannels[CurrentLOD]; ++UVChannelID) { UVChannels.Add( MakeShareable( new FString( FText::Format( NSLOCTEXT("AnimationEditorViewport", "UVChannel_ID", "UV Channel {0}"), FText::AsNumber( UVChannelID ) ).ToString() ) ) ); } int32 CurrentUVChannel = AnimViewportClient->GetUVChannelToDraw(); if (!UVChannels.IsValidIndex(CurrentUVChannel)) { CurrentUVChannel = 0; } AnimViewportClient->SetUVChannelToDraw(CurrentUVChannel); if (UVChannelCombo.IsValid() && UVChannels.IsValidIndex(CurrentUVChannel)) { UVChannelCombo->SetSelectedItem(UVChannels[CurrentUVChannel]); } } } } void SAnimationEditorViewportTabBody::PopulateSkinWeightProfileNames() { SkinWeightProfileNames.Empty(); // Always make sure we have a default 'none' option const FName DefaultProfileName = NAME_None; SkinWeightProfileNames.Add(MakeShared(DefaultProfileName)); // Retrieve all possible skin weight profiles from the component if (UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent()) { if (USkeletalMesh* Mesh = PreviewComponent->GetSkeletalMeshAsset()) { for (const FSkinWeightProfileInfo& Profile : Mesh->GetSkinWeightProfiles()) { SkinWeightProfileNames.AddUnique(MakeShared(Profile.Name)); } } } } void SAnimationEditorViewportTabBody::UpdateSkinWeightSelection(USkeletalMesh* InSkeletalMesh) { // Check (post a mesh build) whether or not our currently selected profile name is still valid, and if not reset to 'none' if (SkinWeightCombo->GetSelectedItem().IsValid()) { const FName OldSelection = *SkinWeightCombo->GetSelectedItem(); PopulateSkinWeightProfileNames(); const int32 SelectionIndex = SkinWeightProfileNames.IndexOfByPredicate([OldSelection](TSharedPtr InName) { return *InName == OldSelection; }); // Select new entry or otherwise select none SkinWeightCombo->SetSelectedItem(SelectionIndex != INDEX_NONE ? SkinWeightProfileNames[SelectionIndex] : SkinWeightProfileNames[0]); } } void SAnimationEditorViewportTabBody::UpdateScrubPanel(UAnimationAsset* AnimAsset) { // We might not have a scrub panel if we're in animation mode. if (ScrubPanelContainer.IsValid()) { ScrubPanelContainer->ClearChildren(); bool bUseDefaultScrubPanel = true; if (UAnimMontage* Montage = Cast(AnimAsset)) { ScrubPanelContainer->AddSlot() .AutoHeight() [ SNew(SAnimMontageScrubPanel, GetPreviewScene()) .ViewInputMin(this, &SAnimationEditorViewportTabBody::GetViewMinInput) .ViewInputMax(this, &SAnimationEditorViewportTabBody::GetViewMaxInput) .bAllowZoom(true) ]; bUseDefaultScrubPanel = false; } if(bUseDefaultScrubPanel) { ScrubPanelContainer->AddSlot() .AutoHeight() [ SNew(SAnimationScrubPanel, GetPreviewScene()) .ViewInputMin(this, &SAnimationEditorViewportTabBody::GetViewMinInput) .ViewInputMax(this, &SAnimationEditorViewportTabBody::GetViewMaxInput) .bAllowZoom(true) .bDisplayAnimScrubBarEditing(false) .TimelineDelegates(TimelineDelegates) ]; } } } float SAnimationEditorViewportTabBody::GetViewMinInput() const { if(TimelineDelegates.GetPlaybackTimeRangeDelegate.IsBound()) { const TOptional TimeRange = TimelineDelegates.GetPlaybackTimeRangeDelegate.Execute(); if(TimeRange.IsSet()) { return TimeRange.GetValue().X; } } if (UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent()) { UObject* PreviewAsset = GetPreviewScene()->GetPreviewAnimationAsset(); if (PreviewAsset != NULL) { return 0.0f; } else if (PreviewComponent->GetAnimInstance() != NULL) { return FMath::Max((float)(PreviewComponent->GetAnimInstance()->LifeTimer - 30.0), 0.0f); } } return 0.f; } float SAnimationEditorViewportTabBody::GetViewMaxInput() const { if(TimelineDelegates.GetPlaybackTimeRangeDelegate.IsBound()) { const TOptional TimeRange = TimelineDelegates.GetPlaybackTimeRangeDelegate.Execute(); if(TimeRange.IsSet()) { return TimeRange.GetValue().Y; } } UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); if (PreviewComponent != NULL) { UObject* PreviewAsset = GetPreviewScene()->GetPreviewAnimationAsset(); if ((PreviewAsset != NULL) && (PreviewComponent->PreviewInstance != NULL)) { return PreviewComponent->PreviewInstance->GetLength(); } else if (PreviewComponent->GetAnimInstance() != NULL) { return static_cast(PreviewComponent->GetAnimInstance()->LifeTimer); } } return 0.f; } void SAnimationEditorViewportTabBody::UpdateShowFlagForMeshEdges() { bool bUseOverlayMaterial = false; if (UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent()) { bUseOverlayMaterial = PreviewComponent->bDrawBoneInfluences || PreviewComponent->bDrawMorphTargetVerts; } //@TODO: SNOWPOCALYPSE: broke UnlitWithMeshEdges bool bShowMeshEdgesViewMode = false; #if 0 bShowMeshEdgesViewMode = (CurrentViewMode == EAnimationEditorViewportMode::UnlitWithMeshEdges); #endif LevelViewportClient->EngineShowFlags.SetMeshEdges(bUseOverlayMaterial || bShowMeshEdgesViewMode); } int32 SAnimationEditorViewportTabBody::GetLODSelection() const { UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); if (PreviewComponent) { // If we are forcing a LOD level, report the actual LOD level we are displaying // as the mesh can potentially change LOD count under the viewport. if(PreviewComponent->GetForcedLOD() > 0) { return PreviewComponent->GetPredictedLODLevel() + 1; } else { return PreviewComponent->GetForcedLOD(); } } return 0; } bool SAnimationEditorViewportTabBody::IsLODModelSelected(int32 LODSelectionType) const { UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); if (PreviewComponent && PreviewComponent->IsTrackingAttachedLOD()) { return false; } return GetLODSelection() == LODSelectionType; } bool SAnimationEditorViewportTabBody::IsTrackingAttachedMeshLOD() const { UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); if (PreviewComponent) { return PreviewComponent->IsTrackingAttachedLOD(); } return false; } void SAnimationEditorViewportTabBody::OnSetLODModel(int32 LODSelectionType) { UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); if( PreviewComponent ) { LODSelection = LODSelectionType; PreviewComponent->SetDebugForcedLOD(LODSelectionType); PreviewComponent->bTrackAttachedInstanceLOD = false; } } void SAnimationEditorViewportTabBody::OnSetLODTrackDebuggedInstance() { UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); if (PreviewComponent) { PreviewComponent->bTrackAttachedInstanceLOD = true; } } void SAnimationEditorViewportTabBody::OnLODModelChanged() { UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); if (PreviewComponent && LODSelection != PreviewComponent->GetForcedLOD()) { LODSelection = PreviewComponent->GetForcedLOD(); PopulateUVChoices(); } } void SAnimationEditorViewportTabBody::OnDebugForcedLODChanged() { UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); if (PreviewComponent) { PopulateUVChoices(); GetPreviewScene()->BroadcastOnSelectedLODChanged(); } } void SAnimationEditorViewportTabBody::OnBeginSliderMovementFloorOffset() { // This value is saved in a UPROPERTY for the floor mesh, so changes are transactional PendingTransaction = MakeUnique(LOCTEXT("SetFloorOffset", "Set Floor Offset")); PinnedCommands->AddCustomWidget(TEXT("FloorOffsetWidget")); } void SAnimationEditorViewportTabBody::OnFloorOffsetChanged(float InNewValue) { FAnimationViewportClient& AnimViewportClient = (FAnimationViewportClient&)GetLevelViewportClient(); AnimViewportClient.SetFloorOffset(InNewValue); PinnedCommands->AddCustomWidget(TEXT("FloorOffsetWidget")); } void SAnimationEditorViewportTabBody::OnFloorOffsetCommitted(float InNewValue, ETextCommit::Type InCommitType) { if (!PendingTransaction) { // Create the transaction here if it doesn't already exist. This can happen when changes come via text entry to the slider. PendingTransaction = MakeUnique(LOCTEXT("SetFloorOffset", "Set Floor Offset")); } FAnimationViewportClient& AnimViewportClient = (FAnimationViewportClient&)GetLevelViewportClient(); AnimViewportClient.SetFloorOffset(InNewValue); PinnedCommands->AddCustomWidget(TEXT("FloorOffsetWidget")); PendingTransaction.Reset(); } void SAnimationEditorViewportTabBody::CreatePinnedCommands() { // Create our pinned commands before we bind commands IPinnedCommandListModule& PinnedCommandListModule = FModuleManager::LoadModuleChecked(TEXT("PinnedCommandList")); PinnedCommands = PinnedCommandListModule.CreatePinnedCommandList((ContextName != NAME_None) ? ContextName : TEXT("PersonaViewport")); PinnedCommands->SetStyle(&FAppStyle::Get(), TEXT("ViewportPinnedCommandList")); UICommandList = MakeShareable(new FUICommandList_Pinnable); } TSharedPtr SAnimationEditorViewportTabBody::GetAnimationViewportClient() const { return StaticCastSharedPtr(LevelViewportClient); } void SAnimationEditorViewportTabBody::OpenPreviewSceneSettings() { OnInvokeTab.ExecuteIfBound(FPersonaTabs::AdvancedPreviewSceneSettingsID); } void SAnimationEditorViewportTabBody::SetCameraFollowMode(EAnimationViewportCameraFollowMode InCameraFollowMode, FName InBoneName) { if (const TSharedPtr& AnimViewportClient = GetAnimationViewportClient()) { AnimViewportClient->SetCameraFollowMode(InCameraFollowMode, InBoneName); } } bool SAnimationEditorViewportTabBody::IsCameraFollowEnabled(EAnimationViewportCameraFollowMode InCameraFollowMode) const { if (const TSharedPtr& AnimViewportClient = GetAnimationViewportClient()) { return (AnimViewportClient->GetCameraFollowMode() == InCameraFollowMode); } return false; } void SAnimationEditorViewportTabBody::ToggleRotateCameraToFollowBone() { if (const TSharedPtr& AnimViewportClient = GetAnimationViewportClient()) { AnimViewportClient->ToggleRotateCameraToFollowBone(); } } bool SAnimationEditorViewportTabBody::GetShouldRotateCameraToFollowBone() const { if (const TSharedPtr& AnimViewportClient = GetAnimationViewportClient()) { return AnimViewportClient->GetShouldRotateCameraToFollowBone(); } return false; } void SAnimationEditorViewportTabBody::TogglePauseAnimationOnCameraMove() { GetMutableDefault()->bPauseAnimationOnCameraMove = !GetMutableDefault()->bPauseAnimationOnCameraMove; } bool SAnimationEditorViewportTabBody::GetShouldPauseAnimationOnCameraMove() const { return GetMutableDefault()->bPauseAnimationOnCameraMove; } FName SAnimationEditorViewportTabBody::GetCameraFollowBoneName() const { if (const TSharedPtr& AnimViewportClient = GetAnimationViewportClient()) { return AnimViewportClient->GetCameraFollowBoneName(); } return NAME_None; } void SAnimationEditorViewportTabBody::SaveCameraAsDefault() { if (const TSharedPtr& AnimViewportClient = GetAnimationViewportClient()) { AnimViewportClient->SaveCameraAsDefault(); } } void SAnimationEditorViewportTabBody::ClearDefaultCamera() { if (const TSharedPtr& AnimViewportClient = GetAnimationViewportClient()) { AnimViewportClient->ClearDefaultCamera(); } } void SAnimationEditorViewportTabBody::JumpToDefaultCamera() { if (const TSharedPtr& AnimViewportClient = GetAnimationViewportClient()) { AnimViewportClient->JumpToDefaultCamera(); } } bool SAnimationEditorViewportTabBody::CanSaveCameraAsDefault() const { if (const TSharedPtr& AnimViewportClient = GetAnimationViewportClient()) { return AnimViewportClient->CanSaveCameraAsDefault(); } return false; } bool SAnimationEditorViewportTabBody::HasDefaultCameraSet() const { if (const TSharedPtr& AnimViewportClient = GetAnimationViewportClient()) { return (AnimViewportClient->HasDefaultCameraSet()); } return false; } bool SAnimationEditorViewportTabBody::CanChangeCameraMode() const { //Not allowed to change camera type when we are in an ortho camera return !LevelViewportClient->IsOrtho(); } void SAnimationEditorViewportTabBody::OnToggleMuteAudio() { if (const TSharedPtr& AnimViewportClient = GetAnimationViewportClient()) { AnimViewportClient->OnToggleMuteAudio(); } } bool SAnimationEditorViewportTabBody::IsAudioMuted() const { if (const TSharedPtr& AnimViewportClient = GetAnimationViewportClient()) { return AnimViewportClient->IsAudioMuted(); } return false; } void SAnimationEditorViewportTabBody::OnToggleUseAudioAttenuation() { if (const TSharedPtr& AnimViewportClient = GetAnimationViewportClient()) { AnimViewportClient->OnToggleUseAudioAttenuation(); } } bool SAnimationEditorViewportTabBody::IsAudioAttenuationEnabled() const { if (const TSharedPtr& AnimViewportClient = GetAnimationViewportClient()) { return AnimViewportClient->IsUsingAudioAttenuation(); } return false; } void SAnimationEditorViewportTabBody::SetProcessRootMotionMode(EProcessRootMotionMode Mode) { if (UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent()) { PreviewComponent->SetProcessRootMotionMode(Mode); } } bool SAnimationEditorViewportTabBody::IsProcessRootMotionModeSet(EProcessRootMotionMode Mode) const { const UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); return PreviewComponent ? (PreviewComponent->GetRequestedProcessRootMotionMode() == Mode) : false; } bool SAnimationEditorViewportTabBody::CanUseProcessRootMotionMode(EProcessRootMotionMode Mode) const { if(const UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent()) { return PreviewComponent->CanUseProcessRootMotionMode(Mode); } return false; } void SAnimationEditorViewportTabBody::SetVisualizeRootMotionMode(EVisualizeRootMotionMode Mode) { if(UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent()) { return PreviewComponent->SetVisualizeRootMotionMode(Mode); } } bool SAnimationEditorViewportTabBody::IsVisualizeRootMotionModeSet(EVisualizeRootMotionMode Mode) const { if(const UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent()) { return PreviewComponent->IsVisualizeRootMotionMode(Mode); } return false; } bool SAnimationEditorViewportTabBody::CanVisualizeRootMotion() const { if(const UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent()) { return PreviewComponent->DoesCurrentAssetHaveRootMotion(); } return false; } void SAnimationEditorViewportTabBody::ToggleNotificationVisualizations() { if (UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent()) { PreviewComponent->SetShowNotificationVisualizations(!PreviewComponent->IsNotificationVisualizationsEnabled()); } } bool SAnimationEditorViewportTabBody::IsNotificationVisualizationsEnabled() const { if (const UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent()) { return PreviewComponent->IsNotificationVisualizationsEnabled(); } return false; } void SAnimationEditorViewportTabBody::ToggleAssetUserDataVisualizations() { if (UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent()) { PreviewComponent->SetShowAssetUserDataVisualizations(!PreviewComponent->IsAssetUserDataVisualizationsEnabled()); } } bool SAnimationEditorViewportTabBody::IsAssetUserDataVisualizationsEnabled() const { if (const UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent()) { return PreviewComponent->IsAssetUserDataVisualizationsEnabled(); } return false; } bool SAnimationEditorViewportTabBody::IsClothSimulationEnabled() const { UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); if( PreviewComponent ) { return !PreviewComponent->bDisableClothSimulation; } return true; } void SAnimationEditorViewportTabBody::OnEnableClothSimulation() { UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); if( PreviewComponent ) { PreviewComponent->bDisableClothSimulation = !PreviewComponent->bDisableClothSimulation; RefreshViewport(); } } void SAnimationEditorViewportTabBody::OnResetClothSimulation() { UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); if( PreviewComponent ) { PreviewComponent->RecreateClothingActors(); RefreshViewport(); } } bool SAnimationEditorViewportTabBody::IsApplyingClothWind() const { return GetPreviewScene()->IsWindEnabled(); } void SAnimationEditorViewportTabBody::OnPauseClothingSimWithAnim() { UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); if(PreviewComponent) { PreviewComponent->bPauseClothingSimulationWithAnim = !PreviewComponent->bPauseClothingSimulationWithAnim; bool bShouldPause = PreviewComponent->bPauseClothingSimulationWithAnim; if(PreviewComponent->IsPreviewOn() && PreviewComponent->PreviewInstance) { UAnimSingleNodeInstance* PreviewInstance = PreviewComponent->PreviewInstance; const bool bPlaying = PreviewInstance->IsPlaying(); if(!bPlaying && bShouldPause) { PreviewComponent->SuspendClothingSimulation(); } else if(!bShouldPause && PreviewComponent->IsClothingSimulationSuspended()) { PreviewComponent->ResumeClothingSimulation(); } } } } bool SAnimationEditorViewportTabBody::IsPausingClothingSimWithAnim() { UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); if(PreviewComponent) { return PreviewComponent->bPauseClothingSimulationWithAnim; } return false; } void SAnimationEditorViewportTabBody::SetWindStrength(float SliderPos) { TSharedRef PreviewScene = GetPreviewScene(); if ( SliderPos <= 0.0f ) { if ( PreviewScene->IsWindEnabled() ) { PreviewScene->EnableWind(false); PreviewScene->SetWindStrength(0.0f); RefreshViewport(); } return; } if ( !PreviewScene->IsWindEnabled() ) { PreviewScene->EnableWind(true); } GetPreviewScene()->SetWindStrength(SliderPos); RefreshViewport(); } TOptional SAnimationEditorViewportTabBody::GetWindStrengthSliderValue() const { return GetPreviewScene()->GetWindStrength(); } void SAnimationEditorViewportTabBody::SetGravityScale(float SliderPos) { GetPreviewScene()->SetGravityScale(SliderPos); RefreshViewport(); } float SAnimationEditorViewportTabBody::GetGravityScaleSliderValue() const { return GetPreviewScene()->GetGravityScale(); } void SAnimationEditorViewportTabBody::OnEnableCollisionWithAttachedClothChildren() { UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); if( PreviewComponent ) { PreviewComponent->bCollideWithAttachedChildren = !PreviewComponent->bCollideWithAttachedChildren; RefreshViewport(); } } bool SAnimationEditorViewportTabBody::IsEnablingCollisionWithAttachedClothChildren() const { UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); if( PreviewComponent ) { return PreviewComponent->bCollideWithAttachedChildren; } return false; } void SAnimationEditorViewportTabBody::OnSetSectionsDisplayMode(ESectionDisplayMode DisplayMode) { UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); if (!PreviewComponent) { return; } SectionsDisplayMode = DisplayMode; switch (SectionsDisplayMode) { case ESectionDisplayMode::ShowAll: // restore to the original states PreviewComponent->RestoreClothSectionsVisibility(); break; case ESectionDisplayMode::ShowOnlyClothSections: // disable all except clothing sections and shows only cloth sections PreviewComponent->ToggleClothSectionsVisibility(true); break; case ESectionDisplayMode::HideOnlyClothSections: // disables only clothing sections PreviewComponent->ToggleClothSectionsVisibility(false); break; } RefreshViewport(); } bool SAnimationEditorViewportTabBody::IsSectionsDisplayMode(ESectionDisplayMode DisplayMode) const { return SectionsDisplayMode == DisplayMode; } void SAnimationEditorViewportTabBody::AddRecordingNotification() { if(WeakRecordingNotification.IsValid()) { return; } auto GetRecordingStateText = [this]() { if(GetPreviewScene()->IsRecording()) { UAnimSequence* Recording = GetPreviewScene()->GetCurrentRecording(); const FString& Name = Recording ? Recording->GetName() : TEXT("None"); float TimeRecorded = GetPreviewScene()->GetCurrentRecordingTime(); FNumberFormattingOptions NumberOption; NumberOption.MaximumFractionalDigits = 2; NumberOption.MinimumFractionalDigits = 2; return FText::Format(LOCTEXT("AnimRecorder", "Recording '{0}' {1} secs"), FText::FromString(Name), FText::AsNumber(TimeRecorded, &NumberOption)); } return FText::GetEmpty(); }; auto GetRecordingStateStateVisibility = [this]() { if (GetPreviewScene()->IsRecording()) { return EVisibility::Visible; } return EVisibility::Collapsed; }; auto StopRecording = [this]() { if (GetPreviewScene()->IsRecording()) { GetPreviewScene()->StopRecording(); } return FReply::Handled(); }; WeakRecordingNotification = AddNotification(EMessageSeverity::Info, true, SNew(SHorizontalBox) .Visibility_Lambda(GetRecordingStateStateVisibility) .ToolTipText(LOCTEXT("RecordingStatusTooltip", "Shows the status of animation recording.")) +SHorizontalBox::Slot() .FillWidth(1.0f) .Padding(2.0f, 4.0f) [ SNew(SHorizontalBox) +SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .Padding(0.0f, 0.0f, 4.0f, 0.0f) [ SNew(STextBlock) .TextStyle(FAppStyle::Get(), "AnimViewport.MessageText") .Font(FAppStyle::Get().GetFontStyle("FontAwesome.9")) .Text(FEditorFontGlyphs::Video_Camera) ] +SHorizontalBox::Slot() .VAlign(VAlign_Center) .FillWidth(1.0f) [ SNew(STextBlock) .Text_Lambda(GetRecordingStateText) .TextStyle(FAppStyle::Get(), "AnimViewport.MessageText") ] ] +SHorizontalBox::Slot() .AutoWidth() .Padding(2.0f, 0.0f) [ SNew(SButton) .ForegroundColor(FSlateColor::UseForeground()) .ButtonStyle(FAppStyle::Get(), "FlatButton.Success") .ToolTipText(LOCTEXT("RecordingInViewportStop", "Stop recording animation.")) .OnClicked_Lambda(StopRecording) [ SNew(SHorizontalBox) +SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .Padding(0.0f, 0.0f, 4.0f, 0.0f) [ SNew(STextBlock) .TextStyle(FAppStyle::Get(), "AnimViewport.MessageText") .Font(FAppStyle::Get().GetFontStyle("FontAwesome.9")) .Text(FEditorFontGlyphs::Stop) ] +SHorizontalBox::Slot() .VAlign(VAlign_Center) .AutoWidth() [ SNew(STextBlock) .TextStyle(FAppStyle::Get(), "AnimViewport.MessageText") .Text(LOCTEXT("AnimViewportStopRecordingButtonLabel", "Stop")) ] ] ], FPersonaViewportNotificationOptions(TAttribute::Create(GetRecordingStateStateVisibility)) ); } void SAnimationEditorViewportTabBody::AddPostProcessNotification() { if(WeakPostProcessNotification.IsValid()) { return; } auto GetVisibility = [this]() { return CanDisablePostProcess() ? EVisibility::Visible : EVisibility::Collapsed; }; auto GetPostProcessGraphName = [this]() { if (UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent()) { if(PreviewComponent->GetSkeletalMeshAsset() && PreviewComponent->GetSkeletalMeshAsset()->GetPostProcessAnimBlueprint() && PreviewComponent->GetSkeletalMeshAsset()->GetPostProcessAnimBlueprint()->ClassGeneratedBy) { return FText::FromString(PreviewComponent->GetSkeletalMeshAsset()->GetPostProcessAnimBlueprint()->ClassGeneratedBy->GetName()); } } return FText::GetEmpty(); }; auto DoesPostProcessModifyCurves = [this]() { if (UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent()) { return (PreviewComponent->PostProcessAnimInstance && PreviewComponent->PostProcessAnimInstance->HasActiveCurves()); } return false; }; auto GetText = [this, GetPostProcessGraphName, DoesPostProcessModifyCurves]() { return IsDisablePostProcessChecked() ? FText::Format(LOCTEXT("PostProcessDisabledText", "Post process Animation Blueprint '{0}' is disabled."), GetPostProcessGraphName()) : FText::Format(LOCTEXT("PostProcessRunningText", "Post process Animation Blueprint '{0}' is running. {1}"), GetPostProcessGraphName(), DoesPostProcessModifyCurves() ? LOCTEXT("PostProcessModifiesCurves", "Post process modifes curves.") : FText::GetEmpty()) ; }; auto GetButtonText = [this]() { return IsDisablePostProcessChecked() ? LOCTEXT("PostProcessEnableText", "Enable") : LOCTEXT("PostProcessDisableText", "Disable"); }; auto GetButtonTooltipText = [this]() { return IsDisablePostProcessChecked() ? LOCTEXT("PostProcessEnableTooltip", "Enable post process animation blueprint.") : LOCTEXT("PostProcessDisableTooltip", "Disable post process animation blueprint."); }; auto GetButtonIcon = [this]() { return IsDisablePostProcessChecked() ? FEditorFontGlyphs::Check : FEditorFontGlyphs::Times; }; auto EnablePostProcess = [this]() { OnToggleDisablePostProcess(); return FReply::Handled(); }; auto EditPostProcess = [this]() { if (UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent()) { if(PreviewComponent->GetSkeletalMeshAsset() && !PreviewComponent->GetSkeletalMeshAsset()->IsCompiling() && PreviewComponent->GetSkeletalMeshAsset()->GetPostProcessAnimBlueprint()) { GEditor->GetEditorSubsystem()->OpenEditorForAssets(TArray({ PreviewComponent->GetSkeletalMeshAsset()->GetPostProcessAnimBlueprint()->ClassGeneratedBy })); } } return FReply::Handled(); }; WeakPostProcessNotification = AddNotification(EMessageSeverity::Warning, true, SNew(SHorizontalBox) .Visibility_Lambda(GetVisibility) +SHorizontalBox::Slot() .FillWidth(1.0f) .Padding(4.0f, 4.0f) [ SNew(SHorizontalBox) .ToolTipText_Lambda(GetText) +SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .Padding(0.0f, 0.0f, 4.0f, 0.0f) [ SNew(STextBlock) .TextStyle(FAppStyle::Get(), "AnimViewport.MessageText") .Font(FAppStyle::Get().GetFontStyle("FontAwesome.9")) .Text(FEditorFontGlyphs::Exclamation_Triangle) ] +SHorizontalBox::Slot() .VAlign(VAlign_Center) .FillWidth(1.0f) [ SNew(STextBlock) .Text_Lambda(GetText) .TextStyle(FAppStyle::Get(), "AnimViewport.MessageText") ] ] +SHorizontalBox::Slot() .AutoWidth() .Padding(2.0f, 0.0f) [ SNew(SButton) .ForegroundColor(FSlateColor::UseForeground()) .ButtonStyle(FAppStyle::Get(), "FlatButton.Success") .ToolTipText_Lambda(GetButtonTooltipText) .OnClicked_Lambda(EnablePostProcess) [ SNew(SHorizontalBox) +SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .Padding(0.0f, 0.0f, 4.0f, 0.0f) [ SNew(STextBlock) .TextStyle(FAppStyle::Get(), "AnimViewport.MessageText") .Font(FAppStyle::Get().GetFontStyle("FontAwesome.9")) .Text_Lambda(GetButtonIcon) ] +SHorizontalBox::Slot() .VAlign(VAlign_Center) .AutoWidth() [ SNew(STextBlock) .TextStyle(FAppStyle::Get(), "AnimViewport.MessageText") .Text_Lambda(GetButtonText) ] ] ] +SHorizontalBox::Slot() .AutoWidth() .Padding(2.0f, 0.0f) [ SNew(SButton) .ForegroundColor(FSlateColor::UseForeground()) .ButtonStyle(FAppStyle::Get(), "FlatButton") .ToolTipText(LOCTEXT("EditPostProcessAnimBPButtonToolTip", "Edit the post process Animation Blueprint.")) .OnClicked_Lambda(EditPostProcess) [ SNew(SHorizontalBox) +SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .Padding(0.0f, 0.0f, 4.0f, 0.0f) [ SNew(STextBlock) .TextStyle(FAppStyle::Get(), "AnimViewport.MessageText") .Font(FAppStyle::Get().GetFontStyle("FontAwesome.9")) .Text(FEditorFontGlyphs::Pencil) ] +SHorizontalBox::Slot() .VAlign(VAlign_Center) .AutoWidth() [ SNew(STextBlock) .TextStyle(FAppStyle::Get(), "AnimViewport.MessageText") .Text(LOCTEXT("EditPostProcessAnimBPButtonText", "Edit")) ] ] ], FPersonaViewportNotificationOptions(TAttribute::Create(GetVisibility)) ); } void SAnimationEditorViewportTabBody::AddMinLODNotification() { if(WeakMinLODNotification.IsValid()) { return; } auto GetMinLODNotificationVisibility = [this]() { if (GetPreviewScene()->GetPreviewMesh() && !GetPreviewScene()->GetPreviewMesh()->IsCompiling() && GetPreviewScene()->GetPreviewMesh()->GetDefaultMinLod() != 0) { return EVisibility::Visible; } return EVisibility::Collapsed; }; WeakMinLODNotification = AddNotification(EMessageSeverity::Info, true, SNew(SHorizontalBox) .Visibility_Lambda(GetMinLODNotificationVisibility) .ToolTipText(LOCTEXT("MinLODNotificationTooltip", "This asset has a minimum LOD applied.")) +SHorizontalBox::Slot() .FillWidth(1.0f) .Padding(2.0f, 4.0f) [ SNew(SHorizontalBox) +SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .Padding(0.0f, 0.0f, 4.0f, 0.0f) [ SNew(STextBlock) .TextStyle(FAppStyle::Get(), "AnimViewport.MessageText") .Font(FAppStyle::Get().GetFontStyle("FontAwesome.9")) .Text(FEditorFontGlyphs::Level_Down) ] +SHorizontalBox::Slot() .VAlign(VAlign_Center) .FillWidth(1.0f) [ SNew(STextBlock) .Text(LOCTEXT("MinLODNotification", "Min LOD applied")) .TextStyle(FAppStyle::Get(), "AnimViewport.MessageText") ] ], FPersonaViewportNotificationOptions(TAttribute::Create(GetMinLODNotificationVisibility)) ); } void SAnimationEditorViewportTabBody::AddSkinWeightProfileNotification() { if(WeakSkinWeightPreviewNotification.IsValid()) { return; } auto GetSkinWeightProfileNotificationVisibility = [this]() { if (GetPreviewScene()->GetPreviewMeshComponent() && GetPreviewScene()->GetPreviewMeshComponent()->IsUsingSkinWeightProfile()) { return EVisibility::Visible; } return EVisibility::Collapsed; }; auto GetSkinWeightProfileNotificationText = [this]() -> FText { FName ProfileName = NAME_None; if (GetPreviewScene()->GetPreviewMeshComponent()) { ProfileName = GetPreviewScene()->GetPreviewMeshComponent()->GetCurrentSkinWeightProfileName(); } return FText::FormatOrdered(LOCTEXT("ProfileSkinWeightPreviewNotification", "Previewing Skin Weight Profile: {0}"), FText::FromName(ProfileName)); }; WeakSkinWeightPreviewNotification = AddNotification(EMessageSeverity::Info, false, SNew(SHorizontalBox) .Visibility_Lambda(GetSkinWeightProfileNotificationVisibility) .ToolTipText(LOCTEXT("ProfileSkinWeightPreviewTooltip", "Previewing a Skin Weight Profile.")) +SHorizontalBox::Slot() .FillWidth(1.0f) .Padding(2.0f, 4.0f) [ SNew(SHorizontalBox) +SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .Padding(0.0f, 0.0f, 4.0f, 0.0f) [ SNew(STextBlock) .TextStyle(FAppStyle::Get(), "AnimViewport.MessageText") .Font(FAppStyle::Get().GetFontStyle("FontAwesome.9")) .Text(FEditorFontGlyphs::Eye) ] +SHorizontalBox::Slot() .VAlign(VAlign_Center) .FillWidth(1.0f) [ SNew(STextBlock) .Text_Lambda(GetSkinWeightProfileNotificationText) .TextStyle(FAppStyle::Get(), "AnimViewport.MessageText") ] ], FPersonaViewportNotificationOptions(TAttribute::Create(GetSkinWeightProfileNotificationVisibility)) ); } void SAnimationEditorViewportTabBody::HandleFocusCamera() { if (const TSharedPtr& AnimViewportClient = GetAnimationViewportClient()) { AnimViewportClient->FocusViewportOnPreviewMesh(false); } } #undef LOCTEXT_NAMESPACE