Files
UnrealEngineUWP/Engine/Source/Runtime/InteractiveToolsFramework/Private/InteractiveGizmoManager.cpp
benoit gadreau 7c004866a5 New TRS Gizmo updates
- arcball rotation: the rotation is computed using a spherical+hyperbolic projection (see code for more explanation)
    - indirect manipulation via MMB: hit parts are stored per mode then are used to drive the proper drag function
    - defered drag function: in order to avoid notifications storm, the drag functions can be applied on tick instead using a pending function
    - a single default TRS gizmo is now created when needed (instead of creating several thru selection changed or when the gizmo manager was ticking) and its visibility is set based on rules

TODO
    - UEditorInteractiveGizmoManager is now closer to UInteractiveGizmoManager so I don't think we need ActiveEditorGizmos, CachedGizmoMap, etc. that are not used anymore. This should be cleaned in another CL
    - UEditorTransformGizmo::OnGizmoTransformBegin should set the current axis (needed for some modes, including IKRig) but this is disabled for now has there are some side effects with dragging (this will be treated in another CL)


#jira UE-152973
#jira UE-161236
#rb brooke.hubert zach.rammell

[CL 27751789 by benoit gadreau in ue5-main branch]
2023-09-11 09:54:48 -04:00

364 lines
12 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "InteractiveGizmoManager.h"
#include "InteractiveToolsContext.h"
#include "BaseGizmos/AxisPositionGizmo.h"
#include "BaseGizmos/GizmoViewContext.h"
#include "BaseGizmos/PlanePositionGizmo.h"
#include "BaseGizmos/AxisAngleGizmo.h"
#include "BaseGizmos/CombinedTransformGizmo.h"
#include "BaseGizmos/RepositionableTransformGizmo.h"
#include "BaseGizmos/ScalableSphereGizmo.h"
#include "ContextObjectStore.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(InteractiveGizmoManager)
#define LOCTEXT_NAMESPACE "UInteractiveGizmoManager"
UInteractiveGizmoManager::UInteractiveGizmoManager()
{
QueriesAPI = nullptr;
TransactionsAPI = nullptr;
InputRouter = nullptr;
}
void UInteractiveGizmoManager::Initialize(IToolsContextQueriesAPI* QueriesAPIIn, IToolsContextTransactionsAPI* TransactionsAPIIn, UInputRouter* InputRouterIn)
{
this->QueriesAPI = QueriesAPIIn;
this->TransactionsAPI = TransactionsAPIIn;
this->InputRouter = InputRouterIn;
}
void UInteractiveGizmoManager::Shutdown()
{
this->QueriesAPI = nullptr;
TArray<FActiveGizmo> AllGizmos = ActiveGizmos;
for (FActiveGizmo& ActiveGizmo : AllGizmos)
{
DestroyGizmo(ActiveGizmo.Gizmo);
}
ActiveGizmos.Reset();
this->TransactionsAPI = nullptr;
if (bDefaultGizmosRegistered)
{
DeregisterGizmoType(DefaultAxisPositionBuilderIdentifier);
DeregisterGizmoType(DefaultPlanePositionBuilderIdentifier);
DeregisterGizmoType(DefaultAxisAngleBuilderIdentifier);
DeregisterGizmoType(DefaultThreeAxisTransformBuilderIdentifier);
DeregisterGizmoType(DefaultScalableSphereBuilderIdentifier);
}
}
void UInteractiveGizmoManager::RegisterGizmoType(const FString& Identifier, UInteractiveGizmoBuilder* Builder)
{
if (ensure(GizmoBuilders.Contains(Identifier) == false))
{
GizmoBuilders.Add(Identifier, Builder);
}
}
bool UInteractiveGizmoManager::DeregisterGizmoType(const FString& BuilderIdentifier)
{
if (GizmoBuilders.Contains(BuilderIdentifier) == false)
{
DisplayMessage(
FText::Format(LOCTEXT("DeregisterFailedMessage", "UInteractiveGizmoManager::DeregisterGizmoType: could not find requested type {0}"), FText::FromString(BuilderIdentifier) ),
EToolMessageLevel::Internal);
return false;
}
GizmoBuilders.Remove(BuilderIdentifier);
return true;
}
UInteractiveGizmo* UInteractiveGizmoManager::CreateGizmo(const FString& BuilderIdentifier, const FString& InstanceIdentifier, void* Owner)
{
if ( GizmoBuilders.Contains(BuilderIdentifier) == false )
{
DisplayMessage(
FText::Format(LOCTEXT("CreateGizmoCannotFindFailedMessage", "UInteractiveGizmoManager::CreateGizmo: could not find requested type {0}"), FText::FromString(BuilderIdentifier) ),
EToolMessageLevel::Internal);
return nullptr;
}
UInteractiveGizmoBuilder* FoundBuilder = GizmoBuilders[BuilderIdentifier];
// check if we have used this instance identifier
if (InstanceIdentifier.IsEmpty() == false)
{
for (FActiveGizmo& ActiveGizmo : ActiveGizmos)
{
if (ActiveGizmo.InstanceIdentifier == InstanceIdentifier)
{
DisplayMessage(
FText::Format(LOCTEXT("CreateGizmoExistsMessage", "UInteractiveGizmoManager::CreateGizmo: instance identifier {0} already in use!"), FText::FromString(InstanceIdentifier) ),
EToolMessageLevel::Internal);
return nullptr;
}
}
}
FToolBuilderState CurrentSceneState;
QueriesAPI->GetCurrentSelectionState(CurrentSceneState);
UInteractiveGizmo* NewGizmo = FoundBuilder->BuildGizmo(CurrentSceneState);
if (NewGizmo == nullptr)
{
DisplayMessage(LOCTEXT("CreateGizmoReturnNullMessage", "UInteractiveGizmoManager::CreateGizmo: BuildGizmo() returned null"), EToolMessageLevel::Internal);
return nullptr;
}
NewGizmo->Setup();
// register new active input behaviors
InputRouter->RegisterSource(NewGizmo);
PostInvalidation();
FActiveGizmo ActiveGizmo = { NewGizmo, BuilderIdentifier, InstanceIdentifier, Owner };
ActiveGizmos.Add(ActiveGizmo);
return NewGizmo;
}
bool UInteractiveGizmoManager::DestroyGizmo(UInteractiveGizmo* Gizmo)
{
auto Pred = [Gizmo](const FActiveGizmo& ActiveGizmo) {return ActiveGizmo.Gizmo == Gizmo; };
if (!ensure(ActiveGizmos.FindByPredicate(Pred)))
{
return false;
}
InputRouter->ForceTerminateSource(Gizmo);
Gizmo->Shutdown();
InputRouter->DeregisterSource(Gizmo);
ActiveGizmos.RemoveAll(Pred);
PostInvalidation();
return true;
}
TArray<UInteractiveGizmo*> UInteractiveGizmoManager::FindAllGizmosOfType(const FString& BuilderIdentifier)
{
TArray<UInteractiveGizmo*> Found;
for (int i = 0; i < ActiveGizmos.Num(); ++i)
{
if (ActiveGizmos[i].BuilderIdentifier == BuilderIdentifier)
{
Found.Add(ActiveGizmos[i].Gizmo);
}
}
return Found;
}
void UInteractiveGizmoManager::DestroyAllGizmosOfType(const FString& BuilderIdentifier)
{
TArray<UInteractiveGizmo*> ToRemove = FindAllGizmosOfType(BuilderIdentifier);
for (int i = 0; i < ToRemove.Num(); ++i)
{
DestroyGizmo(ToRemove[i]);
}
}
void UInteractiveGizmoManager::DestroyAllGizmosByOwner(void* Owner)
{
TArray<UInteractiveGizmo*> Found;
for ( const FActiveGizmo& ActiveGizmo : ActiveGizmos )
{
if (ActiveGizmo.Owner == Owner)
{
Found.Add(ActiveGizmo.Gizmo);
}
}
for (UInteractiveGizmo* Gizmo : Found)
{
DestroyGizmo(Gizmo);
}
}
UInteractiveGizmo* UInteractiveGizmoManager::FindGizmoByInstanceIdentifier(const FString& Identifier) const
{
for (int i = 0; i < ActiveGizmos.Num(); ++i)
{
if (ActiveGizmos[i].InstanceIdentifier == Identifier)
{
return ActiveGizmos[i].Gizmo;
}
}
return nullptr;
}
void UInteractiveGizmoManager::Tick(float DeltaTime)
{
for (FActiveGizmo& ActiveGizmo : ActiveGizmos)
{
ActiveGizmo.Gizmo->Tick(DeltaTime);
}
}
void UInteractiveGizmoManager::Render(IToolsContextRenderAPI* RenderAPI)
{
for (FActiveGizmo& ActiveGizmo : ActiveGizmos)
{
ActiveGizmo.Gizmo->Render(RenderAPI);
}
}
void UInteractiveGizmoManager::DrawHUD( FCanvas* Canvas, IToolsContextRenderAPI* RenderAPI )
{
for (FActiveGizmo& ActiveGizmo : ActiveGizmos)
{
ActiveGizmo.Gizmo->DrawHUD(Canvas, RenderAPI);
}
}
void UInteractiveGizmoManager::DisplayMessage(const FText& Message, EToolMessageLevel Level)
{
TransactionsAPI->DisplayMessage(Message, Level);
}
void UInteractiveGizmoManager::PostInvalidation()
{
TransactionsAPI->PostInvalidation();
}
void UInteractiveGizmoManager::BeginUndoTransaction(const FText& Description)
{
TransactionsAPI->BeginUndoTransaction(Description);
}
void UInteractiveGizmoManager::EndUndoTransaction()
{
TransactionsAPI->EndUndoTransaction();
}
void UInteractiveGizmoManager::EmitObjectChange(UObject* TargetObject, TUniquePtr<FToolCommandChange> Change, const FText& Description)
{
TransactionsAPI->AppendChange(TargetObject, MoveTemp(Change), Description );
}
UContextObjectStore* UInteractiveGizmoManager::GetContextObjectStore() const
{
return Cast<UInteractiveToolsContext>(GetOuter())->ContextObjectStore;
}
FString UInteractiveGizmoManager::DefaultAxisPositionBuilderIdentifier = TEXT("StandardXFormAxisTranslationGizmo");
FString UInteractiveGizmoManager::DefaultPlanePositionBuilderIdentifier = TEXT("StandardXFormPlaneTranslationGizmo");
FString UInteractiveGizmoManager::DefaultAxisAngleBuilderIdentifier = TEXT("StandardXFormAxisRotationGizmo");
FString UInteractiveGizmoManager::DefaultThreeAxisTransformBuilderIdentifier = TEXT("DefaultThreeAxisTransformBuilderIdentifier");
const FString UInteractiveGizmoManager::CustomThreeAxisTransformBuilderIdentifier = TEXT("CustomThreeAxisTransformBuilderIdentifier");
const FString UInteractiveGizmoManager::CustomRepositionableThreeAxisTransformBuilderIdentifier = TEXT("CustomRepositionableThreeAxisTransformBuilderIdentifier");
FString UInteractiveGizmoManager::DefaultScalableSphereBuilderIdentifier = TEXT("DefaultScalableSphereBuilderIdentifier");
void UInteractiveGizmoManager::RegisterDefaultGizmos()
{
check(bDefaultGizmosRegistered == false);
UAxisPositionGizmoBuilder* AxisTranslationBuilder = NewObject<UAxisPositionGizmoBuilder>();
RegisterGizmoType(DefaultAxisPositionBuilderIdentifier, AxisTranslationBuilder);
UPlanePositionGizmoBuilder* PlaneTranslationBuilder = NewObject<UPlanePositionGizmoBuilder>();
RegisterGizmoType(DefaultPlanePositionBuilderIdentifier, PlaneTranslationBuilder);
UAxisAngleGizmoBuilder* AxisRotationBuilder = NewObject<UAxisAngleGizmoBuilder>();
RegisterGizmoType(DefaultAxisAngleBuilderIdentifier, AxisRotationBuilder);
UCombinedTransformGizmoBuilder* TransformBuilder = NewObject<UCombinedTransformGizmoBuilder>();
TransformBuilder->AxisPositionBuilderIdentifier = DefaultAxisPositionBuilderIdentifier;
TransformBuilder->PlanePositionBuilderIdentifier = DefaultPlanePositionBuilderIdentifier;
TransformBuilder->AxisAngleBuilderIdentifier = DefaultAxisAngleBuilderIdentifier;
RegisterGizmoType(DefaultThreeAxisTransformBuilderIdentifier, TransformBuilder);
UGizmoViewContext* GizmoViewContext = GetContextObjectStore()->FindContext<UGizmoViewContext>();
if (!GizmoViewContext)
{
GizmoViewContext = NewObject<UGizmoViewContext>();
GetContextObjectStore()->AddContextObject(GizmoViewContext);
}
GizmoActorBuilder = MakeShared<FCombinedTransformGizmoActorFactory>(GizmoViewContext);
UCombinedTransformGizmoBuilder* CustomThreeAxisBuilder = NewObject<UCombinedTransformGizmoBuilder>();
CustomThreeAxisBuilder->AxisPositionBuilderIdentifier = DefaultAxisPositionBuilderIdentifier;
CustomThreeAxisBuilder->PlanePositionBuilderIdentifier = DefaultPlanePositionBuilderIdentifier;
CustomThreeAxisBuilder->AxisAngleBuilderIdentifier = DefaultAxisAngleBuilderIdentifier;
CustomThreeAxisBuilder->GizmoActorBuilder = GizmoActorBuilder;
RegisterGizmoType(CustomThreeAxisTransformBuilderIdentifier, CustomThreeAxisBuilder);
URepositionableTransformGizmoBuilder* CustomRepositionableThreeAxisBuilder = NewObject<URepositionableTransformGizmoBuilder>();
CustomRepositionableThreeAxisBuilder->AxisPositionBuilderIdentifier = DefaultAxisPositionBuilderIdentifier;
CustomRepositionableThreeAxisBuilder->PlanePositionBuilderIdentifier = DefaultPlanePositionBuilderIdentifier;
CustomRepositionableThreeAxisBuilder->AxisAngleBuilderIdentifier = DefaultAxisAngleBuilderIdentifier;
CustomRepositionableThreeAxisBuilder->GizmoActorBuilder = GizmoActorBuilder;
RegisterGizmoType(CustomRepositionableThreeAxisTransformBuilderIdentifier, CustomRepositionableThreeAxisBuilder);
UScalableSphereGizmoBuilder* ScalableSphereBuilder = NewObject<UScalableSphereGizmoBuilder>();
RegisterGizmoType(DefaultScalableSphereBuilderIdentifier, ScalableSphereBuilder);
bDefaultGizmosRegistered = true;
}
UCombinedTransformGizmo* UInteractiveGizmoManager::Create3AxisTransformGizmo(void* Owner, const FString& InstanceIdentifier)
{
check(bDefaultGizmosRegistered);
UInteractiveGizmo* NewGizmo = CreateGizmo(DefaultThreeAxisTransformBuilderIdentifier, InstanceIdentifier, Owner);
check(NewGizmo);
return Cast<UCombinedTransformGizmo>(NewGizmo);
}
UCombinedTransformGizmo* UInteractiveGizmoManager::CreateCustomTransformGizmo(ETransformGizmoSubElements Elements, void* Owner, const FString& InstanceIdentifier)
{
check(bDefaultGizmosRegistered);
GizmoActorBuilder->EnableElements = Elements;
UInteractiveGizmo* NewGizmo = CreateGizmo(CustomThreeAxisTransformBuilderIdentifier, InstanceIdentifier, Owner);
check(NewGizmo);
return Cast<UCombinedTransformGizmo>(NewGizmo);
}
UCombinedTransformGizmo* UInteractiveGizmoManager::CreateCustomRepositionableTransformGizmo(ETransformGizmoSubElements Elements, void* Owner, const FString& InstanceIdentifier)
{
check(bDefaultGizmosRegistered);
GizmoActorBuilder->EnableElements = Elements;
UInteractiveGizmo* NewGizmo = CreateGizmo(CustomRepositionableThreeAxisTransformBuilderIdentifier, InstanceIdentifier, Owner);
check(NewGizmo);
return Cast<UCombinedTransformGizmo>(NewGizmo);
}
#undef LOCTEXT_NAMESPACE