Repository navigation
Conversation
Wheels like the Logitech G29 only support constant force on Linux, so the existing SDL rumble never played on them. Synthesize a speed dependent centering spring, damper, drift countersteer pull, offroad bumps and emulated rumble through a single constant force effect.
| SDL_HapticEffect effect = {}; | ||
| effect.type = SDL_HAPTIC_CONSTANT; | ||
| effect.constant.direction.type = SDL_HAPTIC_CARTESIAN; | ||
| effect.constant.direction.dir[0] = 1; |
There was a problem hiding this comment.
SDL_HapticDirection is where the force comes from, not where it pushes (see SDL_haptic.h). So dir = {1, 0, 0} with a positive level pushes the wheel to the left, the opposite of what setSteeringForce() documents. That flips the centering spring into an anti-spring, and the wheel would slam to full lock as soon as it leaves the center. force-feedback-invert can work around it, but the default should be right, and a first test on a 2+ Nm wheel shouldn't start with a runaway. Either use dir[0] = -1 or negate the level.
|
|
||
| // The raw input is used instead of KartControl::getSteer(), which lags | ||
| // behind the wheel position and would make the spring feel spongy. | ||
| const float wheel = -m_steer_val / 32767.0f; |
There was a problem hiding this comment.
m_steer_val has already gone through the gamepad deadzone, and it isn't rescaled afterwards. With the default deadzone of 4096 (12.5%), a 900° G29 reads 0 for about ±56° around center. In that range there's no centering force at all, and the spring jumps from 0 to ~0.09 at the edge. The value is also in game steering space, not device axis space, so a user who bound the axis the other way round gets the force sign flipped again.
Reading the raw position of the bound steering axis with SDL_JoystickGetAxis() in SDLController would avoid both problems. The spring would then work in the same frame as the force it outputs.
| // behind the wheel position and would make the spring feel spongy. | ||
| const float wheel = -m_steer_val / 32767.0f; | ||
| const float dt = stk_config->ticks2Time(ticks); | ||
| float wheel_speed = dt > 0.0f ? (wheel - m_ffb_prev_wheel) / dt : 0.0f; |
There was a problem hiding this comment.
Physics runs at 120 Hz, but input is only read once per frame. At 60 fps every frame runs two ticks: the first sees the whole movement within 1/120 s (2x the real speed), the second sees 0. Since setSteeringForce() keeps the last value, the damper never actually reaches the device at 60 fps, and at other frame rates it comes and goes from frame to frame. m_ffb_prev_wheel also goes stale over pause and rewinds, which gives a jolt on the first tick after them.
Computing the wheel speed in SDLController::updateForceFeedback() from the real frame time would fix all three.
| effect.constant.direction.dir[0] = 1; | ||
| effect.constant.length = SDL_HAPTIC_INFINITY; | ||
| effect.constant.level = (Sint16)(force * 32767); | ||
| SDL_HapticUpdateEffect(m_haptic, m_steering_effect, &effect); |
There was a problem hiding this comment.
On Linux, hid-lg4ff sits on ff-memless, which replays a playing effect when it's re-uploaded. So every call here becomes a USB output report. The frame rate isn't capped (vsync off, menus), so this can easily run at several hundred Hz while the noise and rumble change the level on every frame, and that fills the usbhid output queue (output queue full). I'd skip the update when the level hasn't changed and cap the update rate at something like 250-500 Hz.
| SDL_JoystickClose(m_joystick); | ||
| if (m_haptic) | ||
| { | ||
| stopSteeringEffect(); |
There was a problem hiding this comment.
This now stops and destroys the effect and sets autocenter after the joystick has already been closed above. SDL_HapticOpenFromJoystick() docs say to close the haptic device before the joystick, because on some backends closing the joystick frees the haptic device too. Move this block above the joystick close.
Fix the constant force direction, read the raw steering axis so the deadzone and axis binding don't affect the spring, compute the wheel speed from the real frame time, cap device uploads at 400 Hz and skip unchanged levels, and close the haptic device before the joystick.
Wheels like the Logitech G29 only support constant force with the mainline Linux driver, so the existing SDL rumble never played on them. This drives a single constant force effect with a speed dependent centering spring and damper, drift countersteer pull, offroad bumps and emulated rumble, and disables the device autocenter while racing. Not yet tested on hardware: if the force direction turns out reversed,
force-feedback-invertin the gamepad config flips it, andforce-feedback-strength(0-100) scales it.