Browse Source

Add ability to reposition manipulator by picking in the scene (#8612)

* add initial manipulator position picking

Signed-off-by: Tom Hulton-Harrop <[email protected]>

* add tests for repositioning manipulator

Signed-off-by: Tom Hulton-Harrop <[email protected]>

* update test cases to use new controls, plus minor doxygen update

Signed-off-by: Tom Hulton-Harrop <[email protected]>

* updates to how manipulator should be moved if no entity is picked

Signed-off-by: Tom Hulton-Harrop <[email protected]>

* update controls for new manipulator pick placement

Signed-off-by: Tom Hulton-Harrop <[email protected]>

* remove pivot entity id override which was no longer used

Signed-off-by: Tom Hulton-Harrop <[email protected]>

* minor updates following PR feedack

Signed-off-by: Tom Hulton-Harrop <[email protected]>
Tom Hulton-Harrop 3 years ago
parent
commit
1de064ea5b

+ 12 - 21
Code/Framework/AzToolsFramework/AzToolsFramework/Commands/EntityManipulatorCommand.cpp

@@ -12,30 +12,23 @@
 
 namespace AzToolsFramework
 {
-    EntityManipulatorCommand::State::State(
-        const AZ::u8 pivotOverride, const AZ::Transform& transform, const AZ::EntityId entityId)
-        : m_transform(transform), m_entityId(entityId), m_pivotOverride(pivotOverride)
+    EntityManipulatorCommand::State::State(const AZ::u8 pivotOverride, const AZ::Transform& transform)
+        : m_transform(transform)
+        , m_pivotOverride(pivotOverride)
     {
     }
 
-    bool operator==(
-        const EntityManipulatorCommand::State& lhs, const EntityManipulatorCommand::State& rhs)
+    bool operator==(const EntityManipulatorCommand::State& lhs, const EntityManipulatorCommand::State& rhs)
     {
-        return lhs.m_pivotOverride == rhs.m_pivotOverride
-            && lhs.m_entityId == rhs.m_entityId
-            && lhs.m_transform.IsClose(rhs.m_transform);
+        return lhs.m_pivotOverride == rhs.m_pivotOverride && lhs.m_transform.IsClose(rhs.m_transform);
     }
 
-    bool operator!=(
-        const EntityManipulatorCommand::State& lhs, const EntityManipulatorCommand::State& rhs)
+    bool operator!=(const EntityManipulatorCommand::State& lhs, const EntityManipulatorCommand::State& rhs)
     {
-        return lhs.m_pivotOverride != rhs.m_pivotOverride
-            || lhs.m_entityId != rhs.m_entityId
-            || !lhs.m_transform.IsClose(rhs.m_transform);
+        return !(lhs == rhs);
     }
 
-    EntityManipulatorCommand::EntityManipulatorCommand(
-        const State& stateBefore, const AZStd::string& friendlyName)
+    EntityManipulatorCommand::EntityManipulatorCommand(const State& stateBefore, const AZStd::string& friendlyName)
         : URSequencePoint(friendlyName)
         , m_stateBefore(stateBefore)
         , m_stateAfter(stateBefore) // default to the same as before in case it does not move
@@ -46,14 +39,14 @@ namespace AzToolsFramework
     {
         EditorManipulatorCommandUndoRedoRequestBus::Event(
             GetEntityContextId(), &EditorManipulatorCommandUndoRedoRequests::UndoRedoEntityManipulatorCommand,
-            m_stateBefore.m_pivotOverride, m_stateBefore.m_transform, m_stateBefore.m_entityId);
+            m_stateBefore.m_pivotOverride, m_stateBefore.m_transform);
     }
 
     void EntityManipulatorCommand::Redo()
     {
         EditorManipulatorCommandUndoRedoRequestBus::Event(
-            GetEntityContextId(), &EditorManipulatorCommandUndoRedoRequests::UndoRedoEntityManipulatorCommand,
-            m_stateAfter.m_pivotOverride, m_stateAfter.m_transform, m_stateAfter.m_entityId);
+            GetEntityContextId(), &EditorManipulatorCommandUndoRedoRequests::UndoRedoEntityManipulatorCommand, m_stateAfter.m_pivotOverride,
+            m_stateAfter.m_transform);
     }
 
     void EntityManipulatorCommand::SetManipulatorAfter(const State& stateAfter)
@@ -68,8 +61,7 @@ namespace AzToolsFramework
         return m_stateBefore != m_stateAfter;
     }
 
-    AZ::u8 BuildPivotOverride(
-        const bool translationOverride, const bool orientationOverride)
+    AZ::u8 BuildPivotOverride(const bool translationOverride, const bool orientationOverride)
     {
         AZ::u8 pivotOverride = EntityManipulatorCommand::PivotOverride::None;
 
@@ -100,5 +92,4 @@ namespace AzToolsFramework
     {
         return PivotHasTranslationOverride(pivotOverride) || PivotHasOrientationOverride(pivotOverride);
     }
-
 } // namespace AzToolsFramework

+ 15 - 24
Code/Framework/AzToolsFramework/AzToolsFramework/Commands/EntityManipulatorCommand.h

@@ -16,29 +16,26 @@
 
 namespace AzToolsFramework
 {
-    /// Provide interface for Entity manipulator Undo/Redo requests.
-    class EditorManipulatorCommandUndoRedoRequests
-        : public AZ::EBusTraits
+    //! Provide interface for Entity manipulator Undo/Redo requests.
+    class EditorManipulatorCommandUndoRedoRequests : public AZ::EBusTraits
     {
     public:
         using BusIdType = AzFramework::EntityContextId;
         static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
         static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
 
-        /// Set the transform of the current entity manipulator.
-        virtual void UndoRedoEntityManipulatorCommand(
-            AZ::u8 pivotOverride, const AZ::Transform& transform, AZ::EntityId entityId) = 0;
+        //! Set the transform of the current entity manipulator.
+        virtual void UndoRedoEntityManipulatorCommand(AZ::u8 pivotOverride, const AZ::Transform& transform) = 0;
 
     protected:
         ~EditorManipulatorCommandUndoRedoRequests() = default;
     };
 
-    /// Type to inherit to implement EditorManipulatorCommandUndoRedoRequests.
+    //! Type to inherit to implement EditorManipulatorCommandUndoRedoRequests.
     using EditorManipulatorCommandUndoRedoRequestBus = AZ::EBus<EditorManipulatorCommandUndoRedoRequests>;
 
-    /// Stores a manipulator transform change.
-    class EntityManipulatorCommand
-        : public UndoSystem::URSequencePoint
+    //! Stores a manipulator transform change.
+    class EntityManipulatorCommand : public UndoSystem::URSequencePoint
     {
     public:
         AZ_CLASS_ALLOCATOR(EntityManipulatorCommand, AZ::SystemAllocator, 0);
@@ -54,12 +51,12 @@ namespace AzToolsFramework
             };
         };
 
-        /// State relevant to EntityManipulatorCommand before and after
-        /// any kind of Entity manipulation.
+        //! State relevant to EntityManipulatorCommand before and after
+        //! any kind of Entity manipulation.
         struct State
         {
             State() = default;
-            State(AZ::u8 pivotOverrideBefore, const AZ::Transform& transformBefore, AZ::EntityId entityIdBefore);
+            State(AZ::u8 pivotOverrideBefore, const AZ::Transform& transformBefore);
 
             State(const State&) = default;
             State& operator=(const State&) = default;
@@ -68,16 +65,14 @@ namespace AzToolsFramework
             ~State() = default;
 
             AZ::Transform m_transform = AZ::Transform::CreateIdentity();
-            AZ::EntityId m_entityId;
             AZ::u8 m_pivotOverride = 0;
         };
 
-        EntityManipulatorCommand(
-            const State& state, const AZStd::string& friendlyName);
+        EntityManipulatorCommand(const State& state, const AZStd::string& friendlyName);
 
         void SetManipulatorAfter(const State& state);
 
-        // URSequencePoint
+        // URSequencePoint overrides ...
         void Undo() override;
         void Redo() override;
         bool Changed() const override;
@@ -87,16 +82,12 @@ namespace AzToolsFramework
         State m_stateAfter;
     };
 
-    bool operator==(
-        const EntityManipulatorCommand::State& lhs, const EntityManipulatorCommand::State& rhs);
-    bool operator!=(
-        const EntityManipulatorCommand::State& lhs, const EntityManipulatorCommand::State& rhs);
+    bool operator==(const EntityManipulatorCommand::State& lhs, const EntityManipulatorCommand::State& rhs);
+    bool operator!=(const EntityManipulatorCommand::State& lhs, const EntityManipulatorCommand::State& rhs);
 
-    AZ::u8 BuildPivotOverride(
-        bool translationOverride, bool orientationOverride);
+    AZ::u8 BuildPivotOverride(bool translationOverride, bool orientationOverride);
 
     bool PivotHasTranslationOverride(AZ::u8 pivotOverride);
     bool PivotHasOrientationOverride(AZ::u8 pivotOverride);
     bool PivotHasTransformOverride(AZ::u8 pivotOverride);
-
 } // namespace AzToolsFramework

+ 134 - 108
Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp

@@ -8,6 +8,7 @@
 
 #include "EditorTransformComponentSelection.h"
 
+#include <AzCore/Math/MathStringConversions.h>
 #include <AzCore/Math/Matrix3x3.h>
 #include <AzCore/Math/Matrix3x4.h>
 #include <AzCore/Math/Matrix4x4.h>
@@ -81,6 +82,13 @@ namespace AzToolsFramework
         nullptr,
         AZ::ConsoleFunctorFlags::Null,
         "The screen position of the gizmo in normalized (0-1) ndc space");
+    AZ_CVAR(
+        bool,
+        ed_viewportDisplayManipulatorPosition,
+        false,
+        nullptr,
+        AZ::ConsoleFunctorFlags::Null,
+        "Display the position of the manipulator to the viewport as debug text");
 
     // strings related to new viewport interaction model (EditorTransformComponentSelection)
     static const char* const TogglePivotTitleRightClick = "Toggle pivot";
@@ -174,41 +182,20 @@ namespace AzToolsFramework
         return m_translationOverride.has_value() || m_orientationOverride.has_value();
     }
 
-    bool OptionalFrame::HasEntityOverride() const
-    {
-        return m_pickedEntityIdOverride.IsValid();
-    }
-
-    bool OptionalFrame::PickedTranslation() const
-    {
-        return (m_pickTypes & PickType::Translation) != 0;
-    }
-
-    bool OptionalFrame::PickedOrientation() const
-    {
-        return (m_pickTypes & PickType::Orientation) != 0;
-    }
-
     void OptionalFrame::Reset()
     {
         m_orientationOverride.reset();
         m_translationOverride.reset();
-        m_pickedEntityIdOverride.SetInvalid();
-        m_pickTypes = PickType::None;
     }
 
     void OptionalFrame::ResetPickedTranslation()
     {
         m_translationOverride.reset();
-        m_pickedEntityIdOverride.SetInvalid();
-        m_pickTypes &= ~PickType::Translation;
     }
 
     void OptionalFrame::ResetPickedOrientation()
     {
         m_orientationOverride.reset();
-        m_pickedEntityIdOverride.SetInvalid();
-        m_pickTypes &= ~PickType::Orientation;
     }
 
     using EntityIdTransformMap = AZStd::unordered_map<AZ::EntityId, AZ::Transform>;
@@ -261,6 +248,14 @@ namespace AzToolsFramework
                 mouseInteraction.m_mouseInteraction.m_keyboardModifiers.Alt();
         }
 
+        static bool ManipulatorDittoExact(
+            const AzFramework::ClickDetector::ClickOutcome clickOutcome, const ViewportInteraction::MouseInteractionEvent& mouseInteraction)
+        {
+            return clickOutcome == AzFramework::ClickDetector::ClickOutcome::Click &&
+                mouseInteraction.m_mouseInteraction.m_keyboardModifiers.Ctrl() &&
+                mouseInteraction.m_mouseInteraction.m_keyboardModifiers.Shift();
+        }
+
         static bool IndividualSelect(const AzFramework::ClickDetector::ClickOutcome clickOutcome)
         {
             return clickOutcome == AzFramework::ClickDetector::ClickOutcome::Click;
@@ -271,7 +266,8 @@ namespace AzToolsFramework
         {
             return clickOutcome == AzFramework::ClickDetector::ClickOutcome::Click &&
                 mouseInteraction.m_mouseInteraction.m_keyboardModifiers.Ctrl() &&
-                !mouseInteraction.m_mouseInteraction.m_keyboardModifiers.Alt();
+                !mouseInteraction.m_mouseInteraction.m_keyboardModifiers.Alt() &&
+                !mouseInteraction.m_mouseInteraction.m_keyboardModifiers.Shift();
         }
     } // namespace Input
 
@@ -533,7 +529,7 @@ namespace AzToolsFramework
             buttonIdFromFrameFn(referenceFrame));
     }
 
-    namespace ETCS
+    namespace Etcs
     {
         PivotOrientationResult CalculatePivotOrientation(const AZ::EntityId entityId, const ReferenceFrame referenceFrame)
         {
@@ -568,17 +564,17 @@ namespace AzToolsFramework
 
             return result;
         }
-    } // namespace ETCS
+    } // namespace Etcs
 
     // return parent space from selection - if entity(s) share a common parent,
     // return that space, otherwise return world space
     template<typename EntityIdMapIterator>
-    static ETCS::PivotOrientationResult CalculateParentSpace(EntityIdMapIterator begin, EntityIdMapIterator end)
+    static Etcs::PivotOrientationResult CalculateParentSpace(EntityIdMapIterator begin, EntityIdMapIterator end)
     {
         AZ_PROFILE_FUNCTION(AzToolsFramework);
 
         // initialize to world with no parent
-        ETCS::PivotOrientationResult result{ AZ::Quaternion::CreateIdentity(), AZ::EntityId() };
+        Etcs::PivotOrientationResult result{ AZ::Quaternion::CreateIdentity(), AZ::EntityId() };
 
         AZ::EntityId commonParentId;
         for (EntityIdMapIterator entityIdLookupIt = begin; entityIdLookupIt != end; ++entityIdLookupIt)
@@ -647,7 +643,7 @@ namespace AzToolsFramework
         return AZ::Vector3::CreateZero();
     }
 
-    namespace ETCS
+    namespace Etcs
     {
         template<typename EntityIdMap>
         PivotOrientationResult CalculatePivotOrientationForEntityIds(const EntityIdMap& entityIdMap, const ReferenceFrame referenceFrame)
@@ -659,7 +655,7 @@ namespace AzToolsFramework
             // simple case with one entity
             if (entityIdMap.size() == 1)
             {
-                return ETCS::CalculatePivotOrientation(entityIdMap.begin()->first, referenceFrame);
+                return Etcs::CalculatePivotOrientation(entityIdMap.begin()->first, referenceFrame);
             }
 
             // local/parent logic the same
@@ -674,9 +670,9 @@ namespace AzToolsFramework
                 return { AZ::Quaternion::CreateIdentity(), AZ::EntityId() };
             }
         }
-    } // namespace ETCS
+    } // namespace Etcs
 
-    namespace ETCS
+    namespace Etcs
     {
         template<typename EntityIdMap>
         PivotOrientationResult CalculateSelectionPivotOrientation(
@@ -730,7 +726,7 @@ namespace AzToolsFramework
 
             return pivot;
         }
-    } // namespace ETCS
+    } // namespace Etcs
 
     template<typename EntityIdMap>
     static AZ::Vector3 RecalculateAverageManipulatorTranslation(
@@ -749,7 +745,7 @@ namespace AzToolsFramework
     {
         AZ_PROFILE_FUNCTION(AzToolsFramework);
 
-        return ETCS::CalculateSelectionPivotOrientation(entityIdMap, pivotOverrideFrame, referenceFrame).m_worldOrientation;
+        return Etcs::CalculateSelectionPivotOrientation(entityIdMap, pivotOverrideFrame, referenceFrame).m_worldOrientation;
     }
 
     template<typename EntityIdMap>
@@ -825,7 +821,7 @@ namespace AzToolsFramework
 
             // note: used for parent and world depending on the current reference frame
             const auto pivotOrientation =
-                ETCS::CalculateSelectionPivotOrientation(entityIdManipulators.m_lookups, pivotOverrideFrame, referenceFrame);
+                Etcs::CalculateSelectionPivotOrientation(entityIdManipulators.m_lookups, pivotOverrideFrame, referenceFrame);
 
             // note: must use sorted entityIds based on hierarchy order when updating transforms
             for (AZ::EntityId entityId : entityIdContainer)
@@ -856,7 +852,7 @@ namespace AzToolsFramework
                             entityItLookupIt->second.m_initial = AZ::Transform::CreateTranslation(worldTranslation - localOffset);
                         }
 
-                        ETCS::SetEntityWorldTranslation(
+                        Etcs::SetEntityWorldTranslation(
                             entityId, entityItLookupIt->second.m_initial.GetTranslation() + localOffset, transformChangedInternally);
                     }
                     break;
@@ -872,7 +868,7 @@ namespace AzToolsFramework
                             entityItLookupIt->second.m_initial = AZ::Transform::CreateTranslation(worldTranslation - localOffset);
                         }
 
-                        ETCS::SetEntityWorldTranslation(
+                        Etcs::SetEntityWorldTranslation(
                             entityId, entityItLookupIt->second.m_initial.GetTranslation() + localOffset, transformChangedInternally);
                     }
                     break;
@@ -988,7 +984,7 @@ namespace AzToolsFramework
         ToolsApplicationNotificationBus::Broadcast(&ToolsApplicationNotificationBus::Events::InvalidatePropertyDisplay, Refresh_Values);
     }
 
-    // leaves focus mode by focusing on the parent of the current perfab in the entity outliner
+    // leaves focus mode by focusing on the parent of the current prefab in the entity outliner
     static void LeaveFocusMode()
     {
         if (auto prefabFocusPublicInterface = AZ::Interface<Prefab::PrefabFocusPublicInterface>::Get())
@@ -997,6 +993,20 @@ namespace AzToolsFramework
         }
     }
 
+    static AZ::Vector3 EtcsPickEntity(const AZ::EntityId entityId, const ViewportInteraction::MouseInteractionEvent& mouseInteraction)
+    {
+        float distance;
+        const auto& origin = mouseInteraction.m_mouseInteraction.m_mousePick.m_rayOrigin;
+        const auto& direction = mouseInteraction.m_mouseInteraction.m_mousePick.m_rayDirection;
+        if (AzToolsFramework::PickEntity(
+                entityId, origin, direction, distance, mouseInteraction.m_mouseInteraction.m_interactionId.m_viewportId))
+        {
+            return origin + direction * distance;
+        }
+
+        return origin + direction * GetDefaultEntityPlacementDistance();
+    }
+
     EditorTransformComponentSelection::EditorTransformComponentSelection(const EditorVisibleEntityDataCacheInterface* entityDataCache)
         : m_entityDataCache(entityDataCache)
     {
@@ -1184,8 +1194,7 @@ namespace AzToolsFramework
         }
 
         return { BuildPivotOverride(m_pivotOverrideFrame.HasTranslationOverride(), m_pivotOverrideFrame.HasOrientationOverride()),
-                 TransformNormalizedScale(m_entityIdManipulators.m_manipulators->GetLocalTransform()),
-                 m_pivotOverrideFrame.m_pickedEntityIdOverride };
+                 TransformNormalizedScale(m_entityIdManipulators.m_manipulators->GetLocalTransform()) };
     }
 
     void EditorTransformComponentSelection::BeginRecordManipulatorCommand()
@@ -1908,9 +1917,33 @@ namespace AzToolsFramework
             }
 
             // set manipulator pivot override translation or orientation (update manipulators)
-            if (Input::ManipulatorDitto(clickOutcome, mouseInteraction))
-            {
-                PerformManipulatorDitto(entityIdUnderCursor);
+            const bool manipulatorDitto = Input::ManipulatorDitto(clickOutcome, mouseInteraction);
+            const bool manipulatorDittoExact = Input::ManipulatorDittoExact(clickOutcome, mouseInteraction);
+            if (manipulatorDitto || manipulatorDittoExact)
+            {
+                PerformManipulatorDitto(
+                    entityIdUnderCursor,
+                    [manipulatorDitto, manipulatorDittoExact, &mouseInteraction,
+                     entityId = entityIdUnderCursor]() -> AZStd::optional<AZ::Vector3>
+                    {
+                        if (manipulatorDitto)
+                        {
+                            if (entityId.IsValid())
+                            {
+                                return GetWorldTranslation(entityId);
+                            }
+
+                            return AZStd::nullopt;
+                        }
+
+                        if (manipulatorDittoExact)
+                        {
+                            return EtcsPickEntity(entityId, mouseInteraction);
+                        }
+
+                        return AZStd::nullopt;
+                    });
+
                 return false;
             }
 
@@ -2032,7 +2065,9 @@ namespace AzToolsFramework
         }
     }
 
-    void EditorTransformComponentSelection::PerformManipulatorDitto(const AZ::EntityId entityId)
+    template<typename ManipulatorTranslationFn>
+    void EditorTransformComponentSelection::PerformManipulatorDitto(
+        const AZ::EntityId entityId, ManipulatorTranslationFn&& manipulatorTranslationFn)
     {
         if (m_entityIdManipulators.m_manipulators)
         {
@@ -2041,19 +2076,28 @@ namespace AzToolsFramework
             auto manipulatorCommand =
                 AZStd::make_unique<EntityManipulatorCommand>(CreateManipulatorCommandStateFromSelf(), ManipulatorUndoRedoName);
 
-            if (entityId.IsValid())
+            auto overrideManipulatorTranslationFn = [this, &manipulatorTranslationFn]
             {
-                AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity();
-                AZ::TransformBus::EventResult(worldFromLocal, entityId, &AZ::TransformBus::Events::GetWorldTM);
+                if (const auto translation = manipulatorTranslationFn(); translation.has_value())
+                {
+                    OverrideManipulatorTranslation(translation.value());
+                }
+            };
 
+            if (entityId.IsValid())
+            {
                 // set orientation/translation to match picked entity
                 switch (m_mode)
                 {
                 case Mode::Rotation:
-                    OverrideManipulatorOrientation(QuaternionFromTransformNoScaling(worldFromLocal));
+                    {
+                        AZ::Quaternion worldOrientation = AZ::Quaternion::CreateIdentity();
+                        AZ::TransformBus::EventResult(worldOrientation, entityId, &AZ::TransformBus::Events::GetWorldRotationQuaternion);
+                        OverrideManipulatorOrientation(worldOrientation.GetNormalized());
+                    }
                     break;
                 case Mode::Translation:
-                    OverrideManipulatorTranslation(worldFromLocal.GetTranslation());
+                    overrideManipulatorTranslationFn();
                     break;
                 case Mode::Scale:
                     // do nothing
@@ -2061,17 +2105,15 @@ namespace AzToolsFramework
                 default:
                     break;
                 }
-
-                // only update pivot override when in translation or rotation mode
+            }
+            else
+            {
                 switch (m_mode)
                 {
-                case Mode::Rotation:
-                    m_pivotOverrideFrame.m_pickTypes |= OptionalFrame::PickType::Orientation;
-                    [[fallthrough]];
                 case Mode::Translation:
-                    m_pivotOverrideFrame.m_pickTypes |= OptionalFrame::PickType::Translation;
-                    m_pivotOverrideFrame.m_pickedEntityIdOverride = entityId;
+                    overrideManipulatorTranslationFn();
                     break;
+                case Mode::Rotation:
                 case Mode::Scale:
                     // do nothing
                     break;
@@ -2079,15 +2121,10 @@ namespace AzToolsFramework
                     break;
                 }
             }
-            else
-            {
-                // match the same behavior as if we pressed Ctrl+R to reset the manipulator
-                DelegateClearManipulatorOverride();
-            }
 
             manipulatorCommand->SetManipulatorAfter(EntityManipulatorCommand::State(
                 BuildPivotOverride(m_pivotOverrideFrame.HasTranslationOverride(), m_pivotOverrideFrame.HasOrientationOverride()),
-                m_entityIdManipulators.m_manipulators->GetLocalTransform(), entityId));
+                m_entityIdManipulators.m_manipulators->GetLocalTransform()));
 
             manipulatorCommand->SetParent(undoBatch.GetUndoBatch());
             manipulatorCommand.release();
@@ -2148,13 +2185,12 @@ namespace AzToolsFramework
         case Mode::Rotation:
             ClearManipulatorOrientationOverride();
             break;
-        case Mode::Scale:
-            SetManipulatorViewBaseScale(DefaultManipulatorViewBaseScale);
-            m_pivotOverrideFrame.m_pickedEntityIdOverride.SetInvalid();
-            break;
         case Mode::Translation:
             ClearManipulatorTranslationOverride();
             break;
+        case Mode::Scale:
+            SetManipulatorViewBaseScale(DefaultManipulatorViewBaseScale);
+            break;
         }
     }
 
@@ -2753,8 +2789,7 @@ namespace AzToolsFramework
         RegenerateManipulators();
     }
 
-    void EditorTransformComponentSelection::UndoRedoEntityManipulatorCommand(
-        const AZ::u8 pivotOverride, const AZ::Transform& transform, const AZ::EntityId entityId)
+    void EditorTransformComponentSelection::UndoRedoEntityManipulatorCommand(const AZ::u8 pivotOverride, const AZ::Transform& transform)
     {
         m_pivotOverrideFrame.Reset();
 
@@ -2773,11 +2808,6 @@ namespace AzToolsFramework
             {
                 m_pivotOverrideFrame.m_translationOverride = transform.GetTranslation();
             }
-
-            if (entityId.IsValid())
-            {
-                m_pivotOverrideFrame.m_pickedEntityIdOverride = entityId;
-            }
         }
     }
 
@@ -2889,7 +2919,6 @@ namespace AzToolsFramework
                 AZStd::make_unique<EntityManipulatorCommand>(CreateManipulatorCommandStateFromSelf(), ManipulatorUndoRedoName);
 
             m_pivotOverrideFrame.ResetPickedTranslation();
-            m_pivotOverrideFrame.m_pickedEntityIdOverride.SetInvalid();
 
             m_entityIdManipulators.m_manipulators->SetLocalTransform(RecalculateAverageManipulatorTransform(
                 m_entityIdManipulators.m_lookups, m_pivotOverrideFrame, m_pivotMode, m_referenceFrame));
@@ -2915,11 +2944,10 @@ namespace AzToolsFramework
                 AZStd::make_unique<EntityManipulatorCommand>(CreateManipulatorCommandStateFromSelf(), ManipulatorUndoRedoName);
 
             m_pivotOverrideFrame.ResetPickedOrientation();
-            m_pivotOverrideFrame.m_pickedEntityIdOverride.SetInvalid();
 
             // parent reference frame is the default (when no modifiers are held)
             m_entityIdManipulators.m_manipulators->SetLocalTransform(AZ::Transform::CreateFromQuaternionAndTranslation(
-                ETCS::CalculatePivotOrientationForEntityIds(m_entityIdManipulators.m_lookups, ReferenceFrame::Parent).m_worldOrientation,
+                Etcs::CalculatePivotOrientationForEntityIds(m_entityIdManipulators.m_lookups, ReferenceFrame::Parent).m_worldOrientation,
                 m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation()));
 
             m_entityIdManipulators.m_manipulators->SetBoundsDirty();
@@ -2991,8 +3019,7 @@ namespace AzToolsFramework
             manipulatorCommand->SetManipulatorAfter(EntityManipulatorCommand::State(
                 BuildPivotOverride(m_pivotOverrideFrame.HasTranslationOverride(), m_pivotOverrideFrame.HasOrientationOverride()),
                 AZ::Transform::CreateFromQuaternionAndTranslation(
-                    QuaternionFromTransformNoScaling(m_entityIdManipulators.m_manipulators->GetLocalTransform()), translation),
-                m_pivotOverrideFrame.m_pickedEntityIdOverride));
+                    QuaternionFromTransformNoScaling(m_entityIdManipulators.m_manipulators->GetLocalTransform()), translation)));
 
             manipulatorCommand->SetParent(undoBatch.GetUndoBatch());
             manipulatorCommand.release();
@@ -3044,8 +3071,7 @@ namespace AzToolsFramework
             manipulatorCommand->SetManipulatorAfter(EntityManipulatorCommand::State(
                 BuildPivotOverride(m_pivotOverrideFrame.HasTranslationOverride(), m_pivotOverrideFrame.HasOrientationOverride()),
                 AZ::Transform::CreateFromQuaternionAndTranslation(
-                    QuaternionFromTransformNoScaling(m_entityIdManipulators.m_manipulators->GetLocalTransform()), translation),
-                m_pivotOverrideFrame.m_pickedEntityIdOverride));
+                    QuaternionFromTransformNoScaling(m_entityIdManipulators.m_manipulators->GetLocalTransform()), translation)));
 
             manipulatorCommand->SetParent(undoBatch.GetUndoBatch());
             manipulatorCommand.release();
@@ -3457,30 +3483,11 @@ namespace AzToolsFramework
 
         if (!m_selectedEntityIds.empty())
         {
-            if (m_pivotOverrideFrame.m_pickedEntityIdOverride.IsValid())
-            {
-                const AZ::Transform pickedEntityWorldTransform = AZ::Transform::CreateFromQuaternionAndTranslation(
-                    ETCS::CalculatePivotOrientation(m_pivotOverrideFrame.m_pickedEntityIdOverride, referenceFrame).m_worldOrientation,
-                    CalculatePivotTranslation(m_pivotOverrideFrame.m_pickedEntityIdOverride, m_pivotMode));
-
-                const float scaledSize =
-                    PivotSize * CalculateScreenToWorldMultiplier(pickedEntityWorldTransform.GetTranslation(), cameraState);
-
-                debugDisplay.DepthWriteOff();
-                debugDisplay.DepthTestOff();
-                debugDisplay.SetColor(PickedOrientationColor);
-
-                debugDisplay.DrawWireSphere(pickedEntityWorldTransform.GetTranslation(), scaledSize);
-
-                debugDisplay.DepthTestOn();
-                debugDisplay.DepthWriteOn();
-            }
-
             // check what pivot orientation we are in (based on if a modifier is
             // held to move us from parent to world space or parent to local space)
             // or if we set a pivot override
             const auto pivotResult =
-                ETCS::CalculateSelectionPivotOrientation(m_entityIdManipulators.m_lookups, m_pivotOverrideFrame, m_referenceFrame);
+                Etcs::CalculateSelectionPivotOrientation(m_entityIdManipulators.m_lookups, m_pivotOverrideFrame, m_referenceFrame);
 
             // if the reference frame was parent space and the selection does have a
             // valid parent, draw a preview axis at its position/orientation
@@ -3630,6 +3637,25 @@ namespace AzToolsFramework
     {
         AZ_PROFILE_FUNCTION(AzToolsFramework);
 
+        // debug draw manipulator position (when one exists) in screen space
+        if (ed_viewportDisplayManipulatorPosition)
+        {
+            const int viewportId = viewportInfo.m_viewportId;
+            const AzFramework::CameraState editorCameraState = GetCameraState(viewportId);
+            const auto viewportSize = AzFramework::Vector2FromScreenSize(editorCameraState.m_viewportSize);
+
+            AZStd::string manipulatorPosition = "none";
+            if (const auto manipulatorTransform = GetManipulatorTransform(); manipulatorTransform.has_value())
+            {
+                AZStd::to_string(manipulatorPosition, manipulatorTransform->GetTranslation());
+            }
+
+            debugDisplay.SetColor(AZ::Colors::White);
+            debugDisplay.Draw2dTextLabel(
+                15.0f, viewportSize.GetY() - 25.0f, 0.6f,
+                AZStd::string::format("Manipulator World Position: %s", manipulatorPosition.c_str()).c_str());
+        }
+
         DrawAxisGizmo(viewportInfo, debugDisplay);
 
         m_boxSelect.Display2d(viewportInfo, debugDisplay);
@@ -3826,32 +3852,32 @@ namespace AzToolsFramework
 
     void EditorTransformComponentSelection::SetEntityWorldTranslation(const AZ::EntityId entityId, const AZ::Vector3& worldTranslation)
     {
-        ETCS::SetEntityWorldTranslation(entityId, worldTranslation, m_transformChangedInternally);
+        Etcs::SetEntityWorldTranslation(entityId, worldTranslation, m_transformChangedInternally);
     }
 
     void EditorTransformComponentSelection::SetEntityLocalTranslation(const AZ::EntityId entityId, const AZ::Vector3& localTranslation)
     {
-        ETCS::SetEntityLocalTranslation(entityId, localTranslation, m_transformChangedInternally);
+        Etcs::SetEntityLocalTranslation(entityId, localTranslation, m_transformChangedInternally);
     }
 
     void EditorTransformComponentSelection::SetEntityWorldTransform(const AZ::EntityId entityId, const AZ::Transform& worldTransform)
     {
-        ETCS::SetEntityWorldTransform(entityId, worldTransform, m_transformChangedInternally);
+        Etcs::SetEntityWorldTransform(entityId, worldTransform, m_transformChangedInternally);
     }
 
     void EditorTransformComponentSelection::SetEntityLocalScale(const AZ::EntityId entityId, const float localScale)
     {
-        ETCS::SetEntityLocalScale(entityId, localScale, m_transformChangedInternally);
+        Etcs::SetEntityLocalScale(entityId, localScale, m_transformChangedInternally);
     }
 
     void EditorTransformComponentSelection::SetEntityLocalRotation(const AZ::EntityId entityId, const AZ::Vector3& localRotation)
     {
-        ETCS::SetEntityLocalRotation(entityId, localRotation, m_transformChangedInternally);
+        Etcs::SetEntityLocalRotation(entityId, localRotation, m_transformChangedInternally);
     }
 
     void EditorTransformComponentSelection::SetEntityLocalRotation(AZ::EntityId entityId, const AZ::Quaternion& localRotation)
     {
-        ETCS::SetEntityLocalRotation(entityId, localRotation, m_transformChangedInternally);
+        Etcs::SetEntityLocalRotation(entityId, localRotation, m_transformChangedInternally);
     }
 
     void EditorTransformComponentSelection::OnStartPlayInEditor()
@@ -3877,7 +3903,7 @@ namespace AzToolsFramework
         }
     }
 
-    namespace ETCS
+    namespace Etcs
     {
         // little raii wrapper to switch a value from true to false and back
         class ScopeSwitch
@@ -3933,11 +3959,11 @@ namespace AzToolsFramework
             ScopeSwitch sw(internal);
             AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetLocalRotationQuaternion, localRotation);
         }
-    } // namespace ETCS
+    } // namespace Etcs
 
     // explicit instantiations
-    template ETCS::PivotOrientationResult ETCS::CalculatePivotOrientationForEntityIds<EntityIdManipulatorLookups>(
+    template Etcs::PivotOrientationResult Etcs::CalculatePivotOrientationForEntityIds<EntityIdManipulatorLookups>(
         const EntityIdManipulatorLookups&, ReferenceFrame);
-    template ETCS::PivotOrientationResult ETCS::CalculateSelectionPivotOrientation<EntityIdManipulatorLookups>(
+    template Etcs::PivotOrientationResult Etcs::CalculateSelectionPivotOrientation<EntityIdManipulatorLookups>(
         const EntityIdManipulatorLookups&, const OptionalFrame&, const ReferenceFrame referenceFrame);
 } // namespace AzToolsFramework

+ 15 - 25
Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h

@@ -17,8 +17,8 @@
 #include <AzFramework/Viewport/ClickDetector.h>
 #include <AzFramework/Viewport/CursorState.h>
 #include <AzToolsFramework/API/EditorCameraBus.h>
-#include <AzToolsFramework/Commands/EntityManipulatorCommand.h>
 #include <AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h>
+#include <AzToolsFramework/Commands/EntityManipulatorCommand.h>
 #include <AzToolsFramework/Editor/EditorContextMenuBus.h>
 #include <AzToolsFramework/Entity/EntityTypes.h>
 #include <AzToolsFramework/Entity/ReadOnly/ReadOnlyEntityBus.h>
@@ -64,24 +64,9 @@ namespace AzToolsFramework
     //! Temporary manipulator frame used during selection.
     struct OptionalFrame
     {
-        //! What part of the transform did we pick (when using ditto on
-        //! the manipulator). This will depend on the transform mode we're in.
-        struct PickType
-        {
-            enum : AZ::u8
-            {
-                None = 0,
-                Orientation = 1 << 0,
-                Translation = 1 << 1
-            };
-        };
-
         bool HasTranslationOverride() const;
         bool HasOrientationOverride() const;
         bool HasTransformOverride() const;
-        bool HasEntityOverride() const;
-        bool PickedTranslation() const;
-        bool PickedOrientation() const;
 
         //! Clear all state associated with the frame.
         void Reset();
@@ -90,10 +75,8 @@ namespace AzToolsFramework
         //! Clear only picked orientation state.
         void ResetPickedOrientation();
 
-        AZ::EntityId m_pickedEntityIdOverride; //!< 'Picked' Entity - frame and parent space relative to this if active.
         AZStd::optional<AZ::Vector3> m_translationOverride; //!< Translation override, if set, reset when selection is empty.
         AZStd::optional<AZ::Quaternion> m_orientationOverride; //!< Orientation override, if set, reset when selection is empty.
-        AZ::u8 m_pickTypes = PickType::None; //!< What mode(s) were we in when picking an EntityId override.
     };
 
     //! How a manipulator should treat an adjustment.
@@ -261,7 +244,7 @@ namespace AzToolsFramework
         void SnapSelectedEntitiesToWorldGrid(float gridSize) override;
 
         // EditorManipulatorCommandUndoRedoRequestBus overrides ...
-        void UndoRedoEntityManipulatorCommand(AZ::u8 pivotOverride, const AZ::Transform& transform, AZ::EntityId entityId) override;
+        void UndoRedoEntityManipulatorCommand(AZ::u8 pivotOverride, const AZ::Transform& transform) override;
 
         // EditorContextMenuBus overrides ...
         void PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2& point, int flags) override;
@@ -301,17 +284,24 @@ namespace AzToolsFramework
         // ReadOnlyEntityPublicNotificationBus overrides ...
         void OnReadOnlyEntityStatusChanged(const AZ::EntityId& entityId, bool readOnly) override;
 
-        // Helpers to safely interact with the TransformBus (requests).
+        //!@{
+        //! Helpers to safely interact with the TransformBus (requests).
         void SetEntityWorldTranslation(AZ::EntityId entityId, const AZ::Vector3& worldTranslation);
         void SetEntityLocalTranslation(AZ::EntityId entityId, const AZ::Vector3& localTranslation);
         void SetEntityWorldTransform(AZ::EntityId entityId, const AZ::Transform& worldTransform);
         void SetEntityLocalScale(AZ::EntityId entityId, float localScale);
         void SetEntityLocalRotation(AZ::EntityId entityId, const AZ::Vector3& localRotation);
         void SetEntityLocalRotation(AZ::EntityId entityId, const AZ::Quaternion& localRotation);
+        //!@}
 
+        // Note: 'Ditto' is an immediate copy (it updates part of one entity transform to perfectly match
+        // another depending on the transform mode - translate/rotate/scale).
         bool PerformGroupDitto(AZ::EntityId entityId);
         bool PerformIndividualDitto(AZ::EntityId entityId);
-        void PerformManipulatorDitto(AZ::EntityId entityId);
+        //! ManipulatorTranslationFn returns the position to use when 'dittoing' the manipulator.
+        //! @note: signature - AZ::Vector3(void).
+        template<typename ManipulatorTranslationFn>
+        void PerformManipulatorDitto(AZ::EntityId entityId, ManipulatorTranslationFn&& manipulatorTranslationFn);
         void PerformSnapToSurface(const ViewportInteraction::MouseInteractionEvent& mouseInteraction);
 
         //! Responsible for keeping the space cluster in sync with the current reference frame.
@@ -325,7 +315,7 @@ namespace AzToolsFramework
         AZ::EntityId m_editorCameraComponentEntityId; //!< The EditorCameraComponent EntityId if it is set.
         EntityIdSet m_selectedEntityIds; //!< Represents the current entities in the selection.
         //! A cache of packed EntityData that can be iterated over efficiently without the need to make individual EBus calls.
-        const EditorVisibleEntityDataCacheInterface* m_entityDataCache = nullptr; 
+        const EditorVisibleEntityDataCacheInterface* m_entityDataCache = nullptr;
         AZStd::unique_ptr<EditorHelpers> m_editorHelpers; //!< Editor visualization of entities (icons, shapes, debug visuals etc).
         EntityIdManipulators m_entityIdManipulators; //!< Mapping from a Manipulator to potentially many EntityIds.
 
@@ -376,10 +366,10 @@ namespace AzToolsFramework
         ViewportInteraction::MouseButtons mouseButtons,
         const AZStd::function<void(AZ::EntityId, bool)>& setEntityAccentedFn);
 
-    //! The ETCS (EntityTransformComponentSelection) namespace contains functions and data used exclusively by
+    //! The Etcs (EntityTransformComponentSelection) namespace contains functions and data used exclusively by
     //! the EditorTransformComponentSelection type. Functions in this namespace are exposed to facilitate testing
     //! and should not be used outside of EditorTransformComponentSelection or EditorTransformComponentSelectionTests.
-    namespace ETCS
+    namespace Etcs
     {
         //! The result from calculating the entity (transform component) orientation.
         //! Does the entity have a parent or not, and what orientation should the manipulator have when
@@ -410,5 +400,5 @@ namespace AzToolsFramework
         void SetEntityLocalScale(AZ::EntityId entityId, float localScale, bool& internal);
         void SetEntityLocalRotation(AZ::EntityId entityId, const AZ::Vector3& localRotation, bool& internal);
         void SetEntityLocalRotation(AZ::EntityId entityId, const AZ::Quaternion& localRotation, bool& internal);
-    } // namespace ETCS
+    } // namespace Etcs
 } // namespace AzToolsFramework

+ 106 - 21
Code/Framework/AzToolsFramework/Tests/EditorTransformComponentSelectionTests.cpp

@@ -1149,7 +1149,7 @@ namespace UnitTest
 
     TEST_P(
         EditorTransformComponentSelectionViewportPickingManipulatorTestFixtureParam,
-        StickyAndUnstickyDittoManipulatorToOtherEntityChangesManipulatorAndClickOffResetsManipulator)
+        StickyAndUnstickyDittoManipulatorToOtherEntityChangesManipulatorAndClickOffHasNoEffect)
     {
         PositionEntities();
         PositionCamera(m_cameraState);
@@ -1200,8 +1200,8 @@ namespace UnitTest
             manipulatorTransform, AzToolsFramework::GetEntityContextId(),
             &AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform);
 
-        // manipulator transform is reset
-        EXPECT_THAT(manipulatorTransform->GetTranslation(), IsClose(Entity1WorldTranslation));
+        // manipulator transform remains where it was (when using Ctrl+Alt to update the position of the manipulator)
+        EXPECT_THAT(manipulatorTransform->GetTranslation(), IsClose(Entity2WorldTranslation));
     }
 
     INSTANTIATE_TEST_CASE_P(All, EditorTransformComponentSelectionViewportPickingManipulatorTestFixtureParam, testing::Values(true, false));
@@ -1755,6 +1755,91 @@ namespace UnitTest
                 AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity2WorldTranslation),
                 AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity3WorldTranslation) }));
 
+    struct ManipulatorPick
+    {
+        AZStd::array<AzToolsFramework::ViewportInteraction::KeyboardModifier, 3> m_keyboardModifiers{};
+        AZ::Vector3 m_pickPosition;
+        AZ::Vector3 m_expectedManipulatorPosition;
+    };
+
+    class EditorTransformComponentSelectionTranslationManipulatorPickingEntityTestFixtureParam
+        : public EditorTransformComponentSelectionManipulatorInteractionTestFixture
+        , public ::testing::WithParamInterface<ManipulatorPick>
+    {
+    };
+
+    static constexpr float ManipulatorPickBoxHalfSize = 0.5f;
+    static constexpr float ManipulatorPickOffsetTolerance = 0.1f;
+
+    TEST_P(
+        EditorTransformComponentSelectionTranslationManipulatorPickingEntityTestFixtureParam,
+        DittoManipulatorOnEntityChangesManipulatorToEntityTransformOrPickIntersectionBasedOnModifiers)
+    {
+        using AzToolsFramework::EditorTransformComponentSelectionRequestBus;
+
+        PositionEntities();
+
+        // camera (go to position format) - 10.00, 15.00, 12.00, 0.00, 90.00
+        m_cameraState.m_viewportSize = AzFramework::ScreenSize(1280, 720);
+        AzFramework::SetCameraTransform(
+            m_cameraState,
+            AZ::Transform::CreateFromQuaternionAndTranslation(
+                AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(0.0f, 0.0f, 90.0f)), AZ::Vector3(10.0f, 15.0f, 12.0f)));
+
+        SetTransformMode(EditorTransformComponentSelectionRequestBus::Events::Mode::Translation);
+
+        // at position 5.0, 16.0, 10.0 (right most entity)
+        AzToolsFramework::SelectEntities({ m_entityId3 });
+
+        const auto clickPositionWorld = GetParam().m_pickPosition;
+        const auto clickPositionScreen = AzFramework::WorldToScreen(clickPositionWorld, m_cameraState);
+
+        for (auto modifier : GetParam().m_keyboardModifiers)
+        {
+            m_actionDispatcher->KeyboardModifierDown(modifier);
+        }
+
+        // click the corner of the box
+        m_actionDispatcher->CameraState(m_cameraState)->MousePosition(clickPositionScreen)->MouseLButtonDown()->MouseLButtonUp();
+
+        const auto manipulatorPosition = GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity()).GetTranslation();
+        EXPECT_THAT(manipulatorPosition, IsCloseTolerance(GetParam().m_expectedManipulatorPosition, 0.01f));
+    }
+
+    static const AZ::Vector3 ManipulatorPickBoxCorner = EditorTransformComponentSelectionViewportPickingFixture::Entity2WorldTranslation +
+        AZ::Vector3(ManipulatorPickBoxHalfSize,
+                    -ManipulatorPickBoxHalfSize + ManipulatorPickOffsetTolerance,
+                    -ManipulatorPickBoxHalfSize + ManipulatorPickOffsetTolerance);
+
+    INSTANTIATE_TEST_CASE_P(
+        All,
+        EditorTransformComponentSelectionTranslationManipulatorPickingEntityTestFixtureParam,
+        testing::Values(
+            // manipulator should move to exact pick position when ctrl and shift are held
+            ManipulatorPick{ { AzToolsFramework::ViewportInteraction::KeyboardModifier::Control,
+                               AzToolsFramework::ViewportInteraction::KeyboardModifier::Shift },
+                             ManipulatorPickBoxCorner,
+                             ManipulatorPickBoxCorner },
+            // manipulator should move to picked entity position when ctrl and alt is held
+            ManipulatorPick{ { AzToolsFramework::ViewportInteraction::KeyboardModifier::Control,
+                               AzToolsFramework::ViewportInteraction::KeyboardModifier::Alt },
+                             ManipulatorPickBoxCorner,
+                             EditorTransformComponentSelectionViewportPickingFixture::Entity2WorldTranslation },
+            ManipulatorPick{ { AzToolsFramework::ViewportInteraction::KeyboardModifier::Control,
+                               AzToolsFramework::ViewportInteraction::KeyboardModifier::Shift },
+                             // click position above boxes/entities
+                             AZ::Vector3(5.0f, 15.0f, 12.0f),
+                             // position in front of camera when there was no pick intersection (uses GetDefaultEntityPlacementDistance,
+                             // which has a default value of 10) note: the camera is positioned 10 units along the x-axis looking down it
+                             // (negative) and the near clip plane is set to 0.1, so the absolute position is -0.1 on the x-axis
+                             AZ::Vector3(-0.1f, 15.0f, 12.0f) },
+            ManipulatorPick{ { AzToolsFramework::ViewportInteraction::KeyboardModifier::Control,
+                               AzToolsFramework::ViewportInteraction::KeyboardModifier::Alt },
+                             // click position above boxes/entities
+                             AZ::Vector3(5.0f, 15.0f, 12.0f),
+                             // position remains unchanged (manipulator won't move as an entity wasn't picked)
+                             EditorTransformComponentSelectionViewportPickingFixture::Entity3WorldTranslation }));
+
     using EditorTransformComponentSelectionScaleManipulatorInteractionTestFixture =
         EditorTransformComponentSelectionManipulatorInteractionTestFixture;
 
@@ -2040,8 +2125,8 @@ namespace UnitTest
 
     TEST_P(EditorTransformComponentSelectionSingleEntityPivotFixture, PivotOrientationMatchesReferenceFrameSingleEntity)
     {
-        using AzToolsFramework::ETCS::CalculatePivotOrientation;
-        using AzToolsFramework::ETCS::PivotOrientationResult;
+        using AzToolsFramework::Etcs::CalculatePivotOrientation;
+        using AzToolsFramework::Etcs::PivotOrientationResult;
 
         ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
         // Given
@@ -2080,8 +2165,8 @@ namespace UnitTest
 
     TEST_P(EditorTransformComponentSelectionSingleEntityWithParentPivotFixture, PivotOrientationMatchesReferenceFrameEntityWithParent)
     {
-        using AzToolsFramework::ETCS::CalculatePivotOrientation;
-        using AzToolsFramework::ETCS::PivotOrientationResult;
+        using AzToolsFramework::Etcs::CalculatePivotOrientation;
+        using AzToolsFramework::Etcs::PivotOrientationResult;
 
         ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
         // Given
@@ -2129,8 +2214,8 @@ namespace UnitTest
 
     TEST_P(EditorTransformComponentSelectionMultipleEntitiesPivotFixture, PivotOrientationMatchesReferenceFrameMultipleEntities)
     {
-        using AzToolsFramework::ETCS::CalculatePivotOrientationForEntityIds;
-        using AzToolsFramework::ETCS::PivotOrientationResult;
+        using AzToolsFramework::Etcs::CalculatePivotOrientationForEntityIds;
+        using AzToolsFramework::Etcs::PivotOrientationResult;
 
         ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
         // Given
@@ -2188,8 +2273,8 @@ namespace UnitTest
         EditorTransformComponentSelectionMultipleEntitiesWithSameParentPivotFixture,
         PivotOrientationMatchesReferenceFrameMultipleEntitiesSameParent)
     {
-        using AzToolsFramework::ETCS::CalculatePivotOrientationForEntityIds;
-        using AzToolsFramework::ETCS::PivotOrientationResult;
+        using AzToolsFramework::Etcs::CalculatePivotOrientationForEntityIds;
+        using AzToolsFramework::Etcs::PivotOrientationResult;
 
         ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
         // Given
@@ -2251,8 +2336,8 @@ namespace UnitTest
         EditorTransformComponentSelectionMultipleEntitiesWithDifferentParentPivotFixture,
         PivotOrientationMatchesReferenceFrameMultipleEntitiesDifferentParent)
     {
-        using AzToolsFramework::ETCS::CalculatePivotOrientationForEntityIds;
-        using AzToolsFramework::ETCS::PivotOrientationResult;
+        using AzToolsFramework::Etcs::CalculatePivotOrientationForEntityIds;
+        using AzToolsFramework::Etcs::PivotOrientationResult;
 
         ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
         // Given
@@ -2314,8 +2399,8 @@ namespace UnitTest
         EditorTransformComponentSelectionSingleEntityPivotAndOverrideFixture,
         PivotOrientationMatchesReferenceFrameSingleEntityOptionalOverride)
     {
-        using AzToolsFramework::ETCS::CalculateSelectionPivotOrientation;
-        using AzToolsFramework::ETCS::PivotOrientationResult;
+        using AzToolsFramework::Etcs::CalculateSelectionPivotOrientation;
+        using AzToolsFramework::Etcs::PivotOrientationResult;
 
         ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
         // Given
@@ -2364,8 +2449,8 @@ namespace UnitTest
         EditorTransformComponentSelectionMultipleEntitiesPivotAndOverrideFixture,
         PivotOrientationMatchesReferenceFrameMultipleEntitiesOptionalOverride)
     {
-        using AzToolsFramework::ETCS::CalculateSelectionPivotOrientation;
-        using AzToolsFramework::ETCS::PivotOrientationResult;
+        using AzToolsFramework::Etcs::CalculateSelectionPivotOrientation;
+        using AzToolsFramework::Etcs::PivotOrientationResult;
 
         ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
         // Given
@@ -2424,8 +2509,8 @@ namespace UnitTest
         EditorTransformComponentSelectionMultipleEntitiesPivotAndNoOverrideFixture,
         PivotOrientationMatchesReferenceFrameMultipleEntitiesNoOptionalOverride)
     {
-        using AzToolsFramework::ETCS::CalculateSelectionPivotOrientation;
-        using AzToolsFramework::ETCS::PivotOrientationResult;
+        using AzToolsFramework::Etcs::CalculateSelectionPivotOrientation;
+        using AzToolsFramework::Etcs::PivotOrientationResult;
 
         ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
         // Given
@@ -2482,8 +2567,8 @@ namespace UnitTest
         EditorTransformComponentSelectionMultipleEntitiesSameParentPivotAndNoOverrideFixture,
         PivotOrientationMatchesReferenceFrameMultipleEntitiesSameParentNoOptionalOverride)
     {
-        using AzToolsFramework::ETCS::CalculateSelectionPivotOrientation;
-        using AzToolsFramework::ETCS::PivotOrientationResult;
+        using AzToolsFramework::Etcs::CalculateSelectionPivotOrientation;
+        using AzToolsFramework::Etcs::PivotOrientationResult;
 
         ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
         // Given