Files
2025-05-18 13:04:45 +08:00

603 lines
22 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "BaseGizmos/IntervalGizmo.h"
#include "InteractiveGizmoManager.h"
#include "BaseGizmos/AxisPositionGizmo.h"
#include "BaseGizmos/GizmoComponents.h"
#include "BaseGizmos/GizmoLineHandleComponent.h"
#include "BaseGizmos/GizmoViewContext.h"
// need this to implement hover
#include "BaseGizmos/GizmoBaseComponent.h"
#include "Components/SphereComponent.h"
#include "Components/PrimitiveComponent.h"
#include "ContextObjectStore.h"
#include "Engine/World.h"
#include "Engine/CollisionProfile.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(IntervalGizmo)
#define LOCTEXT_NAMESPACE "UIntervalGizmo"
/**
* FFloatParameterProxyChange tracks a change to the base transform for a FloatParameter
*/
class FFloatParameterProxyChange : public FToolCommandChange
{
public:
FGizmoFloatParameterChange To;
FGizmoFloatParameterChange From;
virtual void Apply(UObject* Object) override
{
UGizmoLocalFloatParameterSource* ParameterSource = CastChecked<UGizmoLocalFloatParameterSource>(Object);
ParameterSource->SetParameter(To.CurrentValue);
}
virtual void Revert(UObject* Object) override
{
UGizmoLocalFloatParameterSource* ParameterSource = CastChecked<UGizmoLocalFloatParameterSource>(Object);
ParameterSource->SetParameter(From.CurrentValue);
}
virtual FString ToString() const override { return TEXT("FFloatParameterProxyChange"); }
};
/**
* FGizmoFloatParameterChangeSource generates FFloatParameterProxyChange instances on Begin/End.
* Instances of this class can (for example) be attached to a UGizmoTransformChangeStateTarget for use TransformGizmo change tracking.
*/
class FGizmoFloatParameterChangeSource : public IToolCommandChangeSource
{
public:
FGizmoFloatParameterChangeSource(UGizmoLocalFloatParameterSource* ProxyIn)
{
Proxy = ProxyIn;
}
virtual ~FGizmoFloatParameterChangeSource() {}
TWeakObjectPtr<UGizmoLocalFloatParameterSource> Proxy;
TUniquePtr<FFloatParameterProxyChange> ActiveChange;
virtual void BeginChange() override
{
if (Proxy.IsValid())
{
ActiveChange = MakeUnique<FFloatParameterProxyChange>();
ActiveChange->From = Proxy->LastChange;
}
}
virtual TUniquePtr<FToolCommandChange> EndChange() override
{
if (Proxy.IsValid())
{
ActiveChange->To = Proxy->LastChange;
return MoveTemp(ActiveChange);
}
return TUniquePtr<FToolCommandChange>();
}
virtual UObject* GetChangeTarget() override
{
return Proxy.Get();
}
virtual FText GetChangeDescription() override
{
return LOCTEXT("FFGizmoFloatParameterChangeDescription", "GizmoFloatParameterChange");
}
};
/**
* This change source doesn't actually issue any valid transactions. Instead, it is a helper class
* that can get attached to the interval gizmo's state target to fire off BeginEditSequence and
* EndEditSequence on the start/end of a drag.
*/
class FIntervalGizmoChangeBroadcaster : public IToolCommandChangeSource
{
public:
FIntervalGizmoChangeBroadcaster(UIntervalGizmo* IntervalGizmoIn) : IntervalGizmo(IntervalGizmoIn) {}
virtual ~FIntervalGizmoChangeBroadcaster() {}
TWeakObjectPtr<UIntervalGizmo> IntervalGizmo;
virtual void BeginChange() override
{
if (IntervalGizmo.IsValid())
{
IntervalGizmo->BeginEditSequence();
}
}
virtual TUniquePtr<FToolCommandChange> EndChange() override
{
if (IntervalGizmo.IsValid())
{
IntervalGizmo->EndEditSequence();
}
return TUniquePtr<FToolCommandChange>();
}
virtual UObject* GetChangeTarget() override
{
return IntervalGizmo.Get();
}
virtual FText GetChangeDescription() override
{
return LOCTEXT("FIntervalGizmoChangeBroadcaster", "IntervalGizmoEdit");
}
};
AIntervalGizmoActor::AIntervalGizmoActor()
{
// root component is a hidden sphere
USphereComponent* SphereComponent = CreateDefaultSubobject<USphereComponent>(TEXT("GizmoCenter"));
RootComponent = SphereComponent;
SphereComponent->InitSphereRadius(1.0f);
SphereComponent->SetVisibility(false);
SphereComponent->SetCollisionProfileName(UCollisionProfile::NoCollision_ProfileName);
}
AIntervalGizmoActor* AIntervalGizmoActor::ConstructDefaultIntervalGizmo(UWorld* World, UGizmoViewContext* GizmoViewContext)
{
FActorSpawnParameters SpawnInfo;
AIntervalGizmoActor* NewActor = World->SpawnActor<AIntervalGizmoActor>(FVector::ZeroVector, FRotator::ZeroRotator, SpawnInfo);
const FLinearColor MintGreen(152 / 255.f, 1.f, 152 / 255.f);
// add all possible interval components (note: some may be hidden / unused)
NewActor->UpIntervalComponent = AddDefaultLineHandleComponent(World, NewActor, GizmoViewContext, MintGreen, FVector(0, 1, 0), FVector(0, 0, 1));
NewActor->DownIntervalComponent = AddDefaultLineHandleComponent(World, NewActor, GizmoViewContext, MintGreen, FVector(0, 1, 0), FVector(0, 0, 1));
NewActor->ForwardIntervalComponent = AddDefaultLineHandleComponent(World, NewActor, GizmoViewContext, MintGreen, FVector(1, 0, 0), FVector(0, 1, 0));
NewActor->BackwardIntervalComponent = AddDefaultLineHandleComponent(World, NewActor, GizmoViewContext, MintGreen, FVector(1, 0, 0), FVector(0, 1, 0));
NewActor->RightIntervalComponent = AddDefaultLineHandleComponent(World, NewActor, GizmoViewContext, MintGreen, FVector(0, 0, 1), FVector(1, 0, 0));
NewActor->LeftIntervalComponent = AddDefaultLineHandleComponent(World, NewActor, GizmoViewContext, MintGreen, FVector(0, 0, 1), FVector(1, 0, 0));
return NewActor;
}
UInteractiveGizmo* UIntervalGizmoBuilder::BuildGizmo(const FToolBuilderState& SceneState) const
{
UIntervalGizmo* NewGizmo = NewObject<UIntervalGizmo>(SceneState.GizmoManager);
NewGizmo->SetWorld(SceneState.World);
UGizmoViewContext* GizmoViewContext = SceneState.ToolManager->GetContextObjectStore()->FindContext<UGizmoViewContext>();
check(GizmoViewContext && GizmoViewContext->IsValidLowLevel());
// use default gizmo actor if client has not given us a new builder
NewGizmo->SetGizmoActorBuilder(GizmoActorBuilder ? GizmoActorBuilder : MakeShared<FIntervalGizmoActorFactory>(GizmoViewContext));
// override default hover function if proposed
if (UpdateHoverFunction)
{
NewGizmo->SetUpdateHoverFunction(UpdateHoverFunction);
}
if (UpdateCoordSystemFunction)
{
NewGizmo->SetUpdateCoordSystemFunction(UpdateCoordSystemFunction);
}
return NewGizmo;
}
// Init static FName
FString UIntervalGizmo::GizmoName = TEXT("IntervalGizmo");
void UIntervalGizmo::SetWorld(UWorld* WorldIn)
{
this->World = WorldIn;
}
void UIntervalGizmo::SetGizmoActorBuilder(TSharedPtr<FIntervalGizmoActorFactory> Builder)
{
GizmoActorBuilder = Builder;
}
void UIntervalGizmo::SetUpdateHoverFunction(TFunction<void(UPrimitiveComponent*, bool)> HoverFunction)
{
UpdateHoverFunction = HoverFunction;
}
void UIntervalGizmo::SetUpdateCoordSystemFunction(TFunction<void(UPrimitiveComponent*, EToolContextCoordinateSystem)> CoordSysFunction)
{
UpdateCoordSystemFunction = CoordSysFunction;
}
void UIntervalGizmo::SetWorldAlignmentFunctions(TUniqueFunction<bool()>&& ShouldAlignDestinationIn, TUniqueFunction<bool(const FRay&, FVector&)>&& DestinationAlignmentRayCasterIn)
{
// Save these so that any later gizmo resets (using SetActiveTarget) keep the settings.
ShouldAlignDestination = MoveTemp(ShouldAlignDestinationIn);
DestinationAlignmentRayCaster = MoveTemp(DestinationAlignmentRayCasterIn);
for (UInteractiveGizmo* SubGizmo : this->ActiveGizmos)
{
if (UAxisPositionGizmo* CastGizmo = Cast<UAxisPositionGizmo>(SubGizmo))
{
CastGizmo->ShouldUseCustomDestinationFunc = [this]() { return ShouldAlignDestination(); };
CastGizmo->CustomDestinationFunc =
[this](const UAxisPositionGizmo::FCustomDestinationParams& Params, FVector& OutputPoint) {
return DestinationAlignmentRayCaster(*Params.WorldRay, OutputPoint);
};
CastGizmo->bCustomDestinationAlignsAxisOrigin = false; // We're aligning the endpoints of the intervals
}
}
}
void UIntervalGizmo::Setup()
{
UInteractiveGizmo::Setup();
UpdateHoverFunction = [](UPrimitiveComponent* Component, bool bHovering)
{
if (Cast<UGizmoBaseComponent>(Component) != nullptr)
{
Cast<UGizmoBaseComponent>(Component)->UpdateHoverState(bHovering);
}
};
UpdateCoordSystemFunction = [](UPrimitiveComponent* Component, EToolContextCoordinateSystem CoordSystem)
{
if (Cast<UGizmoBaseComponent>(Component) != nullptr)
{
Cast<UGizmoBaseComponent>(Component)->UpdateWorldLocalState(CoordSystem == EToolContextCoordinateSystem::World);
}
};
GizmoActor = GizmoActorBuilder->CreateNewGizmoActor(World);
}
void UIntervalGizmo::Shutdown()
{
ClearActiveTarget();
if (GizmoActor)
{
GizmoActor->Destroy();
GizmoActor = nullptr;
}
ClearSources();
}
void UIntervalGizmo::Tick(float DeltaTime)
{
EToolContextCoordinateSystem CoordSystem = GetGizmoManager()->GetContextQueriesAPI()->GetCurrentCoordinateSystem();
check(CoordSystem == EToolContextCoordinateSystem::World || CoordSystem == EToolContextCoordinateSystem::Local)
bool bUseLocalAxes =
(GetGizmoManager()->GetContextQueriesAPI()->GetCurrentCoordinateSystem() == EToolContextCoordinateSystem::Local);
// Update gizmo location.
{
USceneComponent* GizmoComponent = GizmoActor->GetRootComponent();
// move gizmo to target location
FTransform TargetTransform = TransformProxy->GetTransform();
FVector SaveScale = TargetTransform.GetScale3D();
TargetTransform.SetScale3D(FVector(1, 1, 1));
GizmoComponent->SetWorldTransform(TargetTransform);
}
// Update the lengths
EnumerateValidIntervals([this](UGizmoLocalFloatParameterSource* Source, UGizmoLineHandleComponent* Component, UGizmoComponentAxisSource* Axis, UE::Geometry::FInterval1f& IntervalRange, FVector3d Direction, float DirectionAxisSign)
{
if (Component)
{
Component->Length = Source->GetParameter();
}
});
if (UpdateCoordSystemFunction)
{
for (UPrimitiveComponent* Component : ActiveComponents)
{
UpdateCoordSystemFunction(Component, CoordSystem);
}
}
}
void UIntervalGizmo::SetActiveTarget(UTransformProxy* TransformTargetIn, UGizmoLocalFloatParameterSource* UpInterval, UGizmoLocalFloatParameterSource* DownInterval, UGizmoLocalFloatParameterSource* ForwardInterval, IToolContextTransactionProvider* TransactionProvider)
{
FParameterSources Sources;
Sources.UpInterval = UpInterval;
Sources.DownInterval = DownInterval;
Sources.ForwardInterval = ForwardInterval;
SetActiveTarget(TransformTargetIn, Sources, TransactionProvider);
}
void UIntervalGizmo::SetActiveTarget(UTransformProxy* TransformTargetIn, const FParameterSources& ParameterSources, IToolContextTransactionProvider* TransactionProvider)
{
if (TransformProxy != nullptr)
{
ClearActiveTarget();
ClearSources();
}
// This state target emits an explicit FChange that moves the GizmoActor root component during undo/redo.
// It also opens/closes the Transaction that saves/restores the target object locations.
if (TransactionProvider == nullptr)
{
TransactionProvider = GetGizmoManager();
}
TransformProxy = TransformTargetIn;
// parameters and init lengths for each interval
UpIntervalSource = ParameterSources.UpInterval;
DownIntervalSource = ParameterSources.DownInterval;
ForwardIntervalSource = ParameterSources.ForwardInterval;
BackwardIntervalSource = ParameterSources.BackwardInterval;
RightIntervalSource = ParameterSources.RightInterval;
LeftIntervalSource = ParameterSources.LeftInterval;
if (ParameterSources.InitParameterRanges == EDefaultParameterRanges::HalfRange)
{
EnumerateAllIntervals([](UGizmoLocalFloatParameterSource* Source, UGizmoLocalFloatParameterSource* OppositeSource, UGizmoLineHandleComponent* Component, UE::Geometry::FInterval1f& IntervalRange, float DirectionAxisSign)
{
IntervalRange = DirectionAxisSign < 0 ? UE::Geometry::FInterval1f(-FLT_MAX, 0.f) : UE::Geometry::FInterval1f(0.f, FLT_MAX);
}
);
}
else if (ParameterSources.InitParameterRanges == EDefaultParameterRanges::FullRange)
{
EnumerateAllIntervals([](UGizmoLocalFloatParameterSource* Source, UGizmoLocalFloatParameterSource* OppositeSource, UGizmoLineHandleComponent* Component, UE::Geometry::FInterval1f& IntervalRange, float DirectionAxisSign)
{
IntervalRange = UE::Geometry::FInterval1f(-FLT_MAX, FLT_MAX);
}
);
}
else // EDefaultParameterRanges::HalfIfMatched
{
EnumerateAllIntervals([](UGizmoLocalFloatParameterSource* Source, UGizmoLocalFloatParameterSource* OppositeSource, UGizmoLineHandleComponent* Component, UE::Geometry::FInterval1f& IntervalRange, float DirectionAxisSign)
{
IntervalRange = UE::Geometry::FInterval1f(-FLT_MAX, FLT_MAX);
if (OppositeSource) // if opposite interval exists, cut range in half
{
if (DirectionAxisSign < 0)
{
IntervalRange.Max = 0;
}
else
{
IntervalRange.Min = 0;
}
}
}
);
}
// Get the parameter source to notify our delegate of any changes
EnumerateValidIntervals([this](UGizmoLocalFloatParameterSource* Source, UGizmoLineHandleComponent* Component, UGizmoComponentAxisSource* Axis, UE::Geometry::FInterval1f& IntervalRange, FVector3d Direction, float DirectionAxisSign)
{
Source->OnParameterChanged.AddWeakLambda(this, [this, Direction, DirectionAxisSign](IGizmoFloatParameterSource*, FGizmoFloatParameterChange Change) {
OnIntervalChanged.Broadcast(this, Direction, DirectionAxisSign * Change.CurrentValue);
});
});
USceneComponent* GizmoComponent = GizmoActor->GetRootComponent();
// move gizmo to target location
FTransform TargetTransform = TransformTargetIn->GetTransform();
FVector SaveScale = TargetTransform.GetScale3D();
TargetTransform.SetScale3D(FVector(1, 1, 1));
GizmoComponent->SetWorldTransform(TargetTransform);
// TargetTransform tracks location of GizmoComponent. Note that TransformUpdated is not called during undo/redo transactions!
// We currently rely on the transaction system to undo/redo target object locations. This will not work during runtime...
GizmoComponent->TransformUpdated.AddLambda(
[this, SaveScale](USceneComponent* Component, EUpdateTransformFlags /*UpdateTransformFlags*/, ETeleportType /*Teleport*/) {
//this->GetGizmoManager()->DisplayMessage(TEXT("TRANSFORM UPDATED"), EToolMessageLevel::Internal);
FTransform NewXForm = Component->GetComponentToWorld();
NewXForm.SetScale3D(SaveScale);
this->TransformProxy->SetTransform(NewXForm);
});
StateTarget = UGizmoTransformChangeStateTarget::Construct(GizmoComponent,
LOCTEXT("UIntervalGizmoTransaction", "Interval"), TransactionProvider, this);
StateTarget->DependentChangeSources.Add(MakeUnique<FTransformProxyChangeSource>(TransformProxy));
EnumerateValidIntervals([this](UGizmoLocalFloatParameterSource* Source, UGizmoLineHandleComponent* Component, UGizmoComponentAxisSource* Axis, UE::Geometry::FInterval1f& IntervalRange, FVector3d Direction, float DirectionAxisSign)
{
StateTarget->DependentChangeSources.Add(MakeUnique<FGizmoFloatParameterChangeSource>(Source));
});
// Have the state target notify us of the start/end of drags
StateTarget->DependentChangeSources.Add(MakeUnique<FIntervalGizmoChangeBroadcaster>(this));
// root component provides local X/Y/Z axis, identified by AxisIndex
AxisXSource = UGizmoComponentAxisSource::Construct(GizmoComponent, 0, true, this);
AxisYSource = UGizmoComponentAxisSource::Construct(GizmoComponent, 1, true, this);
AxisZSource = UGizmoComponentAxisSource::Construct(GizmoComponent, 2, true, this);
EnumerateAllIntervals([](UGizmoLocalFloatParameterSource* Source, UGizmoLocalFloatParameterSource* OppositeSource, UGizmoLineHandleComponent* Component, UE::Geometry::FInterval1f& IntervalRange, float DirectionAxisSign)
{
Component->SetVisibility(false);
});
EnumerateValidIntervals([this, GizmoComponent](UGizmoLocalFloatParameterSource* Source, UGizmoLineHandleComponent* Component, UGizmoComponentAxisSource* Axis, UE::Geometry::FInterval1f& IntervalRange, FVector3d Direction, float DirectionAxisSign)
{
AddIntervalHandleGizmo(GizmoComponent, Component, Axis, Source, IntervalRange.Min, IntervalRange.Max, StateTarget);
ActiveComponents.Add(Component);
Component->SetVisibility(true);
});
}
void UIntervalGizmo::SetVisibility(bool bVisible)
{
GizmoActor->SetActorHiddenInGame(bVisible == false);
#if WITH_EDITOR
GizmoActor->SetIsTemporarilyHiddenInEditor(bVisible == false);
#endif
}
void UIntervalGizmo::ClearSources()
{
UpIntervalSource = nullptr;
DownIntervalSource = nullptr;
ForwardIntervalSource = nullptr;
BackwardIntervalSource = nullptr;
RightIntervalSource = nullptr;
LeftIntervalSource = nullptr;
}
void UIntervalGizmo::ClearActiveTarget()
{
for (UInteractiveGizmo* Gizmo : ActiveGizmos)
{
GetGizmoManager()->DestroyGizmo(Gizmo);
}
ActiveGizmos.Empty();
ActiveComponents.Empty();
ClearSources();
TransformProxy = nullptr;
}
FTransform UIntervalGizmo::GetGizmoTransform() const
{
return TransformProxy->GetTransform();
}
UInteractiveGizmo* UIntervalGizmo::AddIntervalHandleGizmo(
USceneComponent* RootComponent,
UPrimitiveComponent* HandleComponent,
IGizmoAxisSource* AxisSource,
IGizmoFloatParameterSource* FloatParameterSource,
float MinParameter,
float MaxParameter,
IGizmoStateTarget* StateTargetIn)
{
// create axis-position gizmo, axis-position parameter will drive translation
UAxisPositionGizmo* IntervalGizmo = Cast<UAxisPositionGizmo>(GetGizmoManager()->CreateGizmo(
UInteractiveGizmoManager::DefaultAxisPositionBuilderIdentifier));
check(IntervalGizmo);
// axis source provides the scale axis
IntervalGizmo->AxisSource = Cast<UObject>(AxisSource);
// parameter source maps axis-parameter-change to change in interval length
IntervalGizmo->ParameterSource = UGizmoAxisIntervalParameterSource::Construct(FloatParameterSource, MinParameter, MaxParameter, this);
// sub-component provides hit target
UGizmoComponentHitTarget* HitTarget = UGizmoComponentHitTarget::Construct(HandleComponent, this);
if (this->UpdateHoverFunction)
{
HitTarget->UpdateHoverFunction = [HandleComponent, this](bool bHovering) { this->UpdateHoverFunction(HandleComponent, bHovering); };
}
IntervalGizmo->HitTarget = HitTarget;
IntervalGizmo->StateTarget = Cast<UObject>(StateTargetIn);
IntervalGizmo->ShouldUseCustomDestinationFunc = [this]() { return ShouldAlignDestination(); };
IntervalGizmo->CustomDestinationFunc =
[this](const UAxisPositionGizmo::FCustomDestinationParams& Params, FVector& OutputPoint) {
return DestinationAlignmentRayCaster(*Params.WorldRay, OutputPoint);
};
ActiveGizmos.Add(IntervalGizmo);
return IntervalGizmo;
}
// Call IterFn on each Source/Component combination where the Source is not null. Note IterFn will still be called if the component and/or axis are null.
void UIntervalGizmo::EnumerateValidIntervals(
TFunctionRef<void(UGizmoLocalFloatParameterSource* Source, UGizmoLineHandleComponent* Component, UGizmoComponentAxisSource* Axis, UE::Geometry::FInterval1f& IntervalRange, FVector3d Direction, float DirectionAxisSign)> IterFn
)
{
constexpr float IntervalMax = FLT_MAX;
if (RightIntervalSource)
{
IterFn(RightIntervalSource, GizmoActor ? GizmoActor->RightIntervalComponent : nullptr, AxisXSource, RightIntervalRange, FVector3d(1, 0, 0), 1.f);
}
if (LeftIntervalSource)
{
IterFn(LeftIntervalSource, GizmoActor ? GizmoActor->LeftIntervalComponent : nullptr, AxisXSource, LeftIntervalRange, FVector3d(-1, 0, 0), -1.f);
}
if (ForwardIntervalSource)
{
IterFn(ForwardIntervalSource, GizmoActor ? GizmoActor->ForwardIntervalComponent : nullptr, AxisYSource, ForwardIntervalRange, FVector3d(0, 1, 0), 1.f);
}
if (BackwardIntervalSource)
{
IterFn(BackwardIntervalSource, GizmoActor ? GizmoActor->BackwardIntervalComponent : nullptr, AxisYSource, BackwardIntervalRange, FVector3d(0, -1, 0), -1.f);
}
if (UpIntervalSource)
{
IterFn(UpIntervalSource, GizmoActor ? GizmoActor->UpIntervalComponent : nullptr, AxisZSource, UpIntervalRange, FVector3d(0, 0, 1), 1.f);
}
if (DownIntervalSource)
{
IterFn(DownIntervalSource, GizmoActor ? GizmoActor->DownIntervalComponent : nullptr, AxisZSource, DownIntervalRange, FVector3d(0, 0, -1), -1.f);
}
}
// Call IterFn on each Source/Component combination, including those where the Source is null
void UIntervalGizmo::EnumerateAllIntervals(TFunctionRef<void(UGizmoLocalFloatParameterSource* Source, UGizmoLocalFloatParameterSource* OppositeSource, UGizmoLineHandleComponent* Component, UE::Geometry::FInterval1f& IntervalRange, float DirectionAxisSign)> IterFn)
{
if (GizmoActor)
{
IterFn(RightIntervalSource, LeftIntervalSource, GizmoActor->RightIntervalComponent, RightIntervalRange, 1.f);
IterFn(LeftIntervalSource, RightIntervalSource, GizmoActor->LeftIntervalComponent, LeftIntervalRange, -1.f);
IterFn(ForwardIntervalSource, BackwardIntervalSource, GizmoActor->ForwardIntervalComponent, ForwardIntervalRange, 1.f);
IterFn(BackwardIntervalSource, ForwardIntervalSource, GizmoActor->BackwardIntervalComponent, BackwardIntervalRange, -1.f);
IterFn(UpIntervalSource, DownIntervalSource, GizmoActor->UpIntervalComponent, UpIntervalRange, 1.f);
IterFn(DownIntervalSource, UpIntervalSource, GizmoActor->DownIntervalComponent, DownIntervalRange, -1.f);
}
}
float UGizmoAxisIntervalParameterSource::GetParameter() const
{
return FloatParameterSource->GetParameter();
}
void UGizmoAxisIntervalParameterSource::SetParameter(float NewValue)
{
NewValue = FMath::Clamp(NewValue, MinParameter, MaxParameter);
FloatParameterSource->SetParameter(NewValue);
}
void UGizmoAxisIntervalParameterSource::BeginModify()
{
FloatParameterSource->BeginModify();
}
void UGizmoAxisIntervalParameterSource::EndModify()
{
FloatParameterSource->EndModify();
}
UGizmoAxisIntervalParameterSource* UGizmoAxisIntervalParameterSource::Construct(
IGizmoFloatParameterSource* FloatSourceIn,
float ParameterMin,
float ParameterMax,
UObject* Outer)
{
UGizmoAxisIntervalParameterSource* NewSource = NewObject<UGizmoAxisIntervalParameterSource>(Outer);
NewSource->FloatParameterSource = Cast<UObject>(FloatSourceIn);
// Clamp the initial value
float DefaultValue = NewSource->FloatParameterSource->GetParameter();
DefaultValue = FMath::Clamp(DefaultValue, ParameterMin, ParameterMax);
NewSource->FloatParameterSource->SetParameter(DefaultValue);
// record the min / max allowed
NewSource->MinParameter = ParameterMin;
NewSource->MaxParameter = ParameterMax;
return NewSource;
}
#undef LOCTEXT_NAMESPACE