EM_Task/UnrealEd/Private/SEditorViewport.cpp
Boshuang Zhao 5144a49c9b add
2026-02-13 16:18:33 +08:00

740 lines
29 KiB
C++

// 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<FEditorViewportClient> 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<SWidget> 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<FEditorViewportClient> 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<SWidget> 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<float>)
.Font(FEditorStyle::GetFontStyle(TEXT("MenuItem.Font")))
.MinValue(EV100Min)
.MaxValue(EV100Max)
.Value(this, &SEditorViewport::OnGetFixedEV100Value)
.OnValueChanged(const_cast<SEditorViewport*>(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<ULevelEditorViewportSettings>()->bAllowTranslateRotateZWidget;
}
bool SEditorViewport::Is2DModeVisible() const
{
return GetDefault<ULevelEditor2DSettings>()->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<ULevelEditorViewportSettings>()->bAllowTranslateRotateZWidget))
{
++WidgetModeAsInt;
}
if ((WidgetModeAsInt == FWidget::WM_2D) && (!GetDefault<ULevelEditor2DSettings>()->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<ULevelEditorViewportSettings>();
Settings->SnapToSurface.bEnabled = !Settings->SnapToSurface.bEnabled;
}
bool SEditorViewport::OnIsSurfaceSnapEnabled()
{
return GetDefault<ULevelEditorViewportSettings>()->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<SWidget> SEditorViewport::BuildFeatureLevelWidget() const
{
TSharedRef<SWidget> 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