// Copyright Epic Games, Inc. All Rights Reserved. #include "SAnimViewportToolBar.h" #include "AnimViewportToolBarToolMenuContext.h" #include "AnimPreviewInstance.h" #include "Widgets/Layout/SBorder.h" #include "Widgets/Text/STextBlock.h" #include "Framework/MultiBox/MultiBoxBuilder.h" #include "ToolMenus.h" #include "EngineGlobals.h" #include "AssetRegistry/AssetData.h" #include "Engine/Engine.h" #include "Styling/AppStyle.h" #include "PropertyEditorModule.h" #include "IDetailsView.h" #include "Framework/Application/SlateApplication.h" #include "Editor/EditorPerProjectUserSettings.h" #include "Preferences/PersonaOptions.h" #include "EditorViewportCommands.h" #include "AnimViewportMenuCommands.h" #include "AnimViewportShowCommands.h" #include "AnimViewportPlaybackCommands.h" #include "SEditorViewportToolBarMenu.h" #include "STransformViewportToolbar.h" #include "SEditorViewportViewMenu.h" #include "Widgets/Input/SNumericEntryBox.h" #include "Widgets/Input/STextComboBox.h" #include "AssetViewerSettings.h" #include "PreviewSceneCustomizations.h" #include "SimulationEditorExtender.h" #include "ClothingSimulationFactory.h" #include "ClothingSystemEditorInterfaceModule.h" #include "Widgets/SWidget.h" #include "Types/ISlateMetaData.h" #include "Textures/SlateIcon.h" #include "BufferVisualizationMenuCommands.h" #include "IPinnedCommandList.h" #include "UICommandList_Pinnable.h" #include "ContentBrowserModule.h" #include "IContentBrowserSingleton.h" #include "SCommonEditorViewportToolbarBase.h" #include "Animation/MirrorDataTable.h" #include "ScopedTransaction.h" #include "ViewportToolbar/AnimationEditorMenus.h" #include "ViewportToolbar/AnimationEditorWidgets.h" #include "ViewportToolbar/UnrealEdViewportToolbar.h" #define LOCTEXT_NAMESPACE "AnimViewportToolBar" /////////////////////////////////////////////////////////// // SAnimViewportToolBar void SAnimViewportToolBar::Construct( const FArguments& InArgs, TSharedPtr InViewport, TSharedPtr InRealViewport ) { bShowShowMenu = InArgs._ShowShowMenu; bShowCharacterMenu = InArgs._ShowCharacterMenu; bShowLODMenu = InArgs._ShowLODMenu; bShowPlaySpeedMenu = InArgs._ShowPlaySpeedMenu; bShowFloorOptions = InArgs._ShowFloorOptions; bShowTurnTable = InArgs._ShowTurnTable; bShowPhysicsMenu = InArgs._ShowPhysicsMenu; if (!InRealViewport) { return; } CommandList = InRealViewport->GetCommandList(); Extenders = InArgs._Extenders; Extenders.Add(GetViewMenuExtender(InRealViewport)); // If we have no extender, make an empty one if (Extenders.Num() == 0) { Extenders.Add(MakeShared()); } const FMargin ToolbarSlotPadding(4.0f, 1.0f); const FMargin ToolbarButtonPadding(4.0f, 1.0f); // clang-format off TSharedRef LeftToolbar = SNew(SHorizontalBox) + SHorizontalBox::Slot() .AutoWidth() .Padding(ToolbarSlotPadding) [ SNew(SEditorViewportToolbarMenu) .ToolTipText(LOCTEXT("ViewMenuTooltip", "View Options.\nShift-clicking items will 'pin' them to the toolbar.")) .ParentToolBar(SharedThis(this)) .Cursor(EMouseCursor::Default) .Image("EditorViewportToolBar.OptionsDropdown") .AddMetaData(FTagMetaData(TEXT("EditorViewportToolBar.MenuDropdown"))) .OnGetMenuContent(this, &SAnimViewportToolBar::GenerateViewMenu) ] + SHorizontalBox::Slot() .AutoWidth() .Padding(ToolbarSlotPadding) [ SNew(SEditorViewportToolbarMenu) .ToolTipText(LOCTEXT("ViewportMenuTooltip", "Viewport Options. Use this to switch between different orthographic or perspective views.")) .ParentToolBar(SharedThis(this)) .Cursor(EMouseCursor::Default) .Label(this, &SAnimViewportToolBar::GetCameraMenuLabel) .LabelIcon(this, &SAnimViewportToolBar::GetCameraMenuLabelIcon) .AddMetaData(FTagMetaData(TEXT("EditorViewportToolBar.CameraMenu"))) .OnGetMenuContent(this, &SAnimViewportToolBar::GenerateViewportTypeMenu) ] // View menu (lit, unlit, etc...) + SHorizontalBox::Slot() .AutoWidth() .Padding(ToolbarSlotPadding) [ SNew(SEditorViewportViewMenu, InRealViewport.ToSharedRef(), SharedThis(this)) .ToolTipText(LOCTEXT("ViewModeMenuTooltip", "View Mode Options. Use this to change how the view is rendered, e.g. Lit/Unlit.")) .MenuExtenders(FExtender::Combine(Extenders)) ] + SHorizontalBox::Slot() .AutoWidth() .Padding(ToolbarSlotPadding) [ SNew(SEditorViewportToolbarMenu) .ToolTipText(LOCTEXT("ShowMenuTooltip", "Show Options. Use this enable/disable the rendering of types of scene elements.")) .ParentToolBar(SharedThis(this)) .Cursor(EMouseCursor::Default) .Label(LOCTEXT("ShowMenu", "Show")) .AddMetaData(FTagMetaData(TEXT("ViewMenuButton"))) .OnGetMenuContent(this, &SAnimViewportToolBar::GenerateShowMenu) ] + SHorizontalBox::Slot() .AutoWidth() .Padding(ToolbarSlotPadding) [ SNew(SPreviewSceneProfileSelector) .PreviewProfileController(InRealViewport->GetPreviewProfileController()) .Visibility_Lambda( [ViewportWidgetWeak = InRealViewport.ToWeakPtr()]() { if (TSharedPtr ViewportWidget = ViewportWidgetWeak.Pin()) { // only show this menu if the user has customized it by adding their own profiles // this behavior was requested by UX to match the behavior of the static mesh editor if (TSharedPtr PreviewProfileController = ViewportWidget->GetPreviewProfileController()) { return PreviewProfileController->HasAnyUserProfiles() ? EVisibility::Visible : EVisibility::Collapsed; } } return EVisibility::Hidden; }) ] + SHorizontalBox::Slot() .AutoWidth() .Padding(ToolbarSlotPadding) [ SNew(SEditorViewportToolbarMenu) .ToolTipText(LOCTEXT("PhysicsMenuTooltip", "Physics Options. Use this to control the physics of the scene.")) .ParentToolBar(SharedThis(this)) .Label(LOCTEXT("Physics", "Physics")) .OnGetMenuContent(this, &SAnimViewportToolBar::GeneratePhysicsMenu) .Visibility(bShowPhysicsMenu ? EVisibility::Visible : EVisibility::Collapsed) ] + SHorizontalBox::Slot() .AutoWidth() .Padding(ToolbarSlotPadding) [ SNew(SEditorViewportToolbarMenu) .ToolTipText(LOCTEXT("CharacterMenuTooltip", "Character Options. Control character-related rendering options.\nShift-clicking items will 'pin' them to the toolbar.")) .ParentToolBar(SharedThis(this)) .Label(LOCTEXT("Character", "Character")) .OnGetMenuContent(this, &SAnimViewportToolBar::GenerateCharacterMenu) .Visibility(bShowCharacterMenu ? EVisibility::Visible : EVisibility::Collapsed) ] + SHorizontalBox::Slot() .AutoWidth() .Padding(ToolbarSlotPadding) [ //LOD SNew(SEditorViewportToolbarMenu) .ToolTipText(LOCTEXT("LODMenuTooltip", "LOD Options. Control how LODs are displayed.\nShift-clicking items will 'pin' them to the toolbar.")) .ParentToolBar(SharedThis(this)) .Label(this, &SAnimViewportToolBar::GetLODMenuLabel) .OnGetMenuContent(this, &SAnimViewportToolBar::GenerateLODMenu) ] + SHorizontalBox::Slot() .AutoWidth() .Padding(ToolbarSlotPadding) [ SNew(SEditorViewportToolbarMenu) .ToolTipText(LOCTEXT("PlaybackSpeedMenuTooltip", "Playback Speed Options. Control the time dilation of the scene's update.\nShift-clicking items will 'pin' them to the toolbar.")) .ParentToolBar(SharedThis(this)) .Label(this, &SAnimViewportToolBar::GetPlaybackMenuLabel) .LabelIcon(FAppStyle::GetBrush("AnimViewportMenu.PlayBackSpeed")) .OnGetMenuContent(this, &SAnimViewportToolBar::GeneratePlaybackMenu) ] + SHorizontalBox::Slot() .Padding(ToolbarSlotPadding) .HAlign(HAlign_Right) [ SNew(STransformViewportToolBar) .Viewport(InRealViewport) .CommandList(InRealViewport->GetCommandList()) .Visibility(this, &SAnimViewportToolBar::GetTransformToolbarVisibility) .OnCamSpeedChanged(this, &SAnimViewportToolBar::OnCamSpeedChanged) .OnCamSpeedScalarChanged(this, &SAnimViewportToolBar::OnCamSpeedScalarChanged) ]; // clang-format on TWeakPtr AnimViewportToolbarWeak = SharedThis(this); UE::UnrealEd::OnViewportClientCamSpeedChanged().BindLambda( [AnimViewportToolbarWeak, EditorViewportWeak = InRealViewport.ToWeakPtr()](const TSharedRef& InEditorViewport, int32 InNewValue) { TSharedPtr AnimViewportToolbar = AnimViewportToolbarWeak.Pin(); if (!AnimViewportToolbar) { return; } if (TSharedPtr EditorViewport = EditorViewportWeak.Pin()) { if (EditorViewport == InEditorViewport) { AnimViewportToolbar->OnCamSpeedChanged(InNewValue); } } } ); UE::UnrealEd::OnViewportClientCamSpeedScalarChanged().BindLambda( [AnimViewportToolbarWeak, EditorViewportWeak = InRealViewport.ToWeakPtr()](const TSharedRef& InEditorViewport, float InNewValue) { TSharedPtr AnimViewportToolbar = AnimViewportToolbarWeak.Pin(); if (!AnimViewportToolbar) { return; } if (TSharedPtr EditorViewport = EditorViewportWeak.Pin()) { if (EditorViewport == InEditorViewport) { AnimViewportToolbar->OnCamSpeedScalarChanged(InNewValue); } } } ); //@TODO: Need clipping horizontal box: LeftToolbar->AddWrapButton(); TSharedPtr PinnedCommands = InViewport->GetPinnedCommands(); // clang-format off ChildSlot [ SNew(SVerticalBox) + SVerticalBox::Slot() .AutoHeight() [ SNew(SBorder) .BorderImage(FAppStyle::Get().GetBrush("EditorViewportToolBar.Background")) .Cursor(EMouseCursor::Default) [ LeftToolbar ] ] ]; // clang-format on SViewportToolBar::Construct(SViewportToolBar::FArguments()); if (PinnedCommands) { // Register all the custom widgets we can use here PinnedCommands->RegisterCustomWidget( IPinnedCommandList::FOnGenerateCustomWidget::CreateSP(this, &SAnimViewportToolBar::MakeFloorOffsetWidget), TEXT("FloorOffsetWidget"), LOCTEXT("FloorHeightOffset", "Floor Height Offset") ); PinnedCommands->RegisterCustomWidget( IPinnedCommandList::FOnGenerateCustomWidget::CreateSP(this, &SAnimViewportToolBar::MakeFOVWidget), TEXT("FOVWidget"), LOCTEXT("Viewport_FOVLabel", "Field Of View") ); } // We assign the viewport pointer here rather than initially, as SViewportToolbar::Construct // ends up calling through and attempting to perform operations on the not-yet-full-constructed viewport Viewport = InViewport; } EVisibility SAnimViewportToolBar::GetTransformToolbarVisibility() const { return Viewport.Pin()->CanUseGizmos() ? EVisibility::Visible : EVisibility::Hidden; } TSharedRef SAnimViewportToolBar::MakeFloorOffsetWidget() const { return UE::AnimationEditor::MakeFloorOffsetWidget(Viewport); } TSharedRef SAnimViewportToolBar::MakeFOVWidget() const { const float FOVMin = 5.f; const float FOVMax = 170.f; return SNew(SBox) .HAlign(HAlign_Right) [ SNew(SBox) .Padding(FMargin(4.0f, 0.0f, 0.0f, 0.0f)) .WidthOverride(100.0f) [ SNew(SNumericEntryBox) .Font(FAppStyle::GetFontStyle(TEXT("MenuItem.Font"))) .AllowSpin(true) .MinValue(FOVMin) .MaxValue(FOVMax) .MinSliderValue(FOVMin) .MaxSliderValue(FOVMax) .Value(this, &SAnimViewportToolBar::OnGetFOVValue) .OnValueChanged(const_cast(this), &SAnimViewportToolBar::OnFOVValueChanged) .OnValueCommitted(const_cast(this), &SAnimViewportToolBar::OnFOVValueCommitted) ] ]; } TSharedRef SAnimViewportToolBar::MakeFollowBoneComboWidget() const { TSharedRef ComboButton = SNew(SComboButton) .ComboButtonStyle(FAppStyle::Get(), "ViewportPinnedCommandList.ComboButton") .ContentPadding(0.0f) .ButtonContent() [ SNew(STextBlock) .TextStyle(FAppStyle::Get(), "ViewportPinnedCommandList.Label") .Text_Lambda([this]() { const FName BoneName = Viewport.Pin()->GetCameraFollowBoneName(); if(BoneName != NAME_None) { return FText::Format(LOCTEXT("FollowingBoneMenuTitleFormat", "Following Bone: {0}"), FText::FromName(BoneName)); } else { return LOCTEXT("FollowBoneMenuTitle", "Focus On Bone"); } }) ]; TWeakPtr WeakComboButton = ComboButton; ComboButton->SetOnGetMenuContent(FOnGetContent::CreateLambda( [ViewportWeak = Viewport, WeakComboButton]() { return UE::AnimationEditor::MakeFollowBoneWidget(ViewportWeak, WeakComboButton); } )); return ComboButton; } TSharedRef SAnimViewportToolBar::MakeFollowBoneWidget() const { return UE::AnimationEditor::MakeFollowBoneWidget(Viewport, nullptr); } TSharedRef SAnimViewportToolBar::GenerateViewMenu() const { const FAnimViewportMenuCommands& Actions = FAnimViewportMenuCommands::Get(); TSharedPtr MenuExtender = FExtender::Combine(Extenders); const bool bInShouldCloseWindowAfterMenuSelection = true; FMenuBuilder InMenuBuilder(bInShouldCloseWindowAfterMenuSelection, Viewport.Pin()->GetCommandList(), MenuExtender); InMenuBuilder.PushCommandList(Viewport.Pin()->GetCommandList().ToSharedRef()); InMenuBuilder.PushExtender(MenuExtender.ToSharedRef()); InMenuBuilder.BeginSection("AnimViewportSceneSetup", LOCTEXT("ViewMenu_SceneSetupLabel", "Scene Setup")); { InMenuBuilder.PushCommandList(Viewport.Pin()->GetCommandList().ToSharedRef()); InMenuBuilder.AddMenuEntry(FAnimViewportMenuCommands::Get().PreviewSceneSettings); InMenuBuilder.PopCommandList(); if(bShowFloorOptions) { InMenuBuilder.AddWidget(MakeFloorOffsetWidget(), LOCTEXT("FloorHeightOffset", "Floor Height Offset")); InMenuBuilder.PushCommandList(Viewport.Pin()->GetCommandList().ToSharedRef()); InMenuBuilder.AddMenuEntry(FAnimViewportShowCommands::Get().AutoAlignFloorToMesh); InMenuBuilder.PopCommandList(); } if (bShowTurnTable) { InMenuBuilder.AddSubMenu( LOCTEXT("TurnTableLabel", "Turn Table"), LOCTEXT("TurnTableTooltip", "Set up auto-rotation of preview."), FNewMenuDelegate::CreateRaw(this, &SAnimViewportToolBar::GenerateTurnTableMenu), false, FSlateIcon(FAppStyle::GetAppStyleSetName(), "AnimViewportMenu.TurnTableSpeed") ); } } InMenuBuilder.EndSection(); InMenuBuilder.BeginSection("AnimViewportCamera", LOCTEXT("ViewMenu_CameraLabel", "Camera")); { InMenuBuilder.AddMenuEntry(FEditorViewportCommands::Get().FocusViewportToSelection); InMenuBuilder.AddWidget(MakeFOVWidget(), LOCTEXT("Viewport_FOVLabel", "Field Of View")); InMenuBuilder.AddMenuEntry(FAnimViewportMenuCommands::Get().TogglePauseAnimationOnCameraMove); InMenuBuilder.AddSubMenu( LOCTEXT("CameraFollowModeLabel", "Follow Mode"), LOCTEXT("CameraFollowModeTooltip", "Set various camera follow modes"), FNewMenuDelegate::CreateLambda([ViewportWeak = Viewport](FMenuBuilder& InSubMenuBuilder) { InSubMenuBuilder.BeginSection(NAME_None, FText()); { InSubMenuBuilder.AddWidget(UE::AnimationEditor::CreateFollowModeMenuWidget(ViewportWeak), FText()); } InSubMenuBuilder.EndSection(); }), false, FSlateIcon(FAppStyle::GetAppStyleSetName(), "AnimViewportMenu.CameraFollow") ); } InMenuBuilder.EndSection(); InMenuBuilder.BeginSection("AnimViewportDefaultCamera", LOCTEXT("ViewMenu_DefaultCameraLabel", "Default Camera")); { InMenuBuilder.PushCommandList(Viewport.Pin()->GetCommandList().ToSharedRef()); InMenuBuilder.AddMenuEntry(FAnimViewportMenuCommands::Get().JumpToDefaultCamera); InMenuBuilder.AddMenuEntry(FAnimViewportMenuCommands::Get().SaveCameraAsDefault); InMenuBuilder.AddMenuEntry(FAnimViewportMenuCommands::Get().ClearDefaultCamera); InMenuBuilder.PopCommandList(); } InMenuBuilder.EndSection(); InMenuBuilder.PopCommandList(); InMenuBuilder.PopExtender(); return InMenuBuilder.MakeWidget(); } TSharedRef SAnimViewportToolBar::GeneratePhysicsMenu() const { return UE::AnimationEditor::GeneratePhysicsMenuWidget(Viewport, FExtender::Combine(Extenders)); } TSharedRef SAnimViewportToolBar::GenerateCharacterMenu() const { static const FName MenuName("Persona.AnimViewportCharacterMenu"); if (!UToolMenus::Get()->IsMenuRegistered(MenuName)) { UToolMenu* Menu = UToolMenus::Get()->RegisterMenu(MenuName); { FToolMenuSection& Section = Menu->AddSection("AnimViewportSceneElements", LOCTEXT("CharacterMenu_SceneElements", "Scene Elements")); Section.AddSubMenu(TEXT("MeshSubMenu"), LOCTEXT("CharacterMenu_MeshSubMenu", "Mesh"), LOCTEXT("CharacterMenu_MeshSubMenuToolTip", "Mesh-related options"), FNewToolMenuDelegate::CreateLambda([](UToolMenu* InSubMenu) { { FToolMenuSection& Section = InSubMenu->AddSection("AnimViewportMesh", LOCTEXT("CharacterMenu_Actions_Mesh", "Mesh")); Section.AddMenuEntry(FAnimViewportShowCommands::Get().ShowBound); Section.AddMenuEntry(FAnimViewportShowCommands::Get().UseInGameBound); Section.AddMenuEntry(FAnimViewportShowCommands::Get().UseFixedBounds); Section.AddMenuEntry(FAnimViewportShowCommands::Get().UsePreSkinnedBounds); Section.AddMenuEntry(FAnimViewportShowCommands::Get().ShowPreviewMesh); Section.AddMenuEntry(FAnimViewportShowCommands::Get().ShowMorphTargets); } { FToolMenuSection& Section = InSubMenu->AddSection("AnimViewportMeshInfo", LOCTEXT("CharacterMenu_Actions_MeshInfo", "Mesh Info")); Section.AddMenuEntry(FAnimViewportShowCommands::Get().ShowDisplayInfoBasic); Section.AddMenuEntry(FAnimViewportShowCommands::Get().ShowDisplayInfoDetailed); Section.AddMenuEntry(FAnimViewportShowCommands::Get().ShowDisplayInfoSkelControls); Section.AddMenuEntry(FAnimViewportShowCommands::Get().HideDisplayInfo); } { FToolMenuSection& Section = InSubMenu->AddSection("AnimViewportPreviewOverlayDraw", LOCTEXT("CharacterMenu_Actions_Overlay", "Mesh Overlay Drawing")); Section.AddMenuEntry(FAnimViewportShowCommands::Get().ShowOverlayNone); Section.AddMenuEntry(FAnimViewportShowCommands::Get().ShowBoneWeight); Section.AddMenuEntry(FAnimViewportShowCommands::Get().ShowMorphTargetVerts); } }) ); Section.AddSubMenu(TEXT("AnimationSubMenu"), LOCTEXT("CharacterMenu_AnimationSubMenu", "Animation"), LOCTEXT("CharacterMenu_AnimationSubMenuToolTip", "Animation-related options"), FNewToolMenuDelegate::CreateLambda([](UToolMenu* InSubMenu) { UAnimViewportToolBarToolMenuContext* Context = InSubMenu->FindContext(); const SAnimViewportToolBar* ContextThis = Context ? Context->AnimViewportToolBar.Pin().Get() : nullptr; { FToolMenuSection& Section = InSubMenu->AddSection("AnimViewportRootMotion", LOCTEXT("CharacterMenu_RootMotionLabel", "Root Motion")); Section.AddMenuEntry(FAnimViewportShowCommands::Get().DoNotProcessRootMotion); Section.AddMenuEntry(FAnimViewportShowCommands::Get().ProcessRootMotionLoop); Section.AddMenuEntry(FAnimViewportShowCommands::Get().ProcessRootMotionLoopAndReset); } { FToolMenuSection& Section = InSubMenu->AddSection("AnimViewportVisualization", LOCTEXT("CharacterMenu_VisualizationsLabel", "Visualizations")); Section.AddMenuEntry(FAnimViewportShowCommands::Get().ShowNotificationVisualizations); Section.AddMenuEntry(FAnimViewportShowCommands::Get().DoNotVisualizeRootMotion); Section.AddMenuEntry(FAnimViewportShowCommands::Get().VisualizeRootMotionTrajectory); Section.AddMenuEntry(FAnimViewportShowCommands::Get().VisualizeRootMotionTrajectoryAndOrientation); Section.AddMenuEntry(FAnimViewportShowCommands::Get().ShowAssetUserDataVisualizations); } { FToolMenuSection& Section = InSubMenu->AddSection("AnimViewportAnimation", LOCTEXT("CharacterMenu_Actions_AnimationAsset", "Animation")); Section.AddMenuEntry(FAnimViewportShowCommands::Get().ShowRawAnimation); Section.AddMenuEntry(FAnimViewportShowCommands::Get().ShowNonRetargetedAnimation); Section.AddMenuEntry(FAnimViewportShowCommands::Get().ShowAdditiveBaseBones); Section.AddMenuEntry(FAnimViewportShowCommands::Get().ShowSourceRawAnimation); if (ContextThis && ContextThis->Viewport.IsValid()) { if ( UDebugSkelMeshComponent* PreviewComponent = ContextThis->Viewport.Pin()->GetPreviewScene()->GetPreviewMeshComponent()) { FUIAction DisableUnlessPreviewInstance( FExecuteAction::CreateLambda([](){}), FCanExecuteAction::CreateLambda([PreviewComponent]() { return (PreviewComponent->PreviewInstance && (PreviewComponent->PreviewInstance == PreviewComponent->GetAnimInstance() ) ); }) ); Section.AddSubMenu(TEXT("MirrorSubMenu"), LOCTEXT("CharacterMenu_AnimationSubMenu_MirrorSubMenu", "Mirror"), LOCTEXT("CharacterMenu_AnimationSubMenu_MirrorSubMenuToolTip", "Mirror the animation using the selected mirror data table"), FNewToolMenuChoice(FNewMenuDelegate::CreateRaw(ContextThis, &SAnimViewportToolBar::FillCharacterMirrorMenu)), FToolUIActionChoice(DisableUnlessPreviewInstance), EUserInterfaceActionType::Button, false, FSlateIcon(), false); } } Section.AddMenuEntry(FAnimViewportShowCommands::Get().ShowBakedAnimation); Section.AddMenuEntry(FAnimViewportShowCommands::Get().DisablePostProcessBlueprint); } if (ContextThis && ContextThis->Viewport.IsValid()) { FToolMenuSection& Section = InSubMenu->AddSection("SkinWeights", LOCTEXT("SkinWeights_Label", "Skin Weight Profiles")); Section.AddEntry(FToolMenuEntry::InitWidget(TEXT("SkinWeightCombo"), ContextThis->Viewport.Pin()->SkinWeightCombo.ToSharedRef(), FText())); } }) ); Section.AddSubMenu(TEXT("BonesSubMenu"), LOCTEXT("CharacterMenu_BoneDrawSubMenu", "Bones"), LOCTEXT("CharacterMenu_BoneDrawSubMenuToolTip", "Bone Drawing Options"), FNewToolMenuDelegate::CreateLambda([](UToolMenu* InSubMenu) { UAnimViewportToolBarToolMenuContext* Context = InSubMenu->FindContext(); const SAnimViewportToolBar* ContextThis = Context ? Context->AnimViewportToolBar.Pin().Get() : nullptr; { FToolMenuSection& Section = InSubMenu->AddSection("BonesAndSockets", LOCTEXT("CharacterMenu_BonesAndSocketsLabel", "Show")); Section.AddMenuEntry(FAnimViewportShowCommands::Get().ShowSockets); Section.AddMenuEntry(FAnimViewportShowCommands::Get().ShowAttributes); Section.AddMenuEntry(FAnimViewportShowCommands::Get().ShowBoneNames); Section.AddMenuEntry(FAnimViewportShowCommands::Get().ShowBoneColors); } { FToolMenuSection& Section = InSubMenu->AddSection("AnimViewportPreviewHierarchyBoneDraw", LOCTEXT("CharacterMenu_Actions_BoneDrawing", "Bone Drawing")); if (ContextThis) { TSharedPtr BoneSizeWidget = SNew(UE::AnimationEditor::SBoneDrawSizeSetting).AnimEditorViewport(ContextThis->Viewport); Section.AddEntry(FToolMenuEntry::InitWidget(TEXT("BoneDrawSize"), BoneSizeWidget.ToSharedRef(), LOCTEXT("CharacterMenu_Actions_BoneDrawSize", "Bone Draw Size:"))); } Section.AddMenuEntry(FAnimViewportShowCommands::Get().ShowBoneDrawAll); Section.AddMenuEntry(FAnimViewportShowCommands::Get().ShowBoneDrawSelected); Section.AddMenuEntry(FAnimViewportShowCommands::Get().ShowBoneDrawSelectedAndParents); Section.AddMenuEntry(FAnimViewportShowCommands::Get().ShowBoneDrawSelectedAndChildren); Section.AddMenuEntry(FAnimViewportShowCommands::Get().ShowBoneDrawSelectedAndParentsAndChildren); Section.AddMenuEntry(FAnimViewportShowCommands::Get().ShowBoneDrawNone); } }) ); Section.AddDynamicEntry("ClothingSubMenu", FNewToolMenuSectionDelegate::CreateLambda([](FToolMenuSection& InSection) { UAnimViewportToolBarToolMenuContext* Context = InSection.FindContext(); const SAnimViewportToolBar* ContextThis = Context ? Context->AnimViewportToolBar.Pin().Get() : nullptr; UDebugSkelMeshComponent* PreviewComp = ContextThis && ContextThis->Viewport.IsValid() ? ContextThis->Viewport.Pin()->GetPreviewScene()->GetPreviewMeshComponent() : nullptr; if (PreviewComp && GetDefault()->bExposeClothingSceneElementMenu) { constexpr bool bInOpenSubMenuOnClick = false; constexpr bool bShouldCloseWindowAfterMenuSelection = false; InSection.AddSubMenu(TEXT("ClothingSubMenu"), LOCTEXT("CharacterMenu_ClothingSubMenu", "Clothing"), LOCTEXT("CharacterMenu_ClothingSubMenuToolTip", "Options relating to clothing"), FNewToolMenuChoice(FNewMenuDelegate::CreateRaw(const_cast(ContextThis), &SAnimViewportToolBar::FillCharacterClothingMenu)), bInOpenSubMenuOnClick, TAttribute(), bShouldCloseWindowAfterMenuSelection); } })); Section.AddSubMenu(TEXT("AudioSubMenu"), LOCTEXT("CharacterMenu_AudioSubMenu", "Audio"), LOCTEXT("CharacterMenu_AudioSubMenuToolTip", "Audio options"), FNewToolMenuDelegate::CreateLambda([](UToolMenu* InSubMenu) { FToolMenuSection& Section = InSubMenu->AddSection("AnimViewportAudio", LOCTEXT("CharacterMenu_Audio", "Audio")); Section.AddMenuEntry(FAnimViewportShowCommands::Get().MuteAudio); Section.AddMenuEntry(FAnimViewportShowCommands::Get().UseAudioAttenuation); })); Section.AddDynamicEntry("AdvancedSubMenu", FNewToolMenuSectionDelegate::CreateLambda([](FToolMenuSection& InSection) { UAnimViewportToolBarToolMenuContext* Context = InSection.FindContext(); const SAnimViewportToolBar* ContextThis = Context ? Context->AnimViewportToolBar.Pin().Get() : nullptr; if (ContextThis) { InSection.AddSubMenu(TEXT("AdvancedSubMenu"), LOCTEXT("CharacterMenu_AdvancedSubMenu", "Advanced"), LOCTEXT("CharacterMenu_AdvancedSubMenuToolTip", "Advanced options"), FNewToolMenuChoice(FNewMenuDelegate::CreateRaw(ContextThis, &SAnimViewportToolBar::FillCharacterAdvancedMenu))); } })); Section.AddDynamicEntry("Timecode", FNewToolMenuSectionDelegate::CreateLambda([](FToolMenuSection& InSection) { UAnimViewportToolBarToolMenuContext* Context = InSection.FindContext(); const SAnimViewportToolBar* ContextThis = Context ? Context->AnimViewportToolBar.Pin().Get() : nullptr; if (ContextThis) { InSection.AddSubMenu(TEXT("TimecodeSubMenu"), LOCTEXT("CharacterMenu_TimecodeSubMenu", "Timecode"), LOCTEXT("CharacterMenu_TimecodeSubMenuToolTip", "Timecode options"), FNewToolMenuChoice(FNewMenuDelegate::CreateRaw(ContextThis, &SAnimViewportToolBar::FillCharacterTimecodeMenu))); } })); } } TSharedPtr MenuExtender = FExtender::Combine(Extenders); TSharedPtr PinnedViewport = Viewport.Pin(); FToolMenuContext MenuContext(PinnedViewport->GetCommandList(), MenuExtender); PinnedViewport->GetAssetEditorToolkit()->InitToolMenuContext(MenuContext); UAnimViewportToolBarToolMenuContext* AnimViewportContext = NewObject(); AnimViewportContext->AnimViewportToolBar = SharedThis(this); MenuContext.AddObject(AnimViewportContext); return UToolMenus::Get()->GenerateWidget(MenuName, MenuContext); } void SAnimViewportToolBar::FillCharacterTimecodeMenu(FMenuBuilder& MenuBuilder) const { const FAnimViewportShowCommands& Actions = FAnimViewportShowCommands::Get(); MenuBuilder.BeginSection("Timecode", LOCTEXT("Timecode_Label", "Timecode")); { MenuBuilder.AddMenuEntry( Actions.ShowTimecode ); } MenuBuilder.EndSection(); } void SAnimViewportToolBar::FillCharacterAdvancedMenu(FMenuBuilder& MenuBuilder) const { const FAnimViewportShowCommands& Actions = FAnimViewportShowCommands::Get(); // Draw UVs MenuBuilder.BeginSection("UVVisualization", LOCTEXT("UVVisualization_Label", "UV Visualization")); { MenuBuilder.AddWidget(Viewport.Pin()->UVChannelCombo.ToSharedRef(), FText()); } MenuBuilder.EndSection(); MenuBuilder.BeginSection("Skinning", LOCTEXT("Skinning_Label", "Skinning")); { MenuBuilder.AddMenuEntry(FAnimViewportMenuCommands::Get().SetCPUSkinning); } MenuBuilder.EndSection(); MenuBuilder.BeginSection("ShowVertex", LOCTEXT("ShowVertex_Label", "Vertex Normal Visualization")); { // Vertex debug flags MenuBuilder.AddMenuEntry(FAnimViewportMenuCommands::Get().SetShowNormals); MenuBuilder.AddMenuEntry(FAnimViewportMenuCommands::Get().SetShowTangents); MenuBuilder.AddMenuEntry(FAnimViewportMenuCommands::Get().SetShowBinormals); } MenuBuilder.EndSection(); MenuBuilder.BeginSection("AnimViewportPreviewHierarchyLocalAxes", LOCTEXT("ShowMenu_Actions_HierarchyAxes", "Hierarchy Local Axes") ); { MenuBuilder.AddMenuEntry( Actions.ShowLocalAxesAll ); MenuBuilder.AddMenuEntry( Actions.ShowLocalAxesSelected ); MenuBuilder.AddMenuEntry( Actions.ShowLocalAxesNone ); } MenuBuilder.EndSection(); } void SAnimViewportToolBar::FillCharacterMirrorMenu(FMenuBuilder& MenuBuilder) const { FAssetPickerConfig AssetPickerConfig; UDebugSkelMeshComponent* PreviewComp = Viewport.Pin()->GetPreviewScene()->GetPreviewMeshComponent(); USkeletalMesh* Mesh = PreviewComp->GetSkeletalMeshAsset(); UAnimPreviewInstance* PreviewInstance = PreviewComp->PreviewInstance; if (Mesh && PreviewInstance) { USkeleton* Skeleton = Mesh->GetSkeleton(); AssetPickerConfig.Filter.ClassPaths.Add(UMirrorDataTable::StaticClass()->GetClassPathName()); AssetPickerConfig.Filter.bRecursiveClasses = false; AssetPickerConfig.bAllowNullSelection = true; AssetPickerConfig.OnShouldFilterAsset = FOnShouldFilterAsset::CreateUObject(Skeleton, &USkeleton::ShouldFilterAsset, TEXT("Skeleton")); AssetPickerConfig.InitialAssetSelection = FAssetData(PreviewInstance->GetMirrorDataTable()); AssetPickerConfig.OnAssetSelected = FOnAssetSelected::CreateRaw(const_cast(this), &SAnimViewportToolBar::OnMirrorDataTableSelected); AssetPickerConfig.InitialAssetViewType = EAssetViewType::List; AssetPickerConfig.ThumbnailScale = 0.1f; AssetPickerConfig.bAddFilterUI = false; FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked(TEXT("ContentBrowser")); MenuBuilder.AddWidget( ContentBrowserModule.Get().CreateAssetPicker(AssetPickerConfig), FText::GetEmpty() ); } } void SAnimViewportToolBar::OnMirrorDataTableSelected(const FAssetData& SelectedMirrorTableData) { UMirrorDataTable* MirrorDataTable = Cast(SelectedMirrorTableData.GetAsset()); if (Viewport.Pin().IsValid()) { UDebugSkelMeshComponent* PreviewComp = Viewport.Pin()->GetPreviewScene()->GetPreviewMeshComponent(); USkeletalMesh* Mesh = PreviewComp->GetSkeletalMeshAsset(); UAnimPreviewInstance* PreviewInstance = PreviewComp->PreviewInstance; if (Mesh && PreviewInstance) { PreviewInstance->SetMirrorDataTable(MirrorDataTable); PreviewComp->OnMirrorDataTableChanged(); } } } void SAnimViewportToolBar::FillCharacterClothingMenu(FMenuBuilder& MenuBuilder) { const FAnimViewportShowCommands& Actions = FAnimViewportShowCommands::Get(); MenuBuilder.BeginSection("ClothPreview", LOCTEXT("ClothPreview_Label", "Simulation")); { MenuBuilder.AddMenuEntry(Actions.EnableClothSimulation); MenuBuilder.AddMenuEntry(Actions.ResetClothSimulation); TSharedPtr WindWidget = SNew(UE::AnimationEditor::SClothWindSettings).AnimEditorViewport(Viewport); MenuBuilder.AddWidget(WindWidget.ToSharedRef(), LOCTEXT("ClothPreview_WindStrength", "Wind Strength:")); TSharedPtr GravityWidget = SNew(UE::AnimationEditor::SGravitySettings).AnimEditorViewport(Viewport); MenuBuilder.AddWidget(GravityWidget.ToSharedRef(), LOCTEXT("ClothPreview_GravityScale", "Gravity Scale:")); MenuBuilder.AddMenuEntry(Actions.EnableCollisionWithAttachedClothChildren); MenuBuilder.AddMenuEntry(Actions.PauseClothWithAnim); } MenuBuilder.EndSection(); MenuBuilder.BeginSection("ClothAdditionalVisualization", LOCTEXT("ClothAdditionalVisualization_Label", "Sections Display Mode")); { MenuBuilder.AddMenuEntry(Actions.ShowAllSections); MenuBuilder.AddMenuEntry(Actions.ShowOnlyClothSections); MenuBuilder.AddMenuEntry(Actions.HideOnlyClothSections); } MenuBuilder.EndSection(); // Call into the clothing editor module to customize the menu (this is mainly for debug visualizations and sim-specific options) TSharedPtr SharedViewport = Viewport.Pin(); if (SharedViewport.IsValid()) { if (const TSharedPtr& AnimationViewportClient = SharedViewport->GetAnimationViewportClient()) { TSharedRef PreviewScene = AnimationViewportClient->GetPreviewScene(); if (UDebugSkelMeshComponent* PreviewComponent = PreviewScene->GetPreviewMeshComponent()) { if (PreviewComponent->ClothingSimulationFactory) // The cloth plugin could be disabled, and the factory would be null in this case { FClothingSystemEditorInterfaceModule& ClothingEditorModule = FModuleManager::LoadModuleChecked(TEXT("ClothingSystemEditorInterface")); if (ISimulationEditorExtender* Extender = ClothingEditorModule.GetSimulationEditorExtender(PreviewComponent->ClothingSimulationFactory->GetFName())) { Extender->ExtendViewportShowMenu(MenuBuilder, PreviewScene); } } } } } } TSharedRef SAnimViewportToolBar::GenerateShowMenu() const { if (TSharedPtr ViewportPinned = Viewport.Pin()) { constexpr bool bShowViewportStatsToggle = false; return UE::AnimationEditor::CreateShowMenuWidget( ViewportPinned->GetViewportWidget().ToSharedRef(), Extenders, bShowViewportStatsToggle ); } return SNullWidget::NullWidget; } FText SAnimViewportToolBar::GetLODMenuLabel() const { return UE::AnimationEditor::GetLODMenuLabel(Viewport); } TSharedRef SAnimViewportToolBar::GenerateLODMenu() const { return UE::AnimationEditor::GenerateLODMenuWidget(Viewport); } TSharedRef SAnimViewportToolBar::GenerateViewportTypeMenu() const { TSharedPtr MenuExtender = FExtender::Combine(Extenders); const bool bInShouldCloseWindowAfterMenuSelection = true; FMenuBuilder InMenuBuilder(bInShouldCloseWindowAfterMenuSelection, CommandList, MenuExtender); InMenuBuilder.SetStyle(&FAppStyle::Get(), "Menu"); InMenuBuilder.PushCommandList(CommandList.ToSharedRef()); InMenuBuilder.PushExtender(MenuExtender.ToSharedRef()); // Camera types InMenuBuilder.AddMenuEntry(FEditorViewportCommands::Get().Perspective); InMenuBuilder.BeginSection("LevelViewportCameraType_Ortho", LOCTEXT("CameraTypeHeader_Ortho", "Orthographic")); InMenuBuilder.AddMenuEntry(FEditorViewportCommands::Get().Top); InMenuBuilder.AddMenuEntry(FEditorViewportCommands::Get().Bottom); InMenuBuilder.AddMenuEntry(FEditorViewportCommands::Get().Left); InMenuBuilder.AddMenuEntry(FEditorViewportCommands::Get().Right); InMenuBuilder.AddMenuEntry(FEditorViewportCommands::Get().Front); InMenuBuilder.AddMenuEntry(FEditorViewportCommands::Get().Back); InMenuBuilder.EndSection(); InMenuBuilder.PopCommandList(); InMenuBuilder.PopExtender(); return InMenuBuilder.MakeWidget(); } TSharedRef SAnimViewportToolBar::GeneratePlaybackMenu() const { if (const TSharedPtr& ViewportTabPinned = Viewport.Pin()) { return UE::AnimationEditor::GeneratePlaybackMenu(ViewportTabPinned->GetPreviewScene(), Extenders); } return SNullWidget::NullWidget; } void SAnimViewportToolBar::GenerateTurnTableMenu(FMenuBuilder& MenuBuilder) const { MenuBuilder.AddWidget( UE::AnimationEditor::GenerateTurnTableMenu(Viewport), FText(), false, true, LOCTEXT("TurnTableTooltip", "Set up auto-rotation of preview.") ); } FSlateColor SAnimViewportToolBar::GetFontColor() const { const UAssetViewerSettings* Settings = UAssetViewerSettings::Get(); const UEditorPerProjectUserSettings* PerProjectUserSettings = GetDefault(); const int32 ProfileIndex = Settings->Profiles.IsValidIndex(PerProjectUserSettings->AssetViewerProfileIndex) ? PerProjectUserSettings->AssetViewerProfileIndex : 0; ensureMsgf(Settings->Profiles.IsValidIndex(PerProjectUserSettings->AssetViewerProfileIndex), TEXT("Invalid default settings pointer or current profile index")); FLinearColor FontColor; if (Settings->Profiles[ProfileIndex].bShowEnvironment) { FontColor = FLinearColor::White; } else { FLinearColor Color = Settings->Profiles[ProfileIndex].EnvironmentColor * Settings->Profiles[ProfileIndex].EnvironmentIntensity; // see if it's dark, if V is less than 0.2 if (Color.B < 0.3f ) { FontColor = FLinearColor::White; } else { FontColor = FLinearColor::Black; } } return FontColor; } FText SAnimViewportToolBar::GetPlaybackMenuLabel() const { FText Label = LOCTEXT("PlaybackError", "Error"); if (const TSharedPtr& ViewportTabPinned = Viewport.Pin()) { return UE::AnimationEditor::GetPlaybackMenuLabel(ViewportTabPinned->GetPreviewScene()); } return Label; } FText SAnimViewportToolBar::GetCameraMenuLabel() const { TSharedPtr< SAnimationEditorViewportTabBody > PinnedViewport(Viewport.Pin()); if( PinnedViewport.IsValid() ) { return UE::UnrealEd::GetCameraSubmenuLabelFromViewportType(PinnedViewport->GetLevelViewportClient().ViewportType); } return LOCTEXT("Viewport_Default", "Camera"); } const FSlateBrush* SAnimViewportToolBar::GetCameraMenuLabelIcon() const { TSharedPtr< SAnimationEditorViewportTabBody > PinnedViewport( Viewport.Pin() ); if( PinnedViewport.IsValid() ) { return GetCameraMenuLabelIconFromViewportType(PinnedViewport->GetLevelViewportClient().ViewportType ); } return FAppStyle::Get().GetBrush("NoBrush"); } TOptional SAnimViewportToolBar::OnGetFOVValue() const { if(Viewport.IsValid()) { return Viewport.Pin()->GetLevelViewportClient().ViewFOV; } return 0.0f; } void SAnimViewportToolBar::OnFOVValueChanged( float NewValue ) { FEditorViewportClient& ViewportClient = Viewport.Pin()->GetLevelViewportClient(); ViewportClient.FOVAngle = NewValue; FAnimationViewportClient& AnimViewportClient = (FAnimationViewportClient&)(ViewportClient); AnimViewportClient.ConfigOption->SetViewFOV(AnimViewportClient.GetAssetEditorToolkit()->GetEditorName(), NewValue, AnimViewportClient.GetViewportIndex()); ViewportClient.ViewFOV = NewValue; ViewportClient.Invalidate(); if (TSharedPtr ViewportTabPinned = Viewport.Pin()) { if (TSharedPtr CommandsPinned = ViewportTabPinned->GetPinnedCommands()) { CommandsPinned->AddCustomWidget(TEXT("FOVWidget")); } } } void SAnimViewportToolBar::OnFOVValueCommitted( float NewValue, ETextCommit::Type CommitInfo ) { //OnFOVValueChanged will be called... nothing needed here. } void SAnimViewportToolBar::OnCamSpeedChanged(int32 NewValue) { FEditorViewportClient& ViewportClient = Viewport.Pin()->GetLevelViewportClient(); FAnimationViewportClient& AnimViewportClient = (FAnimationViewportClient&)(ViewportClient); AnimViewportClient.ConfigOption->SetCameraSpeed(AnimViewportClient.GetAssetEditorToolkit()->GetEditorName(), NewValue, AnimViewportClient.GetViewportIndex()); } void SAnimViewportToolBar::OnCamSpeedScalarChanged(float NewValue) { FEditorViewportClient& ViewportClient = Viewport.Pin()->GetLevelViewportClient(); FAnimationViewportClient& AnimViewportClient = (FAnimationViewportClient&)(ViewportClient); AnimViewportClient.ConfigOption->SetCameraSpeedScalar(AnimViewportClient.GetAssetEditorToolkit()->GetEditorName(), NewValue, AnimViewportClient.GetViewportIndex()); } void SAnimViewportToolBar::AddMenuExtender(FName MenuToExtend, FMenuExtensionDelegate MenuBuilderDelegate) { TSharedRef Extender(new FExtender()); Extender->AddMenuExtension( MenuToExtend, EExtensionHook::After, CommandList, MenuBuilderDelegate ); Extenders.Add(Extender); } TSharedRef SAnimViewportToolBar::GetViewMenuExtender(TSharedPtr InRealViewport) { TSharedRef Extender(new FExtender()); Extender->AddMenuExtension( TEXT("ViewMode"), EExtensionHook::After, InRealViewport->GetCommandList(), FMenuExtensionDelegate::CreateLambda([this](FMenuBuilder& InMenuBuilder) { InMenuBuilder.AddSubMenu( LOCTEXT("VisualizeBufferViewModeDisplayName", "Buffer Visualization"), LOCTEXT("BufferVisualizationMenu_ToolTip", "Select a mode for buffer visualization"), FNewMenuDelegate::CreateStatic(&FBufferVisualizationMenuCommands::BuildVisualisationSubMenu), FUIAction( FExecuteAction(), FCanExecuteAction(), FIsActionChecked::CreateLambda([this]() { const TSharedPtr ViewportPtr = Viewport.Pin(); if (ViewportPtr.IsValid()) { const FEditorViewportClient& ViewportClient = ViewportPtr->GetViewportClient(); return ViewportClient.IsViewModeEnabled(VMI_VisualizeBuffer); } return false; }) ), "VisualizeBufferViewMode", EUserInterfaceActionType::RadioButton, /* bInOpenSubMenuOnClick = */ false, FSlateIcon(FAppStyle::GetAppStyleSetName(), "EditorViewport.VisualizeBufferMode") ); })); return Extender; } #undef LOCTEXT_NAMESPACE