From 632b0f489c851ba06acb32bbe6407fe5d8c6f201 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sat, 29 Dec 2018 15:56:18 +0100 Subject: [PATCH] Qt/Input: add keyboard stick interpolation --- rpcs3/Emu/Io/PadHandler.h | 3 + rpcs3/Json/tooltips.json | 14 +- rpcs3/keyboard_pad_handler.cpp | 64 ++++-- rpcs3/keyboard_pad_handler.h | 6 + rpcs3/rpcs3qt/pad_settings_dialog.cpp | 25 ++- rpcs3/rpcs3qt/pad_settings_dialog.ui | 294 +++++++++++++++++++++----- rpcs3/rpcs3qt/settings_dialog.ui | 16 +- 7 files changed, 338 insertions(+), 84 deletions(-) diff --git a/rpcs3/Emu/Io/PadHandler.h b/rpcs3/Emu/Io/PadHandler.h index f54e2bb677..59b5f3feae 100644 --- a/rpcs3/Emu/Io/PadHandler.h +++ b/rpcs3/Emu/Io/PadHandler.h @@ -340,6 +340,9 @@ struct pad_config final : cfg::node cfg::_int<0, 500> mouse_acceleration_x{ this, "Mouse Acceleration X Axis", 200 }; cfg::_int<0, 500> mouse_acceleration_y{ this, "Mouse Acceleration Y Axis", 250 }; + 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 }; + bool load() { if (fs::file cfg_file{ cfg_name, fs::read }) diff --git a/rpcs3/Json/tooltips.json b/rpcs3/Json/tooltips.json index 6930c0767f..9001db5569 100644 --- a/rpcs3/Json/tooltips.json +++ b/rpcs3/Json/tooltips.json @@ -67,17 +67,17 @@ "exitOnStop": "Automatically close RPCS3 when closing a game, or when a game closes itself.", "alwaysStart": "Leave this enabled unless you are a developer.", "startGameFullscreen": "Automatically puts the game window in fullscreen.\nDouble click on the game window or press alt+enter to toggle fullscreen and windowed mode.", - "showFPSInTitle": "Shows the framerate in the game window title. May cause buggy or outdated recording software to not notice RPCS3.", + "showFPSInTitle": "Shows the frame rate in the game window title. May cause buggy or outdated recording software to not notice RPCS3.", "gs_resizeOnBoot": "Automatically resizes the game window on boot.\nThis does not change the internal game resolution.", - "showTrophyPopups": "Show trophy popups when a trophy is unlocked.", - "gs_disableMouse": "Disables the activation of fullscreen mode per doubleclick while the game screen is active.\nCheck this if you want to play with mouse and keyboard (for example with UCR).", + "showTrophyPopups": "Show trophy pop-ups when a trophy is unlocked.", + "gs_disableMouse": "Disables the activation of fullscreen mode per double-click while the game screen is active.\nCheck this if you want to play with mouse and keyboard (for example with UCR).", "maxLLVMThreads": "Limits the maximum number of threads used for PPU Module compilation.\nLower this in order to increase performance of other open applications.\nThe default uses all available threads.", "showShaderCompilationHint": "Show shader compilation hints using the native overlay.", "useNativeInterface": "Enables use of native HUD within the game window that can interact with game controllers.\nWhen disabled, regular Qt dialogs are used instead.\nCurrently, only the English language is supported." }, "overlay": { "perfOverlayEnabled": "Enables or disables the performance overlay.", - "perfOverlayPosition": "Sets the on-screen position (quadrant) of the perfomance overlay.", + "perfOverlayPosition": "Sets the on-screen position (quadrant) of the performance overlay.", "perfOverlayDetailLevel": "Controls the amount of information displayed on the performance overlay.", "perfOverlayUpdateInterval": "Sets the time interval in which the performance overlay is being updated (measured in milliseconds).", "perfOverlayFontSize": "Sets the font size of the performance overlay (measured in pixels).", @@ -101,7 +101,7 @@ "graphicsAdapterBox": "On multi GPU systems select which GPU to use in RPCS3 when using Vulkan or DirectX 12.\nThis is not needed when using OpenGL.", "graphicsAdapterBox_Linux": "On multi GPU systems select which GPU to use in RPCS3 when using Vulkan.\nThis is not needed when using OpenGL.", "aspectBox": "Leave this on 16:9 unless you have a 4:3 monitor.\nAuto also works well, especially if you use a resolution that is not 720p.", - "frameLimitBox": "Off is the best option as it performs faster.\nUsing the frame limiter will add extra overhead and slow down the game.\nHowever, some games will crash if the framerate is too high.\nIf that happens, set value to anything other than Off.", + "frameLimitBox": "Off is the best option as it performs faster.\nUsing the frame limiter will add extra overhead and slow down the game.\nHowever, some games will crash if the frame rate is too high.\nIf that happens, set value to anything other than Off.", "anisotropicFilterOverride": "Higher values increase sharpness of textures on sloped surfaces at the cost of GPU resources.\nModern GPUs can handle this setting just fine even at 16x.\nKeep this on Automatic if you want to use the original setting used by a real PS3." }, "sliders": { @@ -127,7 +127,7 @@ }, "input": { "padHandlerBox": "If you want to use the keyboard to control, select the Keyboard option.\nIf you have a DualShock 4, select DualShock 4.\nIf you have an Xbox controller, or another compatible device, use XInput.\nOlder controllers such as PS2 controllers with an adapter usually work fine with MMJoystick.\nCheck button mappings in the Windows control panel.", - "padHandlerBox_Linux": "If you want to use the keyboard to control, select the Keyboard option.\nIf you have a DualShock 4, select DualShock 4.\nevdev input is WIP.", + "padHandlerBox_Linux": "If you want to use the keyboard to control, select the Keyboard option.\nIf you have a DualShock 4, select DualShock 4.", "keyboardHandlerBox": "Some games support native keyboard input.\nBasic will work in these cases.", "mouseHandlerBox": "Some games support native mouse input.\nBasic will work in these cases.", "cameraBox": "Camera support is not implemented, leave this on null.", @@ -141,6 +141,6 @@ "sysLangBox": "Some games may fail to boot if the system language is not available in the game itself.\nOther games will switch language automatically to what is selected here.\nIt is recommended leaving this on a language supported by the game.", "enterButtonAssignment": "The button used for enter/accept/confirm in system dialogs.\nChange this to use the circle button instead, which is the default configuration on Japanese systems and in many Japanese games.\nIn these cases having the cross button assigned can often lead to confusion.", "enableHostRoot": "Required for some Homebrew.\nIf unsure, don't use this option.", - "limitCacheSize": "Automatically removes older files from disk cache on boot if it grows larger than the specified value.\nGames can use the cache folder to temporarily store data outside of system memory. It is not used for long term storage." + "limitCacheSize": "Automatically removes older files from disk cache on boot if it grows larger than the specified value.\nGames can use the cache folder to temporarily store data outside of system memory. It is not used for long-term storage." } } diff --git a/rpcs3/keyboard_pad_handler.cpp b/rpcs3/keyboard_pad_handler.cpp index 31948e6c6c..99df8b8d54 100644 --- a/rpcs3/keyboard_pad_handler.cpp +++ b/rpcs3/keyboard_pad_handler.cpp @@ -89,7 +89,17 @@ void keyboard_pad_handler::Key(const u32 code, bool pressed, u16 value) m_stick_min[i] = pressed ? normalized_value : 0; if (is_max || is_min) - pad->m_sticks[i].m_value = m_stick_max[i] - m_stick_min[i]; + { + m_stick_val[i] = m_stick_max[i] - m_stick_min[i]; + + const f32 stick_lerp_factor = (i < 2) ? m_l_stick_lerp_factor : m_r_stick_lerp_factor; + + // to get the fastest response time possible we don't wanna use any lerp with factor 1 + if (stick_lerp_factor >= 1.0f) + { + pad->m_sticks[i].m_value = m_stick_val[i]; + } + } } } } @@ -515,8 +525,10 @@ bool keyboard_pad_handler::bindPadToDevice(std::shared_ptr pad, const std:: m_deadzone_x = p_profile->mouse_deadzone_x; m_deadzone_y = p_profile->mouse_deadzone_y; - m_multi_x = p_profile->mouse_acceleration_x / 100; - m_multi_y = p_profile->mouse_acceleration_y / 100; + m_multi_x = (double)p_profile->mouse_acceleration_x / 100.0; + m_multi_y = (double)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; auto find_key = [&](const cfg::string& name) { @@ -579,7 +591,6 @@ void keyboard_pad_handler::ThreadProc() { if (last_connection_status[i] == false) { - //QThread::msleep(100); // Hack Simpsons. It calls a seemingly useless cellPadGetInfo at boot that would swallow the first CELL_PAD_STATUS_ASSIGN_CHANGES otherwise bindings[i]->m_port_status |= CELL_PAD_STATUS_CONNECTED; bindings[i]->m_port_status |= CELL_PAD_STATUS_ASSIGN_CHANGES; last_connection_status[i] = true; @@ -590,32 +601,61 @@ void keyboard_pad_handler::ThreadProc() static std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); now = std::chrono::steady_clock::now(); - double elapsed_left = std::chrono::duration_cast(now - m_last_mouse_move_left).count() / 1000.0; - double elapsed_right = std::chrono::duration_cast(now - m_last_mouse_move_right).count() / 1000.0; - double elapsed_up = std::chrono::duration_cast(now - m_last_mouse_move_up).count() / 1000.0; - double elapsed_down = std::chrono::duration_cast(now - m_last_mouse_move_down).count() / 1000.0; + 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 > 30.0) + if (elapsed_left > mouse_interval) { Key(mouse::move_left, 0); m_last_mouse_move_left = now; } - if (elapsed_right > 30.0) + if (elapsed_right > mouse_interval) { Key(mouse::move_right, 0); m_last_mouse_move_right = now; } - if (elapsed_up> 30.0) + if (elapsed_up > mouse_interval) { Key(mouse::move_up, 0); m_last_mouse_move_up = now; } - if (elapsed_down > 30.0) + if (elapsed_down > mouse_interval) { Key(mouse::move_down, 0); m_last_mouse_move_down = now; } + + if (elapsed_stick > stick_interval) + { + for (int j = 0; j < static_cast(bindings[i]->m_sticks.size()); j++) + { + const f32 stick_lerp_factor = (j < 2) ? m_l_stick_lerp_factor : m_r_stick_lerp_factor; + + // we already applied the following values on keypress if we used factor 1 + if (stick_lerp_factor < 1.0f) + { + const f32 v0 = (f32)bindings[i]->m_sticks[j].m_value; + const f32 v1 = (f32)m_stick_val[j]; + + // linear interpolation from the current stick value v0 to the desired stick value v1 + f32 res = 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); + + bindings[i]->m_sticks[j].m_value = (u16)res; + } + } + + m_stick_time = now; + } } } } diff --git a/rpcs3/keyboard_pad_handler.h b/rpcs3/keyboard_pad_handler.h index 8ecbaf3c78..bef354253d 100644 --- a/rpcs3/keyboard_pad_handler.h +++ b/rpcs3/keyboard_pad_handler.h @@ -89,8 +89,14 @@ protected: private: QWindow* m_target = nullptr; std::vector> bindings; + + // Stick Movements + std::chrono::steady_clock::time_point m_stick_time; + f32 m_l_stick_lerp_factor = 1.0f; + f32 m_r_stick_lerp_factor = 1.0f; u8 m_stick_min[4] = { 0, 0, 0, 0 }; u8 m_stick_max[4] = { 128, 128, 128, 128 }; + u8 m_stick_val[4] = { 128, 128, 128, 128 }; // Mouse Movements std::chrono::steady_clock::time_point m_last_mouse_move_left; diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.cpp b/rpcs3/rpcs3qt/pad_settings_dialog.cpp index da8680fc3c..43a73c1e22 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/pad_settings_dialog.cpp @@ -478,12 +478,21 @@ void pad_settings_dialog::ReloadButtons() // Enable Mouse Acceleration std::vector mouse_accel_range_x = m_handler_cfg.mouse_acceleration_x.to_list(); - ui->mouse_accel_x->setRange(std::stod(mouse_accel_range_x.front()) / (double)100, std::stod(mouse_accel_range_x.back()) / (double)100); - ui->mouse_accel_x->setValue((double)m_handler_cfg.mouse_acceleration_x / (double)100); + ui->mouse_accel_x->setRange(std::stod(mouse_accel_range_x.front()) / 100.0, std::stod(mouse_accel_range_x.back()) / 100.0); + ui->mouse_accel_x->setValue((double)m_handler_cfg.mouse_acceleration_x / 100.0); std::vector mouse_accel_range_y = m_handler_cfg.mouse_acceleration_y.to_list(); - ui->mouse_accel_y->setRange(std::stod(mouse_accel_range_y.front()) / (double)100, std::stod(mouse_accel_range_y.back()) / (double)100); - ui->mouse_accel_y->setValue((double)m_handler_cfg.mouse_acceleration_y / (double)100); + ui->mouse_accel_y->setRange(std::stod(mouse_accel_range_y.front()) / 100.0, std::stod(mouse_accel_range_y.back()) / 100.0); + ui->mouse_accel_y->setValue((double)m_handler_cfg.mouse_acceleration_y / 100.0); + + // Enable Stick Lerp Factors + std::vector left_stick_lerp_range = m_handler_cfg.l_stick_lerp_factor.to_list(); + ui->left_stick_lerp->setRange(std::stod(left_stick_lerp_range.front()) / 100.0, std::stod(left_stick_lerp_range.back()) / 100.0); + ui->left_stick_lerp->setValue((double)m_handler_cfg.l_stick_lerp_factor / 100.0); + + std::vector right_stick_lerp_range = m_handler_cfg.r_stick_lerp_factor.to_list(); + ui->right_stick_lerp->setRange(std::stod(right_stick_lerp_range.front()) / 100.0, std::stod(right_stick_lerp_range.back()) / 100.0); + ui->right_stick_lerp->setValue((double)m_handler_cfg.r_stick_lerp_factor / 100.0); // Enable Vibration Checkboxes m_enable_rumble = m_handler->has_rumble(); @@ -726,6 +735,9 @@ void pad_settings_dialog::SwitchButtons(bool is_enabled) ui->gb_vibration->setEnabled(is_enabled && m_enable_rumble); ui->gb_sticks->setEnabled(is_enabled && m_enable_deadzones); ui->gb_triggers->setEnabled(is_enabled && m_enable_deadzones); + ui->gb_mouse_accel->setEnabled(is_enabled && m_handler->m_type == pad_handler::keyboard); + ui->gb_mouse_dz->setEnabled(is_enabled && m_handler->m_type == pad_handler::keyboard); + ui->gb_stick_lerp->setEnabled(is_enabled && m_handler->m_type == pad_handler::keyboard); for (int i = button_ids::id_pad_begin + 1; i < button_ids::id_pad_end; i++) { @@ -847,7 +859,8 @@ void pad_settings_dialog::ChangeInputType() const auto device_list = m_handler->ListDevices(); // change our contextual widgets - ui->stackedWidget->setCurrentIndex((m_handler->m_type == pad_handler::keyboard) ? 1 : 0); + ui->left_stack->setCurrentIndex((m_handler->m_type == pad_handler::keyboard) ? 1 : 0); + ui->right_stack->setCurrentIndex((m_handler->m_type == pad_handler::keyboard) ? 1 : 0); // Refill the device combobox with currently available devices switch (m_handler->m_type) @@ -1045,6 +1058,8 @@ void pad_settings_dialog::SaveProfile() m_handler_cfg.mouse_acceleration_y.set(ui->mouse_accel_y->value() * 100); m_handler_cfg.mouse_deadzone_x.set(ui->mouse_dz_x->value()); m_handler_cfg.mouse_deadzone_y.set(ui->mouse_dz_y->value()); + m_handler_cfg.l_stick_lerp_factor.set(ui->left_stick_lerp->value() * 100); + m_handler_cfg.r_stick_lerp_factor.set(ui->right_stick_lerp->value() * 100); } m_handler_cfg.save(); diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.ui b/rpcs3/rpcs3qt/pad_settings_dialog.ui index ed20ed6486..9307cee2bc 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.ui +++ b/rpcs3/rpcs3qt/pad_settings_dialog.ui @@ -626,7 +626,7 @@ - + 0 @@ -786,6 +786,18 @@ Mouse Acceleration + + 5 + + + 5 + + + 5 + + + 5 + @@ -794,7 +806,11 @@ - + + + 0.100000000000000 + + @@ -820,7 +836,11 @@ - + + + 0.100000000000000 + + @@ -847,6 +867,18 @@ Mouse Deadzone + + 5 + + + 5 + + + 5 + + + 5 + @@ -902,6 +934,22 @@ + + + + Qt::Vertical + + + QSizePolicy::MinimumExpanding + + + + 0 + 0 + + + + @@ -1933,60 +1981,202 @@ - - - Analog Deadzones + + + 0 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - - - Qt::Horizontal + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Analog Deadzones + + + + 5 - - - - - - + + 5 - - - - - - - - - - Qt::Horizontal + + 5 - - - - - - + + 5 - - - - - + + + + + + Qt::Horizontal + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + + + + + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Stick Interpolation + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + Left + + + + + + + 0.100000000000000 + + + + + + + Qt::Horizontal + + + QSizePolicy::MinimumExpanding + + + + 0 + 0 + + + + + + + + Right + + + + + + + 0.100000000000000 + + + + + + + Qt::Horizontal + + + QSizePolicy::MinimumExpanding + + + + 0 + 0 + + + + + + + + + + + Qt::Vertical + + + QSizePolicy::MinimumExpanding + + + + 0 + 0 + + + + + + diff --git a/rpcs3/rpcs3qt/settings_dialog.ui b/rpcs3/rpcs3qt/settings_dialog.ui index b80ff2bf2d..59e33d82d8 100644 --- a/rpcs3/rpcs3qt/settings_dialog.ui +++ b/rpcs3/rpcs3qt/settings_dialog.ui @@ -270,7 +270,7 @@ - + 0 @@ -349,7 +349,7 @@ - + Description @@ -388,7 +388,7 @@ - + Renderer @@ -400,7 +400,7 @@ - + Graphics Device @@ -414,7 +414,7 @@ - + Aspect Ratio @@ -426,7 +426,7 @@ - + Framelimit @@ -1155,7 +1155,7 @@ Disk cache - + @@ -2275,7 +2275,7 @@ - +