From 7b49249f5f09a7864a4ed740491959fa656a288c Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sun, 23 Feb 2020 11:48:45 +0100 Subject: [PATCH] Input: Add config lerp factor for buttons and triggers Adds new lerp factors to the keyboard pad handler In order to simulate triggers and analog buttons. See "Analog Button Lerp Factor" and "Trigger Lerp Factor" in the yml in InputConfigs/Keyboard/. Values Range from 0-100 as before, where 100 is instant press and 0 is never. Currently I'm not planning any GUI element for this. --- rpcs3/Emu/Io/PadHandler.h | 25 +++++ rpcs3/Emu/Io/pad_config.h | 2 + rpcs3/Input/keyboard_pad_handler.cpp | 157 +++++++++++++++++++-------- rpcs3/Input/keyboard_pad_handler.h | 5 + 4 files changed, 144 insertions(+), 45 deletions(-) diff --git a/rpcs3/Emu/Io/PadHandler.h b/rpcs3/Emu/Io/PadHandler.h index 68f07c3574..2cdcce6491 100644 --- a/rpcs3/Emu/Io/PadHandler.h +++ b/rpcs3/Emu/Io/PadHandler.h @@ -112,11 +112,36 @@ struct Button u16 m_value = 0; bool m_pressed = false; + u16 m_actual_value = 0; // only used in keyboard_pad_handler + bool m_analog = false; // only used in keyboard_pad_handler + bool m_trigger = false; // only used in keyboard_pad_handler + Button(u32 offset, u32 keyCode, u32 outKeyCode) : m_offset(offset) , m_keyCode(keyCode) , m_outKeyCode(outKeyCode) { + if (offset == CELL_PAD_BTN_OFFSET_DIGITAL1) + { + if (outKeyCode == CELL_PAD_CTRL_LEFT || outKeyCode == CELL_PAD_CTRL_RIGHT || + outKeyCode == CELL_PAD_CTRL_UP || outKeyCode == CELL_PAD_CTRL_DOWN) + { + m_analog = true; + } + } + else if (offset == CELL_PAD_BTN_OFFSET_DIGITAL2) + { + if (outKeyCode == CELL_PAD_CTRL_CROSS || outKeyCode == CELL_PAD_CTRL_CIRCLE || + outKeyCode == CELL_PAD_CTRL_SQUARE || outKeyCode == CELL_PAD_CTRL_TRIANGLE || + outKeyCode == CELL_PAD_CTRL_L1 || outKeyCode == CELL_PAD_CTRL_R1) + { + m_analog = true; + } + else if (outKeyCode == CELL_PAD_CTRL_L2 || outKeyCode == CELL_PAD_CTRL_R2) + { + m_trigger = true; + } + } } }; diff --git a/rpcs3/Emu/Io/pad_config.h b/rpcs3/Emu/Io/pad_config.h index 96434a18a7..23dcab66ce 100644 --- a/rpcs3/Emu/Io/pad_config.h +++ b/rpcs3/Emu/Io/pad_config.h @@ -99,6 +99,8 @@ struct pad_config final : cfg::node cfg::_int<0, 100> l_stick_lerp_factor{ this, "Left Stick Lerp Factor", 100 }; cfg::_int<0, 100> r_stick_lerp_factor{ this, "Right Stick Lerp Factor", 100 }; + cfg::_int<0, 100> analog_lerp_factor{ this, "Analog Button Lerp Factor", 100 }; + cfg::_int<0, 100> trigger_lerp_factor{ this, "Trigger Lerp Factor", 100 }; cfg::_int<0, 5> device_class_type{ this, "Device Class Type", 0 }; diff --git a/rpcs3/Input/keyboard_pad_handler.cpp b/rpcs3/Input/keyboard_pad_handler.cpp index 9dcfa91c1b..0c4bb70c41 100644 --- a/rpcs3/Input/keyboard_pad_handler.cpp +++ b/rpcs3/Input/keyboard_pad_handler.cpp @@ -75,8 +75,25 @@ void keyboard_pad_handler::Key(const u32 code, bool pressed, u16 value) if (button.m_keyCode != code) continue; - button.m_pressed = pressed; - button.m_value = pressed ? value : 0; + button.m_actual_value = pressed ? value : 0; + + bool update_button = true; + + // to get the fastest response time possible we don't wanna use any lerp with factor 1 + if (button.m_analog) + { + update_button = m_analog_lerp_factor >= 1.0f; + } + else if (button.m_trigger) + { + update_button = m_trigger_lerp_factor >= 1.0f; + } + + if (update_button) + { + button.m_value = pressed ? value : 0; + button.m_pressed = pressed; + } } for (int i = 0; i < static_cast(pad->m_sticks.size()); i++) @@ -576,6 +593,8 @@ bool keyboard_pad_handler::bindPadToDevice(std::shared_ptr pad, const std:: m_multi_y = p_profile->mouse_acceleration_y / 100.0; m_l_stick_lerp_factor = p_profile->l_stick_lerp_factor / 100.0f; m_r_stick_lerp_factor = p_profile->r_stick_lerp_factor / 100.0f; + m_analog_lerp_factor = p_profile->analog_lerp_factor / 100.0f; + m_trigger_lerp_factor = p_profile->trigger_lerp_factor / 100.0f; auto find_key = [&](const cfg::string& name) { @@ -635,6 +654,63 @@ bool keyboard_pad_handler::bindPadToDevice(std::shared_ptr pad, const std:: void keyboard_pad_handler::ThreadProc() { + static const double mouse_interval = 30.0; + static const double stick_interval = 10.0; + static const double button_interval = 10.0; + + const auto now = std::chrono::steady_clock::now(); + + const double elapsed_left = std::chrono::duration_cast(now - m_last_mouse_move_left).count() / 1000.0; + const double elapsed_right = std::chrono::duration_cast(now - m_last_mouse_move_right).count() / 1000.0; + const double elapsed_up = std::chrono::duration_cast(now - m_last_mouse_move_up).count() / 1000.0; + const double elapsed_down = std::chrono::duration_cast(now - m_last_mouse_move_down).count() / 1000.0; + const double elapsed_stick = std::chrono::duration_cast(now - m_stick_time).count() / 1000.0; + const double elapsed_button = std::chrono::duration_cast(now - m_button_time).count() / 1000.0; + + const bool update_sticks = elapsed_stick > stick_interval; + const bool update_buttons = elapsed_button > button_interval; + + if (update_sticks) + { + m_stick_time = now; + } + + if (update_buttons) + { + m_button_time = now; + } + + // roughly 1-2 frames to process the next mouse move + if (elapsed_left > mouse_interval) + { + Key(mouse::move_left, false); + m_last_mouse_move_left = now; + } + if (elapsed_right > mouse_interval) + { + Key(mouse::move_right, false); + m_last_mouse_move_right = now; + } + if (elapsed_up > mouse_interval) + { + Key(mouse::move_up, false); + m_last_mouse_move_up = now; + } + if (elapsed_down > mouse_interval) + { + Key(mouse::move_down, false); + m_last_mouse_move_down = now; + } + + const auto get_lerped = [](f32 v0, f32 v1, f32 lerp_factor) + { + // linear interpolation from the current value v0 to the desired value v1 + const f32 res = std::lerp(v0, v1, lerp_factor); + + // round to the correct direction to prevent sticky values on small factors + return (v0 <= v1) ? std::ceil(res) : std::floor(res); + }; + for (uint i = 0; i < bindings.size(); i++) { if (last_connection_status[i] == false) @@ -646,41 +722,7 @@ void keyboard_pad_handler::ThreadProc() } else { - static std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); - now = std::chrono::steady_clock::now(); - - const double elapsed_left = std::chrono::duration_cast(now - m_last_mouse_move_left).count() / 1000.0; - const double elapsed_right = std::chrono::duration_cast(now - m_last_mouse_move_right).count() / 1000.0; - const double elapsed_up = std::chrono::duration_cast(now - m_last_mouse_move_up).count() / 1000.0; - const double elapsed_down = std::chrono::duration_cast(now - m_last_mouse_move_down).count() / 1000.0; - const double elapsed_stick = std::chrono::duration_cast(now - m_stick_time).count() / 1000.0; - - const double mouse_interval = 30.0; - const double stick_interval = 10.0; - - // roughly 1-2 frames to process the next mouse move - if (elapsed_left > mouse_interval) - { - Key(mouse::move_left, false); - m_last_mouse_move_left = now; - } - if (elapsed_right > mouse_interval) - { - Key(mouse::move_right, false); - m_last_mouse_move_right = now; - } - if (elapsed_up > mouse_interval) - { - Key(mouse::move_up, false); - m_last_mouse_move_up = now; - } - if (elapsed_down > mouse_interval) - { - Key(mouse::move_down, false); - m_last_mouse_move_down = now; - } - - if (elapsed_stick > stick_interval) + if (update_sticks) { for (int j = 0; j < static_cast(bindings[i]->m_sticks.size()); j++) { @@ -691,25 +733,50 @@ void keyboard_pad_handler::ThreadProc() { const f32 v0 = static_cast(bindings[i]->m_sticks[j].m_value); const f32 v1 = static_cast(m_stick_val[j]); - - // linear interpolation from the current stick value v0 to the desired stick value v1 - f32 res = std::lerp(v0, v1, stick_lerp_factor); - - // round to the correct direction to prevent sticky sticks on small factors - res = (v0 <= v1) ? std::ceil(res) : std::floor(res); + const f32 res = get_lerped(v0, v1, stick_lerp_factor); bindings[i]->m_sticks[j].m_value = static_cast(res); } } + } - m_stick_time = now; + if (update_buttons) + { + for (auto& button : bindings[i]->m_buttons) + { + if (button.m_analog) + { + // we already applied the following values on keypress if we used factor 1 + if (m_analog_lerp_factor < 1.0f) + { + const f32 v0 = static_cast(button.m_value); + const f32 v1 = static_cast(button.m_actual_value); + const f32 res = get_lerped(v0, v1, m_analog_lerp_factor); + + button.m_value = static_cast(res); + button.m_pressed = button.m_value > 0; + } + } + else if (button.m_trigger) + { + // we already applied the following values on keypress if we used factor 1 + if (m_trigger_lerp_factor < 1.0f) + { + const f32 v0 = static_cast(button.m_value); + const f32 v1 = static_cast(button.m_actual_value); + const f32 res = get_lerped(v0, v1, m_trigger_lerp_factor); + + button.m_value = static_cast(res); + button.m_pressed = button.m_value > 0; + } + } + } } } } // Releases the wheel buttons 0,1 sec after they've been triggered // Next activation is set to distant future to avoid activating this on every proc - const auto now = std::chrono::steady_clock::now(); const auto update_threshold = now - std::chrono::milliseconds(100); const auto distant_future = now + std::chrono::hours(24); if (update_threshold >= m_last_wheel_move_up) diff --git a/rpcs3/Input/keyboard_pad_handler.h b/rpcs3/Input/keyboard_pad_handler.h index 870bc1c099..21ad893a46 100644 --- a/rpcs3/Input/keyboard_pad_handler.h +++ b/rpcs3/Input/keyboard_pad_handler.h @@ -101,6 +101,11 @@ private: QWindow* m_target = nullptr; std::vector> bindings; + // Button Movements + std::chrono::steady_clock::time_point m_button_time; + f32 m_analog_lerp_factor = 1.0f; + f32 m_trigger_lerp_factor = 1.0f; + // Stick Movements std::chrono::steady_clock::time_point m_stick_time; f32 m_l_stick_lerp_factor = 1.0f;