From 05e20803a89253bd8f7ba6ef507242e08d833d72 Mon Sep 17 00:00:00 2001 From: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com> Date: Fri, 4 Jun 2021 14:32:06 +0100 Subject: [PATCH] First pass for getting things ready for grid snap button (#1118) * first pass of change to simplify snapping for snap-to-grid button and fix snapping bug caused by non-uniform scale --- .../AzManipulatorTestFrameworkUtils.h | 25 ++-- .../AzManipulatorTestFrameworkUtils.cpp | 58 +++++---- .../Tests/GridSnappingTest.cpp | 120 +++++++++++++----- .../Manipulators/BaseManipulator.cpp | 5 + .../Manipulators/EditorVertexSelection.cpp | 27 ++-- .../Manipulators/LinearManipulator.cpp | 70 ++++------ .../Manipulators/LinearManipulator.h | 12 +- .../Manipulators/ManipulatorSnapping.cpp | 36 ++++-- .../Manipulators/ManipulatorSnapping.h | 22 ++-- .../Manipulators/MultiLinearManipulator.cpp | 28 ++-- .../Manipulators/MultiLinearManipulator.h | 2 - .../Manipulators/PlanarManipulator.cpp | 50 +++----- .../Manipulators/PlanarManipulator.h | 13 +- .../EditorNonUniformScaleComponentMode.cpp | 48 ++++--- .../EditorTransformComponentSelection.cpp | 39 ++---- 15 files changed, 295 insertions(+), 260 deletions(-) diff --git a/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h b/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h index dcddf1e5a5..f1c32e4d8d 100644 --- a/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h +++ b/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h @@ -12,17 +12,22 @@ #pragma once -#include -#include #include +#include +#include +#include namespace AzManipulatorTestFramework { - //! Create a linear manipulator with a unit sphere bounds. + //! Create a linear manipulator with a unit sphere bound. AZStd::shared_ptr CreateLinearManipulator( - const AzToolsFramework::ManipulatorManagerId manipulatorManagerId, - const AZ::Vector3& position = AZ::Vector3::CreateZero(), - const float radius = 1.0f); + const AzToolsFramework::ManipulatorManagerId manipulatorManagerId, const AZ::Vector3& position = AZ::Vector3::CreateZero(), + float radius = 1.0f); + + //! Create a planar manipulator with a unit sphere bound. + AZStd::shared_ptr CreatePlanarManipulator( + const AzToolsFramework::ManipulatorManagerId manipulatorManagerId, const AZ::Vector3& position = AZ::Vector3::CreateZero(), + float radius = 1.0f); //! Create a mouse pick from the specified ray and screen point. AzToolsFramework::ViewportInteraction::MousePick CreateMousePick( @@ -34,14 +39,12 @@ namespace AzManipulatorTestFramework //! Create a mouse interaction from the specified pick, buttons, interaction id and keyboard modifiers. AzToolsFramework::ViewportInteraction::MouseInteraction CreateMouseInteraction( - const AzToolsFramework::ViewportInteraction::MousePick& mousePick, - AzToolsFramework::ViewportInteraction::MouseButtons buttons, + const AzToolsFramework::ViewportInteraction::MousePick& mousePick, AzToolsFramework::ViewportInteraction::MouseButtons buttons, AzToolsFramework::ViewportInteraction::InteractionId interactionId, AzToolsFramework::ViewportInteraction::KeyboardModifiers modifiers); //! Create a mouse buttons from the specified mouse button. - AzToolsFramework::ViewportInteraction::MouseButtons CreateMouseButtons( - AzToolsFramework::ViewportInteraction::MouseButton button); + AzToolsFramework::ViewportInteraction::MouseButtons CreateMouseButtons(AzToolsFramework::ViewportInteraction::MouseButton button); //! Create a mouse interaction event from the specified interaction and event. AzToolsFramework::ViewportInteraction::MouseInteractionEvent CreateMouseInteractionEvent( @@ -61,5 +64,5 @@ namespace AzManipulatorTestFramework AzFramework::ScreenPoint GetCameraStateViewportCenter(const AzFramework::CameraState& cameraState); //! Default viewport size (1080p) in 16:9 aspect ratio. - const auto DefaultViewportSize = AZ::Vector2(1920.0f, 1080.0f); + inline const auto DefaultViewportSize = AZ::Vector2(1920.0f, 1080.0f); } // namespace AzManipulatorTestFramework diff --git a/Code/Framework/AzManipulatorTestFramework/Source/AzManipulatorTestFrameworkUtils.cpp b/Code/Framework/AzManipulatorTestFramework/Source/AzManipulatorTestFrameworkUtils.cpp index b255fcae73..985c21cf8e 100644 --- a/Code/Framework/AzManipulatorTestFramework/Source/AzManipulatorTestFrameworkUtils.cpp +++ b/Code/Framework/AzManipulatorTestFramework/Source/AzManipulatorTestFrameworkUtils.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include namespace AzManipulatorTestFramework @@ -28,22 +27,21 @@ namespace AzManipulatorTestFramework using MouseEvent = AzToolsFramework::ViewportInteraction::MouseEvent; using MousePick = AzToolsFramework::ViewportInteraction::MousePick; - AZStd::shared_ptr CreateLinearManipulator( - const AzToolsFramework::ManipulatorManagerId manipulatorManagerId, - const AZ::Vector3& position, - const float radius) + // create a default sphere view for a manipulator for simple intersection + template + void SetupManipulatorView( + AZStd::shared_ptr manipulator, const AzToolsFramework::ManipulatorManagerId manipulatorManagerId, + const AZ::Vector3& position, const float radius) { - auto manipulator = AzToolsFramework::LinearManipulator::MakeShared(AZ::Transform::CreateIdentity()); - manipulator->SetLocalPosition(position); - // unit sphere view auto sphereView = AzToolsFramework::CreateManipulatorViewSphere( AZ::Colors::Red, radius, - [](const MouseInteraction& /*mouseInteraction*/, const bool /*mouseOver*/, - const AZ::Color& defaultColor) - { - return defaultColor; - }, true); + []([[maybe_unused]] const MouseInteraction& mouseInteraction, [[maybe_unused]] const bool mouseOver, + const AZ::Color& defaultColor) + { + return defaultColor; + }, + true); // unit sphere bound AzToolsFramework::Picking::BoundShapeSphere sphereBound; @@ -62,6 +60,26 @@ namespace AzManipulatorTestFramework // this would occur internally when the manipulator is drawn but we must do manually here to ensure that the // bounds will always be valid upon instantiation view->RefreshBound(manipulatorManagerId, manipulator->GetManipulatorId(), sphereBound); + } + + AZStd::shared_ptr CreateLinearManipulator( + const AzToolsFramework::ManipulatorManagerId manipulatorManagerId, const AZ::Vector3& position, const float radius) + { + auto manipulator = AzToolsFramework::LinearManipulator::MakeShared(AZ::Transform::CreateIdentity()); + manipulator->SetLocalPosition(position); + + SetupManipulatorView(manipulator, manipulatorManagerId, position, radius); + + return manipulator; + } + + AZStd::shared_ptr CreatePlanarManipulator( + const AzToolsFramework::ManipulatorManagerId manipulatorManagerId, const AZ::Vector3& position, const float radius) + { + auto manipulator = AzToolsFramework::PlanarManipulator::MakeShared(AZ::Transform::CreateIdentity()); + manipulator->SetLocalPosition(position); + + SetupManipulatorView(manipulator, manipulatorManagerId, position, radius); return manipulator; } @@ -104,8 +122,7 @@ namespace AzManipulatorTestFramework return buttons; } - MouseInteractionEvent CreateMouseInteractionEvent( - const MouseInteraction& mouseInteraction, MouseEvent event) + MouseInteractionEvent CreateMouseInteractionEvent(const MouseInteraction& mouseInteraction, MouseEvent event) { return MouseInteractionEvent(mouseInteraction, event); } @@ -114,8 +131,7 @@ namespace AzManipulatorTestFramework { AzToolsFramework::EditorInteractionSystemViewportSelectionRequestBus::Event( AzToolsFramework::GetEntityContextId(), - &AzToolsFramework::ViewportInteraction::InternalMouseViewportRequests::InternalHandleAllMouseInteractions, - event); + &AzToolsFramework::ViewportInteraction::InternalMouseViewportRequests::InternalHandleAllMouseInteractions, event); } AzFramework::CameraState SetCameraStatePosition(const AZ::Vector3& position, AzFramework::CameraState& cameraState) @@ -133,9 +149,7 @@ namespace AzManipulatorTestFramework AzFramework::ScreenPoint GetCameraStateViewportCenter(const AzFramework::CameraState& cameraState) { - return { - aznumeric_cast(cameraState.m_viewportSize.GetX() / 2.f), - aznumeric_cast(cameraState.m_viewportSize.GetY() / 2.f) - }; + return { aznumeric_cast(cameraState.m_viewportSize.GetX() / 2.f), + aznumeric_cast(cameraState.m_viewportSize.GetY() / 2.f) }; } -} // namespace UnitTest +} // namespace AzManipulatorTestFramework diff --git a/Code/Framework/AzManipulatorTestFramework/Tests/GridSnappingTest.cpp b/Code/Framework/AzManipulatorTestFramework/Tests/GridSnappingTest.cpp index e6468c1ebb..d6006ba74f 100644 --- a/Code/Framework/AzManipulatorTestFramework/Tests/GridSnappingTest.cpp +++ b/Code/Framework/AzManipulatorTestFramework/Tests/GridSnappingTest.cpp @@ -10,52 +10,55 @@ * */ +#include "AzManipulatorTestFrameworkTestFixtures.h" + #include #include #include -#include "AzManipulatorTestFrameworkTestFixtures.h" -#include -#include -#include #include +#include +#include +#include +#include +#include #include +#include namespace UnitTest { - class GridSnappingFixture - : public ToolsApplicationFixture + class GridSnappingFixture : public ToolsApplicationFixture { public: GridSnappingFixture() : m_viewportManipulatorInteraction(AZStd::make_unique()) - , m_actionDispatcher(AZStd::make_unique(*m_viewportManipulatorInteraction)) - , m_linearManipulator( - AzManipulatorTestFramework::CreateLinearManipulator( - m_viewportManipulatorInteraction->GetManipulatorManager().GetId(), - /*position=*/AZ::Vector3(0.0f, 50.0f, 0.0f), - /*radius=*/m_boundsRadius)) - {} + , m_actionDispatcher( + AZStd::make_unique(*m_viewportManipulatorInteraction)) + { + } protected: void SetUpEditorFixtureImpl() override { - m_cameraState = AzFramework::CreateIdentityDefaultCamera( - AZ::Vector3::CreateZero(), AzManipulatorTestFramework::DefaultViewportSize); + m_cameraState = + AzFramework::CreateIdentityDefaultCamera(AZ::Vector3::CreateZero(), AzManipulatorTestFramework::DefaultViewportSize); } public: - const float m_boundsRadius = 1.0f; AZStd::unique_ptr m_viewportManipulatorInteraction; AZStd::unique_ptr m_actionDispatcher; - AZStd::shared_ptr m_linearManipulator; AzFramework::CameraState m_cameraState; }; TEST_F(GridSnappingFixture, MouseDownWithSnappingEnabledSnapsToClosestGridSize) { + AZStd::shared_ptr linearManipulator(AzManipulatorTestFramework::CreateLinearManipulator( + m_viewportManipulatorInteraction->GetManipulatorManager().GetId(), + /*position=*/AZ::Vector3(0.0f, 50.0f, 0.0f), + /*radius=*/m_boundsRadius)); + // the initial starting position of the manipulator (in front of the camera) - const auto initialPositionWorld = m_linearManipulator->GetLocalPosition(); + const auto initialPositionWorld = linearManipulator->GetLocalPosition(); // where the manipulator should end up (in front and to the left of the camera) const auto finalPositionWorld = AZ::Vector3(-10.0f, 50.0f, 0.0f); // perspective scale factor for manipulator distance to camera @@ -66,21 +69,18 @@ namespace UnitTest // adjusted final world position taking into account the manipulator position relative to the camera const auto finalPositionWorldAdjusted = finalPositionWorld - (vectorToInitialPositionWorld * scaledRadiusBound); // calculate the position in screen space of the initial position of the manipulator - const auto initialPositionScreen = - AzFramework::WorldToScreen(initialPositionWorld, m_cameraState); + const auto initialPositionScreen = AzFramework::WorldToScreen(initialPositionWorld, m_cameraState); // calculate the position in screen space of the final position of the manipulator const auto finalPositionScreen = AzFramework::WorldToScreen(finalPositionWorldAdjusted, m_cameraState); // callback to update the manipulator's current position - m_linearManipulator->InstallMouseMoveCallback( - [this](const AzToolsFramework::LinearManipulator::Action& action) - { - auto pos = action.LocalPosition(); - m_linearManipulator->SetLocalPosition(pos); - }); + linearManipulator->InstallMouseMoveCallback( + [this, linearManipulator](const AzToolsFramework::LinearManipulator::Action& action) + { + linearManipulator->SetLocalPosition(action.LocalPosition()); + }); - m_actionDispatcher - ->EnableSnapToGrid() + m_actionDispatcher->EnableSnapToGrid() ->GridSize(5.0f) ->CameraState(m_cameraState) ->MousePosition(initialPositionScreen) @@ -89,7 +89,67 @@ namespace UnitTest ->MousePosition(finalPositionScreen) ->MouseLButtonUp() ->ExpectManipulatorNotBeingInteracted() - ->ExpectTrue(m_linearManipulator->GetLocalPosition().IsClose(finalPositionWorld, 0.01f)) - ; + ->ExpectTrue(linearManipulator->GetLocalPosition().IsClose(finalPositionWorld, 0.01f)); + } + + template + void ValidateManipulatorSnappingBehavior( + AZStd::shared_ptr manipulator, AzManipulatorTestFramework::ImmediateModeActionDispatcher* actionDispatcher, + const AzFramework::CameraState& cameraState) + { + manipulator->SetLocalOrientation(AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(180.0f, 0.0f, 135.0f))); + + // the initial starting position of the manipulator (in front of the camera) + const auto initialPositionWorld = manipulator->GetLocalPosition() + AZ::Vector3::CreateAxisX(0.15f); + // where the manipulator should end up (unmoved) + const auto finalPositionWorld = manipulator->GetLocalPosition(); + // where we should move the mouse to + const auto attemptPositionWorld = manipulator->GetLocalPosition() + AZ::Vector3::CreateAxisX(0.35f); + // calculate the position in screen space of the initial position of the manipulator + const auto initialPositionScreen = AzFramework::WorldToScreen(initialPositionWorld, cameraState); + // calculate the position in screen space of the final position of the manipulator + const auto attemptPositionScreen = AzFramework::WorldToScreen(attemptPositionWorld, cameraState); + + // callback to update the manipulator's current position + manipulator->InstallMouseMoveCallback( + [manipulator](const typename Manipulator::Action& action) + { + manipulator->SetLocalPosition(action.LocalPosition()); + }); + + actionDispatcher->EnableSnapToGrid() + ->GridSize(1.0f) + ->CameraState(cameraState) + ->MousePosition(initialPositionScreen) + ->MouseLButtonDown() + ->ExpectManipulatorBeingInteracted() + ->MousePosition(attemptPositionScreen) + ->MouseLButtonUp() + ->ExpectManipulatorNotBeingInteracted() + ->ExpectThat(manipulator->GetLocalPosition(), IsCloseTolerance(finalPositionWorld, 0.01f)); + } + + TEST_F(GridSnappingFixture, MouseDownAndMoveLinearManipulatorDoesNotSnapWithMovementSmallerThanHalfGridSize) + { + AZStd::shared_ptr linearManipulator(AzManipulatorTestFramework::CreateLinearManipulator( + m_viewportManipulatorInteraction->GetManipulatorManager().GetId(), + /*position=*/AZ::Vector3(0.0f, 10.0f, 0.0f), + /*radius=*/m_boundsRadius)); + + linearManipulator->SetAxis(AZ::Vector3::CreateAxisY()); + + ValidateManipulatorSnappingBehavior(linearManipulator, m_actionDispatcher.get(), m_cameraState); + } + + TEST_F(GridSnappingFixture, MouseDownAndMovePlanarManipulatorDoesNotSnapWithMovementSmallerThanHalfGridSize) + { + AZStd::shared_ptr planarManipulator(AzManipulatorTestFramework::CreatePlanarManipulator( + m_viewportManipulatorInteraction->GetManipulatorManager().GetId(), + /*position=*/AZ::Vector3(0.0f, 10.0f, 0.0f), + /*radius=*/m_boundsRadius)); + + planarManipulator->SetAxes(AZ::Vector3::CreateAxisY(), AZ::Vector3::CreateAxisZ()); + + ValidateManipulatorSnappingBehavior(planarManipulator, m_actionDispatcher.get(), m_cameraState); } } // namespace UnitTest diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BaseManipulator.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BaseManipulator.cpp index e7235f0f82..955d10d3bd 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BaseManipulator.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BaseManipulator.cpp @@ -433,6 +433,11 @@ namespace AzToolsFramework return m_manipulatorSpaceWithLocalTransform.GetSpace(); } + const AZ::Vector3& Manipulators::GetNonUniformScale() const + { + return m_manipulatorSpaceWithLocalTransform.GetNonUniformScale(); + } + void Manipulators::SetSpace(const AZ::Transform& worldFromLocal) { m_manipulatorSpaceWithLocalTransform.SetSpace(worldFromLocal); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/EditorVertexSelection.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/EditorVertexSelection.cpp index 4d10a4c171..55a8464ba6 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/EditorVertexSelection.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/EditorVertexSelection.cpp @@ -192,8 +192,7 @@ namespace AzToolsFramework /// for each vertex associated with the translation manipulator to use with offset calculations when updating. template void InitializeVertexLookup( - IndexedTranslationManipulator& translationManipulator, - const AZ::EntityId entityId, const AZ::Vector3& snapOffset) + IndexedTranslationManipulator& translationManipulator, const AZ::EntityId entityId) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); @@ -202,7 +201,7 @@ namespace AzToolsFramework AZ::FixedVerticesRequestBus::Bind(fixedVertices, entityId); translationManipulator.Process( - [snapOffset, fixedVertices] + [fixedVertices] (typename IndexedTranslationManipulator::VertexLookup& vertexLookup) { Vertex vertex; @@ -213,7 +212,7 @@ namespace AzToolsFramework if (found) { - vertexLookup.m_start = vertex + AZ::AdaptVertexIn(snapOffset); + vertexLookup.m_start = vertex; vertexLookup.m_offset = Vertex::CreateZero(); } }); @@ -250,10 +249,10 @@ namespace AzToolsFramework // linear manipulator callbacks m_translationManipulator->m_manipulator.InstallLinearManipulatorMouseDownCallback( - [this](const LinearManipulator::Action& action) + [this]([[maybe_unused]] const LinearManipulator::Action& action) { BeginBatchMovement(); - InitializeVertexLookup(*m_translationManipulator, GetEntityId(), action.m_start.m_positionSnapOffset); + InitializeVertexLookup(*m_translationManipulator, GetEntityId()); }); m_translationManipulator->m_manipulator.InstallLinearManipulatorMouseMoveCallback( @@ -264,17 +263,17 @@ namespace AzToolsFramework }); m_translationManipulator->m_manipulator.InstallLinearManipulatorMouseUpCallback( - [this](const LinearManipulator::Action& /*action*/) + [this]([[maybe_unused]] const LinearManipulator::Action& action) { EndBatchMovement(); }); // planar manipulator callbacks m_translationManipulator->m_manipulator.InstallPlanarManipulatorMouseDownCallback( - [this](const PlanarManipulator::Action& action) + [this]([[maybe_unused]] const PlanarManipulator::Action& action) { BeginBatchMovement(); - InitializeVertexLookup(*m_translationManipulator, GetEntityId(), action.m_start.m_snapOffset); + InitializeVertexLookup(*m_translationManipulator, GetEntityId()); }); m_translationManipulator->m_manipulator.InstallPlanarManipulatorMouseMoveCallback( @@ -285,17 +284,17 @@ namespace AzToolsFramework }); m_translationManipulator->m_manipulator.InstallPlanarManipulatorMouseUpCallback( - [this](const PlanarManipulator::Action& /*action*/) + [this]([[maybe_unused]] const PlanarManipulator::Action& action) { EndBatchMovement(); }); // surface manipulator callbacks m_translationManipulator->m_manipulator.InstallSurfaceManipulatorMouseDownCallback( - [this](const SurfaceManipulator::Action& action) + [this]([[maybe_unused]] const SurfaceManipulator::Action& action) { BeginBatchMovement(); - InitializeVertexLookup(*m_translationManipulator, GetEntityId(), action.m_start.m_snapOffset); + InitializeVertexLookup(*m_translationManipulator, GetEntityId()); }); m_translationManipulator->m_manipulator.InstallSurfaceManipulatorMouseMoveCallback( @@ -306,7 +305,7 @@ namespace AzToolsFramework }); m_translationManipulator->m_manipulator.InstallSurfaceManipulatorMouseUpCallback( - [this](const SurfaceManipulator::Action& /*action*/) + [this]([[maybe_unused]] const SurfaceManipulator::Action& action) { EndBatchMovement(); }); @@ -893,7 +892,7 @@ namespace AzToolsFramework { BeginBatchMovement(); - InitializeVertexLookup(*m_translationManipulator, GetEntityId(), AZ::Vector3::CreateZero()); + InitializeVertexLookup(*m_translationManipulator, GetEntityId()); // note: AdaptVertexIn/Out is to ensure we clamp the vertex local Z position to 0 if // dealing with Vector2s when setting the position of the manipulator. const AZ::Vector3 localOffset = diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LinearManipulator.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LinearManipulator.cpp index aa84fc5752..34ae28bd13 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LinearManipulator.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LinearManipulator.cpp @@ -23,8 +23,8 @@ namespace AzToolsFramework { LinearManipulator::Starter CalculateLinearManipulationDataStart( const LinearManipulator::Fixed& fixed, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, - const AZ::Transform& localTransform, const GridSnapAction& gridSnapAction, const ViewportInteraction::MouseInteraction& interaction, - const float intersectionDistance, const AzFramework::CameraState& cameraState) + const AZ::Transform& localTransform, const ViewportInteraction::MouseInteraction& interaction, const float intersectionDistance, + const AzFramework::CameraState& cameraState) { const ManipulatorInteraction manipulatorInteraction = BuildManipulatorInteraction( @@ -50,28 +50,9 @@ namespace AzToolsFramework manipulatorInteraction.m_localRayOrigin, manipulatorInteraction.m_localRayDirection, localIntersectionPoint, startTransition.m_localNormal, start.m_localHitPosition); - const float gridSize = gridSnapAction.m_gridSnapParams.m_gridSize; - const bool snapping = gridSnapAction.m_gridSnapParams.m_gridSnap; - const float scaleRecip = manipulatorInteraction.m_scaleReciprocal; - - // calculate position amount to snap, to align with grid - const AZ::Vector3 positionSnapOffset = snapping && !gridSnapAction.m_localSnapping - ? CalculateSnappedOffset(localTransform.GetTranslation(), axis, gridSize * scaleRecip) - : AZ::Vector3::CreateZero(); - - const AZ::Vector3 localScale = AZ::Vector3(localTransform.GetUniformScale()); - const AZ::Quaternion localRotation = QuaternionFromTransformNoScaling(localTransform); - // calculate scale amount to snap, to align to round scale value - const AZ::Vector3 scaleSnapOffset = snapping && !gridSnapAction.m_localSnapping - ? localRotation.GetInverseFull().TransformVector(CalculateSnappedOffset( - localRotation.TransformVector(localScale), axis, gridSize * scaleRecip)) - : AZ::Vector3::CreateZero(); - start.m_screenPosition = interaction.m_mousePick.m_screenCoordinates; - start.m_positionSnapOffset = positionSnapOffset; - start.m_scaleSnapOffset = scaleSnapOffset; - start.m_localPosition = localTransform.GetTranslation() + positionSnapOffset; - start.m_localScale = localScale + scaleSnapOffset; + start.m_localPosition = localTransform.GetTranslation(); + start.m_localScale = AZ::Vector3(localTransform.GetUniformScale());; start.m_localAxis = axis; // sign to determine which side of the linear axis we pressed // (useful to know when the visual axis flips to face the camera) @@ -87,7 +68,7 @@ namespace AzToolsFramework LinearManipulator::Action CalculateLinearManipulationDataAction( const LinearManipulator::Fixed& fixed, const LinearManipulator::Starter& starter, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, const AZ::Transform& localTransform, - const GridSnapAction& gridSnapAction, const ViewportInteraction::MouseInteraction& interaction) + const GridSnapParameters& gridSnapParams, const ViewportInteraction::MouseInteraction& interaction) { const ManipulatorInteraction manipulatorInteraction = BuildManipulatorInteraction( @@ -108,31 +89,34 @@ namespace AzToolsFramework GetCameraState(interaction.m_interactionId.m_viewportId)); const AZ::Vector3 axis = TransformDirectionNoScaling(localTransform, fixed.m_axis); - // The local positions have been transformed to the reference frame of the object being manipulated. But they appear in the world - // with non-uniform scale applied, and the object being manipulated will want to work with unscaled deltas, so we need to divide by - // the non-uniform scale here. + // the local positions have been transformed to the reference frame of the object being manipulated, but they appear in the world + // with non-uniform scale applied, the object being manipulated will want to work with unscaled deltas, so we need to divide by + // the non-uniform scale here const AZ::Vector3 hitDelta = (localHitPosition - start.m_localHitPosition) / nonUniformScale; const AZ::Vector3 unsnappedOffset = axis * axis.Dot(hitDelta); - const float scaleRecip = manipulatorInteraction.m_scaleReciprocal * axis.Dot(manipulatorInteraction.m_nonUniformScaleReciprocal); - const float gridSize = gridSnapAction.m_gridSnapParams.m_gridSize; - const bool snapping = gridSnapAction.m_gridSnapParams.m_gridSnap; + const float scaleRecip = + manipulatorInteraction.m_scaleReciprocal * fixed.m_axis.Dot(manipulatorInteraction.m_nonUniformScaleReciprocal); + const float gridSize = gridSnapParams.m_gridSize; + const bool snapping = gridSnapParams.m_gridSnap; LinearManipulator::Action action; action.m_fixed = fixed; action.m_start = start; action.m_current.m_localPositionOffset = snapping - ? unsnappedOffset + CalculateSnappedOffset(unsnappedOffset, axis, gridSize * scaleRecip) + ? CalculateSnappedAmount(unsnappedOffset, axis, gridSize * scaleRecip) : unsnappedOffset; action.m_current.m_screenPosition = interaction.m_mousePick.m_screenCoordinates; action.m_viewportId = interaction.m_interactionId.m_viewportId; const AZ::Quaternion localRotation = QuaternionFromTransformNoScaling(localTransform); - const AZ::Vector3 scaledUnsnappedOffset = unsnappedOffset * startTransition.m_screenToWorldScale * NonUniformScaleReciprocal(nonUniformScale); + const AZ::Vector3 scaledUnsnappedOffset = + unsnappedOffset * startTransition.m_screenToWorldScale * NonUniformScaleReciprocal(nonUniformScale); + // how much to adjust the scale based on movement const AZ::Quaternion invLocalRotation = localRotation.GetInverseFull(); action.m_current.m_localScaleOffset = snapping - ? invLocalRotation.TransformVector((scaledUnsnappedOffset + CalculateSnappedOffset(scaledUnsnappedOffset, axis, gridSize * scaleRecip))) + ? invLocalRotation.TransformVector(CalculateSnappedAmount(scaledUnsnappedOffset, axis, gridSize * scaleRecip)) : invLocalRotation.TransformVector(scaledUnsnappedOffset); // record what modifier keys are held during this action @@ -171,19 +155,18 @@ namespace AzToolsFramework const ViewportInteraction::MouseInteraction& interaction, const float rayIntersectionDistance) { const AZ::Transform worldFromLocalUniformScale = TransformUniformScale(GetSpace()); - const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId); // note: m_localTransform must not be made uniform as it may contain a local scale we want to snap m_starter = CalculateLinearManipulationDataStart( - m_fixed, worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(), - GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()), interaction, rayIntersectionDistance, + m_fixed, worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(), interaction, rayIntersectionDistance, GetCameraState(interaction.m_interactionId.m_viewportId)); if (m_onLeftMouseDownCallback) { + const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId); + m_onLeftMouseDownCallback(CalculateLinearManipulationDataAction( - m_fixed, m_starter, worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(), - GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()), interaction)); + m_fixed, m_starter, worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(), gridSnapParams, interaction)); } } @@ -195,8 +178,8 @@ namespace AzToolsFramework // note: m_localTransform must not be made uniform as it may contain a local scale we want to snap m_onMouseMoveCallback(CalculateLinearManipulationDataAction( - m_fixed, m_starter, TransformUniformScale(GetSpace()), GetNonUniformScale(), GetLocalTransform(), - GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()), interaction)); + m_fixed, m_starter, TransformUniformScale(GetSpace()), GetNonUniformScale(), GetLocalTransform(), gridSnapParams, + interaction)); } } @@ -208,8 +191,7 @@ namespace AzToolsFramework // note: m_localTransform must not be made uniform as it may contain a local scale we want to snap m_onLeftMouseUpCallback(CalculateLinearManipulationDataAction( - m_fixed, m_starter, TransformUniformScale(GetSpace()), GetNonUniformScale(), GetLocalTransform(), - GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()), interaction)); + m_fixed, m_starter, TransformUniformScale(GetSpace()), GetNonUniformScale(), GetLocalTransform(), gridSnapParams, interaction)); } } @@ -232,8 +214,8 @@ namespace AzToolsFramework GridSnapSettings(mouseInteraction.m_interactionId.m_viewportId); const auto action = CalculateLinearManipulationDataAction( - m_fixed, m_starter, TransformUniformScale(GetSpace()), GetNonUniformScale(), GetLocalTransform(), - GridSnapAction(gridSnapParams, mouseInteraction.m_keyboardModifiers.Alt()), mouseInteraction); + m_fixed, m_starter, TransformUniformScale(GetSpace()), GetNonUniformScale(), GetLocalTransform(), gridSnapParams, + mouseInteraction); // display the exact hit (ray intersection) of the mouse pick on the manipulator DrawTransformAxes( diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LinearManipulator.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LinearManipulator.h index 576c543dce..c3d43a2535 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LinearManipulator.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/LinearManipulator.h @@ -20,7 +20,7 @@ namespace AzToolsFramework { - struct GridSnapAction; + struct GridSnapParameters; /// LinearManipulator serves as a visual tool for users to modify values /// in one dimension on an axis defined in 3D space. @@ -68,8 +68,6 @@ namespace AzToolsFramework AZ::Vector3 m_localScale; ///< The current scale of the manipulator in local space. AZ::Vector3 m_localHitPosition; ///< The intersection point in local space between the ray and the manipulator when the mouse down event happens. AZ::Vector3 m_localAxis; ///< The axis in the local space of the manipulator itself. - AZ::Vector3 m_positionSnapOffset; ///< The snap offset amount to ensure manipulator is aligned to the grid. - AZ::Vector3 m_scaleSnapOffset; ///< The snap offset amount to ensure manipulator is aligned to round scale increments. float m_sign; ///< Used to determine which side of the axis we clicked on in case it's flipped to face the camera. AzFramework::ScreenPoint m_screenPosition; ///< The initial position in screen space of the manipulator. }; @@ -91,7 +89,7 @@ namespace AzToolsFramework ViewportInteraction::KeyboardModifiers m_modifiers; int m_viewportId; ///< The id of the viewport this manipulator is being used in. AZ::Vector3 LocalScale() const { return m_start.m_localScale + m_current.m_localScaleOffset; } - AZ::Vector3 LocalScaleOffset() const { return m_start.m_scaleSnapOffset + m_current.m_localScaleOffset; } + AZ::Vector3 LocalScaleOffset() const { return m_current.m_localScaleOffset; } AZ::Vector3 LocalPosition() const { return m_start.m_localPosition + m_current.m_localPositionOffset; } AZ::Vector3 LocalPositionOffset() const { return m_current.m_localPositionOffset; } AZ::Vector2 ScreenOffset() const @@ -162,11 +160,11 @@ namespace AzToolsFramework LinearManipulator::Starter CalculateLinearManipulationDataStart( const LinearManipulator::Fixed& fixed, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, - const AZ::Transform& localTransform, const GridSnapAction& gridSnapAction, const ViewportInteraction::MouseInteraction& interaction, - float intersectionDistance, const AzFramework::CameraState& cameraState); + const AZ::Transform& localTransform, const ViewportInteraction::MouseInteraction& interaction, float intersectionDistance, + const AzFramework::CameraState& cameraState); LinearManipulator::Action CalculateLinearManipulationDataAction( const LinearManipulator::Fixed& fixed, const LinearManipulator::Starter& starter, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, const AZ::Transform& localTransform, - const GridSnapAction& gridSnapAction, const ViewportInteraction::MouseInteraction& interaction); + const GridSnapParameters& gridSnapParams, const ViewportInteraction::MouseInteraction& interaction); } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.cpp index 015ea8a3e3..8a6398d025 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.cpp @@ -42,12 +42,6 @@ namespace AzToolsFramework { } - GridSnapAction::GridSnapAction(const GridSnapParameters& gridSnapParameters, const bool localSnapping) - : m_gridSnapParams(gridSnapParameters) - , m_localSnapping(localSnapping) - { - } - ManipulatorInteraction BuildManipulatorInteraction( const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, const AZ::Vector3& worldRayOrigin, const AZ::Vector3& worldRayDirection) @@ -57,19 +51,39 @@ namespace AzToolsFramework return {localFromWorldUniform.TransformPoint(worldRayOrigin), TransformDirectionNoScaling(localFromWorldUniform, worldRayDirection), - ScaleReciprocal(worldFromLocalUniform), - NonUniformScaleReciprocal(nonUniformScale)}; + NonUniformScaleReciprocal(nonUniformScale), + ScaleReciprocal(worldFromLocalUniform)}; } - AZ::Vector3 CalculateSnappedOffset( - const AZ::Vector3& unsnappedPosition, const AZ::Vector3& axis, const float size) + struct SnapAdjustment + { + float m_existingSnapDistance; //!< How far to snap up or down to align to the grid. + float m_nextSnapDistance; //!< The snap increment (will return full signed value (grid size) when distance + //!< moved is greater than half of the grid size in either direction). + }; + + static SnapAdjustment CalculateSnapDistance(const AZ::Vector3& unsnappedPosition, const AZ::Vector3& axis, const float size) { // calculate total distance along axis const float axisDistance = axis.Dot(unsnappedPosition); // round to nearest step size const float snappedAxisDistance = floorf((axisDistance / size) + 0.5f) * size; + + return { axisDistance, snappedAxisDistance }; + } + + AZ::Vector3 CalculateSnappedOffset(const AZ::Vector3& unsnappedPosition, const AZ::Vector3& axis, const float size) + { + const auto snapAdjustment = CalculateSnapDistance(unsnappedPosition, axis, size); // return offset along axis to snap to step size - return axis * (snappedAxisDistance - axisDistance); + return axis * (snapAdjustment.m_nextSnapDistance - snapAdjustment.m_existingSnapDistance); + } + + AZ::Vector3 CalculateSnappedAmount(const AZ::Vector3& unsnappedPosition, const AZ::Vector3& axis, const float size) + { + const auto snapAdjustment = CalculateSnapDistance(unsnappedPosition, axis, size); + // return offset along axis to snap to step size + return axis * snapAdjustment.m_nextSnapDistance; } AZ::Vector3 CalculateSnappedTerrainPosition( diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.h index e6c70079df..11860780c7 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/ManipulatorSnapping.h @@ -31,24 +31,15 @@ namespace AzToolsFramework float m_gridSize; }; - /// Structure to encapsulate the current grid snapping state. - struct GridSnapAction - { - GridSnapAction(const GridSnapParameters& gridSnapParameters, bool localSnapping); - - GridSnapParameters m_gridSnapParams; - bool m_localSnapping; - }; - /// Structure to hold transformed incoming viewport interaction from world space to manipulator space. struct ManipulatorInteraction { AZ::Vector3 m_localRayOrigin; ///< The ray origin (start) in the reference from of the manipulator. AZ::Vector3 m_localRayDirection; ///< The ray direction in the reference from of the manipulator. - float m_scaleReciprocal; ///< The scale reciprocal (1.0 / scale) of the transform used to move the - ///< ray from world space to local space. AZ::Vector3 m_nonUniformScaleReciprocal; ///< Handles inverting any non-uniform scale which was applied ///< separately from the transform. + float m_scaleReciprocal; ///< The scale reciprocal (1.0 / scale) of the transform used to move the + ///< ray from world space to local space. }; /// Build a ManipulatorInteraction structure from the incoming viewport interaction. @@ -56,11 +47,16 @@ namespace AzToolsFramework const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, const AZ::Vector3& worldRayOrigin, const AZ::Vector3& worldRayDirection); - /// Calculate the offset along an axis to adjust a position - /// to stay snapped to a given grid size. + /// Calculate the offset along an axis to adjust a position to stay snapped to a given grid size. + /// @note This is snap up or down to the nearest grid segment (e.g. 0.2 snaps to 0.0 -> delta 0.2, + /// 0.7 snaps to 1.0 -> delta 0.3). AZ::Vector3 CalculateSnappedOffset( const AZ::Vector3& unsnappedPosition, const AZ::Vector3& axis, float size); + /// Return the amount to snap from the starting position given the current grid size. + /// @note A movement of more than half size (in either direction) will cause a snap by size. + AZ::Vector3 CalculateSnappedAmount(const AZ::Vector3& unsnappedPosition, const AZ::Vector3& axis, float size); + /// For a given point on the terrain, calculate the closest xy position snapped to the grid /// (z position is aligned to terrain height, not snapped to z grid) AZ::Vector3 CalculateSnappedTerrainPosition( diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/MultiLinearManipulator.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/MultiLinearManipulator.cpp index 566de9a8a6..83c4c28e9a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/MultiLinearManipulator.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/MultiLinearManipulator.cpp @@ -59,17 +59,16 @@ namespace AzToolsFramework const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, const AZ::Transform& localTransform, const ViewportInteraction::MouseInteraction& interaction, const AZStd::vector& fixedAxes, - const AZStd::vector& starterStates, const GridSnapAction& gridSnapAction) + const AZStd::vector& starterStates, const GridSnapParameters& gridSnapParams) { MultiLinearManipulator::Action action; action.m_viewportId = interaction.m_interactionId.m_viewportId; // build up action state for each axis for (size_t fixedIndex = 0; fixedIndex < fixedAxes.size(); ++fixedIndex) { - action.m_actions.push_back( - CalculateLinearManipulationDataAction( - fixedAxes[fixedIndex], starterStates[fixedIndex], worldFromLocal, nonUniformScale, localTransform, - gridSnapAction, interaction)); + action.m_actions.push_back(CalculateLinearManipulationDataAction( + fixedAxes[fixedIndex], starterStates[fixedIndex], worldFromLocal, nonUniformScale, localTransform, gridSnapParams, + interaction)); } return action; @@ -79,8 +78,6 @@ namespace AzToolsFramework const ViewportInteraction::MouseInteraction& interaction, const float rayIntersectionDistance) { const AZ::Transform worldFromLocalUniformScale = TransformUniformScale(GetSpace()); - - const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId); const AzFramework::CameraState cameraState = GetCameraState(interaction.m_interactionId.m_viewportId); // build up initial start state for each axis @@ -88,20 +85,19 @@ namespace AzToolsFramework { // note: m_localTransform must not be made uniform as it may contain a local scale we want to snap const auto linearStart = CalculateLinearManipulationDataStart( - fixed, worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(), - GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()), interaction, - rayIntersectionDistance, cameraState); + fixed, worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(), interaction, rayIntersectionDistance, + cameraState); m_starters.push_back(linearStart); } if (m_onLeftMouseDownCallback) { - const GridSnapAction gridSnapAction = GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()); + const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId); // pass action containing all linear actions for each axis to handler m_onLeftMouseDownCallback(BuildMultiLinearManipulatorAction( worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(), - interaction, m_fixedAxes, m_starters, gridSnapAction)); + interaction, m_fixedAxes, m_starters, gridSnapParams)); } } @@ -111,11 +107,9 @@ namespace AzToolsFramework { const AZ::Transform worldFromLocalUniformScale = TransformUniformScale(GetSpace()); const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId); - const GridSnapAction gridSnapAction = GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()); - m_onMouseMoveCallback(BuildMultiLinearManipulatorAction( worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(), - interaction, m_fixedAxes, m_starters, gridSnapAction)); + interaction, m_fixedAxes, m_starters, gridSnapParams)); } } @@ -125,11 +119,9 @@ namespace AzToolsFramework { const AZ::Transform worldFromLocalUniformScale = TransformUniformScale(GetSpace()); const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId); - const GridSnapAction gridSnapAction = GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()); - m_onLeftMouseUpCallback(BuildMultiLinearManipulatorAction( worldFromLocalUniformScale, GetNonUniformScale(), GetLocalTransform(), - interaction, m_fixedAxes, m_starters, gridSnapAction)); + interaction, m_fixedAxes, m_starters, gridSnapParams)); m_starters.clear(); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/MultiLinearManipulator.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/MultiLinearManipulator.h index 4a99435008..8e31e02605 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/MultiLinearManipulator.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/MultiLinearManipulator.h @@ -20,8 +20,6 @@ namespace AzToolsFramework { - struct GridSnapAction; - //! MultiLinearManipulator serves as a visual tool for users to modify values //! in one or more dimensions on axes defined in 3D space. class MultiLinearManipulator diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/PlanarManipulator.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/PlanarManipulator.cpp index abd5414ce3..6eb96f081c 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/PlanarManipulator.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/PlanarManipulator.cpp @@ -22,8 +22,7 @@ namespace AzToolsFramework { PlanarManipulator::StartInternal PlanarManipulator::CalculateManipulationDataStart( - const Fixed& fixed, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, - const AZ::Transform& localTransform, const GridSnapAction& gridSnapAction, + const Fixed& fixed, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, const AZ::Transform& localTransform, const ViewportInteraction::MouseInteraction& interaction, const float intersectionDistance) { const ManipulatorInteraction manipulatorInteraction = @@ -31,8 +30,6 @@ namespace AzToolsFramework worldFromLocal, nonUniformScale, interaction.m_mousePick.m_rayOrigin, interaction.m_mousePick.m_rayDirection); const AZ::Vector3 normal = TransformDirectionNoScaling(localTransform, fixed.m_normal); - const AZ::Vector3 axis1 = TransformDirectionNoScaling(localTransform, fixed.m_axis1); - const AZ::Vector3 axis2 = TransformDirectionNoScaling(localTransform, fixed.m_axis2); // initial intersect point const AZ::Vector3 localIntersectionPoint = @@ -43,25 +40,14 @@ namespace AzToolsFramework manipulatorInteraction.m_localRayOrigin, manipulatorInteraction.m_localRayDirection, localIntersectionPoint, normal, startInternal.m_localHitPosition); - const float scaleRecip = manipulatorInteraction.m_scaleReciprocal; - const float gridSize = gridSnapAction.m_gridSnapParams.m_gridSize; - const bool snapping = gridSnapAction.m_gridSnapParams.m_gridSnap; - - // calculate amount to snap to align with grid - const AZ::Vector3 snapOffset = snapping && !gridSnapAction.m_localSnapping - ? CalculateSnappedOffset(localTransform.GetTranslation(), axis1, gridSize * scaleRecip) + - CalculateSnappedOffset(localTransform.GetTranslation(), axis2, gridSize * scaleRecip) - : AZ::Vector3::CreateZero(); - - startInternal.m_snapOffset = snapOffset; - startInternal.m_localPosition = localTransform.GetTranslation() + snapOffset; + startInternal.m_localPosition = localTransform.GetTranslation(); return startInternal; } PlanarManipulator::Action PlanarManipulator::CalculateManipulationDataAction( - const Fixed& fixed, const StartInternal& startInternal, const AZ::Transform& worldFromLocal, - const AZ::Vector3& nonUniformScale, const AZ::Transform& localTransform, const GridSnapAction& gridSnapAction, + const Fixed& fixed, const StartInternal& startInternal, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, + const AZ::Transform& localTransform, const GridSnapParameters& gridSnapParams, const ViewportInteraction::MouseInteraction& interaction) { const ManipulatorInteraction manipulatorInteraction = @@ -88,20 +74,18 @@ namespace AzToolsFramework const AZ::Vector3 hitDelta = (localHitPosition - startInternal.m_localHitPosition) / nonUniformScale; const AZ::Vector3 unsnappedOffset = axis1.Dot(hitDelta) * axis1 + axis2.Dot(hitDelta) * axis2; - const float scaleRecip = manipulatorInteraction.m_scaleReciprocal; const AZ::Vector3 nonUniformScaleRecip = manipulatorInteraction.m_nonUniformScaleReciprocal; - const float gridSize = gridSnapAction.m_gridSnapParams.m_gridSize; - const bool snapping = gridSnapAction.m_gridSnapParams.m_gridSnap; + const float scaleRecip = manipulatorInteraction.m_scaleReciprocal; + const float gridSize = gridSnapParams.m_gridSize; + const bool snapping = gridSnapParams.m_gridSnap; Action action; action.m_fixed = fixed; action.m_start.m_localPosition = startInternal.m_localPosition; - action.m_start.m_snapOffset = startInternal.m_snapOffset; action.m_start.m_localHitPosition = startInternal.m_localHitPosition; action.m_current.m_localOffset = snapping - ? unsnappedOffset + - CalculateSnappedOffset(unsnappedOffset, axis1, gridSize * scaleRecip * nonUniformScaleRecip.Dot(axis1)) + - CalculateSnappedOffset(unsnappedOffset, axis2, gridSize * scaleRecip * nonUniformScaleRecip.Dot(axis2)) + ? CalculateSnappedAmount(unsnappedOffset, axis1, gridSize * scaleRecip * nonUniformScaleRecip.Dot(fixed.m_axis1)) + + CalculateSnappedAmount(unsnappedOffset, axis2, gridSize * scaleRecip * nonUniformScaleRecip.Dot(fixed.m_axis2)) : unsnappedOffset; // record what modifier keys are held during this action @@ -141,18 +125,17 @@ namespace AzToolsFramework { const AZ::Transform worldFromLocalUniformScale = TransformUniformScale(GetSpace()); - const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId); - m_startInternal = CalculateManipulationDataStart( m_fixed, worldFromLocalUniformScale, GetNonUniformScale(), TransformNormalizedScale(GetLocalTransform()), - GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()), interaction, rayIntersectionDistance); if (m_onLeftMouseDownCallback) { + const GridSnapParameters gridSnapParams = GridSnapSettings(interaction.m_interactionId.m_viewportId); + m_onLeftMouseDownCallback(CalculateManipulationDataAction( m_fixed, m_startInternal, worldFromLocalUniformScale, GetNonUniformScale(), TransformNormalizedScale(GetLocalTransform()), - GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()), interaction)); + gridSnapParams, interaction)); } } @@ -164,8 +147,7 @@ namespace AzToolsFramework m_onMouseMoveCallback(CalculateManipulationDataAction( m_fixed, m_startInternal, TransformUniformScale(GetSpace()), GetNonUniformScale(), - TransformNormalizedScale(GetLocalTransform()), - GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()), interaction)); + TransformNormalizedScale(GetLocalTransform()), gridSnapParams, interaction)); } } @@ -177,8 +159,7 @@ namespace AzToolsFramework m_onLeftMouseUpCallback(CalculateManipulationDataAction( m_fixed, m_startInternal, TransformUniformScale(GetSpace()), GetNonUniformScale(), - TransformNormalizedScale(GetLocalTransform()), - GridSnapAction(gridSnapParams, interaction.m_keyboardModifiers.Alt()), interaction)); + TransformNormalizedScale(GetLocalTransform()), gridSnapParams, interaction)); } } @@ -195,8 +176,7 @@ namespace AzToolsFramework const GridSnapParameters gridSnapParams = GridSnapSettings(mouseInteraction.m_interactionId.m_viewportId); const auto action = CalculateManipulationDataAction( m_fixed, m_startInternal, TransformUniformScale(GetSpace()), GetNonUniformScale(), - TransformNormalizedScale(GetLocalTransform()), - GridSnapAction(gridSnapParams, mouseInteraction.m_keyboardModifiers.Alt()), mouseInteraction); + TransformNormalizedScale(GetLocalTransform()), gridSnapParams, mouseInteraction); // display the exact hit (ray intersection) of the mouse pick on the manipulator DrawTransformAxes( diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/PlanarManipulator.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/PlanarManipulator.h index 7a3b2a18a9..154ed4c7d6 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/PlanarManipulator.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/PlanarManipulator.h @@ -21,7 +21,7 @@ namespace AzToolsFramework { class ManipulatorView; - struct GridSnapAction; + struct GridSnapParameters; /// PlanarManipulator serves as a visual tool for users to modify values /// in two dimension in a plane defined two non-collinear axes in 3D space. @@ -58,7 +58,6 @@ namespace AzToolsFramework { AZ::Vector3 m_localPosition; ///< The current position of the manipulator in local space. AZ::Vector3 m_localHitPosition; ///< The intersection point in local space between the ray and the manipulator when the mouse down event happens. - AZ::Vector3 m_snapOffset; ///< The snap offset amount to ensure manipulator is aligned to the grid. }; /// The state of the manipulator during an interaction. @@ -120,7 +119,6 @@ namespace AzToolsFramework { AZ::Vector3 m_localPosition; ///< The starting position of the manipulator in local space. AZ::Vector3 m_localHitPosition; ///< The intersection point in world space between the ray and the manipulator when the mouse down event happens. - AZ::Vector3 m_snapOffset; ///< The snap offset amount to ensure manipulator is aligned to the grid. }; Fixed m_fixed; @@ -134,12 +132,11 @@ namespace AzToolsFramework static StartInternal CalculateManipulationDataStart( const Fixed& fixed, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, - const AZ::Transform& localTransform, const GridSnapAction& gridSnapAction, - const ViewportInteraction::MouseInteraction& interaction, float intersectionDistance); + const AZ::Transform& localTransform, const ViewportInteraction::MouseInteraction& interaction, float intersectionDistance); static Action CalculateManipulationDataAction( - const Fixed& fixed, const StartInternal& startInternal, const AZ::Transform& worldFromLocal, - const AZ::Vector3& nonUniformScale, const AZ::Transform& localTransform, - const GridSnapAction& gridSnapAction, const ViewportInteraction::MouseInteraction& interaction); + const Fixed& fixed, const StartInternal& startInternal, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale, + const AZ::Transform& localTransform, const GridSnapParameters& gridSnapParams, + const ViewportInteraction::MouseInteraction& interaction); }; } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponentMode.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponentMode.cpp index 497bcf15d7..51c5406812 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponentMode.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponentMode.cpp @@ -37,13 +37,13 @@ namespace AzToolsFramework axisLength, AzFramework::ViewportColors::XAxisColor, AzFramework::ViewportColors::YAxisColor, AzFramework::ViewportColors::ZAxisColor); - auto mouseDownCallback = [this](const LinearManipulator::Action& action) { + auto mouseDownCallback = [this]([[maybe_unused]] const LinearManipulator::Action& action) + { AZ::Vector3 nonUniformScale = AZ::Vector3::CreateOne(); - AZ::NonUniformScaleRequestBus::EventResult( nonUniformScale, m_entityComponentIdPair.GetEntityId(), &AZ::NonUniformScaleRequests::GetScale); - m_initialScale = nonUniformScale + action.m_start.m_scaleSnapOffset; + m_initialScale = nonUniformScale; AZ::NonUniformScaleRequestBus::Event( m_entityComponentIdPair.GetEntityId(), &AZ::NonUniformScaleRequests::SetScale, m_initialScale); @@ -51,29 +51,37 @@ namespace AzToolsFramework m_manipulators->InstallAxisLeftMouseDownCallback(mouseDownCallback); - m_manipulators->InstallAxisMouseMoveCallback([this](const LinearManipulator::Action& action) { - const AZ::Vector3 scaleMultiplier = - (AZ::Vector3::CreateOne() + ((action.LocalScaleOffset() * action.m_start.m_sign) / m_initialScale)); + m_manipulators->InstallAxisMouseMoveCallback( + [this](const LinearManipulator::Action& action) + { + const AZ::Vector3 scaleMultiplier = + (AZ::Vector3::CreateOne() + ((action.LocalScaleOffset() * action.m_start.m_sign) / m_initialScale)); - AZ::NonUniformScaleRequestBus::Event( - m_entityComponentIdPair.GetEntityId(), &AZ::NonUniformScaleRequests::SetScale, - (scaleMultiplier * m_initialScale).GetClamp(AZ::Vector3(AZ::MinTransformScale), AZ::Vector3(AZ::MaxTransformScale))); - }); + AZ::NonUniformScaleRequestBus::Event( + m_entityComponentIdPair.GetEntityId(), &AZ::NonUniformScaleRequests::SetScale, + (scaleMultiplier * m_initialScale) + .GetClamp(AZ::Vector3(AZ::MinTransformScale), AZ::Vector3(AZ::MaxTransformScale))); + }); m_manipulators->InstallUniformLeftMouseDownCallback(mouseDownCallback); - m_manipulators->InstallUniformMouseMoveCallback([this](const LinearManipulator::Action& action) { - const auto sumVectorElements = [](const AZ::Vector3& vec) { return vec.GetX() + vec.GetY() + vec.GetZ(); }; + m_manipulators->InstallUniformMouseMoveCallback( + [this](const LinearManipulator::Action& action) + { + const auto sumVectorElements = [](const AZ::Vector3& vec) + { + return vec.GetX() + vec.GetY() + vec.GetZ(); + }; - const float minScaleMultiplier = AZ::MinTransformScale / m_initialScale.GetMinElement(); - const float maxScaleMultiplier = AZ::MaxTransformScale / m_initialScale.GetMaxElement(); - const float scaleMultiplier = AZ::GetClamp( - 1.0f + sumVectorElements(action.m_start.m_sign * action.LocalScaleOffset() / m_initialScale), minScaleMultiplier, - maxScaleMultiplier); + const float minScaleMultiplier = AZ::MinTransformScale / m_initialScale.GetMinElement(); + const float maxScaleMultiplier = AZ::MaxTransformScale / m_initialScale.GetMaxElement(); + const float scaleMultiplier = AZ::GetClamp( + 1.0f + sumVectorElements(action.m_start.m_sign * action.LocalScaleOffset() / m_initialScale), minScaleMultiplier, + maxScaleMultiplier); - AZ::NonUniformScaleRequestBus::Event( - m_entityComponentIdPair.GetEntityId(), &AZ::NonUniformScaleRequests::SetScale, scaleMultiplier * m_initialScale); - }); + AZ::NonUniformScaleRequestBus::Event( + m_entityComponentIdPair.GetEntityId(), &AZ::NonUniformScaleRequests::SetScale, scaleMultiplier * m_initialScale); + }); } NonUniformScaleComponentMode::~NonUniformScaleComponentMode() diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp index 5acfa5df59..db321d6818 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp @@ -423,15 +423,14 @@ namespace AzToolsFramework } } - static void InitializeTranslationLookup( - EntityIdManipulators& entityIdManipulators, const AZ::Vector3& snapOffset) + static void InitializeTranslationLookup(EntityIdManipulators& entityIdManipulators) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); for (auto& entityIdLookup : entityIdManipulators.m_lookups) { entityIdLookup.second.m_initial = - AZ::Transform::CreateTranslation(GetWorldTranslation(entityIdLookup.first) + snapOffset); + AZ::Transform::CreateTranslation(GetWorldTranslation(entityIdLookup.first)); } } @@ -820,7 +819,7 @@ namespace AzToolsFramework // moving with ctrl - setting override pivotOverrideFrame.m_translationOverride = entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation(); - InitializeTranslationLookup(entityIdManipulators, -action.LocalPositionOffset()); + InitializeTranslationLookup(entityIdManipulators); } else { @@ -1277,12 +1276,12 @@ namespace AzToolsFramework // linear translationManipulators->InstallLinearManipulatorMouseDownCallback( - [this, manipulatorEntityIds](const LinearManipulator::Action& action) mutable + [this, manipulatorEntityIds]([[maybe_unused]] const LinearManipulator::Action& action) mutable { // important to sort entityIds based on hierarchy order when updating transforms BuildSortedEntityIdVectorFromEntityIdMap(m_entityIdManipulators.m_lookups, manipulatorEntityIds->m_entityIds); - InitializeTranslationLookup(m_entityIdManipulators, action.m_start.m_positionSnapOffset); + InitializeTranslationLookup(m_entityIdManipulators); m_axisPreview.m_translation = m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation(); m_axisPreview.m_orientation = QuaternionFromTransformNoScaling( @@ -1302,19 +1301,19 @@ namespace AzToolsFramework }); translationManipulators->InstallLinearManipulatorMouseUpCallback( - [this](const LinearManipulator::Action& /*action*/) mutable + [this]([[maybe_unused]] const LinearManipulator::Action& action) mutable { EndRecordManipulatorCommand(); }); // planar translationManipulators->InstallPlanarManipulatorMouseDownCallback( - [this, manipulatorEntityIds](const PlanarManipulator::Action& action) + [this, manipulatorEntityIds]([[maybe_unused]] const PlanarManipulator::Action& action) { // important to sort entityIds based on hierarchy order when updating transforms BuildSortedEntityIdVectorFromEntityIdMap(m_entityIdManipulators.m_lookups, manipulatorEntityIds->m_entityIds); - InitializeTranslationLookup(m_entityIdManipulators, action.m_start.m_snapOffset); + InitializeTranslationLookup(m_entityIdManipulators); m_axisPreview.m_translation = m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation(); m_axisPreview.m_orientation = QuaternionFromTransformNoScaling( @@ -1340,11 +1339,11 @@ namespace AzToolsFramework // surface translationManipulators->InstallSurfaceManipulatorMouseDownCallback( - [this, manipulatorEntityIds](const SurfaceManipulator::Action& action) + [this, manipulatorEntityIds]([[maybe_unused]] const SurfaceManipulator::Action& action) { BuildSortedEntityIdVectorFromEntityIdMap(m_entityIdManipulators.m_lookups, manipulatorEntityIds->m_entityIds); - InitializeTranslationLookup(m_entityIdManipulators, action.m_start.m_snapOffset); + InitializeTranslationLookup(m_entityIdManipulators); m_axisPreview.m_translation = m_entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation(); m_axisPreview.m_orientation = QuaternionFromTransformNoScaling( @@ -3326,26 +3325,16 @@ namespace AzToolsFramework } static void DrawManipulatorGrid( - AzFramework::DebugDisplayRequests& debugDisplay, const EntityIdManipulators& entityIdManipulators, - const float gridSize, const float localSnapping) + AzFramework::DebugDisplayRequests& debugDisplay, const EntityIdManipulators& entityIdManipulators, const float gridSize) { const AZ::Matrix3x3 orientation = AZ::Matrix3x3::CreateFromTransform(entityIdManipulators.m_manipulators->GetLocalTransform()); - const AZ::Vector3 unsnappedTranslation = + const AZ::Vector3 translation = entityIdManipulators.m_manipulators->GetLocalTransform().GetTranslation(); - // calculate the offset to snap by to align the manipulator to the grid - // note: only perform this if we are not snapping in local space - const AZ::Vector3 snappedOffset = !localSnapping - ? CalculateSnappedOffset(unsnappedTranslation, orientation.GetBasisX(), gridSize) + - CalculateSnappedOffset(unsnappedTranslation, orientation.GetBasisY(), gridSize) - : AZ::Vector3::CreateZero(); - - const AZ::Vector3 snappedTranslation = unsnappedTranslation + snappedOffset; - DrawSnappingGrid( - debugDisplay, AZ::Transform::CreateFromMatrix3x3AndTranslation(orientation, snappedTranslation), + debugDisplay, AZ::Transform::CreateFromMatrix3x3AndTranslation(orientation, translation), gridSize); } @@ -3484,7 +3473,7 @@ namespace AzToolsFramework const GridSnapParameters gridSnapParams = GridSnapSettings(viewportInfo.m_viewportId); if (gridSnapParams.m_gridSnap && m_entityIdManipulators.m_manipulators) { - DrawManipulatorGrid(debugDisplay, m_entityIdManipulators, gridSnapParams.m_gridSize, modifiers.Alt()); + DrawManipulatorGrid(debugDisplay, m_entityIdManipulators, gridSnapParams.m_gridSize); } } }