From e1ad33155d4c7f2e8dd371616e26e87ec0c66042 Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Mon, 16 Feb 2026 21:47:04 +0100 Subject: [PATCH] KBM -> joystick input smoothing v2 --- src/input/controller.cpp | 35 +++++++++++++++++++++++++++++++---- src/input/controller.h | 26 +++++++++++++++++++------- src/input/input_mouse.cpp | 8 ++++---- src/sdl_window.cpp | 5 +++-- 4 files changed, 57 insertions(+), 17 deletions(-) diff --git a/src/input/controller.cpp b/src/input/controller.cpp index afa531e71..3353ad3b7 100644 --- a/src/input/controller.cpp +++ b/src/input/controller.cpp @@ -28,7 +28,15 @@ void State::OnButton(OrbisPadButtonDataOffset button, bool isPressed) { } } -void State::OnAxis(Axis axis, int value) { +void State::OnAxis(Axis axis, int value, bool smooth) { + auto const i = std::to_underlying(axis); + // forcibly finish the previous smoothing task by jumping to the end + axes[i] = axis_smoothing_end_values[i]; + + axis_smoothing_start_times[i] = time; + axis_smoothing_start_values[i] = axes[i]; + axis_smoothing_end_values[i] = value; + axis_smoothing_flags[i] = smooth; const auto toggle = [&](const auto button) { if (value > 0) { buttonsState |= button; @@ -46,7 +54,6 @@ void State::OnAxis(Axis axis, int value) { default: break; } - axes[static_cast(axis)] = value; } void State::OnTouchpad(int touchIndex, bool isDown, float x, float y) { @@ -67,6 +74,22 @@ void State::OnAccel(const float accel[3]) { acceleration.z = accel[2]; } +void State::UpdateAxisSmoothing() { + for (int i = 0; i < std::to_underlying(Axis::AxisMax); i++) { + // if it's not to be smoothed or close enough, just jump to the end + if (!axis_smoothing_flags[i] || std::abs(axes[i] - axis_smoothing_end_values[i]) < 16) { + if (axes[i] != axis_smoothing_end_values[i]) { + axes[i] = axis_smoothing_end_values[i]; + } + continue; + } + auto now = Libraries::Kernel::sceKernelGetProcessTime(); + f32 t = std::clamp((now - axis_smoothing_start_times[i]) / f32{axis_smoothing_time}, + 0.f, 1.f); + axes[i] = s32(axis_smoothing_start_values[i] * (1 - t) + axis_smoothing_end_values[i] * t); + } +} + GameController::GameController() : m_states_queue(64) {} void GameController::ReadState(State* state, bool* isConnected, int* connectedCount) { @@ -99,8 +122,8 @@ void GameController::Button(OrbisPadButtonDataOffset button, bool is_pressed) { PushState(); } -void GameController::Axis(Input::Axis axis, int value) { - m_state.OnAxis(axis, value); +void GameController::Axis(Input::Axis axis, int value, bool smooth) { + m_state.OnAxis(axis, value, smooth); PushState(); } @@ -124,6 +147,10 @@ void GameController::UpdateAcceleration(const float acceleration[3]) { std::memcpy(accel_buf, acceleration, sizeof(accel_buf)); } +void GameController::UpdateAxisSmoothing() { + m_state.UpdateAxisSmoothing(); +} + void GameController::CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration, Libraries::Pad::OrbisFVector3& angularVelocity, float deltaTime, diff --git a/src/input/controller.h b/src/input/controller.h index 9360b23e1..d91a1bb68 100644 --- a/src/input/controller.h +++ b/src/input/controller.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include #include @@ -11,6 +12,7 @@ #include "common/assert.h" #include "common/types.h" #include "core/libraries/pad/pad.h" +#include "core/libraries/system/userservice.h" struct SDL_Gamepad; @@ -35,15 +37,27 @@ struct TouchpadEntry { }; struct State { +private: + template + using AxisArray = std::array; + static constexpr AxisArray axis_defaults{128, 128, 128, 128, 0, 0}; + static constexpr u64 axis_smoothing_time{33000}; + AxisArray axis_smoothing_flags{true}; + AxisArray axis_smoothing_start_times{0}; + AxisArray axis_smoothing_start_values{axis_defaults}; + AxisArray axis_smoothing_end_values{axis_defaults}; + + public: void OnButton(Libraries::Pad::OrbisPadButtonDataOffset, bool); - void OnAxis(Axis, int); + void OnAxis(Axis, int, bool smooth = true); void OnTouchpad(int touchIndex, bool isDown, float x, float y); void OnGyro(const float[3]); void OnAccel(const float[3]); + void UpdateAxisSmoothing(); Libraries::Pad::OrbisPadButtonDataOffset buttonsState{}; u64 time = 0; - int axes[static_cast(Axis::AxisMax)] = {128, 128, 128, 128, 0, 0}; + AxisArray axes{axis_defaults}; TouchpadEntry touchpad[2] = {{false, 0, 0}, {false, 0, 0}}; Libraries::Pad::OrbisFVector3 acceleration = {0.0f, -9.81f, 0.0f}; Libraries::Pad::OrbisFVector3 angularVelocity = {0.0f, 0.0f, 0.0f}; @@ -97,11 +111,12 @@ public: int ReadStates(State* states, int states_num, bool* isConnected, int* connectedCount); void Button(Libraries::Pad::OrbisPadButtonDataOffset button, bool isPressed); - void Axis(Input::Axis axis, int value); + void Axis(Input::Axis axis, int value, bool smooth = true); void Gyro(int id); void Acceleration(int id); void UpdateGyro(const float gyro[3]); void UpdateAcceleration(const float acceleration[3]); + void UpdateAxisSmoothing(); void SetLightBarRGB(u8 r, u8 g, u8 b); bool SetVibration(u8 smallMotor, u8 largeMotor); void SetTouchpadState(int touchIndex, bool touchDown, float x, float y); @@ -128,11 +143,8 @@ public: float gyro_poll_rate; float accel_poll_rate; float gyro_buf[3] = {0.0f, 0.0f, 0.0f}, accel_buf[3] = {0.0f, 9.81f, 0.0f}; - u32 user_id = -1; // ORBIS_USER_SERVICE_USER_ID_INVALID + u32 user_id = Libraries::UserService::ORBIS_USER_SERVICE_USER_ID_INVALID; SDL_Gamepad* m_sdl_gamepad = nullptr; - static constexpr int max_smoothing_ticks = 2; - int axis_smoothing_ticks[static_cast(Input::Axis::AxisMax)]{0}; - int axis_smoothing_values[static_cast(Input::Axis::AxisMax)]{0}; private: void PushState(); diff --git a/src/input/input_mouse.cpp b/src/input/input_mouse.cpp index b6ff66c37..f90c20484 100644 --- a/src/input/input_mouse.cpp +++ b/src/input/input_mouse.cpp @@ -77,11 +77,11 @@ void EmulateJoystick(GameController* controller, u32 interval) { float a_x = cos(angle) * output_speed, a_y = sin(angle) * output_speed; if (d_x != 0 || d_y != 0) { - controller->Axis(axis_x, GetAxis(-0x80, 0x7f, a_x)); - controller->Axis(axis_y, GetAxis(-0x80, 0x7f, a_y)); + controller->Axis(axis_x, GetAxis(-0x80, 0x7f, a_x), false); + controller->Axis(axis_y, GetAxis(-0x80, 0x7f, a_y), false); } else { - controller->Axis(axis_x, GetAxis(-0x80, 0x7f, 0)); - controller->Axis(axis_y, GetAxis(-0x80, 0x7f, 0)); + controller->Axis(axis_x, GetAxis(-0x80, 0x7f, 0), false); + controller->Axis(axis_y, GetAxis(-0x80, 0x7f, 0), false); } } diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index 150b9829c..6143bdcbd 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -68,8 +68,9 @@ static OrbisPadButtonDataOffset SDLGamepadToOrbisButton(u8 button) { } } -static Uint32 SDLCALL PollGyroAndAccel(void* userdata, SDL_TimerID timer_id, Uint32 interval) { +static Uint32 SDLCALL PollController(void* userdata, SDL_TimerID timer_id, Uint32 interval) { auto* controller = reinterpret_cast(userdata); + controller->UpdateAxisSmoothing(); controller->Gyro(0); controller->Acceleration(0); return 4; @@ -280,7 +281,7 @@ void WindowSDL::WaitEvent() { void WindowSDL::InitTimers() { for (int i = 0; i < 4; ++i) { - SDL_AddTimer(4, &PollGyroAndAccel, controllers[i]); + SDL_AddTimer(4, &PollController, controllers[i]); } SDL_AddTimer(33, Input::MousePolling, (void*)controllers[0]); }