Files
UnrealEngineUWP/Engine/Source/Runtime/UMG/Private/Components/WidgetComponent.cpp
Marc Audy 67e3197eff Copying //UE4/Dev-Framework to //UE4/Dev-Main (Source: //UE4/Dev-Framework @ 2964666)
#lockdown Nick.Penwarden

==========================
MAJOR FEATURES + CHANGES
==========================

Change 2945310 on 2016/04/15 by Jon.Nabozny

	Fix UI locking Angular Rotation Offset for PhysicsConstraintComponents when the motion is for axes is Free or Locked.
	#JIRA UE-29368

Change 2945490 on 2016/04/15 by Jon.Nabozny

	Remove extraneous changes introduced in CL-2945310.

Change 2946706 on 2016/04/18 by James.Golding

	Checkin of slice test assets

Change 2947895 on 2016/04/19 by Benn.Gallagher

	PR #2292: Use ref instead of copy in FAnimNode_ModifyBone::EvaluateBoneTransforms (Contributed by MiKom)
	#jira UE-29567

Change 2947944 on 2016/04/19 by Benn.Gallagher

	Fixed a few extra needless bone container copies

Change 2948279 on 2016/04/19 by Marc.Audy

	Add well defined Map and Set Property names

Change 2948280 on 2016/04/19 by Marc.Audy

	Properly name parameters

Change 2948792 on 2016/04/19 by Marc.Audy

	Remove unused ini class name settings

Change 2948917 on 2016/04/19 by Aaron.McLeran

	UE-29654 FadeIn invalidates Audio Components in 4.11

Change 2949567 on 2016/04/20 by James.Golding

	- Add SliceProceduralMesh utility to UKismetProceduralMeshLibrary. It will slice the ProcMeshComp with a plan, including simple collision geom, and optionally create cap geometry, and create an addition ProceduralMeshComponent for the other half
	- Add support for simple collision on ProceduralMeshComponent, and added bUseComplexAsSimpleCollision to allow it to be used
	- Move GeomTools.h and .cpp from Editor to Engine module, so it can be used at runtime. Also move utils into an FGeomTools namespace.
	- Add GetSectionFromStaticMesh and CopyProceduralMeshFromStaticMeshComponent utilities to UKismetProceduralMeshLibrary
	- Expose UStaticMesh::GetNumLODs to BP, and add BP exposed UStaticMesh:: GetNumSections function

Change 2950482 on 2016/04/20 by Aaron.McLeran

	FORT-22973  SoundMix Fade Time not fading audio properly

	- Bug was due to bApplyToChildren case where the FSoundClassAdjuster wasn't getting the interpolated value before calling RecursiveApplyAdjuster in the case of non-overriden sound mixes.

Change 2951102 on 2016/04/21 by Thomas.Sarkanen

	Un-deprecated blueprint functions for attachment/detachment

	Renamed functions to <FuncName> (Deprecated).
	Hid functions in the BP context menu so new ones cant be added.

	#jira UE-23216 - "Snap to Target, Keep World Scale" when attaching doesn't work properly if parent is scaled.

Change 2951173 on 2016/04/21 by James.Golding

	Fix cap geom generation when more than one polygon is generated
	Fix CIS warning in KismetProceduralMeshLibrary.cpp

Change 2951334 on 2016/04/21 by Osman.Tsjardiwal

	Add CapMaterial param to SliceProceduralMesh util

Change 2951528 on 2016/04/21 by Marc.Audy

	Fix spelling errors in comments

Change 2952933 on 2016/04/22 by Lukasz.Furman

	fixed behavior tree getting stuck on instantly finished gameplay tasks
	copy of CL# 2952930

Change 2953948 on 2016/04/24 by James.Golding

	Put #if WITH_EDITOR back into FPoly::Triangulate to fix non-editor builds (FPoly::Finalize not available in non-editor)

Change 2954558 on 2016/04/25 by Marc.Audy

	Make USceneComponent::Attach* members private and remove deprecation messages and pragmas disabling/enabling deprecation throughout SceneComponent.h/cpp
	#jira UE-29038

Change 2954865 on 2016/04/25 by Aaron.McLeran

	UE-29763 Use HMD audio device only in VR preview mode, not for other PIE session types.

Change 2955009 on 2016/04/25 by Zak.Middleton

	#ue4 - Wrap call from UCharacterMovementComponent::PostPhysicsTickComponent() to UpdateBasedMovement() in a FScopedMovementUpdate to accumulate moves with better perf.

Change 2955878 on 2016/04/26 by Benn.Gallagher

	[Epic Friday] - Added spherical constraints to anim dynamics

Change 2956380 on 2016/04/26 by Lina.Halper

	PR #2298: Step interpolation for UAnimSequence (Contributed by douglaslassance)

Change 2956383 on 2016/04/26 by Lina.Halper

	Fixed to match coding standard

Change 2957866 on 2016/04/27 by Zak.Middleton

	#ue4 - Add max depenetration distance settings for CharacterMovementComponent. Add controls to throttle logging when character is stuck in geometry so it doesn't spam the log.

	- Depenetration settings are separated based on whether overlapping a Pawn versus other geometry, and furthermore by whether the Character is a proxy or not. Simulated proxies typically should not depenetrate a large amount because that effectively ignores the server authoritative location update.

	- "Stuck" logging is controlled by the console var "p.CharacterStuckWarningPeriod". Set to number of seconds between logged events, or less than zero to disable logging.

	#tests QA-Surfaces multiplayer, walking in to moving objects and pawns.

Change 2957953 on 2016/04/27 by Aaron.McLeran

	UE-30018 Fixing up audio component ref-counting to prevent triggering notifications when an audio component is still active after a sound finishes playing.

Change 2958011 on 2016/04/27 by Jon.Nabozny

	CalcAABB wasn't properly accounting for current transform on Convex elements, causing bad results.
	#JIRA UE-29525

Change 2958321 on 2016/04/27 by Lukasz.Furman

	path following update pass, added flags to request result, fixed AITask stacking vs scripted/BP move requests

Change 2959506 on 2016/04/28 by Aaron.McLeran

	PR #2330: Fix for ambient sounds not stopping when active and told to play again (Contributed by hgamiel)

Change 2959686 on 2016/04/28 by Marc.Audy

	Correctly handle multiple viewpoints when significance is being sorted descending

Change 2959773 on 2016/04/28 by Marc.Audy

	Fix shadowing warning

Change 2959785 on 2016/04/28 by Aaron.McLeran

	UE-30083 Sound concatenator node doesn't progress if child nodes don't produce wave instances

Change 2960852 on 2016/04/29 by Marc.Audy

	Merging //UE4/Dev-Main to Dev-Framework (//UE4/Dev-Framework) @ 2960738

Change 2960946 on 2016/04/29 by Marc.Audy

	Fix post merge compile error

Change 2962501 on 2016/05/02 by Marc.Audy

	Remove interim GetMutableAttach accessors and use the variables directly now that they are private

Change 2962535 on 2016/05/02 by Marc.Audy

	Merging //UE4/Dev-Main to Dev-Framework (//UE4/Dev-Framework) @ 2962478

Change 2962578 on 2016/05/02 by Marc.Audy

	Switch ObjectGraphMove to using UserFlags instead of custom move data

Change 2962651 on 2016/05/02 by Marc.Audy

	VS2015 shadow variable fixes

Change 2962662 on 2016/05/02 by Lukasz.Furman

	deprecated old implementation of gameplay debugger
	#jira UE-30011

Change 2962919 on 2016/05/02 by Marc.Audy

	VS2015 shadow variable fixes

Change 2963475 on 2016/05/02 by Mieszko.Zielinski

	Made SimpleMoveToLocation/Actor not reset velocity if agent not already at goal #UE4

	#jira UE-30176

Change 2964098 on 2016/05/03 by Marc.Audy

	Spelling fix

Change 2964099 on 2016/05/03 by Marc.Audy

	VS2015 shadow variable fixes

Change 2964156 on 2016/05/03 by Marc.Audy

	VS2015 shadow variable fixes

Change 2964272 on 2016/05/03 by Marc.Audy

	VS2015 Shadow Variable fixes

Change 2964395 on 2016/05/03 by Marc.Audy

	VS2015 Shadow Variable Fixes

Change 2964460 on 2016/05/03 by Marc.Audy

	Reschedule coolingdown tick functions during pause frames.
	#jira UE-30221

Change 2964666 on 2016/05/03 by Marc.Audy

	Fix shipping compile error

[CL 2964775 by Marc Audy in Main branch]
2016-05-03 15:44:33 -04:00

1416 lines
40 KiB
C++

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "UMGPrivatePCH.h"
#include "WidgetComponent.h"
#include "HittestGrid.h"
#if !UE_SERVER
#include "ISlateRHIRendererModule.h"
#include "ISlate3DRenderer.h"
#endif // !UE_SERVER
#include "DynamicMeshBuilder.h"
#include "Scalability.h"
#include "WidgetLayoutLibrary.h"
#include "PhysicsEngine/BodySetup.h"
#include "SGameLayerManager.h"
#include "Slate/WidgetRenderer.h"
#include "Slate/SWorldWidgetScreenLayer.h"
#include "Widgets/LayerManager/STooltipPresenter.h"
#include "Widgets/Layout/SPopup.h"
DECLARE_CYCLE_STAT(TEXT("3DHitTesting"), STAT_Slate3DHitTesting, STATGROUP_Slate);
static const FName SharedLayerName(TEXT("WidgetComponentScreenLayer"));
class FWorldWidgetScreenLayer : public IGameLayer
{
public:
FWorldWidgetScreenLayer(const FLocalPlayerContext& PlayerContext)
{
OwningPlayer = PlayerContext;
}
virtual ~FWorldWidgetScreenLayer()
{
// empty virtual destructor to help clang warning
}
void AddComponent(UWidgetComponent* Component)
{
if ( Component )
{
Components.AddUnique(Component);
if ( ScreenLayer.IsValid() )
{
if ( UUserWidget* UserWidget = Component->GetUserWidgetObject() )
{
ScreenLayer.Pin()->AddComponent(Component, UserWidget->TakeWidget());
}
}
}
}
void RemoveComponent(UWidgetComponent* Component)
{
if ( Component )
{
Components.RemoveSwap(Component);
if ( ScreenLayer.IsValid() )
{
ScreenLayer.Pin()->RemoveComponent(Component);
}
}
}
virtual TSharedRef<SWidget> AsWidget() override
{
if ( ScreenLayer.IsValid() )
{
return ScreenLayer.Pin().ToSharedRef();
}
TSharedRef<SWorldWidgetScreenLayer> NewScreenLayer = SNew(SWorldWidgetScreenLayer, OwningPlayer);
ScreenLayer = NewScreenLayer;
// Add all the pending user widgets to the surface
for ( TWeakObjectPtr<UWidgetComponent>& WeakComponent : Components )
{
if ( UWidgetComponent* Component = WeakComponent.Get() )
{
if ( UUserWidget* UserWidget = Component->GetUserWidgetObject() )
{
NewScreenLayer->AddComponent(Component, UserWidget->TakeWidget());
}
}
}
return NewScreenLayer;
}
private:
FLocalPlayerContext OwningPlayer;
TWeakPtr<SWorldWidgetScreenLayer> ScreenLayer;
TArray<TWeakObjectPtr<UWidgetComponent>> Components;
};
/** Represents a billboard sprite to the scene manager. */
class FWidget3DSceneProxy : public FPrimitiveSceneProxy
{
public:
/** Initialization constructor. */
FWidget3DSceneProxy( UWidgetComponent* InComponent, ISlate3DRenderer& InRenderer )
: FPrimitiveSceneProxy( InComponent )
, Pivot( InComponent->GetPivot() )
, Renderer( InRenderer )
, RenderTarget( InComponent->GetRenderTarget() )
, MaterialInstance( InComponent->GetMaterialInstance() )
, BodySetup( InComponent->GetBodySetup() )
, BlendMode( InComponent->GetBlendMode() )
, bUseLegacyRotation( InComponent->IsUsingLegacyRotation() )
{
bWillEverBeLit = false;
}
// FPrimitiveSceneProxy interface.
virtual void GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const override
{
#if WITH_EDITOR
const bool bWireframe = AllowDebugViewmodes() && ViewFamily.EngineShowFlags.Wireframe;
auto WireframeMaterialInstance = new FColoredMaterialRenderProxy(
GEngine->WireframeMaterial ? GEngine->WireframeMaterial->GetRenderProxy(IsSelected()) : nullptr,
FLinearColor(0, 0.5f, 1.f)
);
Collector.RegisterOneFrameMaterialProxy(WireframeMaterialInstance);
FMaterialRenderProxy* MaterialProxy = nullptr;
if ( bWireframe )
{
MaterialProxy = WireframeMaterialInstance;
}
else
{
MaterialProxy = MaterialInstance->GetRenderProxy(IsSelected());
}
#else
FMaterialRenderProxy* MaterialProxy = MaterialInstance->GetRenderProxy(IsSelected());
#endif
const FMatrix& ViewportLocalToWorld = GetLocalToWorld();
if( RenderTarget )
{
FTextureResource* TextureResource = RenderTarget->Resource;
if ( TextureResource )
{
float U = -RenderTarget->SizeX * Pivot.X;
float V = -RenderTarget->SizeY * Pivot.Y;
float UL = RenderTarget->SizeX * ( 1.0f - Pivot.X );
float VL = RenderTarget->SizeY * ( 1.0f - Pivot.Y );
int32 VertexIndices[4];
for ( int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++ )
{
FDynamicMeshBuilder MeshBuilder;
if ( VisibilityMap & ( 1 << ViewIndex ) )
{
if( bUseLegacyRotation )
{
VertexIndices[0] = MeshBuilder.AddVertex(FVector(U, V, 0), FVector2D(0, 0), FVector(1, 0, 0), FVector(0, 1, 0), FVector(0, 0, 1), FColor::White);
VertexIndices[1] = MeshBuilder.AddVertex(FVector(U, VL, 0), FVector2D(0, 1), FVector(1, 0, 0), FVector(0, 1, 0), FVector(0, 0, 1), FColor::White);
VertexIndices[2] = MeshBuilder.AddVertex(FVector(UL, VL, 0), FVector2D(1, 1), FVector(1, 0, 0), FVector(0, 1, 0), FVector(0, 0, 1), FColor::White);
VertexIndices[3] = MeshBuilder.AddVertex(FVector(UL, V, 0), FVector2D(1, 0), FVector(1, 0, 0), FVector(0, 1, 0), FVector(0, 0, 1), FColor::White);
}
else
{
VertexIndices[0] = MeshBuilder.AddVertex(-FVector(0, U, V), FVector2D(0, 0), FVector(1, 0, 0), FVector(0, 1, 0), FVector(0, 0, 1), FColor::White);
VertexIndices[1] = MeshBuilder.AddVertex(-FVector(0, U, VL), FVector2D(0, 1), FVector(1, 0, 0), FVector(0, 1, 0), FVector(0, 0, 1), FColor::White);
VertexIndices[2] = MeshBuilder.AddVertex(-FVector(0, UL, VL), FVector2D(1, 1), FVector(1, 0, 0), FVector(0, 1, 0), FVector(0, 0, 1), FColor::White);
VertexIndices[3] = MeshBuilder.AddVertex(-FVector(0, UL, V), FVector2D(1, 0), FVector(1, 0, 0), FVector(0, 1, 0), FVector(0, 0, 1), FColor::White);
}
MeshBuilder.AddTriangle(VertexIndices[0], VertexIndices[1], VertexIndices[2]);
MeshBuilder.AddTriangle(VertexIndices[0], VertexIndices[2], VertexIndices[3]);
MeshBuilder.GetMesh(ViewportLocalToWorld, MaterialProxy, SDPG_World, false, true, ViewIndex, Collector);
}
}
}
}
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
for ( int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++ )
{
if ( VisibilityMap & ( 1 << ViewIndex ) )
{
RenderCollision(BodySetup, Collector, ViewIndex, ViewFamily.EngineShowFlags, GetBounds(), IsSelected());
RenderBounds(Collector.GetPDI(ViewIndex), ViewFamily.EngineShowFlags, GetBounds(), IsSelected());
}
}
#endif
}
void RenderCollision(UBodySetup* InBodySetup, FMeshElementCollector& Collector, int32 ViewIndex, const FEngineShowFlags& EngineShowFlags, const FBoxSphereBounds& InBounds, bool bRenderInEditor) const
{
if ( InBodySetup )
{
bool bDrawCollision = EngineShowFlags.Collision && IsCollisionEnabled();
if ( bDrawCollision && AllowDebugViewmodes() )
{
// Draw simple collision as wireframe if 'show collision', collision is enabled, and we are not using the complex as the simple
const bool bDrawSimpleWireframeCollision = InBodySetup->CollisionTraceFlag != ECollisionTraceFlag::CTF_UseComplexAsSimple;
if ( FMath::Abs(GetLocalToWorld().Determinant()) < SMALL_NUMBER )
{
// Catch this here or otherwise GeomTransform below will assert
// This spams so commented out
//UE_LOG(LogStaticMesh, Log, TEXT("Zero scaling not supported (%s)"), *StaticMesh->GetPathName());
}
else
{
const bool bDrawSolid = !bDrawSimpleWireframeCollision;
const bool bProxyIsSelected = IsSelected();
if ( bDrawSolid )
{
// Make a material for drawing solid collision stuff
auto SolidMaterialInstance = new FColoredMaterialRenderProxy(
GEngine->ShadedLevelColorationUnlitMaterial->GetRenderProxy(IsSelected(), IsHovered()),
WireframeColor
);
Collector.RegisterOneFrameMaterialProxy(SolidMaterialInstance);
FTransform GeomTransform(GetLocalToWorld());
InBodySetup->AggGeom.GetAggGeom(GeomTransform, WireframeColor.ToFColor(true), SolidMaterialInstance, false, true, UseEditorDepthTest(), ViewIndex, Collector);
}
// wireframe
else
{
FColor CollisionColor = FColor(157, 149, 223, 255);
FTransform GeomTransform(GetLocalToWorld());
InBodySetup->AggGeom.GetAggGeom(GeomTransform, GetSelectionColor(CollisionColor, bProxyIsSelected, IsHovered()).ToFColor(true), nullptr, false, false, UseEditorDepthTest(), ViewIndex, Collector);
}
}
}
}
}
virtual FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View) const override
{
bool bVisible = true;
FPrimitiveViewRelevance Result;
Result.bDrawRelevance = IsShown(View) && bVisible;
Result.bOpaqueRelevance = ( BlendMode == EWidgetBlendMode::Opaque || BlendMode == EWidgetBlendMode::Masked );
Result.bMaskedRelevance = BlendMode == EWidgetBlendMode::Masked;
// ideally the TranslucencyRelevance should be filled out by the material, here we do it conservative
Result.bSeparateTranslucencyRelevance = Result.bNormalTranslucencyRelevance = (BlendMode == EWidgetBlendMode::Transparent);
Result.bDynamicRelevance = true;
Result.bShadowRelevance = IsShadowCast(View);
Result.bEditorPrimitiveRelevance = false;
return Result;
}
virtual void GetLightRelevance(const FLightSceneProxy* LightSceneProxy, bool& bDynamic, bool& bRelevant, bool& bLightMapped, bool& bShadowMapped) const override
{
bDynamic = false;
bRelevant = false;
bLightMapped = false;
bShadowMapped = false;
}
virtual void OnTransformChanged() override
{
Origin = GetLocalToWorld().GetOrigin();
}
virtual uint32 GetMemoryFootprint(void) const override { return(sizeof(*this) + GetAllocatedSize()); }
uint32 GetAllocatedSize(void) const { return( FPrimitiveSceneProxy::GetAllocatedSize() ); }
private:
FVector Origin;
FVector2D Pivot;
ISlate3DRenderer& Renderer;
UTextureRenderTarget2D* RenderTarget;
UMaterialInstanceDynamic* MaterialInstance;
UBodySetup* BodySetup;
EWidgetBlendMode BlendMode;
bool bUseLegacyRotation;
};
/**
* The hit tester used by all Widget Component objects.
*/
class FWidget3DHitTester : public ICustomHitTestPath
{
public:
FWidget3DHitTester( UWorld* InWorld )
: World( InWorld )
, CachedFrame(-1)
{}
// ICustomHitTestPath implementation
virtual TArray<FWidgetAndPointer> GetBubblePathAndVirtualCursors(const FGeometry& InGeometry, FVector2D DesktopSpaceCoordinate, bool bIgnoreEnabledStatus) const override
{
SCOPE_CYCLE_COUNTER(STAT_Slate3DHitTesting);
if( World.IsValid() /*&& ensure( World->IsGameWorld() )*/ )
{
UWorld* SafeWorld = World.Get();
if ( SafeWorld )
{
ULocalPlayer* const TargetPlayer = GEngine->GetLocalPlayerFromControllerId(SafeWorld, 0);
if( TargetPlayer && TargetPlayer->PlayerController )
{
if ( UPrimitiveComponent* HitComponent = GetHitResultAtScreenPositionAndCache(TargetPlayer->PlayerController, InGeometry.AbsoluteToLocal(DesktopSpaceCoordinate)) )
{
if ( UWidgetComponent* WidgetComponent = Cast<UWidgetComponent>(HitComponent) )
{
// Get the "forward" vector based on the current rotation system.
const FVector ForwardVector = WidgetComponent->IsUsingLegacyRotation() ? WidgetComponent->GetUpVector() : WidgetComponent->GetForwardVector();
// Make sure the player is interacting with the front of the widget
if ( FVector::DotProduct(ForwardVector, CachedHitResult.ImpactPoint - CachedHitResult.TraceStart) < 0.f )
{
// Make sure the player is close enough to the widget to interact with it
if ( FVector::DistSquared(CachedHitResult.TraceStart, WidgetComponent->GetComponentLocation()) <= FMath::Square(WidgetComponent->GetMaxInteractionDistance()) )
{
return WidgetComponent->GetHitWidgetPath(CachedHitResult.Location, bIgnoreEnabledStatus);
}
}
}
}
}
}
}
return TArray<FWidgetAndPointer>();
}
virtual void ArrangeChildren( FArrangedChildren& ArrangedChildren ) const override
{
for( TWeakObjectPtr<UWidgetComponent> Component : RegisteredComponents )
{
UWidgetComponent* WidgetComponent = Component.Get();
// Check if visible;
if ( WidgetComponent && WidgetComponent->GetSlateWindow().IsValid() )
{
FGeometry WidgetGeom;
ArrangedChildren.AddWidget( FArrangedWidget( WidgetComponent->GetSlateWindow().ToSharedRef(), WidgetGeom.MakeChild( WidgetComponent->GetDrawSize(), FSlateLayoutTransform() ) ) );
}
}
}
virtual TSharedPtr<struct FVirtualPointerPosition> TranslateMouseCoordinateFor3DChild( const TSharedRef<SWidget>& ChildWidget, const FGeometry& ViewportGeometry, const FVector2D& ScreenSpaceMouseCoordinate, const FVector2D& LastScreenSpaceMouseCoordinate ) const override
{
if ( World.IsValid() && ensure(World->IsGameWorld()) )
{
ULocalPlayer* const TargetPlayer = GEngine->GetLocalPlayerFromControllerId(World.Get(), 0);
if ( TargetPlayer && TargetPlayer->PlayerController )
{
FVector2D LocalMouseCoordinate = ViewportGeometry.AbsoluteToLocal(ScreenSpaceMouseCoordinate);
// Check for a hit against any widget components in the world
for ( TWeakObjectPtr<UWidgetComponent> Component : RegisteredComponents )
{
UWidgetComponent* WidgetComponent = Component.Get();
// Check if visible;
if ( WidgetComponent && WidgetComponent->GetSlateWindow() == ChildWidget )
{
if ( UPrimitiveComponent* HitComponent = GetHitResultAtScreenPositionAndCache(TargetPlayer->PlayerController, LocalMouseCoordinate) )
{
if ( WidgetComponent == HitComponent )
{
TSharedPtr<FVirtualPointerPosition> VirtualCursorPos = MakeShareable(new FVirtualPointerPosition);
FVector2D LocalHitLocation;
WidgetComponent->GetLocalHitLocation(CachedHitResult.Location, LocalHitLocation);
VirtualCursorPos->CurrentCursorPosition = LocalHitLocation;
VirtualCursorPos->LastCursorPosition = LocalHitLocation;
return VirtualCursorPos;
}
}
}
}
}
}
return nullptr;
}
// End ICustomHitTestPath
UPrimitiveComponent* GetHitResultAtScreenPositionAndCache(APlayerController* PlayerController, FVector2D ScreenPosition) const
{
UPrimitiveComponent* HitComponent = nullptr;
if ( GFrameNumber != CachedFrame || CachedScreenPosition != ScreenPosition )
{
CachedFrame = GFrameNumber;
CachedScreenPosition = ScreenPosition;
if ( PlayerController )
{
if ( PlayerController->GetHitResultAtScreenPosition(ScreenPosition, ECC_Visibility, true, CachedHitResult) )
{
return CachedHitResult.Component.Get();
}
}
}
else
{
return CachedHitResult.Component.Get();
}
return nullptr;
}
void RegisterWidgetComponent( UWidgetComponent* InComponent )
{
RegisteredComponents.AddUnique( InComponent );
}
void UnregisterWidgetComponent( UWidgetComponent* InComponent )
{
RegisteredComponents.RemoveSingleSwap( InComponent );
}
uint32 GetNumRegisteredComponents() const { return RegisteredComponents.Num(); }
UWorld* GetWorld() const { return World.Get(); }
private:
TArray< TWeakObjectPtr<UWidgetComponent> > RegisteredComponents;
TWeakObjectPtr<UWorld> World;
mutable int64 CachedFrame;
mutable FVector2D CachedScreenPosition;
mutable FHitResult CachedHitResult;
};
UWidgetComponent::UWidgetComponent( const FObjectInitializer& PCIP )
: Super( PCIP )
, DrawSize( FIntPoint( 500, 500 ) )
, MaxInteractionDistance( 1000.f )
, BackgroundColor( FLinearColor::Transparent )
, TintColorAndOpacity( FLinearColor::White )
, OpacityFromTexture( 1.0f )
, BlendMode( EWidgetBlendMode::Masked )
, bIsOpaque_DEPRECATED( false )
, bIsTwoSided( false )
, ParabolaDistortion( 0 )
, TickWhenOffscreen( false )
{
PrimaryComponentTick.bCanEverTick = true;
bTickInEditor = true;
RelativeRotation = FRotator::ZeroRotator;
BodyInstance.SetCollisionProfileName(FName(TEXT("UI")));
// Translucent material instances
static ConstructorHelpers::FObjectFinder<UMaterialInterface> TranslucentMaterial_Finder( TEXT("/Engine/EngineMaterials/Widget3DPassThrough_Translucent") );
static ConstructorHelpers::FObjectFinder<UMaterialInterface> TranslucentMaterial_OneSided_Finder(TEXT("/Engine/EngineMaterials/Widget3DPassThrough_Translucent_OneSided"));
TranslucentMaterial = TranslucentMaterial_Finder.Object;
TranslucentMaterial_OneSided = TranslucentMaterial_OneSided_Finder.Object;
// Opaque material instances
static ConstructorHelpers::FObjectFinder<UMaterialInterface> OpaqueMaterial_Finder( TEXT( "/Engine/EngineMaterials/Widget3DPassThrough_Opaque" ) );
static ConstructorHelpers::FObjectFinder<UMaterialInterface> OpaqueMaterial_OneSided_Finder(TEXT("/Engine/EngineMaterials/Widget3DPassThrough_Opaque_OneSided"));
OpaqueMaterial = OpaqueMaterial_Finder.Object;
OpaqueMaterial_OneSided = OpaqueMaterial_OneSided_Finder.Object;
// Masked material instances
static ConstructorHelpers::FObjectFinder<UMaterialInterface> MaskedMaterial_Finder(TEXT("/Engine/EngineMaterials/Widget3DPassThrough_Masked"));
static ConstructorHelpers::FObjectFinder<UMaterialInterface> MaskedMaterial_OneSided_Finder(TEXT("/Engine/EngineMaterials/Widget3DPassThrough_Masked_OneSided"));
MaskedMaterial = MaskedMaterial_Finder.Object;
MaskedMaterial_OneSided = MaskedMaterial_OneSided_Finder.Object;
LastLocalHitLocation = FVector2D::ZeroVector;
//bGenerateOverlapEvents = false;
bUseEditorCompositing = false;
bUseLegacyRotation = false;
Space = EWidgetSpace::World;
Pivot = FVector2D(0.5, 0.5);
bAddedToScreen = false;
// We want this because we want EndPlay to be called!
bWantsBeginPlay = true;
}
void UWidgetComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
ReleaseResources();
Super::EndPlay(EndPlayReason);
}
FPrimitiveSceneProxy* UWidgetComponent::CreateSceneProxy()
{
if ( Space != EWidgetSpace::Screen && WidgetRenderer.IsValid() )
{
return new FWidget3DSceneProxy(this, *WidgetRenderer->GetSlateRenderer());
}
return nullptr;
}
FBoxSphereBounds UWidgetComponent::CalcBounds(const FTransform & LocalToWorld) const
{
if ( Space != EWidgetSpace::Screen )
{
if( bUseLegacyRotation )
{
const FVector Origin = FVector(
( DrawSize.X * 0.5f ) - ( DrawSize.X * Pivot.X ),
( DrawSize.Y * 0.5f ) - ( DrawSize.Y * Pivot.Y ), .5f);
const FVector BoxExtent = FVector(DrawSize.X / 2.0f, DrawSize.Y / 2.0f, 1.0f);
return FBoxSphereBounds(Origin, BoxExtent, DrawSize.Size() / 2.0f).TransformBy(LocalToWorld);
}
else
{
const FVector Origin = FVector(.5f,
-( DrawSize.X * 0.5f ) + ( DrawSize.X * Pivot.X ),
-( DrawSize.Y * 0.5f ) + ( DrawSize.Y * Pivot.Y ));
const FVector BoxExtent = FVector(1.f, DrawSize.X / 2.0f, DrawSize.Y / 2.0f);
return FBoxSphereBounds(Origin, BoxExtent, DrawSize.Size() / 2.0f).TransformBy(LocalToWorld);
}
}
else
{
return FBoxSphereBounds(ForceInit).TransformBy(LocalToWorld);
}
}
UBodySetup* UWidgetComponent::GetBodySetup()
{
UpdateBodySetup();
return BodySetup;
}
FCollisionShape UWidgetComponent::GetCollisionShape(float Inflation) const
{
if ( Space != EWidgetSpace::Screen )
{
FVector BoxHalfExtent;
if( bUseLegacyRotation )
{
BoxHalfExtent = ( FVector(DrawSize.X * 0.5f, DrawSize.Y * 0.5f, 0.01f) * ComponentToWorld.GetScale3D() ) + Inflation;
}
else
{
BoxHalfExtent = ( FVector(0.01f, DrawSize.X * 0.5f, DrawSize.Y * 0.5f) * ComponentToWorld.GetScale3D() ) + Inflation;
}
if ( Inflation < 0.0f )
{
// Don't shrink below zero size.
BoxHalfExtent = BoxHalfExtent.ComponentMax(FVector::ZeroVector);
}
return FCollisionShape::MakeBox(BoxHalfExtent);
}
else
{
return FCollisionShape::MakeBox(FVector::ZeroVector);
}
}
void UWidgetComponent::OnRegister()
{
Super::OnRegister();
#if !UE_SERVER
if ( !IsRunningDedicatedServer() )
{
if ( Space != EWidgetSpace::Screen )
{
if ( GetWorld()->IsGameWorld() )
{
TSharedPtr<SViewport> GameViewportWidget = GEngine->GetGameViewportWidget();
RegisterHitTesterWithViewport(GameViewportWidget);
}
if( !MaterialInstance )
{
UpdateMaterialInstance();
}
}
if ( Space != EWidgetSpace::Screen )
{
if ( !WidgetRenderer.IsValid() && !GUsingNullRHI )
{
WidgetRenderer = MakeShareable(new FWidgetRenderer());
}
}
BodySetup = nullptr;
InitWidget();
}
#endif // !UE_SERVER
}
void UWidgetComponent::OnUnregister()
{
if( GetWorld()->IsGameWorld() )
{
TSharedPtr<SViewport> GameViewportWidget = GEngine->GetGameViewportWidget();
if( GameViewportWidget.IsValid() )
{
UnregisterHitTesterWithViewport(GameViewportWidget);
}
}
#if WITH_EDITOR
if (!GetWorld()->IsGameWorld())
{
ReleaseResources();
}
#endif
Super::OnUnregister();
}
void UWidgetComponent::RegisterHitTesterWithViewport(TSharedPtr<SViewport> ViewportWidget)
{
#if !UE_SERVER
if ( ViewportWidget.IsValid() )
{
TSharedPtr<ICustomHitTestPath> CustomHitTestPath = ViewportWidget->GetCustomHitTestPath();
if ( !CustomHitTestPath.IsValid() )
{
CustomHitTestPath = MakeShareable(new FWidget3DHitTester(GetWorld()));
ViewportWidget->SetCustomHitTestPath(CustomHitTestPath);
}
TSharedPtr<FWidget3DHitTester> Widget3DHitTester = StaticCastSharedPtr<FWidget3DHitTester>(CustomHitTestPath);
if ( Widget3DHitTester->GetWorld() == GetWorld() )
{
Widget3DHitTester->RegisterWidgetComponent(this);
}
}
#endif
}
void UWidgetComponent::UnregisterHitTesterWithViewport(TSharedPtr<SViewport> ViewportWidget)
{
#if !UE_SERVER
TSharedPtr<ICustomHitTestPath> CustomHitTestPath = ViewportWidget->GetCustomHitTestPath();
if ( CustomHitTestPath.IsValid() )
{
TSharedPtr<FWidget3DHitTester> WidgetHitTestPath = StaticCastSharedPtr<FWidget3DHitTester>(CustomHitTestPath);
WidgetHitTestPath->UnregisterWidgetComponent(this);
if ( WidgetHitTestPath->GetNumRegisteredComponents() == 0 )
{
ViewportWidget->SetCustomHitTestPath(nullptr);
}
}
#endif
}
void UWidgetComponent::DestroyComponent(bool bPromoteChildren/*= false*/)
{
Super::DestroyComponent(bPromoteChildren);
ReleaseResources();
}
void UWidgetComponent::ReleaseResources()
{
if ( Widget )
{
RemoveWidgetFromScreen();
Widget->MarkPendingKill();
Widget = nullptr;
}
WidgetRenderer.Reset();
SlateWindow.Reset();
HitTestGrid.Reset();
}
void UWidgetComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
#if !UE_SERVER
if (!IsRunningDedicatedServer())
{
static const int32 LayerZOrder = -100;
UpdateWidget();
if ( Widget == nullptr && !SlateWidget.IsValid() )
{
return;
}
if ( Space != EWidgetSpace::Screen )
{
if ( ShouldDrawWidget() )
{
DrawWidgetToRenderTarget(DeltaTime);
}
}
else
{
if ( ( Widget && !Widget->IsDesignTime() ) || SlateWidget.IsValid() )
{
UWorld* ThisWorld = GetWorld();
ULocalPlayer* TargetPlayer = GetOwnerPlayer();
APlayerController* PlayerController = TargetPlayer ? TargetPlayer->PlayerController : nullptr;
if ( TargetPlayer && PlayerController && IsVisible() )
{
if ( !bAddedToScreen )
{
if ( ThisWorld->IsGameWorld() )
{
if ( UGameViewportClient* ViewportClient = ThisWorld->GetGameViewport() )
{
TSharedPtr<IGameLayerManager> LayerManager = ViewportClient->GetGameLayerManager();
if ( LayerManager.IsValid() )
{
TSharedPtr<FWorldWidgetScreenLayer> ScreenLayer;
FLocalPlayerContext PlayerContext(TargetPlayer, ThisWorld);
TSharedPtr<IGameLayer> Layer = LayerManager->FindLayerForPlayer(TargetPlayer, SharedLayerName);
if ( !Layer.IsValid() )
{
TSharedRef<FWorldWidgetScreenLayer> NewScreenLayer = MakeShareable(new FWorldWidgetScreenLayer(PlayerContext));
LayerManager->AddLayerForPlayer(TargetPlayer, SharedLayerName, NewScreenLayer, LayerZOrder);
ScreenLayer = NewScreenLayer;
}
else
{
ScreenLayer = StaticCastSharedPtr<FWorldWidgetScreenLayer>(Layer);
}
bAddedToScreen = true;
Widget->SetPlayerContext(PlayerContext);
ScreenLayer->AddComponent(this);
}
}
}
}
}
else if ( bAddedToScreen )
{
RemoveWidgetFromScreen();
}
}
}
}
#endif // !UE_SERVER
}
bool UWidgetComponent::ShouldDrawWidget() const
{
const float RenderTimeThreshold = .5f;
if ( IsVisible() )
{
// If we don't tick when off-screen, don't bother ticking if it hasn't been rendered recently
if ( TickWhenOffscreen || GetWorld()->TimeSince(LastRenderTime) <= RenderTimeThreshold )
{
return true;
}
}
return false;
}
void UWidgetComponent::DrawWidgetToRenderTarget(float DeltaTime)
{
if ( GUsingNullRHI )
{
return;
}
if ( !SlateWindow.IsValid() )
{
return;
}
if ( DrawSize.X == 0 || DrawSize.Y == 0 )
{
return;
}
UpdateRenderTarget();
const float DrawScale = 1.0f;
WidgetRenderer->DrawWindow(
RenderTarget,
HitTestGrid.ToSharedRef(),
SlateWindow.ToSharedRef(),
DrawScale,
DrawSize,
DeltaTime);
}
void UWidgetComponent::RemoveWidgetFromScreen()
{
#if !UE_SERVER
if (!IsRunningDedicatedServer())
{
bAddedToScreen = false;
if ( UGameViewportClient* ViewportClient = GetWorld()->GetGameViewport() )
{
TSharedPtr<IGameLayerManager> LayerManager = ViewportClient->GetGameLayerManager();
if ( LayerManager.IsValid() )
{
ULocalPlayer* TargetPlayer = GetOwnerPlayer();
TSharedPtr<IGameLayer> Layer = LayerManager->FindLayerForPlayer(TargetPlayer, SharedLayerName);
if ( Layer.IsValid() )
{
TSharedPtr<FWorldWidgetScreenLayer> ScreenLayer = StaticCastSharedPtr<FWorldWidgetScreenLayer>(Layer);
ScreenLayer->RemoveComponent(this);
}
}
}
}
#endif // !UE_SERVER
}
class FWidgetComponentInstanceData : public FSceneComponentInstanceData
{
public:
FWidgetComponentInstanceData( const UWidgetComponent* SourceComponent )
: FSceneComponentInstanceData(SourceComponent)
, WidgetClass ( SourceComponent->GetWidgetClass() )
, RenderTarget( SourceComponent->GetRenderTarget() )
{}
virtual void ApplyToComponent(UActorComponent* Component, const ECacheApplyPhase CacheApplyPhase) override
{
FSceneComponentInstanceData::ApplyToComponent(Component, CacheApplyPhase);
CastChecked<UWidgetComponent>(Component)->ApplyComponentInstanceData(this);
}
virtual void AddReferencedObjects(FReferenceCollector& Collector) override
{
FSceneComponentInstanceData::AddReferencedObjects(Collector);
UClass* WidgetUClass = *WidgetClass;
Collector.AddReferencedObject(WidgetUClass);
Collector.AddReferencedObject(RenderTarget);
}
public:
TSubclassOf<UUserWidget> WidgetClass;
UTextureRenderTarget2D* RenderTarget;
};
FActorComponentInstanceData* UWidgetComponent::GetComponentInstanceData() const
{
return new FWidgetComponentInstanceData( this );
}
void UWidgetComponent::ApplyComponentInstanceData(FWidgetComponentInstanceData* WidgetInstanceData)
{
check(WidgetInstanceData);
// Note: ApplyComponentInstanceData is called while the component is registered so the rendering thread is already using this component
// That means all component state that is modified here must be mirrored on the scene proxy, which will be recreated to receive the changes later due to MarkRenderStateDirty.
if (GetWidgetClass() != WidgetClass)
{
return;
}
RenderTarget = WidgetInstanceData->RenderTarget;
if( MaterialInstance )
{
MaterialInstance->SetTextureParameterValue("SlateUI", RenderTarget);
}
MarkRenderStateDirty();
}
#if WITH_EDITORONLY_DATA
void UWidgetComponent::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
UProperty* Property = PropertyChangedEvent.MemberProperty;
if( Property && PropertyChangedEvent.ChangeType != EPropertyChangeType::Interactive )
{
static FName DrawSizeName("DrawSize");
static FName PivotName("Pivot");
static FName WidgetClassName("WidgetClass");
static FName IsOpaqueName("bIsOpaque");
static FName IsTwoSidedName("bIsTwoSided");
static FName BackgroundColorName("BackgroundColor");
static FName TintColorAndOpacityName("TintColorAndOpacity");
static FName OpacityFromTextureName("OpacityFromTexture");
static FName ParabolaDistortionName(TEXT("ParabolaDistortion"));
static FName BlendModeName( TEXT( "BlendMode" ) );
auto PropertyName = Property->GetFName();
if( PropertyName == WidgetClassName )
{
UpdateWidget();
MarkRenderStateDirty();
}
else if ( PropertyName == DrawSizeName || PropertyName == PivotName )
{
MarkRenderStateDirty();
UpdateBodySetup(true);
RecreatePhysicsState();
}
else if ( PropertyName == IsOpaqueName || PropertyName == IsTwoSidedName || PropertyName == BlendModeName )
{
UpdateMaterialInstance();
}
else if( PropertyName == BackgroundColorName || PropertyName == ParabolaDistortionName )
{
MarkRenderStateDirty();
}
else if( PropertyName == TintColorAndOpacityName || PropertyName == OpacityFromTextureName )
{
UpdateMaterialInstanceParameters();
MarkRenderStateDirty();
}
}
Super::PostEditChangeProperty(PropertyChangedEvent);
}
#endif
void UWidgetComponent::InitWidget()
{
// Don't do any work if Slate is not initialized
if ( FSlateApplication::IsInitialized() )
{
if ( WidgetClass && Widget == nullptr && GetWorld() )
{
Widget = CreateWidget<UUserWidget>(GetWorld(), WidgetClass);
}
#if WITH_EDITOR
if ( Widget && !GetWorld()->IsGameWorld() && !bEditTimeUsable )
{
if( !GEnableVREditorHacks )
{
// Prevent native ticking of editor component previews
Widget->SetDesignerFlags(EWidgetDesignFlags::Designing);
}
}
#endif
}
}
void UWidgetComponent::SetOwnerPlayer(ULocalPlayer* LocalPlayer)
{
if ( OwnerPlayer != LocalPlayer )
{
RemoveWidgetFromScreen();
OwnerPlayer = LocalPlayer;
}
}
ULocalPlayer* UWidgetComponent::GetOwnerPlayer() const
{
return OwnerPlayer ? OwnerPlayer : GEngine->GetLocalPlayerFromControllerId(GetWorld(), 0);
}
void UWidgetComponent::SetWidget(UUserWidget* InWidget)
{
if( InWidget != nullptr )
{
SetSlateWidget( nullptr );
}
if ( Widget )
{
RemoveWidgetFromScreen();
Widget->MarkPendingKill();
Widget = nullptr;
}
Widget = InWidget;
UpdateWidget();
}
void UWidgetComponent::SetSlateWidget( const TSharedPtr<SWidget>& InSlateWidget )
{
if( Widget != nullptr )
{
SetWidget( nullptr );
}
if( SlateWidget.IsValid() )
{
RemoveWidgetFromScreen();
SlateWidget.Reset();
}
SlateWidget = InSlateWidget;
UpdateWidget();
}
void UWidgetComponent::UpdateWidget()
{
// Don't do any work if Slate is not initialized
if ( FSlateApplication::IsInitialized() )
{
if ( Space != EWidgetSpace::Screen )
{
TSharedPtr<SWidget> NewSlateWidget;
if (Widget)
{
NewSlateWidget = Widget->TakeWidget();
}
if ( !SlateWindow.IsValid() )
{
SlateWindow = SNew(SVirtualWindow).Size(DrawSize);
}
if ( !HitTestGrid.IsValid() )
{
HitTestGrid = MakeShareable(new FHittestGrid);
}
SlateWindow->Resize(DrawSize);
if ( NewSlateWidget.IsValid() )
{
if ( NewSlateWidget != CurrentSlateWidget )
{
CurrentSlateWidget = NewSlateWidget;
SlateWindow->SetContent(NewSlateWidget.ToSharedRef());
}
}
else if( SlateWidget.IsValid() )
{
if ( SlateWidget != CurrentSlateWidget )
{
CurrentSlateWidget = SlateWidget;
SlateWindow->SetContent(SlateWidget.ToSharedRef());
}
}
else
{
CurrentSlateWidget = SNullWidget::NullWidget;
SlateWindow->SetContent( SNullWidget::NullWidget );
}
}
else
{
SlateWindow.Reset();
}
}
}
void UWidgetComponent::UpdateRenderTarget()
{
bool bWidgetRenderStateDirty = false;
bool bClearColorChanged = false;
FLinearColor ActualBackgroundColor = BackgroundColor;
switch ( BlendMode )
{
case EWidgetBlendMode::Opaque:
ActualBackgroundColor.A = 1.0f;
case EWidgetBlendMode::Masked:
ActualBackgroundColor.A = 0.0f;
}
if ( DrawSize.X != 0 && DrawSize.Y != 0 )
{
if ( RenderTarget == nullptr )
{
RenderTarget = NewObject<UTextureRenderTarget2D>(this);
RenderTarget->ClearColor = ActualBackgroundColor;
bClearColorChanged = bWidgetRenderStateDirty = true;
RenderTarget->InitCustomFormat(DrawSize.X, DrawSize.Y, PF_B8G8R8A8, false);
MaterialInstance->SetTextureParameterValue("SlateUI", RenderTarget);
}
else
{
// Update the format
if ( RenderTarget->SizeX != DrawSize.X || RenderTarget->SizeY != DrawSize.Y )
{
RenderTarget->InitCustomFormat(DrawSize.X, DrawSize.Y, PF_B8G8R8A8, false);
RenderTarget->UpdateResourceImmediate(false);
bWidgetRenderStateDirty = true;
}
// Update the clear color
if ( RenderTarget->ClearColor != ActualBackgroundColor )
{
RenderTarget->ClearColor = ActualBackgroundColor;
bClearColorChanged = bWidgetRenderStateDirty = true;
}
if ( bWidgetRenderStateDirty )
{
RenderTarget->UpdateResource();
}
}
}
if ( RenderTarget )
{
// If the clear color of the render target changed, update the BackColor of the material to match
if ( bClearColorChanged )
{
MaterialInstance->SetVectorParameterValue("BackColor", RenderTarget->ClearColor);
}
static FName ParabolaDistortionName(TEXT("ParabolaDistortion"));
float CurrentParabolaValue;
if ( MaterialInstance->GetScalarParameterValue(ParabolaDistortionName, CurrentParabolaValue) && CurrentParabolaValue != ParabolaDistortion )
{
MaterialInstance->SetScalarParameterValue(ParabolaDistortionName, ParabolaDistortion);
bWidgetRenderStateDirty = true;
}
if ( bWidgetRenderStateDirty )
{
MarkRenderStateDirty();
}
}
}
void UWidgetComponent::UpdateBodySetup( bool bDrawSizeChanged )
{
if (Space == EWidgetSpace::Screen)
{
// We do not have a bodysetup in screen space
BodySetup = nullptr;
}
else if ( !BodySetup || bDrawSizeChanged )
{
BodySetup = NewObject<UBodySetup>(this);
BodySetup->CollisionTraceFlag = CTF_UseSimpleAsComplex;
BodySetup->AggGeom.BoxElems.Add(FKBoxElem());
FKBoxElem* BoxElem = BodySetup->AggGeom.BoxElems.GetData();
FVector Origin;
if( bUseLegacyRotation )
{
Origin = FVector(
( DrawSize.X * 0.5f ) - ( DrawSize.X * Pivot.X ),
( DrawSize.Y * 0.5f ) - ( DrawSize.Y * Pivot.Y ), .5f);
BoxElem->X = DrawSize.X;
BoxElem->Y = DrawSize.Y;
BoxElem->Z = 0.01f;
}
else
{
Origin = FVector(.5f,
-( DrawSize.X * 0.5f ) + ( DrawSize.X * Pivot.X ),
-( DrawSize.Y * 0.5f ) + ( DrawSize.Y * Pivot.Y ));
BoxElem->X = 0.01f;
BoxElem->Y = DrawSize.X;
BoxElem->Z = DrawSize.Y;
}
BoxElem->SetTransform(FTransform::Identity);
BoxElem->Center = Origin;
}
}
void UWidgetComponent::GetLocalHitLocation(FVector WorldHitLocation, FVector2D& OutLocalWidgetHitLocation) const
{
// Find the hit location on the component
FVector ComponentHitLocation = ComponentToWorld.InverseTransformPosition(WorldHitLocation);
// Convert the 3D position of component space, into the 2D equivalent
if ( bUseLegacyRotation )
{
OutLocalWidgetHitLocation = FVector2D(ComponentHitLocation.X, ComponentHitLocation.Y);
}
else
{
OutLocalWidgetHitLocation = FVector2D(-ComponentHitLocation.Y, -ComponentHitLocation.Z);
}
// Offset the position by the pivot to get the position in widget space.
OutLocalWidgetHitLocation.X += DrawSize.X * Pivot.X;
OutLocalWidgetHitLocation.Y += DrawSize.Y * Pivot.Y;
// Apply the parabola distortion
FVector2D NormalizedLocation = OutLocalWidgetHitLocation / DrawSize;
NormalizedLocation.Y += ParabolaDistortion * ( -2.0f * NormalizedLocation.Y + 1.0f ) * NormalizedLocation.X * ( NormalizedLocation.X - 1.0f );
OutLocalWidgetHitLocation.Y = DrawSize.Y * NormalizedLocation.Y;
}
UUserWidget* UWidgetComponent::GetUserWidgetObject() const
{
return Widget;
}
const TSharedPtr<SWidget>& UWidgetComponent::GetSlateWidget() const
{
return SlateWidget;
}
TArray<FWidgetAndPointer> UWidgetComponent::GetHitWidgetPath(FVector WorldHitLocation, bool bIgnoreEnabledStatus, float CursorRadius)
{
FVector2D LocalHitLocation;
GetLocalHitLocation(WorldHitLocation, LocalHitLocation);
TSharedRef<FVirtualPointerPosition> VirtualMouseCoordinate = MakeShareable( new FVirtualPointerPosition );
VirtualMouseCoordinate->CurrentCursorPosition = LocalHitLocation;
VirtualMouseCoordinate->LastCursorPosition = LastLocalHitLocation;
// Cache the location of the hit
LastLocalHitLocation = LocalHitLocation;
TArray<FWidgetAndPointer> ArrangedWidgets;
if ( HitTestGrid.IsValid() )
{
ArrangedWidgets = HitTestGrid->GetBubblePath( LocalHitLocation, CursorRadius, bIgnoreEnabledStatus );
for( FWidgetAndPointer& ArrangedWidget : ArrangedWidgets )
{
ArrangedWidget.PointerPosition = VirtualMouseCoordinate;
}
}
return ArrangedWidgets;
}
TSharedPtr<SWindow> UWidgetComponent::GetSlateWindow() const
{
return SlateWindow;
}
FVector2D UWidgetComponent::GetDrawSize() const
{
return DrawSize;
}
void UWidgetComponent::SetDrawSize(FVector2D Size)
{
FIntPoint NewDrawSize((int32)Size.X, (int32)Size.Y);
if ( NewDrawSize != DrawSize )
{
DrawSize = NewDrawSize;
MarkRenderStateDirty();
UpdateBodySetup( true );
RecreatePhysicsState();
}
}
float UWidgetComponent::GetMaxInteractionDistance() const
{
return MaxInteractionDistance;
}
void UWidgetComponent::SetMaxInteractionDistance(float Distance)
{
MaxInteractionDistance = Distance;
}
void UWidgetComponent::SetBlendMode( const EWidgetBlendMode NewBlendMode )
{
if( NewBlendMode != this->BlendMode )
{
this->BlendMode = NewBlendMode;
if( IsRegistered() )
{
UpdateMaterialInstance();
}
}
}
void UWidgetComponent::SetTwoSided( const bool bWantTwoSided )
{
if( bWantTwoSided != this->bIsTwoSided )
{
this->bIsTwoSided = bWantTwoSided;
if( IsRegistered() )
{
UpdateMaterialInstance();
}
}
}
void UWidgetComponent::SetBackgroundColor( const FLinearColor NewBackgroundColor )
{
if( NewBackgroundColor != this->BackgroundColor)
{
this->BackgroundColor = NewBackgroundColor;
MarkRenderStateDirty();
}
}
void UWidgetComponent::SetTintColorAndOpacity( const FLinearColor NewTintColorAndOpacity )
{
if( NewTintColorAndOpacity != this->TintColorAndOpacity )
{
this->TintColorAndOpacity = NewTintColorAndOpacity;
UpdateMaterialInstanceParameters();
}
}
void UWidgetComponent::SetOpacityFromTexture( const float NewOpacityFromTexture )
{
if( NewOpacityFromTexture != this->OpacityFromTexture )
{
this->OpacityFromTexture = NewOpacityFromTexture;
UpdateMaterialInstanceParameters();
}
}
TSharedPtr< SWindow > UWidgetComponent::GetVirtualWindow() const
{
return StaticCastSharedPtr<SWindow>(SlateWindow);
}
void UWidgetComponent::PostLoad()
{
Super::PostLoad();
if ( GetLinkerUE4Version() < VER_UE4_ADD_PIVOT_TO_WIDGET_COMPONENT )
{
Pivot = FVector2D(0, 0);
}
if ( GetLinkerUE4Version() < VER_UE4_ADD_BLEND_MODE_TO_WIDGET_COMPONENT )
{
BlendMode = bIsOpaque_DEPRECATED ? EWidgetBlendMode::Opaque : EWidgetBlendMode::Transparent;
}
if( GetLinkerUE4Version() < VER_UE4_FIXED_DEFAULT_ORIENTATION_OF_WIDGET_COMPONENT )
{
// This indicates the value does not differ from the default. In some rare cases this could cause incorrect rotation for anyone who directly set a value of 0,0,0 for rotation
// However due to delta serialization we have no way to know if this value is actually different from the default so assume it is not.
if( RelativeRotation == FRotator::ZeroRotator )
{
RelativeRotation = FRotator(0.f, 0.f, 90.f);
}
bUseLegacyRotation = true;
}
}
void UWidgetComponent::UpdateMaterialInstance()
{
UMaterialInterface* Parent = nullptr;
switch ( BlendMode )
{
case EWidgetBlendMode::Opaque:
Parent = bIsTwoSided ? OpaqueMaterial : OpaqueMaterial_OneSided;
break;
case EWidgetBlendMode::Masked:
Parent = bIsTwoSided ? MaskedMaterial : MaskedMaterial_OneSided;
break;
case EWidgetBlendMode::Transparent:
Parent = bIsTwoSided ? TranslucentMaterial : TranslucentMaterial_OneSided;
break;
}
if( MaterialInstance )
{
MaterialInstance->MarkPendingKill();
MaterialInstance = nullptr;
}
MaterialInstance = UMaterialInstanceDynamic::Create(Parent, this);
UpdateMaterialInstanceParameters();
if( IsRegistered() )
{
MarkRenderStateDirty();
}
}
void UWidgetComponent::UpdateMaterialInstanceParameters()
{
if( MaterialInstance )
{
MaterialInstance->SetTextureParameterValue("SlateUI", RenderTarget);
MaterialInstance->SetVectorParameterValue("TintColorAndOpacity", TintColorAndOpacity);
MaterialInstance->SetScalarParameterValue("OpacityFromTexture", OpacityFromTexture);
}
}
void UWidgetComponent::SetWidgetClass(TSubclassOf<UUserWidget> InWidgetClass)
{
WidgetClass = InWidgetClass;
}