Files
UnrealEngineUWP/Engine/Source/Editor/VREditor/UI/VREditorFloatingUI.cpp
Lauren Ridge f361d5ae54 Merging //UE4/Private-Geometry to Dev-VREditor (//UE4/Dev-VREditor) 3339870
#rb Mike.Fricker

[CL 3340396 by Lauren Ridge in Dev-VREditor branch]
2017-03-09 16:53:09 -05:00

346 lines
10 KiB
C++

// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
#include "VREditorFloatingUI.h"
#include "VREditorUISystem.h"
#include "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()
: Super(),
SlateWidget( nullptr ),
UserWidget( nullptr ),
WidgetComponent( nullptr ),
Resolution( 0, 0 ),
Owner( nullptr ),
UserWidgetClass( nullptr ),
bShouldBeVisible(),
FadeAlpha( 1.0f ),
FadeDelay( 0.0f ),
InitialScale( 1.0f )
{
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->bGenerateOverlapEvents = 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
{
WidgetComponent->SetOpacityFromTexture( 1.0f );
WidgetComponent->SetBackgroundColor( FLinearColor::Transparent );
WidgetComponent->SetBlendMode( EWidgetBlendMode::Masked );
}
// @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
{
// @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.
check( UserWidgetClass != nullptr );
UserWidget = CreateWidget<UVREditorBaseUserWidget>( GetWorld(), UserWidgetClass );
check( UserWidget != nullptr );
UserWidget->SetOwner( this );
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 TSharedRef<SWidget>& InitSlateWidget, const FIntPoint InitResolution, const float InitScale, const EDockedTo InitDockedTo )
{
Owner = &InitOwner;
SetVRMode( &Owner->GetOwner() );
SlateWidget = InitSlateWidget;
Resolution = InitResolution;
check( Resolution.X > 0 && Resolution.Y > 0 );
Scale = InitScale;
InitialScale = Scale;
SetDockedTo( InitDockedTo );
SetupWidgetComponent();
}
void AVREditorFloatingUI::SetUMGWidget( UVREditorUISystem& InitOwner, TSubclassOf<UVREditorBaseUserWidget> InitUserWidgetClass, const FIntPoint InitResolution, const float InitScale, const EDockedTo InitDockedTo )
{
Owner = &InitOwner;
SetVRMode( &Owner->GetOwner() );
check( InitUserWidgetClass != nullptr );
UserWidgetClass = InitUserWidgetClass;
Resolution = InitResolution;
check( Resolution.X > 0 && Resolution.Y > 0 );
Scale = InitScale;
InitialScale = Scale;
SetDockedTo( InitDockedTo );
SetupWidgetComponent();
}
void AVREditorFloatingUI::Destroyed()
{
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;
}
this->SlateWidget = nullptr;
// @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->MarkPendingKill();
UserWidget = nullptr;
}
Super::Destroyed();
}
void AVREditorFloatingUI::SetTransform( const FTransform& Transform )
{
if (!bHidden)
{
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::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( bHidden )
{
SetActorHiddenInGame( false );
WidgetComponent->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( !bHidden )
{
SetActorHiddenInGame( true );
WidgetComponent->SetVisibility( false );
FadeDelay = 0.0f;
}
}
// 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);
}
}
UVREditorBaseUserWidget* AVREditorFloatingUI::GetUserWidget()
{
return UserWidget;
}
float AVREditorFloatingUI::GetInitialScale() const
{
return InitialScale;
}
void AVREditorFloatingUI::ShowUI( const bool bShow, const bool bAllowFading, const float InitFadeDelay )
{
if( !bShouldBeVisible.IsSet() || bShow != bShouldBeVisible.GetValue() )
{
bShouldBeVisible = bShow;
if( !bAllowFading )
{
SetActorHiddenInGame( !bShow );
WidgetComponent->SetVisibility( bShow );
FadeAlpha = bShow ? 1.0f : 0.0f;
}
// 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;
}
}
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);
}