Files
UnrealEngine/Engine/Plugins/Runtime/nDisplay/Source/DisplayClusterScenePreview/Public/DisplayClusterLightCardEditorHelper.h
2025-05-18 13:04:45 +08:00

408 lines
20 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "StageActor/DisplayClusterWeakStageActorPtr.h"
#include "Components/DisplayClusterLabelConfiguration.h"
#include "DisplayClusterMeshProjectionRenderer.h"
#include "SceneView.h"
#include "UnrealClient.h"
#include "Containers/Union.h"
#include "UObject/StrongObjectPtr.h"
class ADisplayClusterLightCardActor;
class ADisplayClusterRootActor;
class FPreviewScene;
class FSceneView;
class UProceduralMeshComponent;
class UTexture2D;
struct FSceneViewInitOptions;
#if WITH_EDITOR
class FEditorViewportClient;
#endif
/**
* Helper class for moving lightcards in an nDisplay cluster in various projection modes.
* Contains functions to perform coordinate conversion, scene setup, and lightcard movement, and manages the
* normal maps needed to convert from projected to world coordinates.
*/
struct FDisplayClusterLightCardEditorHelper
{
public:
enum class ECoordinateSystem : uint8
{
Cartesian,
Spherical
};
struct FSphericalCoordinates
{
public:
/** Constructors */
DISPLAYCLUSTERSCENEPREVIEW_API FSphericalCoordinates(const FVector& CartesianPosition);
DISPLAYCLUSTERSCENEPREVIEW_API FSphericalCoordinates();
/** Return equivalent cartesian coordinates */
DISPLAYCLUSTERSCENEPREVIEW_API FVector AsCartesian() const;
/** Addition operator */
DISPLAYCLUSTERSCENEPREVIEW_API FSphericalCoordinates operator+(FSphericalCoordinates const& Other) const;
/** Subtraction operator */
DISPLAYCLUSTERSCENEPREVIEW_API FSphericalCoordinates operator-(FSphericalCoordinates const& Other) const;
/** Conform parameters to their normal ranges */
DISPLAYCLUSTERSCENEPREVIEW_API void Conform();
/** Returns a conformed version of this struct without changing the current one */
DISPLAYCLUSTERSCENEPREVIEW_API FSphericalCoordinates GetConformed() const;
/** Returns true if the inclination is pointing at north or south poles, within the given margin (in radians) */
DISPLAYCLUSTERSCENEPREVIEW_API bool IsPointingAtPole(double Margin = 1e-6) const;
double Radius = 0; // unitless 0+ (when conforming)
double Inclination = 0; // radians 0 to PI (when conforming)
double Azimuth = 0; // radians -PI to PI (when conforming)
};
public:
/** Create a projection helper that automatically creates its own preview renderer for normal map generation. */
DISPLAYCLUSTERSCENEPREVIEW_API FDisplayClusterLightCardEditorHelper();
/** Create a projection helper that uses an existing preview renderer for normal map generation. */
DISPLAYCLUSTERSCENEPREVIEW_API FDisplayClusterLightCardEditorHelper(int32 RendererId);
/** Clean up the projection renderer and any data used by the helper. */
DISPLAYCLUSTERSCENEPREVIEW_API ~FDisplayClusterLightCardEditorHelper();
#if WITH_EDITOR
/** Set the viewport client for which this is handling rendering. If set, its data will be included in the SceneViewInitOptions generated by this helper. */
DISPLAYCLUSTERSCENEPREVIEW_API void SetEditorViewportClient(TWeakPtr<FEditorViewportClient> InViewportClient);
#endif
/** Set the projection mode to use. */
DISPLAYCLUSTERSCENEPREVIEW_API void SetProjectionMode(EDisplayClusterMeshProjectionType Value);
/** Get the projection mode to use. */
DISPLAYCLUSTERSCENEPREVIEW_API EDisplayClusterMeshProjectionType GetProjectionMode() const;
/** Set whether to use an orthographic projection. */
DISPLAYCLUSTERSCENEPREVIEW_API void SetIsOrthographic(bool bValue);
/** Get whether this is using an orthographic projection. */
DISPLAYCLUSTERSCENEPREVIEW_API bool GetIsOrthographic() const;
/**
* Set the root actor of the DisplayCluster being controlled. This will also update the renderer's root actor.
* Don't call this if the helper was created with an existing preview renderer, as it will automatically the root actor from that renderer.
*/
DISPLAYCLUSTERSCENEPREVIEW_API void SetRootActor(ADisplayClusterRootActor& NewRootActor);
/**
* If the root actor is a proxy, use this to set the corresponding level instance that is being proxied.
*/
DISPLAYCLUSTERSCENEPREVIEW_API void SetLevelInstanceRootActor(ADisplayClusterRootActor& NewRootActor);
/**
* Get (or generate) a visualization of one of the helper's normal maps.
*
* @param bShowNorthMap If true, return the texture for the north normal map. Otherwise, return the texture for the south normal map.
*/
UE_DEPRECATED(5.3, "The raw stage geoemetry normal maps are no longer exposed by the stage geometry component")
DISPLAYCLUSTERSCENEPREVIEW_API const UTexture2D* GetNormalMapTexture(bool bShowNorthMap);
/**
* Moves the given light cards to the specified pixel position within the provided scene view.
*
* @param Actors The actors that we are moving
* @param PixelPos The pixel location to move the actors to
* @param SceneView The scene view used to convert from pixel position to 3D position
*/
DISPLAYCLUSTERSCENEPREVIEW_API void MoveActorsToPixel(const TArray<FDisplayClusterWeakStageActorPtr>& Actors, const FIntPoint& PixelPos, const FSceneView& SceneView);
/**
* Moves specified actors to desired coordinates. Actual radius will be based on flush constraint and actor's RadialOffset.
*
* @param Actors The actors that we are moving
* @param SphericalCoords The desired location of the actors in spherical coordinates with respect to origin.
*/
DISPLAYCLUSTERSCENEPREVIEW_API void MoveActorsTo(const TArray<FDisplayClusterWeakStageActorPtr>& Actors, const FSphericalCoordinates& SphericalCoords);
/**
* Moves specified cards to a coordinate in viewport space as if dragged by a translate widget.
*
* @param Actors The actors that we are moving
* @param PixelPos The screen pixel position of the widget
* @param SceneView The scene view used to convert from pixel position to 3D position
* @param CoordinateSystem The coordinate system to use when computing drag constraints
* @param DragWidgetOffset The offset between the actual cursor position and the position of the widget when the drag action started
* @param DragAxis The axis along which the widget is being dragged
* @param PrimaryActor The actor used to calculate the translation/rotation delta. If not provided, the last entry in Actors will be used.
*/
DISPLAYCLUSTERSCENEPREVIEW_API void DragActors(const TArray<FDisplayClusterWeakStageActorPtr>& Actors, const FIntPoint& PixelPos, const FSceneView& SceneView,
ECoordinateSystem CoordinateSystem, const FVector& DragWidgetOffset, EAxisList::Type DragAxis, FDisplayClusterWeakStageActorPtr PrimaryActor = nullptr);
/**
* Moves specified UV actors to a coordinate in viewport space as if dragged by a translate widget.
*
* @param Actors The actors that we are moving
* @param PixelPos The screen pixel position of the widget
* @param SceneView The scene view used to convert from pixel position to 3D position
* @param DragWidgetOffset The offset between the actual cursor position and the position of the widget when the drag action started
* @param DragAxis The axis along which the widget is being dragged
* @param PrimaryActor The actor used to calculate the translation/rotation delta. If not provided, the last entry in Actors will be used.
*/
DISPLAYCLUSTERSCENEPREVIEW_API void DragUVActors(const TArray<FDisplayClusterWeakStageActorPtr>& Actors, const FIntPoint& PixelPos, const FSceneView& SceneView,
const FVector& DragWidgetOffset, EAxisList::Type DragAxis, FDisplayClusterWeakStageActorPtr PrimaryActor = nullptr);
/** Ensures that the actor root component is at the same location as the projection/origin */
DISPLAYCLUSTERSCENEPREVIEW_API void VerifyAndFixActorOrigin(const FDisplayClusterWeakStageActorPtr& Actor);
/**
* Calculates the relative normal vector and world position in the specified direction from the given origin.
*
* @param InOrigin The origin point of the view.
* @param InDirection The direction in which check the normal.
* @param OutWorldPosition The world-space coordinates of the calculated position.
* @param OutRelativeNormal The normal vector at the calculated position.
* @param InDesiredDistanceFromFlush The desired flush distance from the initial calculated position.
* @return true if the position was found, or false if the normal maps need to be updated first.
*/
DISPLAYCLUSTERSCENEPREVIEW_API bool CalculateNormalAndPositionInDirection(const FVector& InOrigin, const FVector& InDirection, FVector& OutWorldPosition,
FVector& OutRelativeNormal, double InDesiredDistanceFromFlush = 0.);
/**
* Calculates the desired direction from the origin given a pixel position within the view.
*
* @param InPixelPos The desired position within the view in pixels.
* @param InSceneView The view that was displayed when the pixel position was chosen.
* @param InOriginOffset An offset to apply to the origin before calling TraceScreenRay (if this is in orthographic mode).
* @param OutOrigin The origin point of the view.
* @param OutDirection The direction relative to the origin.
* @return true if the direction was found
*/
DISPLAYCLUSTERSCENEPREVIEW_API bool CalculateOriginAndDirectionFromPixelPosition(const FIntPoint& InPixelPos, const FSceneView& InSceneView, const FVector& InOriginOffset,
FVector& OutOrigin, FVector& OutDirection);
/** Converts a pixel coordinate into a point and direction vector in world space */
DISPLAYCLUSTERSCENEPREVIEW_API void PixelToWorld(const FSceneView& View, const FIntPoint& PixelPos, FVector& OutOrigin, FVector& OutDirection) const;
/** Converts a world coordinate into a point in screen space, and returns true if the world position is on the screen. */
DISPLAYCLUSTERSCENEPREVIEW_API bool WorldToPixel(const FSceneView& View, const FVector& WorldPos, FVector2D& OutPixelPos) const;
/** Converts a world coordinate into a point in screen space using a specific projection mode, and returns true if the world position is on the screen. */
DISPLAYCLUSTERSCENEPREVIEW_API bool WorldToPixel(const FSceneView& View, const FVector& WorldPos, FVector2D& OutPixelPos, EDisplayClusterMeshProjectionType OverrideProjectionMode) const;
/**
* Sets up FSceneViewInitOptions' basic settings in a consistent way for preview renders.
* If RotationMatrix is not provided, one will be automatically generated based on the view rotation.
*/
DISPLAYCLUSTERSCENEPREVIEW_API void GetSceneViewInitOptions(
FSceneViewInitOptions& OutViewInitOptions,
float InFOV,
const FIntPoint& InViewportSize,
const FVector& InLocation,
const FRotator& InRotation,
const EAspectRatioAxisConstraint InAspectRatioAxisConstraint = EAspectRatioAxisConstraint::AspectRatio_MaintainYFOV,
float InNearClipPlane = GNearClippingPlane,
const FMatrix* InRotationMatrix = nullptr,
float InDPIScale = 1.f);
/**
* Sets up projection-related settings of a preview render in a consistent way.
* This includes the ProjectionType and its related ProjectionTypeSettings.
* If provided, the UV projection's panning settings will updated to reflect the ViewLocation passed in.
*/
DISPLAYCLUSTERSCENEPREVIEW_API void ConfigureRenderProjectionSettings(FDisplayClusterMeshProjectionRenderSettings& OutRenderSettings, FVector ViewLocation = FVector::ZeroVector) const;
/** Gets the spherical coordinates of the specified actor. */
DISPLAYCLUSTERSCENEPREVIEW_API FSphericalCoordinates GetActorCoordinates(const FDisplayClusterWeakStageActorPtr& Actor);
/**
* Creates the default ShouldRenderPrimitive filter that most FDisplayClusterMeshProjectionRenderSettings will use.
* Filters actors so that only UV lightcards are shown in UV mode, and only non-UV light cards are shown otherwise.
*/
DISPLAYCLUSTERSCENEPREVIEW_API FDisplayClusterMeshProjectionPrimitiveFilter::FPrimitiveFilter CreateDefaultShouldRenderPrimitiveFilter() const;
/**
* Creates the default ShouldApplyProjectionToPrimitive filter that most FDisplayClusterMeshProjectionRenderSettings will use.
* Filters actors so that in UV projection mode, UV light cards are rendered linearly.
*/
DISPLAYCLUSTERSCENEPREVIEW_API FDisplayClusterMeshProjectionPrimitiveFilter::FPrimitiveFilter CreateDefaultShouldApplyProjectionToPrimitiveFilter() const;
struct FAddLightCardArgs
{
UE_DEPRECATED(5.3, "Use LabelConfiguration to configure label visibility.")
bool bShowLabels = false;
UE_DEPRECATED(5.3, "Use LabelConfiguration to configure label scale.")
float LabelScale = 1.f;
/** Label configuration to use for the new light card */
FDisplayClusterLabelConfiguration LabelConfiguration;
FAddLightCardArgs():
LabelConfiguration()
{
}
};
struct FSpawnActorArgs
{
/** [Required] The root actor managing the stage */
ADisplayClusterRootActor* RootActor;
/** [Required if Template is null] The class to spawn */
TSubclassOf<AActor> ActorClass;
/** [Required if ActorClass is null] The template to use */
const class UDisplayClusterStageActorTemplate* Template;
/** The name to set for the actor and label */
FName ActorName;
/** The projection mode the actor should be spawned in under */
EDisplayClusterMeshProjectionType ProjectionMode;
/** An override level for the actor, otherwise the RootActor level is used */
ULevel* Level;
/** Args if this is a light card being added to the root actor */
FAddLightCardArgs AddLightCardArgs;
/** Notify this actor should be used as a preview (such as for drag & drop) */
bool bIsPreview;
FSpawnActorArgs():
RootActor(nullptr),
Template(nullptr),
ActorName(NAME_None),
ProjectionMode(),
Level(nullptr),
bIsPreview(false)
{}
};
/** Spawns a new actor for stage use and adds it to the root actor if it is a light card */
static DISPLAYCLUSTERSCENEPREVIEW_API AActor* SpawnStageActor(const FSpawnActorArgs& InSpawnArgs);
/** Adds the given Light Card to the root actor */
static DISPLAYCLUSTERSCENEPREVIEW_API void AddLightCardsToRootActor(const TArray<ADisplayClusterLightCardActor*>& LightCards, ADisplayClusterRootActor* RootActor,
const FAddLightCardArgs& AddLightCardArgs = FAddLightCardArgs());
private:
/**
* Update the cached pointer to the root actor from the associated preview renderer and, if the actor has changed, update related data.
* Returns the root actor.
*/
ADisplayClusterRootActor* UpdateRootActor();
/**
* Update the cached pointer to the component marking the origin point of the projection and return it.
* Note that this has a cost, so avoid calling it frequently.
*/
USceneComponent* UpdateProjectionOriginComponent();
/** Get the origin point of the projection from the ProjectionOriginComponent (or root actor as a fallback). */
FVector GetProjectionOrigin() const;
/** Determines the appropriate delta rotation needed to move the specified light card to the given position within the view. */
FRotator GetActorRotationDelta(const FIntPoint& PixelPos, const FSceneView& View, const FDisplayClusterWeakStageActorPtr& Actor,
ECoordinateSystem CoordinateSystem, EAxisList::Type DragAxis, const FVector& DragWidgetOffset);
/** Determines the appropriate delta in spherical coordinates needed to move the specified actor to the given position within the view. */
FSphericalCoordinates GetActorTranslationDelta(const FIntPoint& PixelPos, const FSceneView& View, const FDisplayClusterWeakStageActorPtr& Actor,
ECoordinateSystem CoordinateSystem, EAxisList::Type DragAxis, const FVector& DragWidgetOffset);
/** Determines the appropriate delta in UV coordinates needed to move the specified UV actor to the given position within the view. */
FVector2D GetUVActorTranslationDelta(const FIntPoint& PixelPos, const FSceneView& View, const FDisplayClusterWeakStageActorPtr& Actor,
EAxisList::Type DragAxis, const FVector& DragWidgetOffset);
/** Moves specified actor to desired coordinates immediately using the current normal maps. Requires valid normal maps. */
void InternalMoveActorTo(const FDisplayClusterWeakStageActorPtr& Actor, const FSphericalCoordinates& Position, bool bIsFinalChange) const;
/** Moves specified actors to a coordinate in viewport space as if dragged by a widget. Requires valid normal maps. */
void InternalDragActors(const TArray<FDisplayClusterWeakStageActorPtr>& Actors, const FIntPoint& PixelPos, const FSceneView& View,
ECoordinateSystem CoordinateSystem, const FVector& DragWidgetOffset, EAxisList::Type DragAxis, FDisplayClusterWeakStageActorPtr PrimaryActor);
/** Sets the actor position to the given spherical coordinates */
void SetActorCoordinates(const FDisplayClusterWeakStageActorPtr& Actor, const FSphericalCoordinates& SphericalCoords) const;
/** Performs a ray trace against the stage's geometry, and returns the hit point. */
bool TraceStage(const FVector& RayStart, const FVector& RayEnd, FVector& OutHitLocation);
/** Traces the world geometry to find the best direction vector from the origin to a valid point in space using a screen ray. Requires valid normal maps. */
FVector TraceScreenRay(const FVector& RayOrigin, const FVector& RayDirection, const FVector& ViewOrigin);
/** Calculates the final distance from the origin of a light card, given its flush distance and a desired offset */
double CalculateFinalLightCardDistance(double FlushDistance, double DesiredOffsetFromFlush = 0.) const;
/** Invalidates the viewport's normal map, forcing it to be re-rendered before using it next */
void InvalidateNormalMap();
/** Update the normal maps and mesh if necessary. Returns true if there are now valid normal maps to read from. */
bool UpdateNormalMaps();
/** Update the normal map mesh. Only call this once both maps are valid. */
void UpdateNormalMapMesh();
/** Called when a world beings cleanup. We destroy our preview scene if the world containing the root actor is cleaned up. */
void OnWorldCleanup(UWorld* World, bool bSessionEnded, bool bCleanupResources);
#if WITH_EDITORONLY_DATA
/** Called when any actor properties changed, so we can invalidate normals if it is the root actor. */
void OnActorPropertyChanged(UObject* ObjectBeingModified, FPropertyChangedEvent& PropertyChangedEvent);
/** Called when the root actor's blueprint is compiled so we can invalidate normals. */
void OnRootActorBlueprintCompiled(UBlueprint* Blueprint);
#endif
#if WITH_EDITOR
/** Send property change events for any properties that may have changed due to a actor being moved. */
void PostEditChangePropertiesForMovedActor(const FDisplayClusterWeakStageActorPtr& Actor) const;
#endif
private:
#if WITH_EDITOR
/** The viewport client for which this is handling projections. This can be null. */
TWeakPtr<FEditorViewportClient> ViewportClient;
#endif
/** The preview scene containing NormalMapMeshComponent. */
TSharedPtr<FPreviewScene> NormalMeshScene;
/** A morphed ico-sphere mesh component that approximates the normal and depth map. */
TWeakObjectPtr<UProceduralMeshComponent> NormalMapMeshComponent;
/** The cached root actor of the DisplayCluster being controlled. You should generally use UpdateRootActor() instead of accessing this directly. */
TWeakObjectPtr<ADisplayClusterRootActor> CachedRootActor;
/** The level instance root actor of the DisplayCluster being controlled, if RootActor is a proxy. */
TWeakObjectPtr<ADisplayClusterRootActor> LevelInstanceRootActor;
/** The component of the root actor that is acting as the projection origin. Can be either the root component (stage origin) or a view point component */
TWeakObjectPtr<USceneComponent> ProjectionOriginComponent;
/** The projection mode of the view this is helping. */
EDisplayClusterMeshProjectionType ProjectionMode;
/** The ID of the preview renderer used for normal map generation, as returned by IDisplayClusterScenePreview. */
int32 RendererId;
/** The radius of the bounding sphere that entirely encapsulates the root actor */
float RootActorBoundingRadius = 0.0f;
/** Whether this created its own preview renderer (and therefore is responsible for destroying it). */
bool bCreatedRenderer = false;
/** If true, this is using an orthographic projection mode. */
bool bIsOrthographic = false;
/** Indicates if the cached normal map is invalid and needs to be redrawn */
bool bNormalMapInvalid = false;
};