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.
This commit is contained in:
Jonathan Toomim 2023-05-23 02:49:18 -07:00
parent e8a025b4f8
commit 88c8042932
2 changed files with 22 additions and 40 deletions

View file

@ -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<float>();
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<int>(x, y) - mouse_origin;
const float sensitivity = Settings::values.mouse_panning_sensitivity.GetValue() * 0.0012f;
SetAxis(identifier, mouse_axis_x, static_cast<float>(mouse_move.x) * sensitivity);
SetAxis(identifier, mouse_axis_y, static_cast<float>(-mouse_move.y) * sensitivity);

View file

@ -2764,14 +2764,11 @@
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="suffix">
<string>%</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>100</number>
<number>10000</number>
</property>
<property name="value">
<number>100</number>