KBM -> joystick input smoothing v2

This commit is contained in:
kalaposfos13 2026-02-16 21:47:04 +01:00
parent d0ea7f04c4
commit e1ad33155d
4 changed files with 57 additions and 17 deletions

View File

@ -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<int>(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,

View File

@ -4,6 +4,7 @@
#pragma once
#include <mutex>
#include <utility>
#include <vector>
#include <SDL3/SDL_gamepad.h>
@ -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 <typename T>
using AxisArray = std::array<T, std::to_underlying(Axis::AxisMax)>;
static constexpr AxisArray<s32> axis_defaults{128, 128, 128, 128, 0, 0};
static constexpr u64 axis_smoothing_time{33000};
AxisArray<bool> axis_smoothing_flags{true};
AxisArray<u64> axis_smoothing_start_times{0};
AxisArray<int> axis_smoothing_start_values{axis_defaults};
AxisArray<int> 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<int>(Axis::AxisMax)] = {128, 128, 128, 128, 0, 0};
AxisArray<s32> 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<int>(Input::Axis::AxisMax)]{0};
int axis_smoothing_values[static_cast<int>(Input::Axis::AxisMax)]{0};
private:
void PushState();

View File

@ -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);
}
}

View File

@ -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<Input::GameController*>(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]);
}