You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#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 Change2957866on 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 Change2964098on 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]
1416 lines
40 KiB
C++
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;
|
|
}
|