// Copyright Epic Games, Inc. All Rights Reserved. #include "SEditorViewport.h" #include "Misc/Paths.h" #include "Framework/Commands/UICommandList.h" #include "Misc/App.h" #include "Widgets/Layout/SBorder.h" #include "Settings/LevelEditorViewportSettings.h" #include "ThumbnailRendering/ThumbnailManager.h" #include "EngineGlobals.h" #include "Engine/TextureStreamingTypes.h" #include "EditorModeManager.h" #include "Slate/SceneViewport.h" #include "EditorViewportCommands.h" #include "Framework/Notifications/NotificationManager.h" #include "Widgets/Notifications/SNotificationList.h" #include "Settings/EditorProjectSettings.h" #include "Kismet2/DebuggerCommands.h" #include "Widgets/Layout/SBox.h" #include "Widgets/Input/SSpinBox.h" #include "Widgets/Input/SCheckBox.h" #include "MaterialShaderQualitySettings.h" #include "RHIShaderPlatformDefinitions.inl" #include "RayTracingDebugVisualizationMenuCommands.h" #define LOCTEXT_NAMESPACE "EditorViewport" SEditorViewport::SEditorViewport() : LastTickTime(0), bInvalidated(false) { } SEditorViewport::~SEditorViewport() { // Close viewport if (Client.IsValid()) { Client->Viewport = NULL; } // Release our reference to the viewport client Client.Reset(); check(SceneViewport.IsUnique()); } void SEditorViewport::Construct(const FArguments& InArgs) { ChildSlot [SNew(SGlobalPlayWorldActions) [SAssignNew(ViewportWidget, SViewport) .ShowEffectWhenDisabled(false) .EnableGammaCorrection(false) // Scene rendering handles this .AddMetaData(InArgs.MetaData.Num() > 0 ? InArgs.MetaData[0] : MakeShareable(new FTagMetaData(TEXT("LevelEditorViewport")))) .ViewportSize(InArgs._ViewportSize) [SAssignNew(ViewportOverlay, SOverlay) + SOverlay::Slot() [SNew(SBorder) .BorderImage(this, &SEditorViewport::OnGetViewportBorderBrush) .BorderBackgroundColor(this, &SEditorViewport::OnGetViewportBorderColorAndOpacity) .Visibility(this, &SEditorViewport::OnGetViewportContentVisibility) .Padding(0.0f) .ShowEffectWhenDisabled(false)]]]]; TSharedRef ViewportClient = MakeEditorViewportClient(); if (!ViewportClient->VisibilityDelegate.IsBound()) { ViewportClient->VisibilityDelegate.BindSP(this, &SEditorViewport::IsVisible); } SceneViewport = MakeShareable(new FSceneViewport(&ViewportClient.Get(), ViewportWidget)); ViewportClient->Viewport = SceneViewport.Get(); ViewportWidget->SetViewportInterface(SceneViewport.ToSharedRef()); Client = ViewportClient; if (Client->IsRealtime()) { ActiveTimerHandle = RegisterActiveTimer(0.f, FWidgetActiveTimerDelegate::CreateSP(this, &SEditorViewport::EnsureTick)); } CommandList = MakeShareable(new FUICommandList); // Ensure the commands are registered FEditorViewportCommands::Register(); BindCommands(); TSharedPtr ViewportToolbar = MakeViewportToolbar(); if (ViewportToolbar.IsValid()) { ViewportOverlay->AddSlot() .VAlign(VAlign_Top) [ViewportToolbar.ToSharedRef()]; } PopulateViewportOverlays(ViewportOverlay.ToSharedRef()); } FReply SEditorViewport::OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent) { FReply Reply = FReply::Unhandled(); if (CommandList->ProcessCommandBindings(InKeyEvent)) { Reply = FReply::Handled(); Client->Invalidate(); } return Reply; } bool SEditorViewport::SupportsKeyboardFocus() const { return true; } FReply SEditorViewport::OnFocusReceived(const FGeometry& MyGeometry, const FFocusEvent& InFocusEvent) { // forward focus to the viewport return FReply::Handled().SetUserFocus(ViewportWidget.ToSharedRef(), InFocusEvent.GetCause()); } void SEditorViewport::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) { LastTickTime = FPlatformTime::Seconds(); } void SEditorViewport::BindCommands() { FUICommandList& CommandListRef = *CommandList; const FEditorViewportCommands& Commands = FEditorViewportCommands::Get(); TSharedRef ClientRef = Client.ToSharedRef(); CommandListRef.MapAction( Commands.ToggleRealTime, FExecuteAction::CreateSP(this, &SEditorViewport::OnToggleRealtime), FCanExecuteAction::CreateSP(this, &SEditorViewport::CanToggleRealtime), FIsActionChecked::CreateSP(this, &SEditorViewport::IsRealtime)); CommandListRef.MapAction( Commands.ToggleStats, FExecuteAction::CreateSP(this, &SEditorViewport::OnToggleStats), FCanExecuteAction(), FIsActionChecked::CreateSP(ClientRef, &FEditorViewportClient::ShouldShowStats)); CommandListRef.MapAction( Commands.ToggleFPS, FExecuteAction::CreateSP(this, &SEditorViewport::ToggleStatCommand, FString("FPS")), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SEditorViewport::IsStatCommandVisible, FString("FPS"))); CommandListRef.MapAction( Commands.IncrementPositionGridSize, FExecuteAction::CreateSP(this, &SEditorViewport::OnIncrementPositionGridSize)); CommandListRef.MapAction( Commands.DecrementPositionGridSize, FExecuteAction::CreateSP(this, &SEditorViewport::OnDecrementPositionGridSize)); CommandListRef.MapAction( Commands.IncrementRotationGridSize, FExecuteAction::CreateSP(this, &SEditorViewport::OnIncrementRotationGridSize)); CommandListRef.MapAction( Commands.DecrementRotationGridSize, FExecuteAction::CreateSP(this, &SEditorViewport::OnDecrementRotationGridSize)); CommandListRef.MapAction( Commands.Perspective, FExecuteAction::CreateSP(ClientRef, &FEditorViewportClient::SetViewportType, LVT_Perspective), FCanExecuteAction(), FIsActionChecked::CreateSP(ClientRef, &FEditorViewportClient::IsActiveViewportType, LVT_Perspective)); CommandListRef.MapAction( Commands.Front, FExecuteAction::CreateSP(ClientRef, &FEditorViewportClient::SetViewportType, LVT_OrthoNegativeYZ), FCanExecuteAction(), FIsActionChecked::CreateSP(ClientRef, &FEditorViewportClient::IsActiveViewportType, LVT_OrthoNegativeYZ)); CommandListRef.MapAction( Commands.Left, FExecuteAction::CreateSP(ClientRef, &FEditorViewportClient::SetViewportType, LVT_OrthoNegativeXZ), FCanExecuteAction(), FIsActionChecked::CreateSP(ClientRef, &FEditorViewportClient::IsActiveViewportType, LVT_OrthoNegativeXZ)); CommandListRef.MapAction( Commands.Top, FExecuteAction::CreateSP(ClientRef, &FEditorViewportClient::SetViewportType, LVT_OrthoXY), FCanExecuteAction(), FIsActionChecked::CreateSP(ClientRef, &FEditorViewportClient::IsActiveViewportType, LVT_OrthoXY)); CommandListRef.MapAction( Commands.Back, FExecuteAction::CreateSP(ClientRef, &FEditorViewportClient::SetViewportType, LVT_OrthoYZ), FCanExecuteAction(), FIsActionChecked::CreateSP(ClientRef, &FEditorViewportClient::IsActiveViewportType, LVT_OrthoYZ)); CommandListRef.MapAction( Commands.Right, FExecuteAction::CreateSP(ClientRef, &FEditorViewportClient::SetViewportType, LVT_OrthoXZ), FCanExecuteAction(), FIsActionChecked::CreateSP(ClientRef, &FEditorViewportClient::IsActiveViewportType, LVT_OrthoXZ)); CommandListRef.MapAction( Commands.Bottom, FExecuteAction::CreateSP(ClientRef, &FEditorViewportClient::SetViewportType, LVT_OrthoNegativeXY), FCanExecuteAction(), FIsActionChecked::CreateSP(ClientRef, &FEditorViewportClient::IsActiveViewportType, LVT_OrthoNegativeXY)); CommandListRef.MapAction( Commands.Next, FExecuteAction::CreateSP(ClientRef, &FEditorViewportClient::RotateViewportType), FCanExecuteAction(), FIsActionChecked::CreateSP(ClientRef, &FEditorViewportClient::IsActiveViewportTypeInRotation)); CommandListRef.MapAction( Commands.ScreenCapture, FExecuteAction::CreateSP(this, &SEditorViewport::OnScreenCapture), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SEditorViewport::DoesAllowScreenCapture)); CommandListRef.MapAction( Commands.ScreenCaptureForProjectThumbnail, FExecuteAction::CreateSP(this, &SEditorViewport::OnScreenCaptureForProjectThumbnail), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SEditorViewport::DoesAllowScreenCapture)); CommandListRef.MapAction( Commands.TranslateMode, FExecuteAction::CreateSP(ClientRef, &FEditorViewportClient::SetWidgetMode, FWidget::WM_Translate), FCanExecuteAction::CreateSP(ClientRef, &FEditorViewportClient::CanSetWidgetMode, FWidget::WM_Translate), FIsActionChecked::CreateSP(this, &SEditorViewport::IsWidgetModeActive, FWidget::WM_Translate)); CommandListRef.MapAction( Commands.RotateMode, FExecuteAction::CreateSP(ClientRef, &FEditorViewportClient::SetWidgetMode, FWidget::WM_Rotate), FCanExecuteAction::CreateSP(ClientRef, &FEditorViewportClient::CanSetWidgetMode, FWidget::WM_Rotate), FIsActionChecked::CreateSP(this, &SEditorViewport::IsWidgetModeActive, FWidget::WM_Rotate)); CommandListRef.MapAction( Commands.ScaleMode, FExecuteAction::CreateSP(ClientRef, &FEditorViewportClient::SetWidgetMode, FWidget::WM_Scale), FCanExecuteAction::CreateSP(ClientRef, &FEditorViewportClient::CanSetWidgetMode, FWidget::WM_Scale), FIsActionChecked::CreateSP(this, &SEditorViewport::IsWidgetModeActive, FWidget::WM_Scale)); CommandListRef.MapAction( Commands.TranslateRotateMode, FExecuteAction::CreateSP(ClientRef, &FEditorViewportClient::SetWidgetMode, FWidget::WM_TranslateRotateZ), FCanExecuteAction::CreateSP(ClientRef, &FEditorViewportClient::CanSetWidgetMode, FWidget::WM_TranslateRotateZ), FIsActionChecked::CreateSP(this, &SEditorViewport::IsWidgetModeActive, FWidget::WM_TranslateRotateZ), FIsActionButtonVisible::CreateSP(this, &SEditorViewport::IsTranslateRotateModeVisible)); CommandListRef.MapAction( Commands.TranslateRotate2DMode, FExecuteAction::CreateSP(ClientRef, &FEditorViewportClient::SetWidgetMode, FWidget::WM_2D), FCanExecuteAction::CreateSP(ClientRef, &FEditorViewportClient::CanSetWidgetMode, FWidget::WM_2D), FIsActionChecked::CreateSP(this, &SEditorViewport::IsWidgetModeActive, FWidget::WM_2D), FIsActionButtonVisible::CreateSP(this, &SEditorViewport::Is2DModeVisible)); CommandListRef.MapAction( Commands.ShrinkTransformWidget, FExecuteAction::CreateSP(ClientRef, &FEditorViewportClient::AdjustTransformWidgetSize, -1)); CommandListRef.MapAction( Commands.ExpandTransformWidget, FExecuteAction::CreateSP(ClientRef, &FEditorViewportClient::AdjustTransformWidgetSize, 1)); CommandListRef.MapAction( Commands.RelativeCoordinateSystem_World, FExecuteAction::CreateSP(ClientRef, &FEditorViewportClient::SetWidgetCoordSystemSpace, COORD_World), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SEditorViewport::IsCoordSystemActive, COORD_World)); CommandListRef.MapAction( Commands.RelativeCoordinateSystem_Local, FExecuteAction::CreateSP(ClientRef, &FEditorViewportClient::SetWidgetCoordSystemSpace, COORD_Local), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SEditorViewport::IsCoordSystemActive, COORD_Local)); CommandListRef.MapAction( Commands.CycleTransformGizmos, FExecuteAction::CreateSP(this, &SEditorViewport::OnCycleWidgetMode), FCanExecuteAction::CreateSP(ClientRef, &FEditorViewportClient::CanCycleWidgetMode)); CommandListRef.MapAction( Commands.CycleTransformGizmoCoordSystem, FExecuteAction::CreateSP(this, &SEditorViewport::OnCycleCoordinateSystem)); CommandListRef.MapAction( Commands.FocusViewportToSelection, FExecuteAction::CreateSP(this, &SEditorViewport::OnFocusViewportToSelection) // FExecuteAction::CreateStatic( &FLevelEditorActionCallbacks::ExecuteExecCommand, FString( TEXT("CAMERA ALIGN ACTIVEVIEWPORTONLY") ) ) ); CommandListRef.MapAction( Commands.SurfaceSnapping, FExecuteAction::CreateStatic(&SEditorViewport::OnToggleSurfaceSnap), FCanExecuteAction(), FIsActionChecked::CreateStatic(&SEditorViewport::OnIsSurfaceSnapEnabled)); CommandListRef.MapAction( (Client.IsValid() && Client->IsLevelEditorClient()) ? Commands.ToggleInGameExposure : Commands.ToggleAutoExposure, FExecuteAction::CreateSP(this, &SEditorViewport::ChangeExposureSetting), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SEditorViewport::IsExposureSettingSelected)); // Simple macro for binding many view mode UI commands #define MAP_VIEWMODEPARAM_ACTION(ViewModeCommand, ViewModeParam) \ CommandListRef.MapAction( \ ViewModeCommand, \ FExecuteAction::CreateSP( ClientRef, &FEditorViewportClient::SetViewModeParam, ViewModeParam ), \ FCanExecuteAction(), \ FIsActionChecked::CreateSP( ClientRef, &FEditorViewportClient::IsViewModeParam, ViewModeParam ) ) #define MAP_VIEWMODE_ACTION(ViewModeCommand, ViewModeID) \ CommandListRef.MapAction( \ ViewModeCommand, \ FExecuteAction::CreateSP( ClientRef, &FEditorViewportClient::SetViewMode, ViewModeID ), \ FCanExecuteAction(), \ FIsActionChecked::CreateSP( ClientRef, &FEditorViewportClient::IsViewModeEnabled, ViewModeID ) ) // Map each view mode MAP_VIEWMODE_ACTION(Commands.WireframeMode, VMI_BrushWireframe); MAP_VIEWMODE_ACTION(Commands.UnlitMode, VMI_Unlit); MAP_VIEWMODE_ACTION(Commands.LitMode, VMI_Lit); #if RHI_RAYTRACING if (IsRayTracingEnabled()) { MAP_VIEWMODE_ACTION(Commands.PathTracingMode, VMI_PathTracing); MAP_VIEWMODE_ACTION(Commands.RayTracingDebugMode, VMI_RayTracingDebug); const FRayTracingDebugVisualizationMenuCommands& RtDebugCommands = FRayTracingDebugVisualizationMenuCommands::Get(); RtDebugCommands.BindCommands(CommandListRef, Client); } #endif MAP_VIEWMODE_ACTION(Commands.DetailLightingMode, VMI_Lit_DetailLighting); MAP_VIEWMODE_ACTION(Commands.LightingOnlyMode, VMI_LightingOnly); MAP_VIEWMODE_ACTION(Commands.LightComplexityMode, VMI_LightComplexity); MAP_VIEWMODE_ACTION(Commands.ShaderComplexityMode, VMI_ShaderComplexity); MAP_VIEWMODE_ACTION(Commands.QuadOverdrawMode, VMI_QuadOverdraw); MAP_VIEWMODE_ACTION(Commands.ShaderComplexityWithQuadOverdrawMode, VMI_ShaderComplexityWithQuadOverdraw); MAP_VIEWMODE_ACTION(Commands.TexStreamAccPrimitiveDistanceMode, VMI_PrimitiveDistanceAccuracy); MAP_VIEWMODE_ACTION(Commands.TexStreamAccMeshUVDensityMode, VMI_MeshUVDensityAccuracy); MAP_VIEWMODE_ACTION(Commands.TexStreamAccMaterialTextureScaleMode, VMI_MaterialTextureScaleAccuracy); MAP_VIEWMODE_ACTION(Commands.RequiredTextureResolutionMode, VMI_RequiredTextureResolution); MAP_VIEWMODE_ACTION(Commands.StationaryLightOverlapMode, VMI_StationaryLightOverlap); MAP_VIEWMODE_ACTION(Commands.LightmapDensityMode, VMI_LightmapDensity); MAP_VIEWMODE_ACTION(Commands.ReflectionOverrideMode, VMI_ReflectionOverride); MAP_VIEWMODE_ACTION(Commands.GroupLODColorationMode, VMI_GroupLODColoration); MAP_VIEWMODE_ACTION(Commands.LODColorationMode, VMI_LODColoration); MAP_VIEWMODE_ACTION(Commands.HLODColorationMode, VMI_HLODColoration); MAP_VIEWMODE_ACTION(Commands.VisualizeBufferMode, VMI_VisualizeBuffer); MAP_VIEWMODE_ACTION(Commands.CollisionPawn, VMI_CollisionPawn); MAP_VIEWMODE_ACTION(Commands.CollisionVisibility, VMI_CollisionVisibility); MAP_VIEWMODEPARAM_ACTION(Commands.TexStreamAccMeshUVDensityAll, -1); for (int32 TexCoordIndex = 0; TexCoordIndex < TEXSTREAM_MAX_NUM_UVCHANNELS; ++TexCoordIndex) { MAP_VIEWMODEPARAM_ACTION(Commands.TexStreamAccMeshUVDensitySingle[TexCoordIndex], TexCoordIndex); } MAP_VIEWMODEPARAM_ACTION(Commands.TexStreamAccMaterialTextureScaleAll, -1); for (int32 TextureIndex = 0; TextureIndex < TEXSTREAM_MAX_NUM_TEXTURES_PER_MATERIAL; ++TextureIndex) { MAP_VIEWMODEPARAM_ACTION(Commands.TexStreamAccMaterialTextureScaleSingle[TextureIndex], TextureIndex); MAP_VIEWMODEPARAM_ACTION(Commands.RequiredTextureResolutionSingle[TextureIndex], TextureIndex); } } EVisibility SEditorViewport::OnGetViewportContentVisibility() const { return GLevelEditorModeTools().IsViewportUIHidden() ? EVisibility::Collapsed : EVisibility::SelfHitTestInvisible; } void SEditorViewport::OnToggleRealtime() { if (Client->IsRealtime()) { Client->SetRealtime(false); if (ActiveTimerHandle.IsValid()) { UnRegisterActiveTimer(ActiveTimerHandle.Pin().ToSharedRef()); } } else { Client->SetRealtime(true); ActiveTimerHandle = RegisterActiveTimer(0.f, FWidgetActiveTimerDelegate::CreateSP(this, &SEditorViewport::EnsureTick)); } } bool SEditorViewport::CanToggleRealtime() const { return !Client->IsRealtimeOverrideSet(); } void SEditorViewport::SetRenderDirectlyToWindow(const bool bInRenderDirectlyToWindow) { ViewportWidget->SetRenderDirectlyToWindow(bInRenderDirectlyToWindow); } void SEditorViewport::EnableStereoRendering(const bool bInEnableStereoRendering) { ViewportWidget->EnableStereoRendering(bInEnableStereoRendering); } void SEditorViewport::OnToggleStats() { bool bIsEnabled = Client->ShouldShowStats(); Client->SetShowStats(!bIsEnabled); if (!bIsEnabled) { // We cannot show stats unless realtime rendering is enabled if (!Client->IsRealtime()) { Client->SetRealtime(true); ActiveTimerHandle = RegisterActiveTimer(0.f, FWidgetActiveTimerDelegate::CreateSP(this, &SEditorViewport::EnsureTick)); } // let the user know how they can enable stats via the console FNotificationInfo Info(LOCTEXT("StatsEnableHint", "Stats display can be toggled via the STAT [type] console command")); Info.ExpireDuration = 3.0f; /* Temporarily remove the link until the page is updated Info.HyperlinkText = LOCTEXT("StatsEnableHyperlink", "Learn more"); Info.Hyperlink = FSimpleDelegate::CreateStatic([](){ IDocumentation::Get()->Open(TEXT("Engine/Basics/ConsoleCommands#statisticscommands")); }); */ FSlateNotificationManager::Get().AddNotification(Info); } } void SEditorViewport::ToggleStatCommand(FString CommandName) { GEngine->ExecEngineStat(GetWorld(), Client.Get(), *CommandName); // Invalidate the client to render once in case the click was on the checkbox itself (which doesn't dismiss the menu) Client->Invalidate(); } bool SEditorViewport::IsStatCommandVisible(FString CommandName) const { // Only if realtime and stats are also enabled should we show the stat as visible return Client->IsRealtime() && Client->ShouldShowStats() && Client->IsStatEnabled(CommandName); } void SEditorViewport::ToggleShowFlag(uint32 EngineShowFlagIndex) { bool bOldState = Client->EngineShowFlags.GetSingleFlag(EngineShowFlagIndex); Client->EngineShowFlags.SetSingleFlag(EngineShowFlagIndex, !bOldState); // If changing collision flag, need to do special handling for hidden objects if (EngineShowFlagIndex == FEngineShowFlags::EShowFlag::SF_Collision) { Client->UpdateHiddenCollisionDrawing(); } // Invalidate clients which aren't real-time so we see the changes Client->Invalidate(); } bool SEditorViewport::IsShowFlagEnabled(uint32 EngineShowFlagIndex) const { return Client->EngineShowFlags.GetSingleFlag(EngineShowFlagIndex); } void SEditorViewport::ChangeExposureSetting() { Client->ExposureSettings.bFixed = !Client->ExposureSettings.bFixed; Client->Invalidate(); } bool SEditorViewport::IsExposureSettingSelected() const { return !Client->ExposureSettings.bFixed; } void SEditorViewport::Invalidate() { bInvalidated = true; if (!ActiveTimerHandle.IsValid()) { ActiveTimerHandle = RegisterActiveTimer(0.f, FWidgetActiveTimerDelegate::CreateSP(this, &SEditorViewport::EnsureTick)); } } bool SEditorViewport::IsRealtime() const { return Client->IsRealtime(); } bool SEditorViewport::IsVisible() const { const float VisibilityTimeThreshold = .5f; // The viewport is visible if we don't have a parent layout (likely a floating window) or this viewport is visible in the parent layout return LastTickTime == 0.0 || // Never been ticked FPlatformTime::Seconds() - LastTickTime <= VisibilityTimeThreshold; // Ticked recently } void SEditorViewport::OnScreenCapture() { Client->TakeScreenshot(Client->Viewport, true); } void SEditorViewport::OnScreenCaptureForProjectThumbnail() { if (FApp::HasProjectName()) { const FString BaseFilename = FString(FApp::GetProjectName()) + TEXT(".png"); const FString ScreenshotFilename = FPaths::Combine(*FPaths::ProjectDir(), *BaseFilename); UThumbnailManager::CaptureProjectThumbnail(Client->Viewport, ScreenshotFilename, true); } } EVisibility SEditorViewport::GetTransformToolbarVisibility() const { return (Client->GetWidgetMode() != FWidget::WM_None) ? EVisibility::Visible : EVisibility::Hidden; } TSharedRef SEditorViewport::BuildFixedEV100Menu() const { const float EV100Min = -10.f; const float EV100Max = 20.f; return SNew(SBox) .HAlign(HAlign_Right) [SNew(SBox) .Padding(FMargin(0.0f, 0.0f, 0.0f, 0.0f)) .WidthOverride(100.0f) [SNew(SSpinBox) .Font(FEditorStyle::GetFontStyle(TEXT("MenuItem.Font"))) .MinValue(EV100Min) .MaxValue(EV100Max) .Value(this, &SEditorViewport::OnGetFixedEV100Value) .OnValueChanged(const_cast(this), &SEditorViewport::OnFixedEV100ValueChanged) .ToolTipText(LOCTEXT("EV100ToolTip", "Sets the exposure value of the camera using the specified EV100. Exposure = 1 / (1.2 * 2^EV100)")) .IsEnabled(this, &SEditorViewport::IsFixedEV100Enabled)]]; }; float SEditorViewport::OnGetFixedEV100Value() const { if (Client.IsValid()) { return Client->ExposureSettings.FixedEV100; } return 0; } bool SEditorViewport::IsFixedEV100Enabled() const { if (Client.IsValid()) { return Client->ExposureSettings.bFixed; } return false; } void SEditorViewport::OnFixedEV100ValueChanged(float NewValue) { if (Client.IsValid()) { Client->ExposureSettings.bFixed = true; Client->ExposureSettings.FixedEV100 = NewValue; Client->Invalidate(); } } bool SEditorViewport::IsWidgetModeActive(FWidget::EWidgetMode Mode) const { return Client->GetWidgetMode() == Mode; } bool SEditorViewport::IsTranslateRotateModeVisible() const { return GetDefault()->bAllowTranslateRotateZWidget; } bool SEditorViewport::Is2DModeVisible() const { return GetDefault()->bEnable2DWidget; } bool SEditorViewport::IsCoordSystemActive(ECoordSystem CoordSystem) const { return Client->GetWidgetCoordSystemSpace() == CoordSystem; } void SEditorViewport::OnCycleWidgetMode() { FWidget::EWidgetMode WidgetMode = Client->GetWidgetMode(); // Can't cycle the widget mode if we don't currently have a widget if (WidgetMode == FWidget::WM_None) { return; } int32 WidgetModeAsInt = WidgetMode; do { ++WidgetModeAsInt; if ((WidgetModeAsInt == FWidget::WM_TranslateRotateZ) && (!GetDefault()->bAllowTranslateRotateZWidget)) { ++WidgetModeAsInt; } if ((WidgetModeAsInt == FWidget::WM_2D) && (!GetDefault()->bEnable2DWidget)) { ++WidgetModeAsInt; } if (WidgetModeAsInt == FWidget::WM_Max) { WidgetModeAsInt -= FWidget::WM_Max; } } while (!Client->CanSetWidgetMode((FWidget::EWidgetMode)WidgetModeAsInt) && WidgetModeAsInt != WidgetMode); Client->SetWidgetMode((FWidget::EWidgetMode)WidgetModeAsInt); } void SEditorViewport::OnCycleCoordinateSystem() { int32 CoordSystemAsInt = Client->GetWidgetCoordSystemSpace(); ++CoordSystemAsInt; if (CoordSystemAsInt == COORD_Max) { CoordSystemAsInt -= COORD_Max; } Client->SetWidgetCoordSystemSpace((ECoordSystem)CoordSystemAsInt); } UWorld* SEditorViewport::GetWorld() const { return Client->GetWorld(); } void SEditorViewport::OnToggleSurfaceSnap() { auto* Settings = GetMutableDefault(); Settings->SnapToSurface.bEnabled = !Settings->SnapToSurface.bEnabled; } bool SEditorViewport::OnIsSurfaceSnapEnabled() { return GetDefault()->SnapToSurface.bEnabled; } EActiveTimerReturnType SEditorViewport::EnsureTick(double InCurrentTime, float InDeltaTime) { // Keep the timer going if we're realtime or were invalidated this frame const bool bShouldContinue = Client->IsRealtime() || bInvalidated; bInvalidated = false; return bShouldContinue ? EActiveTimerReturnType::Continue : EActiveTimerReturnType::Stop; } /////////////////////////////////////////////////////////////////////////////// // begin feature level control functions block /////////////////////////////////////////////////////////////////////////////// EShaderPlatform SEditorViewport::GetShaderPlatformHelper(const ERHIFeatureLevel::Type FeatureLevel) const { UMaterialShaderQualitySettings* MaterialShaderQualitySettings = UMaterialShaderQualitySettings::Get(); const FName& PreviewPlatform = MaterialShaderQualitySettings->GetPreviewPlatform(); EShaderPlatform ShaderPlatform = ShaderFormatToLegacyShaderPlatform(PreviewPlatform); if (ShaderPlatform == SP_NumPlatforms) { ShaderPlatform = GetFeatureLevelShaderPlatform(FeatureLevel); } return ShaderPlatform; } TSharedRef SEditorViewport::BuildFeatureLevelWidget() const { TSharedRef BoxWidget = SNew(SHorizontalBox) .Visibility(this, &SEditorViewport::GetCurrentFeatureLevelPreviewTextVisibility) + SHorizontalBox::Slot() .AutoWidth() .Padding(2.0f, 1.0f, 2.0f, 1.0f) [SNew(STextBlock) .Text(this, &SEditorViewport::GetCurrentFeatureLevelPreviewText, true) .Font(FEditorStyle::GetFontStyle(TEXT("MenuItem.Font"))) .ShadowOffset(FVector2D(1, 1))] + SHorizontalBox::Slot() .AutoWidth() .Padding(4.0f, 1.0f, 2.0f, 1.0f) [SNew(STextBlock) .Text(this, &SEditorViewport::GetCurrentFeatureLevelPreviewText, false) .Font(FEditorStyle::GetFontStyle(TEXT("MenuItem.Font"))) .ColorAndOpacity(FLinearColor(0.4f, 1.0f, 1.0f)) .ShadowOffset(FVector2D(1, 1))]; return BoxWidget; } EVisibility SEditorViewport::GetCurrentFeatureLevelPreviewTextVisibility() const { if (GetWorld()) { return (GetWorld()->FeatureLevel != GMaxRHIFeatureLevel) ? EVisibility::SelfHitTestInvisible : EVisibility::Collapsed; } else { return EVisibility::Collapsed; } } FText SEditorViewport::GetCurrentFeatureLevelPreviewText(bool bDrawOnlyLabel) const { FText LabelName; if (bDrawOnlyLabel) { LabelName = LOCTEXT("FeatureLevelLabel", "Feature Level:"); } else { UWorld* World = GetWorld(); if (World != nullptr) { ERHIFeatureLevel::Type TargetFeatureLevel = World->FeatureLevel; EShaderPlatform ShaderPlatform = GetShaderPlatformHelper(TargetFeatureLevel); const FText& PlatformText = GetFriendlyShaderPlatformName(ShaderPlatform); LabelName = FText::Format(LOCTEXT("WorldFeatureLevel", "{0}"), PlatformText); } } return LabelName; } /////////////////////////////////////////////////////////////////////////////// // end feature level control functions block /////////////////////////////////////////////////////////////////////////////// #undef LOCTEXT_NAMESPACE