Fix issue with viewport interaction ordering and viewport matrix changed handler (#695)
* fix for ctrl+mouse-wheel to cycle transform modes in the viewport * ensure correct callback function is invoked * remove redundant check in CameraInput
This commit is contained in:
committed by
GitHub
parent
bf62687b37
commit
f9fb61cc5d
@@ -407,11 +407,6 @@ namespace AzFramework
|
||||
{
|
||||
if (input->m_state == InputChannel::State::Began)
|
||||
{
|
||||
if (input->m_state == InputChannel::State::Updated)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_translation |= translationFromKey(input->m_channelId);
|
||||
if (m_translation != TranslationType::Nil)
|
||||
{
|
||||
|
||||
@@ -21,206 +21,228 @@
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
static const auto ManipulatorPriority = AzFramework::ViewportControllerPriority::High;
|
||||
static const auto InteractionPriority = AzFramework::ViewportControllerPriority::Low;
|
||||
static const auto ManipulatorPriority = AzFramework::ViewportControllerPriority::Highest;
|
||||
static const auto InteractionPriority = AzFramework::ViewportControllerPriority::High;
|
||||
|
||||
namespace SandboxEditor
|
||||
{
|
||||
|
||||
ViewportManipulatorControllerInstance::ViewportManipulatorControllerInstance(AzFramework::ViewportId viewport, ViewportManipulatorController* controller)
|
||||
: AzFramework::MultiViewportControllerInstanceInterface<ViewportManipulatorController>(viewport, controller)
|
||||
{
|
||||
}
|
||||
|
||||
AzToolsFramework::ViewportInteraction::MouseButton ViewportManipulatorControllerInstance::GetMouseButton(
|
||||
const AzFramework::InputChannel& inputChannel)
|
||||
{
|
||||
using AzToolsFramework::ViewportInteraction::MouseButton;
|
||||
using InputButton = AzFramework::InputDeviceMouse::Button;
|
||||
const auto& id = inputChannel.GetInputChannelId();
|
||||
if (id == InputButton::Left)
|
||||
ViewportManipulatorControllerInstance::ViewportManipulatorControllerInstance(
|
||||
AzFramework::ViewportId viewport, ViewportManipulatorController* controller)
|
||||
: AzFramework::MultiViewportControllerInstanceInterface<ViewportManipulatorController>(viewport, controller)
|
||||
{
|
||||
return MouseButton::Left;
|
||||
}
|
||||
if (id == InputButton::Middle)
|
||||
{
|
||||
return MouseButton::Middle;
|
||||
}
|
||||
if (id == InputButton::Right)
|
||||
{
|
||||
return MouseButton::Right;
|
||||
}
|
||||
return MouseButton::None;
|
||||
}
|
||||
|
||||
bool ViewportManipulatorControllerInstance::IsMouseMove(const AzFramework::InputChannel& inputChannel)
|
||||
{
|
||||
return inputChannel.GetInputChannelId() == AzFramework::InputDeviceMouse::SystemCursorPosition;
|
||||
}
|
||||
|
||||
AzToolsFramework::ViewportInteraction::KeyboardModifier ViewportManipulatorControllerInstance::GetKeyboardModifier(
|
||||
const AzFramework::InputChannel& inputChannel)
|
||||
{
|
||||
using AzToolsFramework::ViewportInteraction::KeyboardModifier;
|
||||
using Key = AzFramework::InputDeviceKeyboard::Key;
|
||||
const auto& id = inputChannel.GetInputChannelId();
|
||||
if (id == Key::ModifierAltL || id == Key::ModifierAltR)
|
||||
{
|
||||
return KeyboardModifier::Alt;
|
||||
}
|
||||
if (id == Key::ModifierCtrlL || id == Key::ModifierCtrlR)
|
||||
{
|
||||
return KeyboardModifier::Ctrl;
|
||||
}
|
||||
if (id == Key::ModifierShiftL || id == Key::ModifierShiftR)
|
||||
{
|
||||
return KeyboardModifier::Shift;
|
||||
}
|
||||
return KeyboardModifier::None;
|
||||
}
|
||||
|
||||
bool ViewportManipulatorControllerInstance::HandleInputChannelEvent(const AzFramework::ViewportControllerInputEvent& event)
|
||||
{
|
||||
// We only care about manipulator and viewport interaction events
|
||||
if (event.m_priority != ManipulatorPriority && event.m_priority != InteractionPriority)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
using InteractionBus = AzToolsFramework::EditorInteractionSystemViewportSelectionRequestBus;
|
||||
using namespace AzToolsFramework::ViewportInteraction;
|
||||
using AzFramework::InputChannel;
|
||||
|
||||
bool interactionHandled = false;
|
||||
AZStd::optional<MouseButton> overrideButton;
|
||||
AZStd::optional<MouseEvent> eventType;
|
||||
|
||||
// Because we receive events multiple times at separate priorities for manipulator events and
|
||||
// viewport interaction events, we want to avoid updating our "last tick state" until we're on our last event,
|
||||
// which currently is the low priority Interaction processor.
|
||||
const bool finishedProcessingEvents = event.m_priority == InteractionPriority;
|
||||
|
||||
if (IsMouseMove(event.m_inputChannel))
|
||||
AzToolsFramework::ViewportInteraction::MouseButton ViewportManipulatorControllerInstance::GetMouseButton(
|
||||
const AzFramework::InputChannel& inputChannel)
|
||||
{
|
||||
// Cache the ray trace results when doing manipulator interaction checks, no need to recalculate after
|
||||
if (event.m_priority == ManipulatorPriority)
|
||||
using AzToolsFramework::ViewportInteraction::MouseButton;
|
||||
using InputButton = AzFramework::InputDeviceMouse::Button;
|
||||
const auto& id = inputChannel.GetInputChannelId();
|
||||
if (id == InputButton::Left)
|
||||
{
|
||||
AzFramework::ScreenPoint screenPosition = AzFramework::ScreenPoint(0, 0);
|
||||
ViewportMouseCursorRequestBus::EventResult(
|
||||
screenPosition, GetViewportId(), &ViewportMouseCursorRequestBus::Events::ViewportCursorScreenPosition);
|
||||
|
||||
m_state.m_mousePick.m_screenCoordinates = screenPosition;
|
||||
AZStd::optional<ProjectedViewportRay> ray;
|
||||
ViewportInteractionRequestBus::EventResult(
|
||||
ray, GetViewportId(), &ViewportInteractionRequestBus::Events::ViewportScreenToWorldRay, screenPosition);
|
||||
|
||||
if (ray.has_value())
|
||||
{
|
||||
m_state.m_mousePick.m_rayOrigin = ray.value().origin;
|
||||
m_state.m_mousePick.m_rayDirection = ray.value().direction;
|
||||
}
|
||||
return MouseButton::Left;
|
||||
}
|
||||
eventType = MouseEvent::Move;
|
||||
}
|
||||
else if (auto mouseButton = GetMouseButton(event.m_inputChannel); mouseButton != MouseButton::None)
|
||||
{
|
||||
const AZ::u32 mouseButtonValue = static_cast<AZ::u32>(mouseButton);
|
||||
overrideButton = mouseButton;
|
||||
if (event.m_inputChannel.GetState() == InputChannel::State::Began)
|
||||
if (id == InputButton::Middle)
|
||||
{
|
||||
m_state.m_mouseButtons.m_mouseButtons |= mouseButtonValue;
|
||||
if (IsDoubleClick(mouseButton))
|
||||
return MouseButton::Middle;
|
||||
}
|
||||
if (id == InputButton::Right)
|
||||
{
|
||||
return MouseButton::Right;
|
||||
}
|
||||
return MouseButton::None;
|
||||
}
|
||||
|
||||
bool ViewportManipulatorControllerInstance::IsMouseMove(const AzFramework::InputChannel& inputChannel)
|
||||
{
|
||||
return inputChannel.GetInputChannelId() == AzFramework::InputDeviceMouse::SystemCursorPosition;
|
||||
}
|
||||
|
||||
AzToolsFramework::ViewportInteraction::KeyboardModifier ViewportManipulatorControllerInstance::GetKeyboardModifier(
|
||||
const AzFramework::InputChannel& inputChannel)
|
||||
{
|
||||
using AzToolsFramework::ViewportInteraction::KeyboardModifier;
|
||||
using Key = AzFramework::InputDeviceKeyboard::Key;
|
||||
const auto& id = inputChannel.GetInputChannelId();
|
||||
if (id == Key::ModifierAltL || id == Key::ModifierAltR)
|
||||
{
|
||||
return KeyboardModifier::Alt;
|
||||
}
|
||||
if (id == Key::ModifierCtrlL || id == Key::ModifierCtrlR)
|
||||
{
|
||||
return KeyboardModifier::Ctrl;
|
||||
}
|
||||
if (id == Key::ModifierShiftL || id == Key::ModifierShiftR)
|
||||
{
|
||||
return KeyboardModifier::Shift;
|
||||
}
|
||||
return KeyboardModifier::None;
|
||||
}
|
||||
|
||||
bool ViewportManipulatorControllerInstance::HandleInputChannelEvent(const AzFramework::ViewportControllerInputEvent& event)
|
||||
{
|
||||
// We only care about manipulator and viewport interaction events
|
||||
if (event.m_priority != ManipulatorPriority && event.m_priority != InteractionPriority)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
using InteractionBus = AzToolsFramework::EditorInteractionSystemViewportSelectionRequestBus;
|
||||
using namespace AzToolsFramework::ViewportInteraction;
|
||||
using AzFramework::InputChannel;
|
||||
|
||||
bool interactionHandled = false;
|
||||
float wheelDelta = 0.0f;
|
||||
AZStd::optional<MouseButton> overrideButton;
|
||||
AZStd::optional<MouseEvent> eventType;
|
||||
|
||||
// Because we receive events multiple times at separate priorities for manipulator events and
|
||||
// viewport interaction events, we want to avoid updating our "last tick state" until we're on our last event,
|
||||
// which currently is the low priority Interaction processor.
|
||||
const bool finishedProcessingEvents = event.m_priority == InteractionPriority;
|
||||
|
||||
const auto state = event.m_inputChannel.GetState();
|
||||
if (IsMouseMove(event.m_inputChannel))
|
||||
{
|
||||
// Cache the ray trace results when doing manipulator interaction checks, no need to recalculate after
|
||||
if (event.m_priority == ManipulatorPriority)
|
||||
{
|
||||
// Only remove the double click flag once we're done processing both Manipulator and Interaction events
|
||||
if (event.m_priority == InteractionPriority)
|
||||
AzFramework::ScreenPoint screenPosition = AzFramework::ScreenPoint(0, 0);
|
||||
ViewportMouseCursorRequestBus::EventResult(
|
||||
screenPosition, GetViewportId(), &ViewportMouseCursorRequestBus::Events::ViewportCursorScreenPosition);
|
||||
|
||||
m_mouseInteraction.m_mousePick.m_screenCoordinates = screenPosition;
|
||||
AZStd::optional<ProjectedViewportRay> ray;
|
||||
ViewportInteractionRequestBus::EventResult(
|
||||
ray, GetViewportId(), &ViewportInteractionRequestBus::Events::ViewportScreenToWorldRay, screenPosition);
|
||||
|
||||
if (ray.has_value())
|
||||
{
|
||||
m_pendingDoubleClicks.erase(mouseButton);
|
||||
m_mouseInteraction.m_mousePick.m_rayOrigin = ray.value().origin;
|
||||
m_mouseInteraction.m_mousePick.m_rayDirection = ray.value().direction;
|
||||
}
|
||||
eventType = MouseEvent::DoubleClick;
|
||||
}
|
||||
else
|
||||
eventType = MouseEvent::Move;
|
||||
}
|
||||
else if (auto mouseButton = GetMouseButton(event.m_inputChannel); mouseButton != MouseButton::None)
|
||||
{
|
||||
const AZ::u32 mouseButtonValue = static_cast<AZ::u32>(mouseButton);
|
||||
overrideButton = mouseButton;
|
||||
if (state == InputChannel::State::Began)
|
||||
{
|
||||
// Only insert the double click timing once we're done processing events, to avoid a false IsDoubleClick positive
|
||||
if (finishedProcessingEvents)
|
||||
m_mouseInteraction.m_mouseButtons.m_mouseButtons |= mouseButtonValue;
|
||||
if (IsDoubleClick(mouseButton))
|
||||
{
|
||||
m_pendingDoubleClicks[mouseButton] = m_curTime;
|
||||
// Only remove the double click flag once we're done processing both Manipulator and Interaction events
|
||||
if (event.m_priority == InteractionPriority)
|
||||
{
|
||||
m_pendingDoubleClicks.erase(mouseButton);
|
||||
}
|
||||
eventType = MouseEvent::DoubleClick;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Only insert the double click timing once we're done processing events, to avoid a false IsDoubleClick positive
|
||||
if (finishedProcessingEvents)
|
||||
{
|
||||
m_pendingDoubleClicks[mouseButton] = m_curTime;
|
||||
}
|
||||
eventType = MouseEvent::Down;
|
||||
}
|
||||
eventType = MouseEvent::Down;
|
||||
}
|
||||
}
|
||||
else if (event.m_inputChannel.GetState() == InputChannel::State::Ended)
|
||||
{
|
||||
// If we've actually logged a mouse down event, forward a mouse up event.
|
||||
// This prevents corner cases like the context menu thinking it should be opened even though no one clicked in this viewport,
|
||||
// due to RenderViewportWidget ensuring all controllers get InputChannel::State::Ended events.
|
||||
if (m_state.m_mouseButtons.m_mouseButtons & mouseButtonValue)
|
||||
else if (state == InputChannel::State::Ended)
|
||||
{
|
||||
// Erase the button from our state if we're done processing events.
|
||||
if (event.m_priority == InteractionPriority)
|
||||
// If we've actually logged a mouse down event, forward a mouse up event.
|
||||
// This prevents corner cases like the context menu thinking it should be opened even though no one clicked in this viewport,
|
||||
// due to RenderViewportWidget ensuring all controllers get InputChannel::State::Ended events.
|
||||
if (m_mouseInteraction.m_mouseButtons.m_mouseButtons & mouseButtonValue)
|
||||
{
|
||||
m_state.m_mouseButtons.m_mouseButtons &= ~mouseButtonValue;
|
||||
// Erase the button from our state if we're done processing events.
|
||||
if (event.m_priority == InteractionPriority)
|
||||
{
|
||||
m_mouseInteraction.m_mouseButtons.m_mouseButtons &= ~mouseButtonValue;
|
||||
}
|
||||
eventType = MouseEvent::Up;
|
||||
}
|
||||
eventType = MouseEvent::Up;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (auto keyboardModifier = GetKeyboardModifier(event.m_inputChannel); keyboardModifier != KeyboardModifier::None)
|
||||
{
|
||||
if (event.m_inputChannel.GetState() == InputChannel::State::Began || event.m_inputChannel.GetState() == InputChannel::State::Updated)
|
||||
else if (auto keyboardModifier = GetKeyboardModifier(event.m_inputChannel); keyboardModifier != KeyboardModifier::None)
|
||||
{
|
||||
m_state.m_keyboardModifiers.m_keyModifiers |= static_cast<AZ::u32>(keyboardModifier);
|
||||
if (state == InputChannel::State::Began || state == InputChannel::State::Updated)
|
||||
{
|
||||
m_mouseInteraction.m_keyboardModifiers.m_keyModifiers |= static_cast<AZ::u32>(keyboardModifier);
|
||||
}
|
||||
else if (state == InputChannel::State::Ended)
|
||||
{
|
||||
m_mouseInteraction.m_keyboardModifiers.m_keyModifiers &= ~static_cast<AZ::u32>(keyboardModifier);
|
||||
}
|
||||
}
|
||||
else if (event.m_inputChannel.GetState() == InputChannel::State::Ended)
|
||||
else if (event.m_inputChannel.GetInputChannelId() == AzFramework::InputDeviceMouse::Movement::Z)
|
||||
{
|
||||
m_state.m_keyboardModifiers.m_keyModifiers &= ~static_cast<AZ::u32>(keyboardModifier);
|
||||
if (state == InputChannel::State::Began || state == InputChannel::State::Updated)
|
||||
{
|
||||
eventType = MouseEvent::Wheel;
|
||||
wheelDelta = event.m_inputChannel.GetValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (eventType)
|
||||
{
|
||||
MouseInteraction mouseInteraction = m_state;
|
||||
if (overrideButton)
|
||||
if (eventType)
|
||||
{
|
||||
mouseInteraction.m_mouseButtons.m_mouseButtons = static_cast<AZ::u32>(overrideButton.value());
|
||||
MouseInteraction mouseInteraction = m_mouseInteraction;
|
||||
if (overrideButton)
|
||||
{
|
||||
mouseInteraction.m_mouseButtons.m_mouseButtons = static_cast<AZ::u32>(overrideButton.value());
|
||||
}
|
||||
|
||||
mouseInteraction.m_interactionId.m_viewportId = GetViewportId();
|
||||
|
||||
// Depending on priority, we dispatch to either the manipulator or viewport interaction event
|
||||
const auto& targetInteractionEvent = event.m_priority == ManipulatorPriority
|
||||
? &InteractionBus::Events::InternalHandleMouseManipulatorInteraction
|
||||
: &InteractionBus::Events::InternalHandleMouseViewportInteraction;
|
||||
|
||||
const auto mouseInteractionEvent = [mouseInteraction, event = eventType.value(), wheelDelta] {
|
||||
switch (event)
|
||||
{
|
||||
case MouseEvent::Up:
|
||||
case MouseEvent::Down:
|
||||
case MouseEvent::Move:
|
||||
case MouseEvent::DoubleClick:
|
||||
return MouseInteractionEvent(AZStd::move(mouseInteraction), event);
|
||||
case MouseEvent::Wheel:
|
||||
return MouseInteractionEvent(AZStd::move(mouseInteraction), wheelDelta);
|
||||
}
|
||||
|
||||
AZ_Assert(false, "Unhandled MouseEvent");
|
||||
return MouseInteractionEvent(MouseInteraction{}, MouseEvent::Up);
|
||||
}();
|
||||
|
||||
InteractionBus::EventResult(
|
||||
interactionHandled, AzToolsFramework::GetEntityContextId(), targetInteractionEvent, mouseInteractionEvent);
|
||||
}
|
||||
mouseInteraction.m_interactionId.m_viewportId = GetViewportId();
|
||||
|
||||
// Depending on priority, we dispatch to either the manipulator or viewport interaction event
|
||||
const auto& targetInteractionEvent =
|
||||
event.m_priority == ManipulatorPriority
|
||||
? &InteractionBus::Events::InternalHandleMouseManipulatorInteraction
|
||||
: &InteractionBus::Events::InternalHandleMouseViewportInteraction;
|
||||
|
||||
InteractionBus::EventResult(
|
||||
interactionHandled,
|
||||
AzToolsFramework::GetEntityContextId(),
|
||||
targetInteractionEvent,
|
||||
MouseInteractionEvent(AZStd::move(mouseInteraction), eventType.value()));
|
||||
return interactionHandled;
|
||||
}
|
||||
|
||||
return interactionHandled;
|
||||
}
|
||||
|
||||
void ViewportManipulatorControllerInstance::ResetInputChannels()
|
||||
{
|
||||
m_pendingDoubleClicks.clear();
|
||||
m_state = AzToolsFramework::ViewportInteraction::MouseInteraction();
|
||||
}
|
||||
|
||||
void ViewportManipulatorControllerInstance::UpdateViewport(const AzFramework::ViewportControllerUpdateEvent& event)
|
||||
{
|
||||
m_curTime = event.m_time;
|
||||
}
|
||||
|
||||
bool ViewportManipulatorControllerInstance::IsDoubleClick(AzToolsFramework::ViewportInteraction::MouseButton button) const
|
||||
{
|
||||
auto clickIt = m_pendingDoubleClicks.find(button);
|
||||
if (clickIt == m_pendingDoubleClicks.end())
|
||||
void ViewportManipulatorControllerInstance::ResetInputChannels()
|
||||
{
|
||||
return false;
|
||||
m_pendingDoubleClicks.clear();
|
||||
m_mouseInteraction = AzToolsFramework::ViewportInteraction::MouseInteraction();
|
||||
}
|
||||
const double doubleClickThresholdMilliseconds = qApp->doubleClickInterval();
|
||||
return (m_curTime.GetMilliseconds() - clickIt->second.GetMilliseconds()) < doubleClickThresholdMilliseconds;
|
||||
}
|
||||
|
||||
void ViewportManipulatorControllerInstance::UpdateViewport(const AzFramework::ViewportControllerUpdateEvent& event)
|
||||
{
|
||||
m_curTime = event.m_time;
|
||||
}
|
||||
|
||||
bool ViewportManipulatorControllerInstance::IsDoubleClick(AzToolsFramework::ViewportInteraction::MouseButton button) const
|
||||
{
|
||||
auto clickIt = m_pendingDoubleClicks.find(button);
|
||||
if (clickIt == m_pendingDoubleClicks.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const double doubleClickThresholdMilliseconds = qApp->doubleClickInterval();
|
||||
return (m_curTime.GetMilliseconds() - clickIt->second.GetMilliseconds()) < doubleClickThresholdMilliseconds;
|
||||
}
|
||||
} //namespace SandboxEditor
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace SandboxEditor
|
||||
static bool IsMouseMove(const AzFramework::InputChannel& inputChannel);
|
||||
static AzToolsFramework::ViewportInteraction::KeyboardModifier GetKeyboardModifier(const AzFramework::InputChannel& inputChannel);
|
||||
|
||||
AzToolsFramework::ViewportInteraction::MouseInteraction m_state;
|
||||
AzToolsFramework::ViewportInteraction::MouseInteraction m_mouseInteraction;
|
||||
AZStd::unordered_map<AzToolsFramework::ViewportInteraction::MouseButton, AZ::ScriptTimePoint> m_pendingDoubleClicks;
|
||||
AZ::ScriptTimePoint m_curTime;
|
||||
};
|
||||
|
||||
@@ -40,9 +40,10 @@ namespace AZ
|
||||
{
|
||||
m_projectionMatrixChangedEvent.Signal(matrix);
|
||||
});
|
||||
|
||||
m_onViewMatrixChangedHandler = ViewportContext::MatrixChangedEvent::Handler([this](const AZ::Matrix4x4& matrix)
|
||||
{
|
||||
m_projectionMatrixChangedEvent.Signal(matrix);
|
||||
m_viewMatrixChangedEvent.Signal(matrix);
|
||||
});
|
||||
|
||||
SetRenderScene(renderScene);
|
||||
|
||||
Reference in New Issue
Block a user