You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
230 lines
11 KiB
230 lines
11 KiB
// Copyright Voulz 2021-2025. All Rights Reserved.
|
|
|
|
#include "ArchVisSceneViewExtension.h"
|
|
|
|
#include "ArchVisCineCameraComponent.h"
|
|
#include "ArchVisSubsystem.h"
|
|
#include "ArchVisToolsDefinitions.h"
|
|
#include "SceneView.h"
|
|
#include "Engine/LocalPlayer.h"
|
|
#include "GameFramework/PlayerController.h"
|
|
|
|
|
|
#if WITH_EDITOR
|
|
#include "LevelEditorViewport.h"
|
|
#endif
|
|
|
|
FArchVisSceneViewExtension::FArchVisSceneViewExtension(const FAutoRegister& AutoRegister, UArchVisSubsystem* InArchVisSubsystem) :
|
|
FSceneViewExtensionBase(AutoRegister), ArchVisSubsystem(InArchVisSubsystem)
|
|
{
|
|
UE_LOG(LogArchVisTools, Log, TEXT("FArchVisSceneViewExtension: Custom SceneViewExtension registered"));
|
|
}
|
|
|
|
void FArchVisSceneViewExtension::SetupViewFamily(FSceneViewFamily& InViewFamily)
|
|
{
|
|
}
|
|
|
|
void FArchVisSceneViewExtension::SetupView(FSceneViewFamily& InViewFamily, FSceneView& InView)
|
|
{
|
|
UArchVisCineCameraComponent* ArchVisComponent = InView.ViewActor ? InView.ViewActor->FindComponentByClass<UArchVisCineCameraComponent>() : nullptr;
|
|
#if WITH_EDITOR
|
|
if (!IsValid(ArchVisComponent))
|
|
{
|
|
if (const FLevelEditorViewportClient* ViewportClient = static_cast<FLevelEditorViewportClient*>(InView.SceneViewInitOptions.ViewElementDrawer))
|
|
{
|
|
ArchVisComponent = Cast<UArchVisCineCameraComponent>(ViewportClient->GetCameraComponentForView());
|
|
}
|
|
}
|
|
#endif
|
|
if (!IsValid(ArchVisComponent))
|
|
{
|
|
return;
|
|
}
|
|
|
|
InView.PreviousViewTransform.Reset();
|
|
|
|
const UWorld* World = InViewFamily.Scene ? InViewFamily.Scene->GetWorld() : nullptr;
|
|
FMinimalViewInfo CurrentPOV;
|
|
CurrentPOV.Location = InView.ViewLocation;
|
|
CurrentPOV.Rotation = InView.ViewRotation;
|
|
CurrentPOV.FOV = InView.FOV;
|
|
FMinimalViewInfo ViewInfo = GetCorrectedViewInfo(World, ArchVisComponent, InView.UnconstrainedViewRect.Size(), CurrentPOV);
|
|
|
|
// as per ULocalPlayer::GetProjectionData and UMoviePipelineImagePassBase::GetSceneViewForSampleState
|
|
FSceneViewProjectionData& ProjectionData = InView.SceneViewInitOptions;
|
|
ProjectionData.ViewOrigin = ViewInfo.Location;
|
|
ProjectionData.ViewRotationMatrix = FInverseRotationMatrix(ViewInfo.Rotation) * FMatrix(
|
|
FPlane(0, 0, 1, 0),
|
|
FPlane(1, 0, 0, 0),
|
|
FPlane(0, 1, 0, 0),
|
|
FPlane(0, 0, 0, 1));
|
|
|
|
const FIntRect ConstrainedViewRect = CalculateViewExtents(ViewInfo.AspectRatio, InView.SceneViewInitOptions.GetViewRect()); //this only affects the ProjectionData which is InView.SceneViewInitOptions
|
|
FMinimalViewInfo::CalculateProjectionMatrixGivenViewRectangle(ViewInfo, ViewInfo.AspectRatioAxisConstraint.Get(AspectRatio_MaintainXFOV), ConstrainedViewRect, ProjectionData);
|
|
|
|
InView.UpdateProjectionMatrix(ProjectionData.ProjectionMatrix);
|
|
}
|
|
|
|
FMinimalViewInfo FArchVisSceneViewExtension::GetCorrectedViewInfo(const UWorld* World, UArchVisCineCameraComponent* ArchVisComponent, const FIntPoint& UnconstrainedViewSize, const FMinimalViewInfo& CurrentViewInfo)
|
|
{
|
|
if (!ArchVisComponent)
|
|
{
|
|
return {};
|
|
}
|
|
|
|
// --- Then we find the cached ViewInfo that we will adjust to regenerate the Projection Matrix;
|
|
FMinimalViewInfo ViewInfo;
|
|
ArchVisComponent->GetCameraView(0.f, ViewInfo);
|
|
// The current issue is that a Camera Shake applied via the camera manager has been baked in the CurrentViewInfo in APlayerCameraManager::ApplyCameraModifiers and UCameraModifier_CameraShake::ModifyCamera on Tick
|
|
// So to keep the adjustments made by the camera shakes, we reuse the position, rotation and FOV of the CurrentViewInfo, inspired by UE::MovieScene::FEvaluateCameraShake::EvaluateCameraComponentShake
|
|
ViewInfo.Location = CurrentViewInfo.Location;
|
|
ViewInfo.Rotation = CurrentViewInfo.Rotation;
|
|
ViewInfo.FOV = CurrentViewInfo.FOV;
|
|
|
|
// We fix the aspect ratio constraint
|
|
if (!ViewInfo.bConstrainAspectRatio)
|
|
{
|
|
#if WITH_EDITOR
|
|
if (!ViewInfo.AspectRatioAxisConstraint.IsSet())
|
|
{
|
|
ViewInfo.AspectRatioAxisConstraint = GetDefault<ULevelEditorViewportSettings>()->AspectRatioAxisConstraint;
|
|
}
|
|
#endif
|
|
if (!ViewInfo.AspectRatioAxisConstraint.IsSet() || ViewInfo.AspectRatioAxisConstraint == EAspectRatioAxisConstraint::AspectRatio_MajorAxisFOV)
|
|
{
|
|
ViewInfo.AspectRatioAxisConstraint = UnconstrainedViewSize.X >= UnconstrainedViewSize.Y ? AspectRatio_MaintainXFOV : AspectRatio_MaintainYFOV;
|
|
}
|
|
}
|
|
|
|
float UnconstrainedAspectRatio = static_cast<float>(UnconstrainedViewSize.X) / static_cast<float>(UnconstrainedViewSize.Y);
|
|
float ViewportAspectRatio = ViewInfo.bConstrainAspectRatio ? ViewInfo.AspectRatio : UnconstrainedAspectRatio;
|
|
|
|
// At this point, InView.ViewRotation contains the already adjusted rotation, and ViewInfo.Rotation the original View rotation
|
|
// --- We are now ready to compute the automatic correction
|
|
if (ArchVisComponent->ProjectionMode == ECameraProjectionMode::Perspective && ArchVisComponent->bCorrectPerspective)
|
|
{
|
|
float HorFov = ArchVisComponent->GetHorizontalFieldOfView(); // The Angle of the Horizontal FOV for the Constrained Viewport
|
|
float VertFov = ArchVisComponent->GetVerticalFieldOfView(); // The Angle of the Vertical FOV for the Constrained Viewport
|
|
|
|
float Camera_Width, Camera_Height;
|
|
if (ViewportAspectRatio > UnconstrainedAspectRatio ||
|
|
(ViewportAspectRatio == UnconstrainedAspectRatio && ViewInfo.AspectRatioAxisConstraint.Get(AspectRatio_MaintainXFOV) == AspectRatio_MaintainXFOV))
|
|
{
|
|
// because the FOV returned by GetHorizontalFieldOfView is the FOV of the unconstrained viewport, we can use it to find the height of the viewport
|
|
// only when the constrained viewport would have the same width as the unconstrained viewport (black bands top and bottom)
|
|
Camera_Width = FMath::Tan(FMath::DegreesToRadians(HorFov) / 2) * 2;
|
|
Camera_Height = Camera_Width / ViewportAspectRatio; // (bIsMRQWorld ? UnconstrainedAspectRatio : ViewportAspectRatio);
|
|
}
|
|
else
|
|
{
|
|
// because the FOV returned by GetVerticalFieldOfView is the FOV of the unconstrained viewport, we can use it to find the width of the viewport
|
|
// only when the constrained viewport would have the same height as the unconstrained viewport (black bands left and right)
|
|
Camera_Height = FMath::Tan(FMath::DegreesToRadians(VertFov) / 2) * 2;
|
|
Camera_Width = Camera_Height * ViewportAspectRatio; //(bIsMRQWorld ? UnconstrainedAspectRatio : ViewportAspectRatio);;
|
|
}
|
|
|
|
float MaxPitch = FMath::Clamp(FMath::Abs(ArchVisComponent->MaxPitch), 0.0F, 89.99F);
|
|
float Target_Pitch = FMath::DegreesToRadians(FMath::Clamp(ViewInfo.Rotation.Pitch, -MaxPitch, MaxPitch)); //ensure the pitch is not 90 degrees
|
|
float Final_Camera_Angle = Target_Pitch * (1 - ArchVisComponent->CorrectionStrength); // apply the strength to get the real angle of the final camera
|
|
float Vert_Dist_Final_Camera = FMath::Tan(Final_Camera_Angle); // *hor_dist; // vertical distance of the final camera rotation
|
|
|
|
// float Camera_Width = FMath::Tan(FMath::DegreesToRadians(HorFov) / 2) * 2;
|
|
// float Camera_Height = FMath::Tan(FMath::DegreesToRadians(VertFov) / 2) * 2;
|
|
// float CameraAspectRatio = Camera_Width / Camera_Height;
|
|
// float Camera_Height = Camera_Width / ViewportAspectRatio;
|
|
|
|
//TODO: need to handle the constraint type
|
|
float Screen_Height = Camera_Height; // ViewInfo.bConstrainAspectRatio ? Camera_Height : Camera_Width / ViewportAspectRatio;
|
|
float Vert_Dist_To_Move = FMath::Tan(Target_Pitch - Final_Camera_Angle); // *hor_dist; // distance we should move vertically to see the same point
|
|
float Ratio_To_Move = Vert_Dist_To_Move / Screen_Height * 2; // the ratio of the screen we will need to offset. A *2 is applied as a projection offset of 1.0 moves the projection half
|
|
ViewInfo.Rotation.Pitch = FMath::RadiansToDegrees(Final_Camera_Angle);
|
|
|
|
// UE_LOG(LogTemp, Log, TEXT("ViewportAspectRatio: %f"), ViewportAspectRatio)
|
|
// UE_LOG(LogTemp, Log, TEXT("HorFov: %f"), HorFov)
|
|
// UE_LOG(LogTemp, Log, TEXT("VertFov: %f"), VertFov)
|
|
// UE_LOG(LogTemp, Log, TEXT("Target_Pitch: %f"), Target_Pitch)
|
|
// UE_LOG(LogTemp, Log, TEXT("Final_Camera_Angle: %f"), Final_Camera_Angle)
|
|
// UE_LOG(LogTemp, Log, TEXT("Vert_Dist_Final_Camera: %f"), Vert_Dist_Final_Camera)
|
|
// UE_LOG(LogTemp, Log, TEXT("Camera_Width: %f"), Camera_Width)
|
|
// UE_LOG(LogTemp, Log, TEXT("Camera_Height: %f"), Camera_Height)
|
|
// // UE_LOG(LogTemp, Log, TEXT("CameraAspectRatio: %f"), CameraAspectRatio)
|
|
// UE_LOG(LogTemp, Log, TEXT("Screen_Height: %f"), Screen_Height)
|
|
// UE_LOG(LogTemp, Log, TEXT("Vert_Dist_To_Move: %f"), Vert_Dist_To_Move)
|
|
// UE_LOG(LogTemp, Log, TEXT("Ratio_To_Move: %f"), Ratio_To_Move)
|
|
// UE_LOG(LogTemp, Log, TEXT("Pitch: %f"), ViewInfo.Rotation.Pitch)
|
|
|
|
FVector2D FinalProjectionOffset;
|
|
if (ArchVisComponent->bAllowRoll && ViewInfo.Rotation.Roll != 0.0f)
|
|
{
|
|
// FinalProjectionOffset = (FVector2D(0, Ratio_To_Move) + ViewInfo.OffCenterProjectionOffset).GetRotated(ViewInfo.Rotation.Roll);
|
|
// FinalProjectionOffset.X /= ViewportAspectRatio;
|
|
FinalProjectionOffset = FVector2D(0, Ratio_To_Move).GetRotated(ViewInfo.Rotation.Roll);
|
|
if (!UArchVisSubsystem::IsMoviePipelineWorld(World))
|
|
{
|
|
FinalProjectionOffset.X /= ViewportAspectRatio; //to compensate as an offset of 1 moves by one half screen, 1 horizontal and 1 vertical are not the same
|
|
// This is not the case in Movie Pipeline as it works differently
|
|
}
|
|
FinalProjectionOffset += ViewInfo.OffCenterProjectionOffset;
|
|
}
|
|
else
|
|
{
|
|
FinalProjectionOffset = FVector2D(0, Ratio_To_Move) + ViewInfo.OffCenterProjectionOffset;
|
|
}
|
|
ViewInfo.OffCenterProjectionOffset = FinalProjectionOffset;
|
|
}
|
|
|
|
if (!ArchVisComponent->bAllowRoll)
|
|
{
|
|
ViewInfo.Rotation.Roll = 0;
|
|
}
|
|
|
|
return ViewInfo;
|
|
}
|
|
|
|
FIntRect FArchVisSceneViewExtension::CalculateViewExtents(float AspectRatio, const FIntRect& ViewRect)
|
|
{
|
|
// Adjustment of FViewport::CalculateViewExtents to remove the need from the FViewport
|
|
|
|
FIntRect Result = ViewRect;
|
|
|
|
const float CurrentSizeX = ViewRect.Width();
|
|
const float CurrentSizeY = ViewRect.Height();
|
|
const float DesiredAspectRatio = CurrentSizeX / CurrentSizeY;
|
|
|
|
// the viewport's SizeX/SizeY may not always match the GetDesiredAspectRatio(), so adjust the requested AspectRatio to compensate
|
|
const float AdjustedAspectRatio = AspectRatio; // / (GetDesiredAspectRatio() / ((float)GetSizeXY().X / (float)GetSizeXY().Y));
|
|
|
|
// If desired, enforce a particular aspect ratio for the render of the scene.
|
|
// Results in black bars at top/bottom etc.
|
|
const float AspectRatioDifference = AdjustedAspectRatio - (CurrentSizeX / CurrentSizeY);
|
|
|
|
if( FMath::Abs( AspectRatioDifference ) > 0.01f )
|
|
{
|
|
// If desired aspect ratio is bigger than current - we need black bars on top and bottom.
|
|
if( AspectRatioDifference > 0.0f )
|
|
{
|
|
// Calculate desired Y size.
|
|
const int32 NewSizeY = FMath::Max(1, FMath::RoundToInt( CurrentSizeX / AdjustedAspectRatio ) );
|
|
Result.Min.Y = FMath::RoundToInt( 0.5f * (CurrentSizeY - NewSizeY) );
|
|
Result.Max.Y = Result.Min.Y + NewSizeY;
|
|
Result.Min.Y += ViewRect.Min.Y;
|
|
Result.Max.Y += ViewRect.Min.Y;
|
|
}
|
|
// Otherwise - will place bars on the sides.
|
|
else
|
|
{
|
|
const int32 NewSizeX = FMath::Max(1, FMath::RoundToInt( CurrentSizeY * AdjustedAspectRatio ) );
|
|
Result.Min.X = FMath::RoundToInt( 0.5f * (CurrentSizeX - NewSizeX) );
|
|
Result.Max.X = Result.Min.X + NewSizeX;
|
|
Result.Min.X += ViewRect.Min.X;
|
|
Result.Max.X += ViewRect.Min.X;
|
|
}
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
bool FArchVisSceneViewExtension::IsActiveThisFrame_Internal(const FSceneViewExtensionContext& Context) const
|
|
{
|
|
// Always enabled unless specifically requested via UArchVisMoviePipelineDeferredPass::AddViewExtensions
|
|
return !Context.IsA(FArchVisSceneViewExtensionContextDisableOnce{});
|
|
}
|
|
|