From 88c8042932df9ceb6649b39b000081026a1052c7 Mon Sep 17 00:00:00 2001 From: Jonathan Toomim Date: Tue, 23 May 2023 02:49:18 -0700 Subject: [PATCH] input_common: Redesign mouse->stick panning for better responsiveness Previously, the mouse processing code had some issues that reduced mouse | control quality. No matter how you configured it or how you used the mouse, it was impossible to be able to make both fine inputs and maximum inputs to the control stick with the same configuration, as inputs were clamped to the range, and *outputs* were multiplied by the sensitivity setting. It was also impossible to make brief inputs, as two built-in exponential moving average filters in the mouse driver caused any mouse inputs, no matter how brief, to continue to produce stick outputs for several hundred milliseconds before dissipating. This commit is an attempt to address those issues. With this commit, an entirely different model of processing inputs is used. Instead of applying the sesnsitivity to the stick output, this commit applies the sensitivity at the beginning of processing to the mouse input. Next, inputs from the mouse are accumulated into a buffer variable. When the stick is updated, stick output is clamped to the range of the stick, and that value is subtracted from the input buffer. Any *excess* motion still remaining in the buffer gets decayed exponentially (similar to one of the exponential moving averages before). This gives a good compromise between responsiveness (brief mouse movement causes a brief stick response) and inertia (large mouse movement which exceeds the stick's instantaneous dynamic range causes stick movement that persists for a short time afterwards, mimicking inertia). Finally, to improve the linearity of the response, the stick motion is square-rooted to amplify small movements. This commit changes the semantics of the sensitivity option. It's no longer a percent, and "100(%)" no longer means anything special, so the input configuration UI has been changed to allow values up to 10,000 instead of being limited to 100. The changes to the filtration also affect the sensitivity. There are some magic constants in the code to try to keep the sensitivity about the same with a given value, but it's not exact, and users are encouraged to play around with it again on the new code. --- src/input_common/drivers/mouse.cpp | 57 +++++++------------ .../configuration/configure_input_advanced.ui | 5 +- 2 files changed, 22 insertions(+), 40 deletions(-) diff --git a/src/input_common/drivers/mouse.cpp b/src/input_common/drivers/mouse.cpp index 0c9f642bb5..1f0304cb79 100644 --- a/src/input_common/drivers/mouse.cpp +++ b/src/input_common/drivers/mouse.cpp @@ -88,13 +88,26 @@ void Mouse::UpdateStickInput() { return; } - const float sensitivity = - Settings::values.mouse_panning_sensitivity.GetValue() * default_stick_sensitivity; + auto mouse_change = last_mouse_change; + auto move_distance = mouse_change.Length(); - // Slow movement by 4% - last_mouse_change *= 0.96f; - SetAxis(identifier, mouse_axis_x, last_mouse_change.x * sensitivity); - SetAxis(identifier, mouse_axis_y, -last_mouse_change.y * sensitivity); + // Perform at most 1 unit of buffered mouse movement + if (move_distance > 1.0f) { + mouse_change *= 1.0f / move_distance; + move_distance = mouse_change.Length(); + } + + last_mouse_change -= mouse_change; + + // Decay remaining buffered movement by 5% + last_mouse_change *= 0.95f; + + // Stick response is nonlinear, and is not sensitive enough to fine changes + const auto sqrt_distance = sqrt(move_distance); + mouse_change *= sqrt_distance / move_distance; + + SetAxis(identifier, mouse_axis_x, mouse_change.x); + SetAxis(identifier, mouse_axis_y, -mouse_change.y); } void Mouse::UpdateMotionInput() { @@ -130,48 +143,20 @@ void Mouse::UpdateMotionInput() { } void Mouse::Move(int x, int y, int center_x, int center_y) { + const float sensitivity = Settings::values.mouse_panning_sensitivity.GetValue() * 0.0012f; if (Settings::values.mouse_panning) { mouse_panning_timeout = 0; auto mouse_change = (Common::MakeVec(x, y) - Common::MakeVec(center_x, center_y)).Cast(); last_motion_change += {-mouse_change.y, -mouse_change.x, 0}; - - const auto move_distance = mouse_change.Length(); - if (move_distance == 0) { - return; - } - - // Make slow movements at least 3 units on length - if (move_distance < 3.0f) { - // Normalize value - mouse_change /= move_distance; - mouse_change *= 3.0f; - } - - // Average mouse movements - last_mouse_change = (last_mouse_change * 0.91f) + (mouse_change * 0.09f); - - const auto last_move_distance = last_mouse_change.Length(); - - // Make fast movements clamp to 8 units on length - if (last_move_distance > 8.0f) { - // Normalize value - last_mouse_change /= last_move_distance; - last_mouse_change *= 8.0f; - } - - // Ignore average if it's less than 1 unit and use current movement value - if (last_move_distance < 1.0f) { - last_mouse_change = mouse_change / mouse_change.Length(); - } + last_mouse_change += mouse_change * sensitivity * 0.1f; return; } if (button_pressed) { const auto mouse_move = Common::MakeVec(x, y) - mouse_origin; - const float sensitivity = Settings::values.mouse_panning_sensitivity.GetValue() * 0.0012f; SetAxis(identifier, mouse_axis_x, static_cast(mouse_move.x) * sensitivity); SetAxis(identifier, mouse_axis_y, static_cast(-mouse_move.y) * sensitivity); diff --git a/src/yuzu/configuration/configure_input_advanced.ui b/src/yuzu/configuration/configure_input_advanced.ui index 2e8b136600..77fb4754aa 100644 --- a/src/yuzu/configuration/configure_input_advanced.ui +++ b/src/yuzu/configuration/configure_input_advanced.ui @@ -2764,14 +2764,11 @@ Qt::AlignCenter - - % - 1 - 100 + 10000 100