Fix camera drift issues (#4576)
* remove some unused code in RenderViewportWidget and make viewing devicePixelRatioF easier Signed-off-by: hultonha <hultonha@amazon.co.uk> * updates to how cursor positions are calculate to handle the viewport widget moving Signed-off-by: hultonha <hultonha@amazon.co.uk> * remove optional for previous position Signed-off-by: hultonha <hultonha@amazon.co.uk> * add test to capture error with moving the widget Signed-off-by: hultonha <hultonha@amazon.co.uk> * minor comment updates before publishing PR Signed-off-by: hultonha <hultonha@amazon.co.uk>
This commit is contained in:
@@ -69,6 +69,7 @@ namespace UnitTest
|
||||
|
||||
m_rootWidget = AZStd::make_unique<QWidget>();
|
||||
m_rootWidget->setFixedSize(WidgetSize);
|
||||
m_rootWidget->move(0, 0); // explicitly set the widget to be in the upper left corner
|
||||
|
||||
m_controllerList = AZStd::make_shared<AzFramework::ViewportControllerList>();
|
||||
m_controllerList->RegisterViewportContext(TestViewportId);
|
||||
@@ -344,4 +345,51 @@ namespace UnitTest
|
||||
// Clean-up
|
||||
HaltCollaborators();
|
||||
}
|
||||
|
||||
// test to verify deltas and cursor positions are handled correctly when the widget is moved
|
||||
TEST_F(ModularViewportCameraControllerFixture, CameraDoesNotStutterAfterWidgetIsMoved)
|
||||
{
|
||||
// Given
|
||||
PrepareCollaborators();
|
||||
SandboxEditor::SetCameraCaptureCursorForLook(true);
|
||||
|
||||
const float deltaTime = 1.0f / 60.0f;
|
||||
|
||||
// When
|
||||
// move cursor to the center of the screen
|
||||
auto start = QPoint(WidgetSize.width() / 2, WidgetSize.height() / 2);
|
||||
MouseMove(m_rootWidget.get(), start, QPoint(0, 0));
|
||||
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() });
|
||||
|
||||
// move camera right
|
||||
const auto mouseDelta = QPoint(200, 0);
|
||||
MousePressAndMove(m_rootWidget.get(), start, mouseDelta, Qt::MouseButton::RightButton);
|
||||
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() });
|
||||
|
||||
QTest::mouseRelease(m_rootWidget.get(), Qt::MouseButton::RightButton, Qt::NoModifier, start + mouseDelta);
|
||||
|
||||
// update the position of the widget
|
||||
const auto offset = QPoint(500, 500);
|
||||
m_rootWidget->move(offset);
|
||||
|
||||
// move cursor back to widget center
|
||||
MouseMove(m_rootWidget.get(), start, QPoint(0, 0));
|
||||
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() });
|
||||
|
||||
// move camera left
|
||||
MousePressAndMove(m_rootWidget.get(), start, -mouseDelta, Qt::MouseButton::RightButton);
|
||||
m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() });
|
||||
|
||||
// Then
|
||||
// ensure the camera rotation has returned to the identity
|
||||
const AZ::Quaternion cameraRotation = m_cameraViewportContextView->GetCameraTransform().GetRotation();
|
||||
const auto eulerAngles = AzFramework::EulerAngles(AZ::Matrix3x3::CreateFromQuaternion(cameraRotation));
|
||||
|
||||
using ::testing::FloatNear;
|
||||
EXPECT_THAT(eulerAngles.GetX(), FloatNear(0.0f, 0.001f));
|
||||
EXPECT_THAT(eulerAngles.GetZ(), FloatNear(0.0f, 0.001f));
|
||||
|
||||
// Clean-up
|
||||
HaltCollaborators();
|
||||
}
|
||||
} // namespace UnitTest
|
||||
|
||||
+10
-10
@@ -261,10 +261,10 @@ namespace AzToolsFramework
|
||||
// ensures cursor positions are refreshed correctly with context menu focus changes)
|
||||
if (eventType == QEvent::FocusIn)
|
||||
{
|
||||
const auto widgetCursorPosition = m_sourceWidget->mapFromGlobal(QCursor::pos());
|
||||
if (m_sourceWidget->geometry().contains(widgetCursorPosition))
|
||||
const auto globalCursorPosition = QCursor::pos();
|
||||
if (m_sourceWidget->geometry().contains(globalCursorPosition))
|
||||
{
|
||||
HandleMouseMoveEvent(widgetCursorPosition);
|
||||
HandleMouseMoveEvent(globalCursorPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -290,7 +290,7 @@ namespace AzToolsFramework
|
||||
else if (eventType == QEvent::Type::MouseMove)
|
||||
{
|
||||
auto mouseEvent = static_cast<QMouseEvent*>(event);
|
||||
HandleMouseMoveEvent(mouseEvent->pos());
|
||||
HandleMouseMoveEvent(mouseEvent->globalPos());
|
||||
}
|
||||
// Map wheel events to the mouse Z movement channel.
|
||||
else if (eventType == QEvent::Type::Wheel)
|
||||
@@ -370,11 +370,12 @@ namespace AzToolsFramework
|
||||
return QPoint{ denormalizedX, denormalizedY };
|
||||
}
|
||||
|
||||
void QtEventToAzInputMapper::HandleMouseMoveEvent(const QPoint& cursorPosition)
|
||||
void QtEventToAzInputMapper::HandleMouseMoveEvent(const QPoint& globalCursorPosition)
|
||||
{
|
||||
const QPoint cursorDelta = cursorPosition - m_previousCursorPosition;
|
||||
const QPoint cursorDelta = globalCursorPosition - m_previousGlobalCursorPosition;
|
||||
|
||||
m_mouseDevice->m_cursorPositionData2D->m_normalizedPosition = WidgetPositionToNormalizedPosition(cursorPosition);
|
||||
m_mouseDevice->m_cursorPositionData2D->m_normalizedPosition =
|
||||
WidgetPositionToNormalizedPosition(m_sourceWidget->mapFromGlobal(globalCursorPosition));
|
||||
m_mouseDevice->m_cursorPositionData2D->m_normalizedPositionDelta = WidgetPositionToNormalizedPosition(cursorDelta);
|
||||
|
||||
ProcessPendingMouseEvents(cursorDelta);
|
||||
@@ -382,12 +383,11 @@ namespace AzToolsFramework
|
||||
if (m_capturingCursor)
|
||||
{
|
||||
// Reset our cursor position to the previous point
|
||||
const QPoint screenCursorPosition = m_sourceWidget->mapToGlobal(m_previousCursorPosition);
|
||||
AzQtComponents::SetCursorPos(screenCursorPosition);
|
||||
AzQtComponents::SetCursorPos(m_previousGlobalCursorPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_previousCursorPosition = cursorPosition;
|
||||
m_previousGlobalCursorPosition = globalCursorPosition;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -129,7 +129,7 @@ namespace AzToolsFramework
|
||||
// Handle mouse click events.
|
||||
void HandleMouseButtonEvent(QMouseEvent* mouseEvent);
|
||||
// Handle mouse move events.
|
||||
void HandleMouseMoveEvent(const QPoint& cursorPosition);
|
||||
void HandleMouseMoveEvent(const QPoint& globalCursorPosition);
|
||||
// Handles key press / release events (or ShortcutOverride events for keys listed in m_highPriorityKeys).
|
||||
void HandleKeyEvent(QKeyEvent* keyEvent);
|
||||
// Handles mouse wheel events.
|
||||
@@ -156,8 +156,8 @@ namespace AzToolsFramework
|
||||
AZStd::unordered_set<Qt::Key> m_highPriorityKeys;
|
||||
// A lookup table for AZ input channel ID -> physical input channel on our mouse or keyboard device.
|
||||
AZStd::unordered_map<AzFramework::InputChannelId, AzFramework::InputChannel*> m_channels;
|
||||
// Where the position of the mouse cursor was at the last cursor event.
|
||||
QPoint m_previousCursorPosition;
|
||||
// Where the mouse cursor was at the last cursor event.
|
||||
QPoint m_previousGlobalCursorPosition;
|
||||
// The source widget to map events from, used to calculate the relative mouse position within the widget bounds.
|
||||
QWidget* m_sourceWidget;
|
||||
// Flags whether or not Qt events should currently be processed.
|
||||
|
||||
Reference in New Issue
Block a user