208 lines
9.2 KiB
C++
208 lines
9.2 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#pragma once
|
|
|
|
#include "Math/BoxSphereBounds.h"
|
|
#include "Math/Transform.h"
|
|
#include "Templates/SharedPointer.h"
|
|
|
|
class USceneComponent;
|
|
class UViewAdjustedStaticMeshGizmoComponent;
|
|
|
|
namespace UE::GizmoRenderingUtil
|
|
{
|
|
class ISceneViewInterface;
|
|
|
|
/**
|
|
* Interface for a helper that can adjust a component's transform based on view information,
|
|
* used by UViewAdjustedStaticMeshGizmoComponent. Adjusters are typically expected to be safely
|
|
* shareable across game and render threads, so if they are not constant, they are expected to
|
|
* handle updates safely in their implementation.
|
|
*/
|
|
class IViewBasedTransformAdjuster
|
|
{
|
|
public:
|
|
virtual ~IViewBasedTransformAdjuster() {}
|
|
|
|
/**
|
|
* Given the component location and the view information, gives the desired transform of the component.
|
|
*/
|
|
virtual FTransform GetAdjustedComponentToWorld(
|
|
const ISceneViewInterface& View, const FTransform& CurrentComponentToWorld) = 0;
|
|
|
|
/**
|
|
* If an adjuster is used by a render proxy, it will use this endpoint for getting the transform.
|
|
* This allows an adjuster to be potentially shared between game and render threads. Of course,
|
|
* a component might instead choose to create a new adjuster whenever the render proxy is recreated,
|
|
* and recreate the proxy whenever any relevant parameters change.
|
|
*/
|
|
virtual FTransform GetAdjustedComponentToWorld_RenderThread(
|
|
const ISceneViewInterface& View, const FTransform& CurrentComponentToWorld)
|
|
{
|
|
// By default, just route to the other implementation. This is safe to do for any adjusters
|
|
// that remain constant for the life of the render proxy.
|
|
return GetAdjustedComponentToWorld(View, CurrentComponentToWorld);
|
|
}
|
|
|
|
/**
|
|
* Not every adjuster will care about the world/local setting, but it is useful for the
|
|
* a component that uses it to be able to blindly pass this information down without
|
|
* caring about the type of adjuster.
|
|
*/
|
|
virtual void UpdateWorldLocalState(bool bWorldIn) {}
|
|
|
|
/**
|
|
* Returns the transform that should be returned by a USceneComponent CalcBounds method, which
|
|
* can't know about the view. Although the default is to return the original bounds, any adjuster
|
|
* that scales the mesh arbitrarily large will probably need to return infinite bounds, whereas
|
|
* an adjuster that only changes orienation must account for any possible orientation.
|
|
* Note that returning infinite bounds causes the relevant components to no longer be frustum-culled,
|
|
* but this is acceptible for gizmos, which are typically few and on-screen.
|
|
*/
|
|
virtual FBoxSphereBounds GetViewIndependentBounds(const FTransform& LocalToWorld,
|
|
const FBoxSphereBounds& OriginalBounds)
|
|
{
|
|
return OriginalBounds;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Adjuster that maintains same view size but otherwise keeps the component transform. Note
|
|
* that this will look wrong if the component is not at the gizmo origin, because the distance
|
|
* relative to gizmo origin won't be scaled. Use FGizmoSubComponentTransformAdjuster if that is needed.
|
|
*/
|
|
class FSimpleConstantViewScaleAdjuster : public IViewBasedTransformAdjuster
|
|
{
|
|
public:
|
|
// IGizmoViewTransformAdjuster
|
|
INTERACTIVETOOLSFRAMEWORK_API virtual FTransform GetAdjustedComponentToWorld(
|
|
const ISceneViewInterface& View,
|
|
const FTransform& CurrentComponentToWorld) override;
|
|
|
|
INTERACTIVETOOLSFRAMEWORK_API virtual FBoxSphereBounds GetViewIndependentBounds(const FTransform& LocalToWorld,
|
|
const FBoxSphereBounds& OriginalBounds) override;
|
|
private:
|
|
};
|
|
|
|
/**
|
|
* An adjuster that can do various transformations common for sub gizmos, which are based off
|
|
* of the parent gizmo transform (in addition to the actual component transform).
|
|
*/
|
|
class FSubGizmoTransformAdjuster : public IViewBasedTransformAdjuster,
|
|
// Used for getting a weak pointer to itself when enqueuing state change on the render thread (in SetGizmoOriginTransform)
|
|
public TSharedFromThis<FSubGizmoTransformAdjuster, ESPMode::ThreadSafe>
|
|
{
|
|
public:
|
|
struct FSettings
|
|
{
|
|
// Some compilers (clang, at least) have issues with using a nested class with default initializers
|
|
// and a default constructor as a default argument in a function, hence this constructor.
|
|
FSettings() {};
|
|
|
|
// Keeps view size and offset relative to parent gizmo constant
|
|
bool bKeepConstantViewSize = true;
|
|
// Mirrors the component depending on which octant of the parent gizmo the view is located
|
|
bool bMirrorBasedOnOctant = true;
|
|
// Considers the parent gizmo transform to be unrotated, and applies the relative component transform on top of that.
|
|
// This is frequently not necessary to set because code upstream will typically update it every tick anyway
|
|
// via UViewAdjustedStaticMeshGizmoComponent's UpdateWorldLocalState implementation.
|
|
bool bUseWorldAxesForGizmo = false;
|
|
// If positive, keeps the component a given distance in front of the ortho camera, to avoid
|
|
// clipping it. Applied before bKeepConstantViewSize.
|
|
double DistanceInFrontOfCameraInOrtho = 0;
|
|
};
|
|
|
|
FSubGizmoTransformAdjuster(
|
|
const FTransform& GizmoOriginToComponent = FTransform::Identity, const FSettings& SettingsIn = FSettings())
|
|
: GizmoOriginToComponent_GameThread(GizmoOriginToComponent)
|
|
, GizmoOriginToComponent_RenderThread(GizmoOriginToComponent)
|
|
, Settings(SettingsIn)
|
|
{}
|
|
|
|
/**
|
|
* Update the transform of the gizmo origin relative to this component. This is safe to do even if the
|
|
* adjuster is used by the render thread (the render-side update gets queued properly).
|
|
*/
|
|
INTERACTIVETOOLSFRAMEWORK_API void SetGizmoOriginTransform(const FTransform& GizmoOriginToComponent);
|
|
|
|
void SetMirrorBasedOnOctant(bool bOn)
|
|
{
|
|
Settings.bMirrorBasedOnOctant = bOn;
|
|
}
|
|
void SetSettings(const FSettings& SettingsIn)
|
|
{
|
|
Settings = SettingsIn;
|
|
}
|
|
|
|
/**
|
|
* Static helper method to create and add this adjuster to a gizmo component for the common case of keeping a
|
|
* constant size gizmo.
|
|
*
|
|
* @param GizmoRootComponent The distance relative to this component is kept constant, and this is the component whose rotation
|
|
* is considered to be 0 when using global mode.
|
|
* @param bMirrorBasedOnOctant If true, mirrors the component around the ComponentToKeepDistanceConstantTo
|
|
* depending on which octant of that component the view is located in.
|
|
*/
|
|
INTERACTIVETOOLSFRAMEWORK_API static TSharedPtr<FSubGizmoTransformAdjuster> AddTransformAdjuster(
|
|
UViewAdjustedStaticMeshGizmoComponent* ComponentIn, USceneComponent* GizmoRootComponent,
|
|
bool bMirrorBasedOnOctant);
|
|
|
|
INTERACTIVETOOLSFRAMEWORK_API static TSharedPtr<FSubGizmoTransformAdjuster> AddTransformAdjuster(
|
|
UViewAdjustedStaticMeshGizmoComponent* ComponentIn, USceneComponent* GizmoRootComponent,
|
|
TFunction<bool()> ShouldMirrorBasedOnOctant);
|
|
|
|
|
|
// IGizmoViewTransformAdjuster
|
|
INTERACTIVETOOLSFRAMEWORK_API virtual FTransform GetAdjustedComponentToWorld(
|
|
const UE::GizmoRenderingUtil::ISceneViewInterface& View,
|
|
const FTransform& CurrentComponentToWorld) override;
|
|
|
|
INTERACTIVETOOLSFRAMEWORK_API virtual FTransform GetAdjustedComponentToWorld_RenderThread(
|
|
const ISceneViewInterface& View, const FTransform& CurrentComponentToWorld) override;
|
|
|
|
virtual void UpdateWorldLocalState(bool bWorldIn) override
|
|
{
|
|
Settings.bUseWorldAxesForGizmo = bWorldIn;
|
|
}
|
|
|
|
INTERACTIVETOOLSFRAMEWORK_API virtual FBoxSphereBounds GetViewIndependentBounds(const FTransform& LocalToWorld,
|
|
const FBoxSphereBounds& OriginalBounds) override;
|
|
private:
|
|
FTransform GizmoOriginToComponent_GameThread = FTransform::Identity;
|
|
FTransform GizmoOriginToComponent_RenderThread = FTransform::Identity;
|
|
// We might decide to have a render thread version of the settings as well, but it's unclear whether it's
|
|
// worth it, since it seems like a brief inconsistency during the update is unlikely to cause much of a
|
|
// problem: a user is unlikely to be changing multiple bools at once, and reading an outdated bool seems
|
|
// fine.
|
|
FSettings Settings;
|
|
};
|
|
|
|
/**
|
|
* Adjuster that takes the the view frame at the component world location, and applies a constant
|
|
* relative transform to that. This can be used to create billboard-like gizmo components that keep
|
|
* some orientation to the camera.
|
|
* This doesn't scale the offset relative to gizmo center, but the simplest option is typically to
|
|
* keep the component at gizmo origin and adjust the view relative transform appropriately.
|
|
*/
|
|
//~ TODO: May someday want a non-const version of this where the relative transform is editable, for sliders.
|
|
class FConstantViewRelativeTransformAdjuster : public IViewBasedTransformAdjuster
|
|
{
|
|
public:
|
|
FConstantViewRelativeTransformAdjuster(const FTransform& ViewRelativeTransformIn, bool bConstantSize = true)
|
|
: ViewRelativeTransform(ViewRelativeTransformIn)
|
|
, bKeepConstantViewSize(bConstantSize)
|
|
{
|
|
}
|
|
|
|
// IGizmoViewTransformAdjuster
|
|
INTERACTIVETOOLSFRAMEWORK_API virtual FTransform GetAdjustedComponentToWorld(
|
|
const UE::GizmoRenderingUtil::ISceneViewInterface& View,
|
|
const FTransform& CurrentComponentToWorld) override;
|
|
|
|
INTERACTIVETOOLSFRAMEWORK_API virtual FBoxSphereBounds GetViewIndependentBounds(const FTransform& LocalToWorld,
|
|
const FBoxSphereBounds& OriginalBounds) override;
|
|
private:
|
|
FTransform ViewRelativeTransform = FTransform::Identity;
|
|
bool bKeepConstantViewSize = true;
|
|
};
|
|
} |