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
This commit is contained in:
Tom Hulton-Harrop
2021-06-04 14:32:06 +01:00
committed by GitHub
parent f20ae8345a
commit 05e20803a8
15 changed files with 295 additions and 260 deletions
@@ -12,17 +12,22 @@
#pragma once
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
#include <AzToolsFramework/Manipulators/ManipulatorBus.h>
#include <AzToolsFramework/Manipulators/LinearManipulator.h>
#include <AzToolsFramework/Manipulators/ManipulatorBus.h>
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
#include <AzToolsFramework/Manipulators/PlanarManipulator.h>
namespace AzManipulatorTestFramework
{
//! Create a linear manipulator with a unit sphere bounds.
//! Create a linear manipulator with a unit sphere bound.
AZStd::shared_ptr<AzToolsFramework::LinearManipulator> 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<AzToolsFramework::PlanarManipulator> 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
@@ -14,7 +14,6 @@
#include <AzFramework/Viewport/ViewportScreen.h>
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h>
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h>
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h>
#include <AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h>
namespace AzManipulatorTestFramework
@@ -28,22 +27,21 @@ namespace AzManipulatorTestFramework
using MouseEvent = AzToolsFramework::ViewportInteraction::MouseEvent;
using MousePick = AzToolsFramework::ViewportInteraction::MousePick;
AZStd::shared_ptr<AzToolsFramework::LinearManipulator> CreateLinearManipulator(
const AzToolsFramework::ManipulatorManagerId manipulatorManagerId,
const AZ::Vector3& position,
const float radius)
// create a default sphere view for a manipulator for simple intersection
template<typename Manipulator>
void SetupManipulatorView(
AZStd::shared_ptr<Manipulator> 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<AzToolsFramework::LinearManipulator> 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<AzToolsFramework::PlanarManipulator> 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<int>(cameraState.m_viewportSize.GetX() / 2.f),
aznumeric_cast<int>(cameraState.m_viewportSize.GetY() / 2.f)
};
return { aznumeric_cast<int>(cameraState.m_viewportSize.GetX() / 2.f),
aznumeric_cast<int>(cameraState.m_viewportSize.GetY() / 2.f) };
}
} // namespace UnitTest
} // namespace AzManipulatorTestFramework
@@ -10,52 +10,55 @@
*
*/
#include "AzManipulatorTestFrameworkTestFixtures.h"
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzFramework/Viewport/ViewportScreen.h>
#include <AzManipulatorTestFramework/AzManipulatorTestFramework.h>
#include "AzManipulatorTestFrameworkTestFixtures.h"
#include <AzManipulatorTestFramework/DirectManipulatorViewportInteraction.h>
#include <AzManipulatorTestFramework/IndirectManipulatorViewportInteraction.h>
#include <AzManipulatorTestFramework/ImmediateModeActionDispatcher.h>
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h>
#include <AzManipulatorTestFramework/DirectManipulatorViewportInteraction.h>
#include <AzManipulatorTestFramework/ImmediateModeActionDispatcher.h>
#include <AzManipulatorTestFramework/IndirectManipulatorViewportInteraction.h>
#include <AzToolsFramework/Manipulators/LinearManipulator.h>
#include <AzToolsFramework/Manipulators/PlanarManipulator.h>
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
#include <AZTestShared/Math/MathTestHelpers.h>
namespace UnitTest
{
class GridSnappingFixture
: public ToolsApplicationFixture
class GridSnappingFixture : public ToolsApplicationFixture
{
public:
GridSnappingFixture()
: m_viewportManipulatorInteraction(AZStd::make_unique<AzManipulatorTestFramework::DirectCallManipulatorViewportInteraction>())
, m_actionDispatcher(AZStd::make_unique<AzManipulatorTestFramework::ImmediateModeActionDispatcher>(*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<AzManipulatorTestFramework::ImmediateModeActionDispatcher>(*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<AzManipulatorTestFramework::ManipulatorViewportInteraction> m_viewportManipulatorInteraction;
AZStd::unique_ptr<AzManipulatorTestFramework::ImmediateModeActionDispatcher> m_actionDispatcher;
AZStd::shared_ptr<AzToolsFramework::LinearManipulator> m_linearManipulator;
AzFramework::CameraState m_cameraState;
};
TEST_F(GridSnappingFixture, MouseDownWithSnappingEnabledSnapsToClosestGridSize)
{
AZStd::shared_ptr<AzToolsFramework::LinearManipulator> 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<typename Manipulator>
void ValidateManipulatorSnappingBehavior(
AZStd::shared_ptr<Manipulator> 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<AzToolsFramework::LinearManipulator> 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<AzToolsFramework::PlanarManipulator> 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
@@ -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);
@@ -192,8 +192,7 @@ namespace AzToolsFramework
/// for each vertex associated with the translation manipulator to use with offset calculations when updating.
template<typename Vertex>
void InitializeVertexLookup(
IndexedTranslationManipulator<Vertex>& translationManipulator,
const AZ::EntityId entityId, const AZ::Vector3& snapOffset)
IndexedTranslationManipulator<Vertex>& translationManipulator, const AZ::EntityId entityId)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
@@ -202,7 +201,7 @@ namespace AzToolsFramework
AZ::FixedVerticesRequestBus<Vertex>::Bind(fixedVertices, entityId);
translationManipulator.Process(
[snapOffset, fixedVertices]
[fixedVertices]
(typename IndexedTranslationManipulator<Vertex>::VertexLookup& vertexLookup)
{
Vertex vertex;
@@ -213,7 +212,7 @@ namespace AzToolsFramework
if (found)
{
vertexLookup.m_start = vertex + AZ::AdaptVertexIn<Vertex>(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 =
@@ -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(
@@ -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
@@ -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(
@@ -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(
@@ -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<LinearManipulator::Fixed>& fixedAxes,
const AZStd::vector<LinearManipulator::Starter>& starterStates, const GridSnapAction& gridSnapAction)
const AZStd::vector<LinearManipulator::Starter>& 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();
}
@@ -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
@@ -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(
@@ -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
@@ -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()
@@ -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);
}
}
}