// Copyright Epic Games, Inc. All Rights Reserved. #include "Components/Viewport.h" #include "Misc/App.h" #include "CanvasTypes.h" #include "Components/LineBatchComponent.h" #include "Engine/LocalPlayer.h" #include "EngineUtils.h" #include "Framework/Application/SlateApplication.h" #include "Widgets/Text/STextBlock.h" #include "Widgets/Layout/SBox.h" #include "Widgets/SViewport.h" #include "PreviewScene.h" #include "EngineModule.h" #include "Slate/SceneViewport.h" #include "LegacyScreenPercentageDriver.h" #include "SceneInterface.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(Viewport) #define LOCTEXT_NAMESPACE "UMG" namespace FocusConstants { const float TransitionTime = 0.25f; } FUMGViewportCameraTransform::FUMGViewportCameraTransform() : TransitionStartTime(0) , ViewLocation(FVector::ZeroVector) , ViewRotation(FRotator::ZeroRotator) , DesiredLocation(FVector::ZeroVector) , LookAt(FVector::ZeroVector) , StartLocation(FVector::ZeroVector) , OrthoZoom(DEFAULT_ORTHOZOOM) {} void FUMGViewportCameraTransform::SetLocation(const FVector& Position) { ViewLocation = Position; DesiredLocation = ViewLocation; } void FUMGViewportCameraTransform::TransitionToLocation(const FVector& InDesiredLocation, bool bInstant) { if ( bInstant ) { SetLocation(InDesiredLocation); TransitionStartTime = FSlateApplication::Get().GetCurrentTime() - FocusConstants::TransitionTime; } else { DesiredLocation = InDesiredLocation; StartLocation = ViewLocation; TransitionStartTime = FSlateApplication::Get().GetCurrentTime(); } } bool FUMGViewportCameraTransform::UpdateTransition() { bool bIsAnimating = false; double TransitionProgress = FMath::Clamp(( FSlateApplication::Get().GetCurrentTime() - TransitionStartTime ) / FocusConstants::TransitionTime, 0.0, 1.0); if (TransitionProgress < 1.0 || ViewLocation != DesiredLocation) { const float Offset = (float)TransitionProgress - 1.0f; float LerpWeight = Offset * Offset * Offset + 1.0f; if (LerpWeight == 1.0f) { // Failsafe for the value not being exact on lerps ViewLocation = DesiredLocation; } else { ViewLocation = FMath::Lerp(StartLocation, DesiredLocation, LerpWeight); } bIsAnimating = true; } return bIsAnimating; } FMatrix FUMGViewportCameraTransform::ComputeOrbitMatrix() const { FTransform Transform = FTransform(-LookAt) * FTransform(FRotator(0, ViewRotation.Yaw, 0)) * FTransform(FRotator(0, 0, ViewRotation.Pitch)) * FTransform(FVector(0, ( ViewLocation - LookAt ).Size(), 0)); return Transform.ToMatrixNoScale() * FInverseRotationMatrix(FRotator(0, 90.f, 0)); } FUMGViewportClient::FUMGViewportClient(FPreviewScene* InPreviewScene) : PreviewScene(InPreviewScene) , Viewport(nullptr) , EngineShowFlags(ESFIM_Game) { FSceneInterface* Scene = GetScene(); ViewState.Allocate(Scene ? Scene->GetFeatureLevel() : GMaxRHIFeatureLevel); BackgroundColor = FColor(55, 55, 55); } FUMGViewportClient::~FUMGViewportClient() { } void FUMGViewportClient::Tick(float InDeltaTime) { if ( !GIntraFrameDebuggingGameThread ) { // Begin Play UWorld* PreviewWorld = PreviewScene->GetWorld(); if ( !PreviewWorld->GetBegunPlay() ) { for ( FActorIterator It(PreviewWorld); It; ++It ) { It->DispatchBeginPlay(); } PreviewWorld->SetBegunPlay(true); } // Tick PreviewWorld->Tick(LEVELTICK_All, InDeltaTime); } } void FUMGViewportClient::Draw(FViewport* InViewport, FCanvas* Canvas) { FViewport* ViewportBackup = Viewport; Viewport = InViewport ? InViewport : Viewport; const bool bIsRealTime = true; UWorld* World = GetWorld(); FGameTime Time; if ( bIsRealTime || GetScene() != World->Scene ) { Time = FGameTime::GetTimeSinceAppStart(); } else { Time = World->GetTime(); } // Setup a FSceneViewFamily/FSceneView for the viewport. FSceneViewFamilyContext ViewFamily(FSceneViewFamily::ConstructionValues( Canvas->GetRenderTarget(), GetScene(), EngineShowFlags) .SetTime(Time) .SetRealtimeUpdate(bIsRealTime)); // Get DPI derived view fraction. float GlobalResolutionFraction = GetDPIDerivedResolutionFraction(); // Force screen percentage show flag for High DPI. ViewFamily.EngineShowFlags.ScreenPercentage = true; //UpdateLightingShowFlags(ViewFamily.EngineShowFlags); //ViewFamily.ExposureSettings = ExposureSettings; //ViewFamily.LandscapeLODOverride = LandscapeLODOverride; FSceneView* View = CalcSceneView(&ViewFamily); ViewFamily.SetScreenPercentageInterface(new FLegacyScreenPercentageDriver( ViewFamily, GlobalResolutionFraction)); CachedViewProjectionMatrix = View->ViewMatrices.GetViewProjectionMatrix(); //SetupViewForRendering(ViewFamily, *View); FSlateRect SafeFrame; View->CameraConstrainedViewRect = View->UnscaledViewRect; //if ( CalculateEditorConstrainedViewRect(SafeFrame, Viewport) ) //{ // View->CameraConstrainedViewRect = FIntRect(SafeFrame.Left, SafeFrame.Top, SafeFrame.Right, SafeFrame.Bottom); //} if ( IsAspectRatioConstrained() ) { // Clear the background to black if the aspect ratio is constrained, as the scene view won't write to all pixels. Canvas->Clear(FLinearColor::Black); } Canvas->Clear(BackgroundColor); // workaround for hacky renderer code that uses GFrameNumber to decide whether to resize render targets --GFrameNumber; GetRendererModule().BeginRenderingViewFamily(Canvas, &ViewFamily); // Remove temporary debug lines. // Possibly a hack. Lines may get added without the scene being rendered etc. if (World) { constexpr const UWorld::ELineBatcherType LineBatchersToFlush[] = { UWorld::ELineBatcherType::World, UWorld::ELineBatcherType::Foreground }; World->FlushLineBatchers(LineBatchersToFlush); } Viewport = ViewportBackup; } FSceneInterface* FUMGViewportClient::GetScene() const { UWorld* World = GetWorld(); if ( World ) { return World->Scene; } return NULL; } UWorld* FUMGViewportClient::GetWorld() const { UWorld* OutWorldPtr = NULL; // If we have a valid scene get its world if ( PreviewScene ) { OutWorldPtr = PreviewScene->GetWorld(); } if ( OutWorldPtr == NULL ) { OutWorldPtr = GWorld; } return OutWorldPtr; } bool FUMGViewportClient::IsAspectRatioConstrained() const { return ViewInfo.bConstrainAspectRatio; } void FUMGViewportClient::SetBackgroundColor(FLinearColor InBackgroundColor) { BackgroundColor = InBackgroundColor; } FLinearColor FUMGViewportClient::GetBackgroundColor() const { return BackgroundColor; } float FUMGViewportClient::GetOrthoUnitsPerPixel(const FViewport* InViewport) const { const int32 SizeX = InViewport->GetSizeXY().X; // 15.0f was coming from the CAMERA_ZOOM_DIV marco, seems it was chosen arbitrarily return ( GetOrthoZoom() / ( SizeX * 15.f ) )/* * ComputeOrthoZoomFactor(SizeX)*/; } FMatrix FUMGViewportClient::GetViewProjectionMatrix() const { return CachedViewProjectionMatrix; } FSceneView* FUMGViewportClient::CalcSceneView(FSceneViewFamily* ViewFamily) { FSceneViewInitOptions ViewInitOptions; const FVector& ViewLocation = GetViewLocation(); const FRotator& ViewRotation = GetViewRotation(); const FIntPoint ViewportSizeXY = Viewport->GetSizeXY(); FIntRect ViewRect = FIntRect(0, 0, ViewportSizeXY.X, ViewportSizeXY.Y); ViewInitOptions.SetViewRectangle(ViewRect); ViewInitOptions.ViewOrigin = ViewLocation; ViewInitOptions.ViewRotationMatrix = FInverseRotationMatrix(ViewRotation); ViewInitOptions.ViewRotationMatrix = ViewInitOptions.ViewRotationMatrix * FMatrix( FPlane(0, 0, 1, 0), FPlane(1, 0, 0, 0), FPlane(0, 1, 0, 0), FPlane(0, 0, 0, 1)); //@TODO: Should probably be locally configurable (or just made into a FMinimalViewInfo property) const EAspectRatioAxisConstraint AspectRatioAxisConstraint = GetDefault()->AspectRatioAxisConstraint; FMinimalViewInfo::CalculateProjectionMatrixGivenView(ViewInfo, AspectRatioAxisConstraint, Viewport, /*inout*/ ViewInitOptions); ViewInitOptions.ViewFamily = ViewFamily; ViewInitOptions.SceneViewStateInterface = ViewState.GetReference(); ViewInitOptions.ViewElementDrawer = this; ViewInitOptions.BackgroundColor = GetBackgroundColor(); //ViewInitOptions.EditorViewBitflag = 0, // send the bit for this view - each actor will check it's visibility bits against this //ViewInitOptions.CursorPos = CurrentMousePos; FSceneView* View = new FSceneView(ViewInitOptions); ViewFamily->Views.Add(View); View->StartFinalPostprocessSettings(ViewLocation); //OverridePostProcessSettings(*View); View->EndFinalPostprocessSettings(ViewInitOptions); return View; } ///////////////////////////////////////////////////// // SAutoRefreshViewport class SAutoRefreshViewport : public SViewport { SLATE_BEGIN_ARGS(SAutoRefreshViewport) { } SLATE_END_ARGS() SAutoRefreshViewport() : PreviewScene(FPreviewScene::ConstructionValues().SetEditor(false)) {} void Construct(const FArguments& InArgs) { SViewport::FArguments ParentArgs; ParentArgs.IgnoreTextureAlpha(false); ParentArgs.EnableBlending(false); //ParentArgs.RenderDirectlyToWindow(true); SViewport::Construct(ParentArgs); ViewportClient = MakeShareable(new FUMGViewportClient(&PreviewScene)); Viewport = MakeShareable(new FSceneViewport(ViewportClient.Get(), SharedThis(this))); // The viewport widget needs an interface so it knows what should render SetViewportInterface(Viewport.ToSharedRef()); } virtual void Tick( const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime ) override { Viewport->Invalidate(); //Viewport->InvalidateDisplay(); Viewport->Tick(AllottedGeometry, InCurrentTime, InDeltaTime); ViewportClient->Tick(InDeltaTime); } public: TSharedPtr ViewportClient; TSharedPtr Viewport; /** preview scene */ FPreviewScene PreviewScene; }; ///////////////////////////////////////////////////// // UViewport UViewport::UViewport(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , ShowFlags(ESFIM_Game) { bIsVariable = true; PRAGMA_DISABLE_DEPRECATION_WARNINGS BackgroundColor = FLinearColor::Black; PRAGMA_ENABLE_DEPRECATION_WARNINGS ShowFlags.DisableAdvancedFeatures(); //ParentArgs.IgnoreTextureAlpha(false); //ParentArgs.EnableBlending(true); ////ParentArgs.RenderDirectlyToWindow(true); } void UViewport::ReleaseSlateResources(bool bReleaseChildren) { Super::ReleaseSlateResources(bReleaseChildren); ViewportWidget.Reset(); } TSharedRef UViewport::RebuildWidget() { if ( IsDesignTime() ) { return SNew(SBox) .HAlign(HAlign_Center) .VAlign(VAlign_Center) [ SNew(STextBlock) .Text(LOCTEXT("Viewport", "Viewport")) ]; } else { ViewportWidget = SNew(SAutoRefreshViewport); if ( GetChildrenCount() > 0 ) { ViewportWidget->SetContent(GetContentSlot()->Content ? GetContentSlot()->Content->TakeWidget() : SNullWidget::NullWidget); } return ViewportWidget.ToSharedRef(); } } void UViewport::SynchronizeProperties() { Super::SynchronizeProperties(); if ( ViewportWidget.IsValid() ) { check(ViewportWidget->ViewportClient.IsValid()); PRAGMA_DISABLE_DEPRECATION_WARNINGS ViewportWidget->ViewportClient->SetBackgroundColor(BackgroundColor); PRAGMA_ENABLE_DEPRECATION_WARNINGS ViewportWidget->ViewportClient->SetEngineShowFlags(ShowFlags); } } void UViewport::OnSlotAdded(UPanelSlot* InSlot) { // Add the child to the live canvas if it already exists if ( ViewportWidget.IsValid() ) { ViewportWidget->SetContent(InSlot->Content ? InSlot->Content->TakeWidget() : SNullWidget::NullWidget); } } void UViewport::OnSlotRemoved(UPanelSlot* InSlot) { // Remove the widget from the live slot if it exists. if ( ViewportWidget.IsValid() ) { ViewportWidget->SetContent(SNullWidget::NullWidget); } } UWorld* UViewport::GetViewportWorld() const { if ( ViewportWidget.IsValid() ) { return ViewportWidget->PreviewScene.GetWorld(); } return NULL; } FVector UViewport::GetViewLocation() const { if ( ViewportWidget.IsValid() ) { return ViewportWidget->ViewportClient->GetViewLocation(); } return FVector(); } void UViewport::SetViewLocation(FVector Vector) { if ( ViewportWidget.IsValid() ) { ViewportWidget->ViewportClient->SetViewLocation(Vector); } } FRotator UViewport::GetViewRotation() const { if ( ViewportWidget.IsValid() ) { return ViewportWidget->ViewportClient->GetViewRotation(); } return FRotator(); } void UViewport::SetViewRotation(FRotator Rotator) { if ( ViewportWidget.IsValid() ) { ViewportWidget->ViewportClient->SetViewRotation(Rotator); } } AActor* UViewport::Spawn(TSubclassOf ActorClass) { if ( ViewportWidget.IsValid() ) { UWorld* World = GetViewportWorld(); FActorSpawnParameters SpawnParameters; SpawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; return World->SpawnActor(ActorClass, FVector::ZeroVector, FRotator::ZeroRotator, SpawnParameters); } // TODO UMG Report spawning actor error before the world is ready. return NULL; } void UViewport::SetShowFlag(FString InShowFlagName, bool InValue) { if (ShowFlags.IsNameThere(*InShowFlagName, 0)) { int32 FlagIndex = ShowFlags.FindIndexByName(*InShowFlagName); ShowFlags.SetSingleFlag(FlagIndex, InValue); ViewportWidget->ViewportClient->SetEngineShowFlags(ShowFlags); } } FMatrix UViewport::GetViewProjectionMatrix() const { FMatrix ProjectionMatrix = FMatrix::Identity; if (ViewportWidget.IsValid()) { check(ViewportWidget->ViewportClient.IsValid()); ProjectionMatrix = ViewportWidget->ViewportClient->GetViewProjectionMatrix(); } return ProjectionMatrix; } void UViewport::SetEnableAdvancedFeatures(bool InEnableAdvancedFeatures) { ShowFlags.DisableAdvancedFeatures(); if (ViewportWidget.IsValid()) { check(ViewportWidget->ViewportClient.IsValid()); if (InEnableAdvancedFeatures) { ShowFlags.EnableAdvancedFeatures(); ViewportWidget->ViewportClient->SetEngineShowFlags(ShowFlags); } else { ShowFlags.DisableAdvancedFeatures(); ViewportWidget->ViewportClient->SetEngineShowFlags(ShowFlags); } } } void UViewport::SetLightIntensity(float InLightIntensity) { ViewportWidget->PreviewScene.SetLightBrightness(InLightIntensity); } void UViewport::SetSkyIntensity(float InLightIntensity) { ViewportWidget->PreviewScene.SetSkyBrightness(InLightIntensity); } PRAGMA_DISABLE_DEPRECATION_WARNINGS const FLinearColor& UViewport::GetBackgroundColor() const { return BackgroundColor; } void UViewport::SetBackgroundColor(const FLinearColor& InColor) { BackgroundColor = InColor; if (ViewportWidget.IsValid()) { check(ViewportWidget->ViewportClient.IsValid()); ViewportWidget->ViewportClient->SetBackgroundColor(BackgroundColor); } } PRAGMA_ENABLE_DEPRECATION_WARNINGS #if WITH_EDITOR const FText UViewport::GetPaletteCategory() { return LOCTEXT("Primitive", "Primitive"); } #endif ///////////////////////////////////////////////////// #undef LOCTEXT_NAMESPACE