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>
This commit is contained in:
Tom Hulton-Harrop
2022-02-14 10:19:14 +00:00
committed by GitHub
parent 9e32c83d30
commit deeb59cb83
10 changed files with 101 additions and 30 deletions
@@ -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<typename DerivedDispatcherT>
const char* ActionDispatcher<DerivedDispatcherT>::KeyboardModifierString(
const AzToolsFramework::ViewportInteraction::KeyboardModifier& keyModifier)
const AzToolsFramework::ViewportInteraction::KeyboardModifier keyModifier)
{
using namespace AzToolsFramework::ViewportInteraction;
switch (keyModifier)
@@ -235,7 +235,7 @@ namespace AzManipulatorTestFramework
template<typename DerivedDispatcherT>
DerivedDispatcherT* ActionDispatcher<DerivedDispatcherT>::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<typename DerivedDispatcherT>
DerivedDispatcherT* ActionDispatcher<DerivedDispatcherT>::KeyboardModifierUp(
const AzToolsFramework::ViewportInteraction::KeyboardModifier& keyModifier)
const AzToolsFramework::ViewportInteraction::KeyboardModifier keyModifier)
{
Log("Keyboard modifier up: %s", KeyboardModifierString(keyModifier));
KeyboardModifierUpImpl(keyModifier);
@@ -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;
@@ -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);
}
@@ -54,7 +54,8 @@ namespace AzToolsFramework
virtual ~BaseManipulator();
using EntityComponentIds = AZStd::unordered_set<AZ::EntityComponentIdPair>;
using UniqueEntityIds = AZStd::unordered_set<AZ::EntityId>;
using UniqueEntityComponentIds = AZStd::unordered_set<AZ::EntityComponentIdPair>;
//! 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;
@@ -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()
@@ -40,6 +40,9 @@ namespace AzToolsFramework
//! A Manipulator must only be created and managed through a shared_ptr.
static AZStd::shared_ptr<SurfaceManipulator> MakeShared(const AZ::Transform& worldFromLocal);
//! Callback function to determine which EntityIds to ignore when performing the ray intersection.
using EntityIdsToIgnoreFn = AZStd::function<UniqueEntityIds(const ViewportInteraction::MouseInteraction&)>;
//! 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<ManipulatorView>&& 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;
@@ -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<LinearManipulator>& manipulator : m_linearManipulators)
@@ -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;
@@ -1360,6 +1360,19 @@ namespace AzToolsFramework
EndRecordManipulatorCommand();
});
translationManipulators->InstallSurfaceManipulatorEntityIdsToIgnoreFn(
[this](const ViewportInteraction::MouseInteraction& interaction)
{
if (interaction.m_keyboardModifiers.Ctrl())
{
return AZStd::unordered_set<AZ::EntityId>();
}
else
{
return m_selectedEntityIds;
}
});
// transfer ownership
m_entityIdManipulators.m_manipulators = AZStd::move(translationManipulators);
}
@@ -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