From deeb59cb837f5ffb56d1b2c0feb08987521e5a09 Mon Sep 17 00:00:00 2001 From: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com> Date: Mon, 14 Feb 2022 10:19:14 +0000 Subject: [PATCH] Allow SurfaceManipulator to self-intersect when holding Ctrl (#6453) * beginning updates to surface manipulator intersect Signed-off-by: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com> * initial changes to support surface manipulator being able to ignore custom entity ids Signed-off-by: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com> * updates to use type aliases Signed-off-by: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com> * add test for self-intersection test for SurfaceManipulator when holding Ctrl Signed-off-by: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com> --- .../ActionDispatcher.h | 16 ++++---- .../ImmediateModeActionDispatcher.h | 4 +- .../Source/ImmediateModeActionDispatcher.cpp | 4 +- .../Manipulators/BaseManipulator.h | 9 ++-- .../Manipulators/SurfaceManipulator.cpp | 23 +++++------ .../Manipulators/SurfaceManipulator.h | 10 ++++- .../Manipulators/TranslationManipulators.cpp | 9 ++++ .../Manipulators/TranslationManipulators.h | 2 + .../EditorTransformComponentSelection.cpp | 13 ++++++ ...EditorTransformComponentSelectionTests.cpp | 41 +++++++++++++++++++ 10 files changed, 101 insertions(+), 30 deletions(-) diff --git a/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ActionDispatcher.h b/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ActionDispatcher.h index 99f11c37da..f056493ffd 100644 --- a/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ActionDispatcher.h +++ b/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ActionDispatcher.h @@ -46,9 +46,9 @@ namespace AzManipulatorTestFramework //! Send a double click event. DerivedDispatcherT* MouseLButtonDoubleClick(); //! Set the keyboard modifier button down. - DerivedDispatcherT* KeyboardModifierDown(const AzToolsFramework::ViewportInteraction::KeyboardModifier& keyModifier); + DerivedDispatcherT* KeyboardModifierDown(AzToolsFramework::ViewportInteraction::KeyboardModifier keyModifier); //! Set the keyboard modifier button up. - DerivedDispatcherT* KeyboardModifierUp(const AzToolsFramework::ViewportInteraction::KeyboardModifier& keyModifier); + DerivedDispatcherT* KeyboardModifierUp(AzToolsFramework::ViewportInteraction::KeyboardModifier keyModifier); //! Set the mouse position to the specified screen space position. DerivedDispatcherT* MousePosition(const AzFramework::ScreenPoint& position); //! Expect the selected manipulator to be interacting. @@ -81,8 +81,8 @@ namespace AzManipulatorTestFramework virtual void MouseMButtonUpImpl() = 0; virtual void MouseLButtonDoubleClickImpl() = 0; virtual void MousePositionImpl(const AzFramework::ScreenPoint& position) = 0; - virtual void KeyboardModifierDownImpl(const AzToolsFramework::ViewportInteraction::KeyboardModifier& keyModifier) = 0; - virtual void KeyboardModifierUpImpl(const AzToolsFramework::ViewportInteraction::KeyboardModifier& keyModifier) = 0; + virtual void KeyboardModifierDownImpl(AzToolsFramework::ViewportInteraction::KeyboardModifier keyModifier) = 0; + virtual void KeyboardModifierUpImpl(AzToolsFramework::ViewportInteraction::KeyboardModifier keyModifier) = 0; virtual void ExpectManipulatorBeingInteractedImpl() = 0; virtual void ExpectManipulatorNotBeingInteractedImpl() = 0; virtual void SetEntityWorldTransformImpl(AZ::EntityId entityId, const AZ::Transform& transform) = 0; @@ -94,7 +94,7 @@ namespace AzManipulatorTestFramework bool m_logging = false; private: - const char* KeyboardModifierString(const AzToolsFramework::ViewportInteraction::KeyboardModifier& keyModifier); + const char* KeyboardModifierString(AzToolsFramework::ViewportInteraction::KeyboardModifier keyModifier); static void DebugPrint(const char* format, ...); }; @@ -215,7 +215,7 @@ namespace AzManipulatorTestFramework template const char* ActionDispatcher::KeyboardModifierString( - const AzToolsFramework::ViewportInteraction::KeyboardModifier& keyModifier) + const AzToolsFramework::ViewportInteraction::KeyboardModifier keyModifier) { using namespace AzToolsFramework::ViewportInteraction; switch (keyModifier) @@ -235,7 +235,7 @@ namespace AzManipulatorTestFramework template DerivedDispatcherT* ActionDispatcher::KeyboardModifierDown( - const AzToolsFramework::ViewportInteraction::KeyboardModifier& keyModifier) + const AzToolsFramework::ViewportInteraction::KeyboardModifier keyModifier) { Log("Keyboard modifier down: %s", KeyboardModifierString(keyModifier)); KeyboardModifierDownImpl(keyModifier); @@ -244,7 +244,7 @@ namespace AzManipulatorTestFramework template DerivedDispatcherT* ActionDispatcher::KeyboardModifierUp( - const AzToolsFramework::ViewportInteraction::KeyboardModifier& keyModifier) + const AzToolsFramework::ViewportInteraction::KeyboardModifier keyModifier) { Log("Keyboard modifier up: %s", KeyboardModifierString(keyModifier)); KeyboardModifierUpImpl(keyModifier); diff --git a/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ImmediateModeActionDispatcher.h b/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ImmediateModeActionDispatcher.h index 8a05a15fd9..16b8966ba4 100644 --- a/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ImmediateModeActionDispatcher.h +++ b/Code/Framework/AzManipulatorTestFramework/Include/AzManipulatorTestFramework/ImmediateModeActionDispatcher.h @@ -66,8 +66,8 @@ namespace AzManipulatorTestFramework void MouseMButtonUpImpl() override; void MouseLButtonDoubleClickImpl() override; void MousePositionImpl(const AzFramework::ScreenPoint& position) override; - void KeyboardModifierDownImpl(const KeyboardModifier& keyModifier) override; - void KeyboardModifierUpImpl(const KeyboardModifier& keyModifier) override; + void KeyboardModifierDownImpl(KeyboardModifier keyModifier) override; + void KeyboardModifierUpImpl(KeyboardModifier keyModifier) override; void ExpectManipulatorBeingInteractedImpl() override; void ExpectManipulatorNotBeingInteractedImpl() override; void SetEntityWorldTransformImpl(AZ::EntityId entityId, const AZ::Transform& transform) override; diff --git a/Code/Framework/AzManipulatorTestFramework/Source/ImmediateModeActionDispatcher.cpp b/Code/Framework/AzManipulatorTestFramework/Source/ImmediateModeActionDispatcher.cpp index 715c2b1083..eb578162a6 100644 --- a/Code/Framework/AzManipulatorTestFramework/Source/ImmediateModeActionDispatcher.cpp +++ b/Code/Framework/AzManipulatorTestFramework/Source/ImmediateModeActionDispatcher.cpp @@ -126,12 +126,12 @@ namespace AzManipulatorTestFramework m_manipulatorViewportInteraction.GetManipulatorManager().ConsumeMouseInteractionEvent(*m_event); } - void ImmediateModeActionDispatcher::KeyboardModifierDownImpl(const KeyboardModifier& keyModifier) + void ImmediateModeActionDispatcher::KeyboardModifierDownImpl(const KeyboardModifier keyModifier) { ToggleOn(GetMouseInteractionEvent()->m_mouseInteraction.m_keyboardModifiers.m_keyModifiers, keyModifier); } - void ImmediateModeActionDispatcher::KeyboardModifierUpImpl(const KeyboardModifier& keyModifier) + void ImmediateModeActionDispatcher::KeyboardModifierUpImpl(const KeyboardModifier keyModifier) { ToggleOff(GetMouseInteractionEvent()->m_mouseInteraction.m_keyboardModifiers.m_keyModifiers, keyModifier); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BaseManipulator.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BaseManipulator.h index 07a622c936..1b76ca7a85 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BaseManipulator.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BaseManipulator.h @@ -54,7 +54,8 @@ namespace AzToolsFramework virtual ~BaseManipulator(); - using EntityComponentIds = AZStd::unordered_set; + using UniqueEntityIds = AZStd::unordered_set; + using UniqueEntityComponentIds = AZStd::unordered_set; //! Callback for the event when the mouse pointer is over this manipulator and the left mouse button is pressed. //! @param interaction It contains various mouse states when the event happens, as well as a ray shooting from the viewing camera @@ -137,7 +138,7 @@ namespace AzToolsFramework } //! Returns all EntityComponentIdPairs associated with this manipulator. - const EntityComponentIds& EntityComponentIdPairs() const + const UniqueEntityComponentIds& EntityComponentIdPairs() const { return m_entityComponentIdPairs; } @@ -147,10 +148,10 @@ namespace AzToolsFramework //! Remove an entity from being affected by this manipulator. //! @note All components on this entity registered with the manipulator will be removed. - EntityComponentIds::iterator RemoveEntityId(AZ::EntityId entityId); + UniqueEntityComponentIds::iterator RemoveEntityId(AZ::EntityId entityId); //! Remove a specific component (via a EntityComponentIdPair) being affected by this manipulator. - EntityComponentIds::iterator RemoveEntityComponentIdPair(const AZ::EntityComponentIdPair& entityComponentIdPair); + UniqueEntityComponentIds::iterator RemoveEntityComponentIdPair(const AZ::EntityComponentIdPair& entityComponentIdPair); //! Is this entity currently being tracked by this manipulator. bool HasEntityId(AZ::EntityId entityId) const; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SurfaceManipulator.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SurfaceManipulator.cpp index 58d579b20c..7de1703769 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SurfaceManipulator.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SurfaceManipulator.cpp @@ -80,23 +80,18 @@ namespace AzToolsFramework m_onMouseMoveCallback = onMouseMoveCallback; } + void SurfaceManipulator::InstallEntityIdsToIgnoreFn(EntityIdsToIgnoreFn entityIdsToIgnoreCallback) + { + m_entityIdsToIgnoreFn = AZStd::move(entityIdsToIgnoreCallback); + } + void SurfaceManipulator::OnLeftMouseDownImpl( const ViewportInteraction::MouseInteraction& interaction, [[maybe_unused]] float rayIntersectionDistance) { const AZ::Transform worldFromLocalUniformScale = TransformUniformScale(GetSpace()); - const AzFramework::ViewportId viewportId = interaction.m_interactionId.m_viewportId; - const auto& entityComponentIdPairs = EntityComponentIdPairs(); - m_rayRequest.m_entityFilter.m_ignoreEntities.clear(); - m_rayRequest.m_entityFilter.m_ignoreEntities.reserve(entityComponentIdPairs.size()); - AZStd::transform( - entityComponentIdPairs.begin(), entityComponentIdPairs.end(), - AZStd::inserter(m_rayRequest.m_entityFilter.m_ignoreEntities, m_rayRequest.m_entityFilter.m_ignoreEntities.begin()), - [](const AZ::EntityComponentIdPair& entityComponentIdPair) - { - return entityComponentIdPair.GetEntityId(); - }); + m_rayRequest.m_entityFilter.m_ignoreEntities = m_entityIdsToIgnoreFn(interaction); // calculate the start and end of the ray RefreshRayRequest( @@ -139,6 +134,8 @@ namespace AzToolsFramework { const AzFramework::ViewportId viewportId = interaction.m_interactionId.m_viewportId; + m_rayRequest.m_entityFilter.m_ignoreEntities = m_entityIdsToIgnoreFn(interaction); + // update the start and end of the ray RefreshRayRequest( m_rayRequest, ViewportInteraction::ViewportScreenToWorldRay(viewportId, interaction.m_mousePick.m_screenCoordinates), @@ -162,12 +159,12 @@ namespace AzToolsFramework const ManipulatorManagerState& managerState, AzFramework::DebugDisplayRequests& debugDisplay, const AzFramework::CameraState& cameraState, - const ViewportInteraction::MouseInteraction& mouseInteraction) + const ViewportInteraction::MouseInteraction& interaction) { m_manipulatorView->Draw( GetManipulatorManagerId(), managerState, GetManipulatorId(), ManipulatorState{ TransformUniformScale(GetSpace()), GetNonUniformScale(), GetLocalPosition(), MouseOver() }, debugDisplay, - cameraState, mouseInteraction); + cameraState, interaction); } void SurfaceManipulator::InvalidateImpl() diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SurfaceManipulator.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SurfaceManipulator.h index 091a71d8da..314ad7b253 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SurfaceManipulator.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/SurfaceManipulator.h @@ -40,6 +40,9 @@ namespace AzToolsFramework //! A Manipulator must only be created and managed through a shared_ptr. static AZStd::shared_ptr MakeShared(const AZ::Transform& worldFromLocal); + //! Callback function to determine which EntityIds to ignore when performing the ray intersection. + using EntityIdsToIgnoreFn = AZStd::function; + //! The state of the manipulator at the start of an interaction. struct Start { @@ -77,11 +80,13 @@ namespace AzToolsFramework void InstallLeftMouseUpCallback(const MouseActionCallback& onMouseUpCallback); void InstallMouseMoveCallback(const MouseActionCallback& onMouseMoveCallback); + void InstallEntityIdsToIgnoreFn(EntityIdsToIgnoreFn entityIdsToIgnoreFn); + void Draw( const ManipulatorManagerState& managerState, AzFramework::DebugDisplayRequests& debugDisplay, const AzFramework::CameraState& cameraState, - const ViewportInteraction::MouseInteraction& mouseInteraction) override; + const ViewportInteraction::MouseInteraction& interaction) override; void SetView(AZStd::unique_ptr&& view); @@ -109,6 +114,9 @@ namespace AzToolsFramework MouseActionCallback m_onLeftMouseUpCallback = nullptr; MouseActionCallback m_onMouseMoveCallback = nullptr; + //! Customization point to determine which (if any) EntityIds to ignore while performing the ray intersection. + EntityIdsToIgnoreFn m_entityIdsToIgnoreFn = nullptr; + //! Cached ray request initialized at mouse down and updated during mouse move. AzFramework::RenderGeometry::RayRequest m_rayRequest; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/TranslationManipulators.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/TranslationManipulators.cpp index 5810662e57..c6e9403f5f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/TranslationManipulators.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/TranslationManipulators.cpp @@ -145,6 +145,15 @@ namespace AzToolsFramework } } + void TranslationManipulators::InstallSurfaceManipulatorEntityIdsToIgnoreFn( + SurfaceManipulator::EntityIdsToIgnoreFn entityIdsToIgnoreFn) + { + if (m_surfaceManipulator) + { + m_surfaceManipulator->InstallEntityIdsToIgnoreFn(AZStd::move(entityIdsToIgnoreFn)); + } + } + void TranslationManipulators::SetLocalTransformImpl(const AZ::Transform& localTransform) { for (AZStd::shared_ptr& manipulator : m_linearManipulators) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/TranslationManipulators.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/TranslationManipulators.h index ea8dbb7975..6c841d16f6 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/TranslationManipulators.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/TranslationManipulators.h @@ -61,6 +61,8 @@ namespace AzToolsFramework void InstallSurfaceManipulatorMouseMoveCallback(const SurfaceManipulator::MouseActionCallback& onMouseMoveCallback); void InstallSurfaceManipulatorMouseUpCallback(const SurfaceManipulator::MouseActionCallback& onMouseUpCallback); + void InstallSurfaceManipulatorEntityIdsToIgnoreFn(SurfaceManipulator::EntityIdsToIgnoreFn entityIdsToIgnoreFn); + void SetSpaceImpl(const AZ::Transform& worldFromLocal) override; void SetLocalTransformImpl(const AZ::Transform& localTransform) override; void SetLocalPositionImpl(const AZ::Vector3& localPosition) override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp index 5c5cdcb4b4..c1037dd1ba 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp @@ -1360,6 +1360,19 @@ namespace AzToolsFramework EndRecordManipulatorCommand(); }); + translationManipulators->InstallSurfaceManipulatorEntityIdsToIgnoreFn( + [this](const ViewportInteraction::MouseInteraction& interaction) + { + if (interaction.m_keyboardModifiers.Ctrl()) + { + return AZStd::unordered_set(); + } + else + { + return m_selectedEntityIds; + } + }); + // transfer ownership m_entityIdManipulators.m_manipulators = AZStd::move(translationManipulators); } diff --git a/Code/Framework/AzToolsFramework/Tests/EditorTransformComponentSelectionTests.cpp b/Code/Framework/AzToolsFramework/Tests/EditorTransformComponentSelectionTests.cpp index 29d72a8270..724819b0c2 100644 --- a/Code/Framework/AzToolsFramework/Tests/EditorTransformComponentSelectionTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/EditorTransformComponentSelectionTests.cpp @@ -3277,4 +3277,45 @@ namespace UnitTest const AZ::Transform finalEntityTransform = AzToolsFramework::GetWorldTransform(m_entityIdBox); EXPECT_THAT(finalEntityTransform.GetTranslation(), IsCloseTolerance(expectedWorldPosition, 0.01f)); } + + TEST_F( + EditorTransformComponentSelectionRenderGeometryIntersectionManipulatorFixture, SurfaceManipulatorSelfIntersectsMeshWhenCtrlIsHeld) + { + // camera (go to position format) - 47.00, -52.00, 20.00, 0.00, -60.00 + m_cameraState.m_viewportSize = AZ::Vector2(1280.0f, 720.0f); + // position camera + AzFramework::SetCameraTransform( + m_cameraState, + AZ::Transform::CreateFromMatrix3x3AndTranslation( + AZ::Matrix3x3::CreateRotationZ(AZ::DegToRad(-60.0f)), AZ::Vector3(47.0f, -52.0f, 20.0f))); + // position box + AzToolsFramework::SetWorldTransform(m_entityIdBox, AZ::Transform::CreateTranslation(AZ::Vector3(50.0f, -50.0f, 20.0f))); + + // the initial starting position of the entity + const auto initialTransformWorld = AzToolsFramework::GetWorldTransform(m_entityIdBox); + // where the surface manipulator should end up (surface of the box) + const auto finalTransformWorld = AZ::Transform::CreateTranslation(AZ::Vector3(49.5f, -49.6337357f, 19.5793953f)); + + // calculate the position in screen space of the initial position of the entity + const auto initialPositionScreen = AzFramework::WorldToScreen(initialTransformWorld.GetTranslation(), m_cameraState); + // calculate the position in screen space of the final position of the entity + const auto finalPositionScreen = AzFramework::WorldToScreen(finalTransformWorld.GetTranslation(), m_cameraState); + + // select the entity (this will cause the manipulators to appear in EditorTransformComponentSelection) + AzToolsFramework::SelectEntity(m_entityIdBox); + + // press and drag the mouse (starting where the surface manipulator is) + m_actionDispatcher->CameraState(m_cameraState) + ->MousePosition(initialPositionScreen) + ->KeyboardModifierDown(AzToolsFramework::ViewportInteraction::KeyboardModifier::Control) + ->MouseLButtonDown() + ->MousePosition(finalPositionScreen) + ->MouseLButtonUp(); + + // read back the position of the entity now + const AZ::Transform finalManipulatorTransform = GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity()); + + // ensure final world positions match + EXPECT_THAT(finalManipulatorTransform, IsCloseTolerance(finalTransformWorld, 0.01f)); + } } // namespace UnitTest