You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
Removed redundant private include paths from build.cs files. Fixed include paths to be relative to the private or public folders. Hid or removed includes that reached into other private module folders. Updated PublicInclude paths when necessary. #jira #preflight 631e5335544fb584da35175c [CL 21989607 by bryan sefcik in ue5-main branch]
481 lines
14 KiB
C++
481 lines
14 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "UI/VREditorFloatingUI.h"
|
|
#include "UI/VREditorUISystem.h"
|
|
#include "UI/VREditorBaseUserWidget.h"
|
|
#include "VREditorMode.h"
|
|
#include "Components/WidgetComponent.h"
|
|
#include "VREditorWidgetComponent.h"
|
|
#include "Components/StaticMeshComponent.h"
|
|
#include "VRModeSettings.h"
|
|
|
|
namespace VREd
|
|
{
|
|
static FAutoConsoleVariable UIFadeSpeed(TEXT("VREd.UIFadeSpeed"), 6.0f, TEXT("How fast UI should fade in and out"));
|
|
}
|
|
|
|
AVREditorFloatingUI::AVREditorFloatingUI(const FObjectInitializer& ObjectInitializer)
|
|
: Super(),
|
|
SlateWidget(nullptr),
|
|
UserWidget(nullptr),
|
|
WidgetComponent(nullptr),
|
|
Resolution(0, 0),
|
|
Owner(nullptr),
|
|
UserWidgetClass(nullptr),
|
|
bShouldBeVisible(),
|
|
FadeAlpha( 1.0f ),
|
|
FadeDelay( 0.0f ),
|
|
InitialScale( 1.0f ),
|
|
UISystemID(NAME_None),
|
|
bClearWidgetOnHide(false)
|
|
{
|
|
if (HasAnyFlags(RF_ClassDefaultObject))
|
|
{
|
|
return;
|
|
}
|
|
|
|
const bool bTransient = true;
|
|
USceneComponent* SceneComponent = CreateDefaultSubobject<USceneComponent>(TEXT("SceneComponent"), bTransient);
|
|
check(SceneComponent != nullptr);
|
|
this->RootComponent = SceneComponent;
|
|
|
|
WidgetComponent = CreateDefaultSubobject<UVREditorWidgetComponent>(TEXT("WidgetComponent"), bTransient);
|
|
WidgetComponent->SetEditTimeUsable(true);
|
|
WidgetComponent->SetupAttachment(SceneComponent);
|
|
WidgetComponent->PrimaryComponentTick.bTickEvenWhenPaused = true;
|
|
InitialScale = Scale;
|
|
|
|
{
|
|
WindowMeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("WindowMesh"));
|
|
WindowMeshComponent->SetMobility(EComponentMobility::Movable);
|
|
WindowMeshComponent->SetupAttachment(RootComponent);
|
|
|
|
WindowMeshComponent->SetGenerateOverlapEvents(false);
|
|
WindowMeshComponent->SetCanEverAffectNavigation(false);
|
|
WindowMeshComponent->bCastDynamicShadow = false;
|
|
WindowMeshComponent->bCastStaticShadow = false;
|
|
WindowMeshComponent->bAffectDistanceFieldLighting = false;
|
|
WindowMeshComponent->bSelectable = false;
|
|
}
|
|
}
|
|
|
|
|
|
void AVREditorFloatingUI::SetupWidgetComponent()
|
|
{
|
|
WidgetComponent->SetTwoSided(false); // No VR UI is two-sided
|
|
|
|
if (SlateWidget.IsValid())
|
|
{
|
|
// Slate UIs have bogus opacity in their texture's alpha, so ignore texture alpha for VR
|
|
WidgetComponent->SetOpacityFromTexture(0.0f); // Slate UIs have bogus opacity in their texture's alpha, so ignore texture alpha for VR
|
|
WidgetComponent->SetBackgroundColor(FLinearColor::Black);
|
|
WidgetComponent->SetBlendMode(EWidgetBlendMode::Opaque);
|
|
}
|
|
else // UMG UIs
|
|
{
|
|
if (!CreationContext.bMaskOutWidgetBackground) // Default behavior
|
|
{
|
|
WidgetComponent->SetOpacityFromTexture(1.0f);
|
|
WidgetComponent->SetBackgroundColor(FLinearColor::Transparent);
|
|
WidgetComponent->SetBlendMode(EWidgetBlendMode::Masked);
|
|
}
|
|
else // User override via CreationContext
|
|
{
|
|
WidgetComponent->SetOpacityFromTexture(1.0f);
|
|
WidgetComponent->SetBackgroundColor(FLinearColor::Transparent);
|
|
WidgetComponent->SetBlendMode(EWidgetBlendMode::Transparent);
|
|
}
|
|
}
|
|
|
|
// @todo vreditor: Ideally we use automatic mip map generation, otherwise the UI looks too crunchy at a distance.
|
|
// However, I tried this and on D3D11 the mips are all black.
|
|
WidgetComponent->SetDrawSize(FVector2D(Resolution.X, Resolution.Y)); // NOTE: Must be called before RegisterComponent() because collision data will be created during registration
|
|
|
|
// NOTE: Must be called *after* RegisterComponent() because UWidgetComponent nulls out Widget if no WidgetClass is set (WidgetClass is protected and there is no accessor)
|
|
if (SlateWidget.IsValid())
|
|
{
|
|
WidgetComponent->SetSlateWidget(SlateWidget.ToSharedRef());
|
|
}
|
|
else if (UserWidgetClass != nullptr)
|
|
{
|
|
// @todo vreditor unreal: Ideally we would do this in the constructor and not again after. Due to an apparent bug in UMG,
|
|
// we need to re-create the widget in lock-step with the WidgetComponent, otherwise input doesn't function correctly on the
|
|
// widget after the widget component is destroyed and recreated with the same user widget.
|
|
UserWidget = CreateWidget<UUserWidget>(GetWorld(), UserWidgetClass);
|
|
check(UserWidget != nullptr);
|
|
|
|
WidgetComponent->SetWidget(UserWidget);
|
|
}
|
|
|
|
// @todo vreditor: Is this useful?
|
|
//WidgetComponent->SetMaxInteractionDistance( 10000.0f );
|
|
|
|
// Default to visible
|
|
ShowUI(false);
|
|
|
|
// Set initial opacity
|
|
UpdateFadingState(0.0f);
|
|
|
|
// Set initial transform
|
|
UpdateTransformIfDocked();
|
|
|
|
// Update the window border mesh
|
|
{
|
|
const float WindowMeshSize = 100.0f; // Size of imported mesh, we need to inverse compensate for
|
|
|
|
const FVector WindowMeshScale = FVector(
|
|
1.0f,
|
|
GetSize().X / WindowMeshSize,
|
|
GetSize().Y / WindowMeshSize) * GetOwner().GetOwner().GetWorldScaleFactor();
|
|
WindowMeshComponent->SetRelativeScale3D(WindowMeshScale);
|
|
}
|
|
}
|
|
|
|
void AVREditorFloatingUI::SetSlateWidget( UVREditorUISystem& InitOwner, const VREditorPanelID& InID, const TSharedRef<SWidget>& InitSlateWidget, const FIntPoint InitResolution, const float InitScale, const EDockedTo InitDockedTo )
|
|
{
|
|
Owner = &InitOwner;
|
|
SetVRMode(&Owner->GetOwner());
|
|
|
|
UISystemID = InID;
|
|
|
|
SlateWidget = InitSlateWidget;
|
|
|
|
Resolution = InitResolution;
|
|
check(Resolution.X > 0 && Resolution.Y > 0);
|
|
|
|
Scale = InitScale;
|
|
InitialScale = Scale;
|
|
|
|
SetDockedTo(InitDockedTo);
|
|
SetupWidgetComponent();
|
|
}
|
|
|
|
void AVREditorFloatingUI::SetSlateWidget(const TSharedRef<SWidget>& InitSlateWidget)
|
|
{
|
|
SlateWidget = InitSlateWidget;
|
|
SetupWidgetComponent();
|
|
}
|
|
|
|
void AVREditorFloatingUI::SetUMGWidget( UVREditorUISystem& InitOwner, const VREditorPanelID& InID, TSubclassOf<UUserWidget> InitUserWidgetClass, const FIntPoint InitResolution, const float InitScale, const EDockedTo InitDockedTo )
|
|
{
|
|
Owner = &InitOwner;
|
|
SetVRMode(&Owner->GetOwner());
|
|
|
|
UISystemID = InID;
|
|
|
|
UserWidgetClass = InitUserWidgetClass;
|
|
|
|
Resolution = InitResolution;
|
|
check(Resolution.X > 0 && Resolution.Y > 0);
|
|
|
|
Scale = InitScale;
|
|
InitialScale = Scale;
|
|
|
|
SetDockedTo(InitDockedTo);
|
|
|
|
SetupWidgetComponent();
|
|
}
|
|
|
|
void AVREditorFloatingUI::TickManually(float DeltaTime)
|
|
{
|
|
Super::TickManually(DeltaTime);
|
|
|
|
if (WindowMeshComponent != nullptr && WidgetComponent->IsVisible())
|
|
{
|
|
const float WorldScaleFactor = GetOwner().GetOwner().GetWorldScaleFactor();
|
|
|
|
const float CurrentScaleFactor = (GetDockedTo() == EDockedTo::Nothing && WorldPlacedScaleFactor != 0) ? WorldPlacedScaleFactor : WorldScaleFactor;
|
|
|
|
const FVector AnimatedScale = CalculateAnimatedScale();
|
|
const FVector2D Size = GetSize();
|
|
const float WindowMeshSize = 100.0f; // Size of imported mesh, we need to inverse compensate for
|
|
|
|
const FVector WindowMeshScale = FVector(1.0,
|
|
Size.X / WindowMeshSize,
|
|
Size.Y / WindowMeshSize) * AnimatedScale * CurrentScaleFactor;
|
|
WindowMeshComponent->SetRelativeScale3D(WindowMeshScale);
|
|
|
|
const FVector NewScale(GetScale() * AnimatedScale * CurrentScaleFactor);
|
|
SetWidgetComponentScale(NewScale);
|
|
}
|
|
}
|
|
|
|
void AVREditorFloatingUI::Destroyed()
|
|
{
|
|
if (IsValid(this))
|
|
{
|
|
CleanupWidgetReferences();
|
|
}
|
|
|
|
Super::Destroyed();
|
|
}
|
|
|
|
void AVREditorFloatingUI::CleanupWidgetReferences()
|
|
{
|
|
if (WidgetComponent != nullptr)
|
|
{
|
|
// NOTE: We're nulling out widgets so that we don't have to wait for a GC to free up Slate resources (avoid shutdown crash)
|
|
WidgetComponent->SetSlateWidget(nullptr);
|
|
WidgetComponent->SetWidget(nullptr);
|
|
WidgetComponent = nullptr;
|
|
}
|
|
|
|
if (SlateWidget.IsValid())
|
|
{
|
|
SlateWidget.Reset();
|
|
}
|
|
|
|
// @todo vreditor unreal: UMG has a bug that prevents you from re-using the user widget for a new widget component
|
|
// after a previous widget component that was using it was destroyed
|
|
if (UserWidget != nullptr)
|
|
{
|
|
UserWidget->MarkAsGarbage();
|
|
UserWidget = nullptr;
|
|
}
|
|
}
|
|
|
|
void AVREditorFloatingUI::SetTransform(const FTransform& Transform)
|
|
{
|
|
if (!IsHidden())
|
|
{
|
|
const FVector AnimatedScale = CalculateAnimatedScale();
|
|
FTransform AnimatedTransform = Transform;
|
|
AnimatedTransform.SetScale3D(AnimatedTransform.GetScale3D() * AnimatedScale);
|
|
|
|
RootComponent->SetWorldLocation(AnimatedTransform.GetLocation());
|
|
RootComponent->SetWorldRotation(AnimatedTransform.GetRotation());
|
|
|
|
SetWidgetComponentScale(AnimatedTransform.GetScale3D());
|
|
}
|
|
}
|
|
|
|
|
|
void AVREditorFloatingUI::BeginDestroy()
|
|
{
|
|
CleanupWidgetReferences();
|
|
|
|
Super::BeginDestroy();
|
|
}
|
|
|
|
void AVREditorFloatingUI::UpdateFadingState(const float DeltaTime)
|
|
{
|
|
if (FadeDelay > 0.f)
|
|
{
|
|
FadeDelay -= DeltaTime;
|
|
}
|
|
else
|
|
{
|
|
if (bShouldBeVisible.GetValue())
|
|
{
|
|
FadeAlpha += VREd::UIFadeSpeed->GetFloat() * DeltaTime;
|
|
}
|
|
else
|
|
{
|
|
FadeAlpha -= VREd::UIFadeSpeed->GetFloat() * DeltaTime;
|
|
}
|
|
FadeAlpha = FMath::Clamp(FadeAlpha, 0.0f, 1.0f);
|
|
|
|
if (FadeAlpha > 0.0f + KINDA_SMALL_NUMBER)
|
|
{
|
|
// At least a little bit visible
|
|
if (IsHidden())
|
|
{
|
|
SetHidden(false);
|
|
|
|
// Iterate as floating UI children may have other mesh components
|
|
TInlineComponentArray<USceneComponent*> ComponentArray;
|
|
GetComponents(ComponentArray);
|
|
for (USceneComponent* Component : ComponentArray)
|
|
{
|
|
Component->SetVisibility(true);
|
|
}
|
|
FadeDelay = 0.0f;
|
|
}
|
|
}
|
|
|
|
if (FadeAlpha >= 1.0f - KINDA_SMALL_NUMBER)
|
|
{
|
|
// Fully visible
|
|
}
|
|
else if (FadeAlpha <= 0.0f + KINDA_SMALL_NUMBER)
|
|
{
|
|
// Fully invisible
|
|
if (!IsHidden())
|
|
{
|
|
SetHidden(true);
|
|
// Iterate as floating UI children may have other mesh components
|
|
TInlineComponentArray<USceneComponent*> ComponentArray;
|
|
GetComponents(ComponentArray);
|
|
for (USceneComponent* Component : ComponentArray)
|
|
{
|
|
Component->SetVisibility(false);
|
|
}
|
|
FadeDelay = 0.0f;
|
|
|
|
if (bClearWidgetOnHide)
|
|
{
|
|
SetSlateWidget(SNullWidget::NullWidget);
|
|
bClearWidgetOnHide = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set material color
|
|
const float UIBrightness = FadeAlpha * GetDefault<UVRModeSettings>()->UIBrightness;
|
|
WidgetComponent->SetTintColorAndOpacity(FLinearColor(UIBrightness, UIBrightness, UIBrightness).CopyWithNewOpacity(FadeAlpha));
|
|
|
|
}
|
|
}
|
|
|
|
FVector AVREditorFloatingUI::CalculateAnimatedScale() const
|
|
{
|
|
const float AnimationOvershootAmount = 0.7f; // @todo vreditor tweak
|
|
float EasedAlpha = UVREditorMode::OvershootEaseOut(FadeAlpha, AnimationOvershootAmount);
|
|
EasedAlpha = FMath::Clamp(EasedAlpha, 0.01f, 1.0f + AnimationOvershootAmount);
|
|
|
|
// Animate vertically more than horizontally; just looks a little better
|
|
const float ZScale = FMath::Max(0.001f, EasedAlpha);
|
|
const float YScale = FMath::Max(0.001f, 0.7f + 0.3f * EasedAlpha);
|
|
|
|
FVector AnimatedScale = FVector(1.0f, YScale, ZScale);
|
|
AnimatedScale.Y *= YScale;
|
|
AnimatedScale.Z *= ZScale;
|
|
|
|
return AnimatedScale;
|
|
}
|
|
|
|
void AVREditorFloatingUI::SetCollision(const ECollisionEnabled::Type InCollisionType, const ECollisionResponse InCollisionResponse, const ECollisionChannel InCollisionChannel)
|
|
{
|
|
WidgetComponent->SetCollisionEnabled(InCollisionType);
|
|
WidgetComponent->SetCollisionResponseToAllChannels(InCollisionResponse);
|
|
WidgetComponent->SetCollisionObjectType(InCollisionChannel);
|
|
|
|
if (WindowMeshComponent)
|
|
{
|
|
WindowMeshComponent->SetCollisionEnabled(InCollisionType);
|
|
WindowMeshComponent->SetCollisionResponseToAllChannels(InCollisionResponse);
|
|
WindowMeshComponent->SetCollisionObjectType(InCollisionChannel);
|
|
}
|
|
}
|
|
|
|
UUserWidget* AVREditorFloatingUI::GetUserWidget()
|
|
{
|
|
return UserWidget;
|
|
}
|
|
|
|
float AVREditorFloatingUI::GetInitialScale() const
|
|
{
|
|
return InitialScale;
|
|
}
|
|
|
|
void AVREditorFloatingUI::ShowUI( const bool bShow, const bool bAllowFading, const float InitFadeDelay, const bool bInClearWidgetOnHide )
|
|
{
|
|
if (!bShouldBeVisible.IsSet() || bShow != bShouldBeVisible.GetValue())
|
|
{
|
|
bShouldBeVisible = bShow;
|
|
|
|
if (!bAllowFading)
|
|
{
|
|
SetHidden(!bShow);
|
|
// Iterate as floating UI children may have other components
|
|
TInlineComponentArray<USceneComponent*> ComponentArray;
|
|
GetComponents(ComponentArray);
|
|
for (USceneComponent* Component : ComponentArray)
|
|
{
|
|
Component->SetVisibility(bShow);
|
|
}
|
|
FadeAlpha = bShow ? 1.0f : 0.0f;
|
|
if (bInClearWidgetOnHide )
|
|
{
|
|
SetSlateWidget(SNullWidget::NullWidget);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (bInClearWidgetOnHide)
|
|
{
|
|
bClearWidgetOnHide = bInClearWidgetOnHide;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Set collision on components
|
|
if (bShow)
|
|
{
|
|
SetCollision(ECollisionEnabled::QueryOnly, ECollisionResponse::ECR_Block, ECollisionChannel::ECC_WorldStatic);
|
|
}
|
|
else
|
|
{
|
|
SetCollision(ECollisionEnabled::NoCollision, ECollisionResponse::ECR_Ignore, ECollisionChannel::ECC_Visibility);
|
|
}
|
|
|
|
|
|
FadeDelay = InitFadeDelay;
|
|
}
|
|
}
|
|
|
|
|
|
void AVREditorFloatingUI::SetResolution(const FIntPoint& InResolution)
|
|
{
|
|
Resolution = InResolution;
|
|
check(Resolution.X > 0 && Resolution.Y > 0);
|
|
|
|
WidgetComponent->SetDrawSize(FVector2D(Resolution.X, Resolution.Y)); // NOTE: Must be called before
|
|
|
|
{
|
|
const float WindowMeshSize = 100.0f; // Size of imported mesh, we need to inverse compensate for
|
|
|
|
const FVector WindowMeshScale = FVector(
|
|
1.0f,
|
|
GetSize().X / WindowMeshSize,
|
|
GetSize().Y / WindowMeshSize) * GetOwner().GetOwner().GetWorldScaleFactor();
|
|
WindowMeshComponent->SetRelativeScale3D(WindowMeshScale);
|
|
}
|
|
}
|
|
|
|
FVector2D AVREditorFloatingUI::GetSize() const
|
|
{
|
|
const float Aspect = (float)Resolution.X / (float)Resolution.Y;
|
|
return FVector2D(Scale, Scale / Aspect);
|
|
}
|
|
|
|
float AVREditorFloatingUI::GetScale() const
|
|
{
|
|
return Scale;
|
|
}
|
|
|
|
void AVREditorFloatingUI::SetScale(const float NewSize, const bool bScaleWidget /*= true*/)
|
|
{
|
|
Scale = NewSize;
|
|
|
|
if (bScaleWidget)
|
|
{
|
|
const float WorldScaleFactor = Owner->GetOwner().GetWorldScaleFactor();
|
|
const FVector NewScale( Scale * WorldScaleFactor );
|
|
SetWidgetComponentScale(NewScale);
|
|
}
|
|
}
|
|
|
|
void AVREditorFloatingUI::SetWidgetComponentScale(const FVector& InScale)
|
|
{
|
|
const float Aspect = (float)Resolution.X / (float)Resolution.Y;
|
|
WidgetComponent->SetWorldScale3D(FVector(1.0f / InScale.X, 1.0f / (float)Resolution.X, 1.0f / (float)Resolution.Y / Aspect) * InScale);
|
|
}
|
|
|
|
VREditorPanelID AVREditorFloatingUI::GetID() const
|
|
{
|
|
return UISystemID;
|
|
}
|
|
|
|
TSharedPtr<SWidget> AVREditorFloatingUI::GetSlateWidget() const
|
|
{
|
|
return SlateWidget;
|
|
}
|
|
|
|
void AVREditorFloatingUI::SetWindowMesh(class UStaticMesh* InWindowMesh)
|
|
{
|
|
check(InWindowMesh != nullptr);
|
|
WindowMeshComponent->SetStaticMesh(InWindowMesh);
|
|
}
|