From a71395e7a871162ae6370c17bc72ec462a530480 Mon Sep 17 00:00:00 2001 From: minenice55 Date: Wed, 25 Feb 2026 11:26:45 -0500 Subject: [PATCH 01/22] prep for accelerometer control --- src/d_ticcmd.h | 1 + src/g_game.c | 15 +++++++++++++++ src/g_input.h | 2 +- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/d_ticcmd.h b/src/d_ticcmd.h index a5f700ad2..023ffcd74 100644 --- a/src/d_ticcmd.h +++ b/src/d_ticcmd.h @@ -67,6 +67,7 @@ struct ticcmd_t INT16 angle; // Predicted angle, use me if you can! INT16 throwdir; // Aiming direction INT16 aiming; // vertical aiming, see G_BuildTicCmd + INT16 tilt; // controller tilt UINT16 buttons; UINT8 latency; // Netgames: how many tics ago was this ticcmd generated from this player's end? UINT8 flags; diff --git a/src/g_game.c b/src/g_game.c index 07695fd2f..3730a0cd3 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1410,10 +1410,12 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) static INT32 turnheld[MAXSPLITSCREENPLAYERS]; // for accelerative turning static boolean resetdown[MAXSPLITSCREENPLAYERS]; // don't cam reset every frame + static boolean joystickactive[MAXSPLITSCREENPLAYERS]; // don't use accelerometer tilt if we're alrady turning with stick INT32 forward, side, tspeed; joystickvector2_t joystickvector; + INT16 accelerometertilt; // you'd BETTER not touch the player while freecamming... player_t *player = &players[g_localplayers[forplayer]]; @@ -1455,6 +1457,15 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) joystickvector.yaxis = 0; G_HandleAxisDeadZone(forplayer, &joystickvector); + if (joystickvector.xaxis != 0) + { + joystickactive[ssplayer] = true; + } + else if (!joystickactive[ssplayer]) + { + + } + // For kart, I've turned the aim axis into a digital axis because we only // use it for aiming to throw items forward/backward and the vote screen // This mean that the turn axis will still be gradient but up/down will be 0 @@ -1464,6 +1475,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) if (encoremode) { joystickvector.xaxis = -joystickvector.xaxis; + accelerometertilt = -accelerometertilt; } forward = side = 0; @@ -1487,7 +1499,10 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) } } else + { turnheld[forplayer] = 0; + joystickactive[ssplayer] = false; + } cmd->turning = 0; diff --git a/src/g_input.h b/src/g_input.h index aa3d27e1e..c085bb9ac 100644 --- a/src/g_input.h +++ b/src/g_input.h @@ -94,7 +94,7 @@ typedef enum gc_respawn, gc_director, gc_horncode, - num_gamecontrols + gc_gamepadtilt, } gamecontrols_e; // mouse values are used once From e87a1353155d6b1a099e2f1749aa37b7aeeb4c13 Mon Sep 17 00:00:00 2001 From: minenice55 Date: Wed, 25 Feb 2026 19:01:31 -0500 Subject: [PATCH 02/22] holy math --- src/d_netcmd.c | 1 + src/d_ticcmd.h | 1 + src/g_game.c | 115 +++++++++++++++++++++++++++++++++++++++++++++---- src/g_game.h | 11 +++++ src/g_input.c | 19 ++++++++ src/g_input.h | 3 +- 6 files changed, 140 insertions(+), 10 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 40f807536..d7521a0b6 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1415,6 +1415,7 @@ void D_RegisterClientCommands(void) CV_RegisterVar(&cv_controllerrumble[i]); CV_RegisterVar(&cv_rumblestrength[i]); CV_RegisterVar(&cv_controllerled[i]); + CV_RegisterVar(&cv_tiltcontrol[i]); } // filesrch.c diff --git a/src/d_ticcmd.h b/src/d_ticcmd.h index 023ffcd74..398a816a6 100644 --- a/src/d_ticcmd.h +++ b/src/d_ticcmd.h @@ -58,6 +58,7 @@ typedef enum #define TICCMD_RECEIVED (0x01) /* Actual tic recieved from client */ #define TICCMD_TYPING (0x02) /* chat window or console open */ #define TICCMD_KEYSTROKE (0x04) /* chat character input */ +#define TICCMD_EXCESSTILT (0x08) /* player tilting controller too far */ struct ticcmd_t { diff --git a/src/g_game.c b/src/g_game.c index 3730a0cd3..2280ac31b 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1306,6 +1306,44 @@ retrygetcontrol: return 0; } +vector3_t G_PlayerInputSensor(UINT8 p, motionsensortype_e sensor) +{ + vector3_t out = {0}; + INT32 deviceID; + + if (p >= MAXSPLITSCREENPLAYERS) + { +#ifdef PARANOIA + CONS_Debug(DBG_GAMELOGIC, "G_PlayerInputAnalog: Invalid player ID %d\n", p); +#endif + return out; + } + + deviceID = I_GetControllerSlotfromID(I_GetControllerIDForPlayer(p)); + if (deviceID >= MAXDEVICES) + return out; + + if (deviceID == INVALID_DEVICE || deviceID == KEYBOARD_MOUSE_DEVICE) + return out; + + switch (sensor) + { + case ACCELEROMETER: + FV3_Load(&out, + gamekeydown[deviceID][KEY_ACCELEROMETER1+0], + gamekeydown[deviceID][KEY_ACCELEROMETER1+1], + gamekeydown[deviceID][KEY_ACCELEROMETER1+2] + ); + case GYROSCOPE: + FV3_Load(&out, + gamekeydown[deviceID][KEY_GYROSCOPE1+0], + gamekeydown[deviceID][KEY_GYROSCOPE1+1], + gamekeydown[deviceID][KEY_GYROSCOPE1+2] + ); + } + return out; +} + boolean G_PlayerInputDown(UINT8 p, INT32 gc, boolean digital, SINT8 type) { return (G_PlayerInputAnalog(p, gc, digital, type) != 0); @@ -1379,6 +1417,54 @@ static void G_HandleAxisDeadZone(UINT8 splitnum, joystickvector2_t *joystickvect } } +// copy/pasted from the lua version of this routine +inline static vector4_t AngleAxis(angle_t angle, vector3_t *axis) +{ + fixed_t cosangle = FINECOSINE(((angle / 2) >> ANGLETOFINESHIFT) & FINEMASK); + fixed_t sinangle = FINESINE(((angle / 2) >> ANGLETOFINESHIFT) & FINEMASK); + vector4_t result; + vector3_t normaxis; + + FV3_NormalizeEx(axis, &normaxis); + + FV4_Load(&result, + FixedMul(normaxis.x, sinangle), + FixedMul(normaxis.y, sinangle), + FixedMul(normaxis.z, sinangle), + cosangle + ); + + return result; +} + +// math sourced from this article +// http://gyrowiki.jibbsmart.com/blog:finding-gravity-with-sensor-fusion +vector3_t localgravityvectors[MAXSPLITSCREENPLAYERS]; +vector3_t G_UpdateGamepadGravity(INT32 p, vector3_t gyro, vector3_t accel) +{ + const vector3_t invGyro = {-gyro.x, -gyro.y, -gyro.z}; + const vector3_t newGravity = {-accel.x, -accel.y, -accel.z}; + const vector4_t invRotation = AngleAxis(FixedMul(FV3_Magnitude(&gyro), FRACUNIT/TICRATE), &invGyro); + vector3_t gravityDelta; + fixed_t correctionRate = FRACUNIT/8; + + FV3_Mul(&localgravityvectors[p], &invRotation); + FV3_SubEx(&gravityDelta, &newGravity, &localgravityvectors[p]); + FV3_Load(&localgravityvectors[p], + FixedMul(gravityDelta.x, FRACUNIT/8), + FixedMul(gravityDelta.y, FRACUNIT/8), + FixedMul(gravityDelta.z, FRACUNIT/8) + ); + + return localgravityvectors[p]; +} + +vector3_t G_GetGamepadTilt(INT32 p, vector3_t gravity) +{ + vector3_t out; + return out; +} + // // G_BuildTiccmd // Builds a ticcmd from all of the available inputs @@ -1415,6 +1501,8 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) INT32 forward, side, tspeed; joystickvector2_t joystickvector; + + vector3_t gravity = {0}; INT16 accelerometertilt; // you'd BETTER not touch the player while freecamming... @@ -1440,6 +1528,21 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) break; } + if (K_PlayerUsesBotMovement(player)) + { + // Bot ticcmd is generated by K_BuildBotTiccmd + return; + } + + // update our gamepad gravity when applicable + // this should always execute so we have an accurate state + if (true) //todo: check if gamepad supports accel or accel and gyro + { + vector3_t accel = G_PlayerInputSensor(forplayer, ACCELEROMETER); + vector3_t gyro = G_PlayerInputSensor(forplayer, GYROSCOPE); + gravity = G_UpdateGamepadGravity(forplayer, gyro, accel); + } + // why build a ticcmd if we're paused? // Or, for that matter, if we're being reborn. if (!thiscam->freecam && (paused || P_AutoPause() || (gamestate == GS_LEVEL && player->playerstate == PST_REBORN))) @@ -1447,21 +1550,15 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) return; } - if (K_PlayerUsesBotMovement(player)) - { - // Bot ticcmd is generated by K_BuildBotTiccmd - return; - } - joystickvector.xaxis = G_PlayerInputAnalog(forplayer, gc_turnright, false, DEADZONE_X) - G_PlayerInputAnalog(forplayer, gc_turnleft, false, DEADZONE_X); joystickvector.yaxis = 0; G_HandleAxisDeadZone(forplayer, &joystickvector); if (joystickvector.xaxis != 0) { - joystickactive[ssplayer] = true; + joystickactive[forplayer] = true; } - else if (!joystickactive[ssplayer]) + else if (!joystickactive[forplayer]) { } @@ -1501,7 +1598,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) else { turnheld[forplayer] = 0; - joystickactive[ssplayer] = false; + joystickactive[forplayer] = false; } cmd->turning = 0; diff --git a/src/g_game.h b/src/g_game.h index 929ec1892..8ef08e215 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -135,6 +135,17 @@ typedef enum DEADZONE_BUTTON, } analogdeadzone_e; +typedef enum +{ + ACCELEROMETER, + GYROSCOPE, +} motionsensortype_e; + +#define MAXGAMEPADTILT (ANG30) +#define ACCEL_GRAVITY (FLOAT_TO_FIXED(9.80665f)) +vector3_t G_UpdateGamepadGravity(INT32 p, vector3_t gyro, vector3_t accel); +vector3_t G_GetGamepadTilt(INT32 p, vector3_t gravity); + fixed_t G_GetDeadZoneType(INT32 p, SINT8 type); INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, boolean digital, SINT8 type); boolean G_PlayerInputDown(UINT8 p, INT32 gc, boolean digital, SINT8 type); diff --git a/src/g_input.c b/src/g_input.c index 2aa6cab8d..805417e52 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -90,6 +90,13 @@ consvar_t cv_controllerled[MAXSPLITSCREENPLAYERS] = { CVAR_INIT ("gamepadled4", "Skincolor", CV_SAVE|CV_CALL|CV_NOINIT, controllerled_cons_t, led_off_handle4) }; +consvar_t cv_tiltcontrol[MAXSPLITSCREENPLAYERS] = { + CVAR_INIT ("tiltcontrol", "Off", CV_SAVE|CV_CALL|CV_NOINIT, CV_OnOff, NULL), + CVAR_INIT ("tiltcontrol2", "Off", CV_SAVE|CV_CALL|CV_NOINIT, CV_OnOff, NULL), + CVAR_INIT ("tiltcontrol3", "Off", CV_SAVE|CV_CALL|CV_NOINIT, CV_OnOff, NULL), + CVAR_INIT ("tiltcontrol4", "Off", CV_SAVE|CV_CALL|CV_NOINIT, CV_OnOff, NULL) +}; + static void rumble_off_handle(void) { if (cv_controllerrumble[0].value == 0) @@ -333,6 +340,18 @@ void G_MapEventsToControls(event_t *ev) gamekeydown[device][i] = max(0, ev->data2); break; + + case ev_accelerometer: + gamekeydown[device][KEY_ACCELEROMETER1+0] = ev->data1; + gamekeydown[device][KEY_ACCELEROMETER1+1] = ev->data2; + gamekeydown[device][KEY_ACCELEROMETER1+2] = ev->data3; + break; + + case ev_gyroscope: + gamekeydown[device][KEY_GYROSCOPE1+0] = ev->data1; + gamekeydown[device][KEY_GYROSCOPE1+1] = ev->data2; + gamekeydown[device][KEY_GYROSCOPE1+2] = ev->data3; + break; default: break; diff --git a/src/g_input.h b/src/g_input.h index c085bb9ac..e34e1c251 100644 --- a/src/g_input.h +++ b/src/g_input.h @@ -94,7 +94,7 @@ typedef enum gc_respawn, gc_director, gc_horncode, - gc_gamepadtilt, + num_gamecontrols } gamecontrols_e; // mouse values are used once @@ -106,6 +106,7 @@ extern consvar_t cv_turnsmooth[MAXSPLITSCREENPLAYERS]; extern consvar_t cv_controllerrumble[MAXSPLITSCREENPLAYERS]; extern consvar_t cv_rumblestrength[MAXSPLITSCREENPLAYERS]; extern consvar_t cv_controllerled[MAXSPLITSCREENPLAYERS]; +extern consvar_t cv_tiltcontrol[MAXSPLITSCREENPLAYERS]; // current state of the keys: JOYAXISRANGE or 0 when boolean. // Or anything inbetween for analog values From db22fc9c46646fe27a92cf762b96def9849d5e81 Mon Sep 17 00:00:00 2001 From: minenice55 Date: Wed, 25 Feb 2026 19:06:53 -0500 Subject: [PATCH 03/22] Update g_game.c --- src/g_game.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 2280ac31b..e20c5ba00 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1445,15 +1445,15 @@ vector3_t G_UpdateGamepadGravity(INT32 p, vector3_t gyro, vector3_t accel) const vector3_t invGyro = {-gyro.x, -gyro.y, -gyro.z}; const vector3_t newGravity = {-accel.x, -accel.y, -accel.z}; const vector4_t invRotation = AngleAxis(FixedMul(FV3_Magnitude(&gyro), FRACUNIT/TICRATE), &invGyro); + const fixed_t correctionRate = FRACUNIT/8; vector3_t gravityDelta; - fixed_t correctionRate = FRACUNIT/8; FV3_Mul(&localgravityvectors[p], &invRotation); FV3_SubEx(&gravityDelta, &newGravity, &localgravityvectors[p]); FV3_Load(&localgravityvectors[p], - FixedMul(gravityDelta.x, FRACUNIT/8), - FixedMul(gravityDelta.y, FRACUNIT/8), - FixedMul(gravityDelta.z, FRACUNIT/8) + FixedMul(gravityDelta.x, correctionRate), + FixedMul(gravityDelta.y, correctionRate), + FixedMul(gravityDelta.z, correctionRate) ); return localgravityvectors[p]; From 0cfb873f4ff538e89c30da556b8fde8fe5bb59e9 Mon Sep 17 00:00:00 2001 From: minenice55 Date: Wed, 25 Feb 2026 19:12:42 -0500 Subject: [PATCH 04/22] convert accelerometer data to gs --- src/sdl/i_video.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sdl/i_video.cpp b/src/sdl/i_video.cpp index 695873dad..bf089779e 100644 --- a/src/sdl/i_video.cpp +++ b/src/sdl/i_video.cpp @@ -1055,17 +1055,19 @@ static void Impl_HandleControllerSensorEvent(SDL_GamepadSensorEvent evt) // data[0]: x acceleration in m/s // data[1]: y acceleration in m/s // data[2]: z acceleration in m/s + // we convert to gs before passing it to the event case SDL_SENSOR_ACCEL: event.type = ev_accelerometer; - event.data1 = FLOAT_TO_FIXED(evt.data[0]); - event.data2 = FLOAT_TO_FIXED(evt.data[1]); - event.data3 = FLOAT_TO_FIXED(evt.data[2]); + event.data1 = FLOAT_TO_FIXED(evt.data[0] / SDL_STANDARD_GRAVITY); + event.data2 = FLOAT_TO_FIXED(evt.data[1] / SDL_STANDARD_GRAVITY); + event.data3 = FLOAT_TO_FIXED(evt.data[2] / SDL_STANDARD_GRAVITY); D_PostEvent(&event); return; // data[0]: delta pitch in rad/s // data[1]: delta yaw in rad/s // data[2]: delta roll in rad/s + // we convert to degrees per second before passing it to the event case SDL_SENSOR_GYRO: #define RAD2DEG 57.295779513f event.type = ev_gyroscope; From a7ddeddbe41e5c018b43cf06e631c74f4d9dd5d8 Mon Sep 17 00:00:00 2001 From: minenice55 Date: Thu, 26 Feb 2026 02:20:10 -0500 Subject: [PATCH 05/22] get sensor fusion and basic tilt steering working --- src/doomdef.h | 2 +- src/g_game.c | 275 ++++++++++++++++++++++++++++++++++++++------ src/g_game.h | 7 +- src/g_input.c | 18 +-- src/g_input.h | 11 +- src/m_cheat.c | 5 + src/sdl/i_gamepad.c | 14 ++- src/sdl/i_video.cpp | 8 +- src/st_stuff.c | 31 +++++ 9 files changed, 310 insertions(+), 61 deletions(-) diff --git a/src/doomdef.h b/src/doomdef.h index 714f743df..ff98a7d6a 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -370,7 +370,7 @@ typedef enum DBG_PLAYER = 0x00000004, DBG_RENDER = 0x00000008, DBG_MUSIC = 0x00000010, - //DBG_NIGHTS = 0x00000020, // free + DBG_IMU = 0x00000020, DBG_POLYOBJ = 0x00000040, DBG_GAMELOGIC = 0x00000080, DBG_NETPLAY = 0x00000100, diff --git a/src/g_game.c b/src/g_game.c index e20c5ba00..4484dbd07 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -51,6 +51,7 @@ #include "m_cond.h" // condition sets #include "r_fps.h" // frame interpolation/uncapped #include "lua_hud.h" +#include "m_easing.h" // SRB2kart #include "k_kart.h" @@ -1334,12 +1335,14 @@ vector3_t G_PlayerInputSensor(UINT8 p, motionsensortype_e sensor) gamekeydown[deviceID][KEY_ACCELEROMETER1+1], gamekeydown[deviceID][KEY_ACCELEROMETER1+2] ); + break; case GYROSCOPE: FV3_Load(&out, gamekeydown[deviceID][KEY_GYROSCOPE1+0], gamekeydown[deviceID][KEY_GYROSCOPE1+1], gamekeydown[deviceID][KEY_GYROSCOPE1+2] ); + break; } return out; } @@ -1417,20 +1420,61 @@ static void G_HandleAxisDeadZone(UINT8 splitnum, joystickvector2_t *joystickvect } } -// copy/pasted from the lua version of this routine -inline static vector4_t AngleAxis(angle_t angle, vector3_t *axis) +// copy/pasted from the lua version of these routines +static vector4_t *QuaternionMul(vector4_t *out, vector4_t *a, vector4_t *b) { - fixed_t cosangle = FINECOSINE(((angle / 2) >> ANGLETOFINESHIFT) & FINEMASK); - fixed_t sinangle = FINESINE(((angle / 2) >> ANGLETOFINESHIFT) & FINEMASK); - vector4_t result; - vector3_t normaxis; + fixed_t ax = a->x, ay = a->y, az = a->z, aw = a->a; + fixed_t bx = b->x, by = b->y, bz = b->z, bw = b->a; - FV3_NormalizeEx(axis, &normaxis); + FV4_NormalizeEx(out, FV4_Load(out, + FixedMul(aw, bx) + FixedMul(ax, bw) + FixedMul(ay, bz) - FixedMul(az, by), + FixedMul(aw, by) - FixedMul(ax, bz) + FixedMul(ay, bw) + FixedMul(az, bx), + FixedMul(aw, bz) + FixedMul(ax, by) - FixedMul(ay, bx) + FixedMul(az, bw), + FixedMul(aw, bw) - FixedMul(ax, bx) - FixedMul(ay, by) - FixedMul(az, bz) + )); + + return out; +} + +static vector3_t *QuaternionMulVec3(vector3_t *out, vector3_t *a, vector4_t *b) +{ + fixed_t ax = a->x, ay = a->y, az = a->z, aw = 0; + fixed_t bx = b->x, by = b->y, bz = b->z, bw = b->a; + + FV3_NormalizeEx(out, FV3_Load(out, + FixedMul(aw, bx) + FixedMul(ax, bw) + FixedMul(ay, bz) - FixedMul(az, by), + FixedMul(aw, by) - FixedMul(ax, bz) + FixedMul(ay, bw) + FixedMul(az, bx), + FixedMul(aw, bz) + FixedMul(ax, by) - FixedMul(ay, bx) + FixedMul(az, bw) + )); + + return out; +} + +static vector4_t *QuaternionInvert(vector4_t *out) +{ + FV4_Load(out, + -out->x, + -out->y, + -out->z, + out->a + ); + + return out; +} + +inline static vector4_t AngleAxis(fixed_t angle, fixed_t x, fixed_t y, fixed_t z) +{ + fixed_t sinangle = FINESINE(FixedAngle(angle/2) >> ANGLETOFINESHIFT); + fixed_t cosangle = FINECOSINE(FixedAngle(angle/2) >> ANGLETOFINESHIFT); + vector3_t axis = {x, y, z}; + vector4_t result; + + FV3_Normalize(&axis); FV4_Load(&result, - FixedMul(normaxis.x, sinangle), - FixedMul(normaxis.y, sinangle), - FixedMul(normaxis.z, sinangle), + FixedMul(axis.x, sinangle), + FixedMul(axis.y, sinangle), + FixedMul(axis.z, sinangle), cosangle ); @@ -1439,32 +1483,177 @@ inline static vector4_t AngleAxis(angle_t angle, vector3_t *axis) // math sourced from this article // http://gyrowiki.jibbsmart.com/blog:finding-gravity-with-sensor-fusion +#define Interpolator (FRACUNIT/4) +// thresholds of trust for accel shakiness. less shakiness = more trust +#define ShakinessMaxThreshold (4*FRACUNIT/10) +#define ShakinessMinThreshold (FRACUNIT/10) +// when we trust the accel a lot (the controller is "still"), how quickly do we correct our gravity vector? +#define CorrectionStillRate (FRACUNIT) +// when we don't trust the accel (the controller is "shaky"), how quickly do we correct our gravity vector? +#define CorrectionShakyRate (FRACUNIT/10) +// if our old gravity vector is close enough to our new one, limit further corrections to this proportion of the rotation speed +#define CorrectionGyroFactor (FRACUNIT/10) +// thresholds for what's considered "close enough" +#define CorrectionGyroMinThreshold (5*FRACUNIT/100) +#define CorrectionGyroMaxThreshold (FRACUNIT/4) +// no matter what, always apply a minimum of this much correction to our gravity vector +#define CorrectionMinimumSpeed (FRACUNIT/10) + +// state +fixed_t localshakinessfac[MAXSPLITSCREENPLAYERS]; +vector3_t localsmoothedaccel[MAXSPLITSCREENPLAYERS]; vector3_t localgravityvectors[MAXSPLITSCREENPLAYERS]; -vector3_t G_UpdateGamepadGravity(INT32 p, vector3_t gyro, vector3_t accel) +void G_UpdateGamepadGravity(INT32 p, vector3_t gyro, vector3_t accel) { - const vector3_t invGyro = {-gyro.x, -gyro.y, -gyro.z}; - const vector3_t newGravity = {-accel.x, -accel.y, -accel.z}; - const vector4_t invRotation = AngleAxis(FixedMul(FV3_Magnitude(&gyro), FRACUNIT/TICRATE), &invGyro); - const fixed_t correctionRate = FRACUNIT/8; - vector3_t gravityDelta; - - FV3_Mul(&localgravityvectors[p], &invRotation); - FV3_SubEx(&gravityDelta, &newGravity, &localgravityvectors[p]); - FV3_Load(&localgravityvectors[p], - FixedMul(gravityDelta.x, correctionRate), - FixedMul(gravityDelta.y, correctionRate), - FixedMul(gravityDelta.z, correctionRate) + // convert gyro input to reverse rotation + const vector3_t invAccel = {-accel.x, -accel.y, -accel.z}; + fixed_t correctionRate = CorrectionMinimumSpeed; + fixed_t correctionMagnitude, gravityDeltaMagnitude, angleRate, correctionLimit; + vector4_t invRotation = AngleAxis( + -gyro.x, + -gyro.y, + -gyro.z, + FixedMul(FV3_Magnitude(&gyro), FRACUNIT/TICRATE) ); + vector3_t gravityDelta = {0}; + vector3_t gravityDeltaDirection = {0}; + vector3_t fv3_temp = {0}; + + // rotate gravity vector + QuaternionMulVec3(&localgravityvectors[p], &localgravityvectors[p], &invRotation); + QuaternionMulVec3(&localsmoothedaccel[p], &localsmoothedaccel[p], &invRotation); + FV3_SubEx(&accel, &localsmoothedaccel[p], &fv3_temp); + localshakinessfac[p] = max( + FixedMul(localshakinessfac[p], Interpolator), + FV3_Magnitude(&fv3_temp) + ); + FV3_Load(&localsmoothedaccel[p], + Easing_Linear(Interpolator, accel.x, localsmoothedaccel[p].x), + Easing_Linear(Interpolator, accel.y, localsmoothedaccel[p].y), + Easing_Linear(Interpolator, accel.z, localsmoothedaccel[p].z) + ); + + FV3_SubEx(&invAccel, &localgravityvectors[p], &gravityDelta); + FV3_NormalizeEx(&gravityDelta, &gravityDeltaDirection); + + if (ShakinessMaxThreshold > ShakinessMinThreshold) + { + fixed_t stillness = CLAMP(FixedDiv((localshakinessfac[p] - ShakinessMinThreshold), (ShakinessMaxThreshold - ShakinessMinThreshold)), 0, FRACUNIT); + correctionRate = CorrectionStillRate + FixedMul(stillness, (CorrectionShakyRate - CorrectionStillRate)); + } + else if (localshakinessfac[p] > ShakinessMaxThreshold) + { + correctionRate = CorrectionShakyRate; + } + else + { + correctionRate = CorrectionStillRate; + } + + // limit in proportion to rotation rate + angleRate = FixedMul(FV3_Magnitude(&gyro), M_PI_FIXED)/180; + correctionLimit = FixedMul(FixedMul(angleRate, FV3_Magnitude(&localgravityvectors[p])), CorrectionGyroFactor); + if (correctionRate > correctionLimit) { + fixed_t closeEnoughFactor; + if (CorrectionGyroMaxThreshold > CorrectionGyroMinThreshold) + { + closeEnoughFactor = CLAMP(FixedDiv((FV3_Magnitude(&gravityDelta) - CorrectionGyroMinThreshold), (CorrectionGyroMaxThreshold - CorrectionGyroMinThreshold)), 0, FRACUNIT); + } + else if (FV3_Magnitude(&gravityDelta) > CorrectionGyroMaxThreshold) + { + closeEnoughFactor = FRACUNIT; + } + else + { + closeEnoughFactor = 0; + } + correctionRate = correctionRate + FixedMul((correctionLimit - correctionRate), closeEnoughFactor); + } + + // finally, let's always allow a little bit of correction + correctionRate = max(correctionRate, CorrectionMinimumSpeed); + + FV3_Load(&gravityDeltaDirection, + FixedMul(FixedMul(correctionRate, FRACUNIT/TICRATE), gravityDeltaDirection.x), + FixedMul(FixedMul(correctionRate, FRACUNIT/TICRATE), gravityDeltaDirection.y), + FixedMul(FixedMul(correctionRate, FRACUNIT/TICRATE), gravityDeltaDirection.z) + ); + + correctionMagnitude = FV3_Magnitude(&gravityDeltaDirection); + gravityDeltaMagnitude = FV3_Magnitude(&gravityDelta); + if (FixedMul(correctionMagnitude, correctionMagnitude) > FixedMul(gravityDeltaMagnitude, gravityDeltaMagnitude)) + { + FV3_Add(&localgravityvectors[p], &gravityDeltaDirection); + } + else + { + FV3_Add(&localgravityvectors[p], &gravityDelta); + } +} + +#define TILTRANGE 35*FRACUNIT +fixed_t G_GetGamepadTilt(INT32 p) +{ + fixed_t tilt; + fixed_t axis; + if (p >= MAXSPLITSCREENPLAYERS) + { +#ifdef PARANOIA + CONS_Debug(DBG_GAMELOGIC, "G_PlayerInputAnalog: Invalid player ID %d\n", p); +#endif + return 0; + } + + tilt = CLAMP(G_GetGamepadGravity(p).x, -TILTRANGE, TILTRANGE); + //TODO: this is really dumb, pinch towards 0 and towards the farthest we can tilt comfortably + axis = Easing_Linear(FixedDiv(tilt + TILTRANGE, (2*TILTRANGE)), -32768, 32767); + return axis; +} +#undef TILTRANGE + +vector3_t G_GetGamepadGravity(INT32 p) +{ + const vector3_t zero = {0}; + + if (p >= MAXSPLITSCREENPLAYERS) + { +#ifdef PARANOIA + CONS_Debug(DBG_GAMELOGIC, "G_PlayerInputAnalog: Invalid player ID %d\n", p); +#endif + return zero; + } return localgravityvectors[p]; } -vector3_t G_GetGamepadTilt(INT32 p, vector3_t gravity) +vector3_t G_GetGamepadShake(INT32 p) { - vector3_t out; - return out; + const vector3_t zero = {0}; + vector3_t accel; + + if (p >= MAXSPLITSCREENPLAYERS) + { +#ifdef PARANOIA + CONS_Debug(DBG_GAMELOGIC, "G_PlayerInputAnalog: Invalid player ID %d\n", p); +#endif + return zero; + } + + accel = G_PlayerInputSensor(p, ACCELEROMETER); + FV3_Add(&accel, &localgravityvectors[p]); + return accel; } +#undef Interpolator +#undef ShakinessMaxThreshold +#undef ShakinessMinThreshold +#undef CorrectionStillRate +#undef CorrectionShakyRate +#undef CorrectionGyroFactor +#undef CorrectionGyroMinThreshold +#undef CorrectionGyroMaxThreshold +#undef CorrectionMinimumSpeed + // // G_BuildTiccmd // Builds a ticcmd from all of the available inputs @@ -1501,8 +1690,6 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) INT32 forward, side, tspeed; joystickvector2_t joystickvector; - - vector3_t gravity = {0}; INT16 accelerometertilt; // you'd BETTER not touch the player while freecamming... @@ -1528,19 +1715,13 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) break; } - if (K_PlayerUsesBotMovement(player)) - { - // Bot ticcmd is generated by K_BuildBotTiccmd - return; - } - // update our gamepad gravity when applicable // this should always execute so we have an accurate state if (true) //todo: check if gamepad supports accel or accel and gyro { vector3_t accel = G_PlayerInputSensor(forplayer, ACCELEROMETER); vector3_t gyro = G_PlayerInputSensor(forplayer, GYROSCOPE); - gravity = G_UpdateGamepadGravity(forplayer, gyro, accel); + G_UpdateGamepadGravity(forplayer, gyro, accel); } // why build a ticcmd if we're paused? @@ -1550,6 +1731,12 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) return; } + if (K_PlayerUsesBotMovement(player)) + { + // Bot ticcmd is generated by K_BuildBotTiccmd + return; + } + joystickvector.xaxis = G_PlayerInputAnalog(forplayer, gc_turnright, false, DEADZONE_X) - G_PlayerInputAnalog(forplayer, gc_turnleft, false, DEADZONE_X); joystickvector.yaxis = 0; G_HandleAxisDeadZone(forplayer, &joystickvector); @@ -1557,10 +1744,11 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) if (joystickvector.xaxis != 0) { joystickactive[forplayer] = true; + accelerometertilt = 0; } else if (!joystickactive[forplayer]) { - + accelerometertilt = G_GetGamepadTilt(forplayer); } // For kart, I've turned the aim axis into a digital axis because we only @@ -1576,7 +1764,14 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) } forward = side = 0; - tspeed = joystickvector.xaxis; + if (joystickactive[forplayer]) + { + tspeed = joystickvector.xaxis; + } + else + { + tspeed = accelerometertilt; + } // use two stage accelerative turning // on the keyboard and (NOT!) joystick @@ -1609,6 +1804,12 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) cmd->angle -= (tspeed * KART_FULLTURN) / JOYAXISRANGE; side += (joystickvector.xaxis * 4) / JOYAXISRANGE; } + else if (accelerometertilt != 0) + { + cmd->turning -= (tspeed * KART_FULLTURN) / JOYAXISRANGE; + cmd->angle -= (tspeed * KART_FULLTURN) / JOYAXISRANGE; + side += (accelerometertilt * 4) / JOYAXISRANGE; + } // Specator mouse turning if (spectating) diff --git a/src/g_game.h b/src/g_game.h index 8ef08e215..bddde7852 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -143,8 +143,11 @@ typedef enum #define MAXGAMEPADTILT (ANG30) #define ACCEL_GRAVITY (FLOAT_TO_FIXED(9.80665f)) -vector3_t G_UpdateGamepadGravity(INT32 p, vector3_t gyro, vector3_t accel); -vector3_t G_GetGamepadTilt(INT32 p, vector3_t gravity); +void G_UpdateGamepadGravity(INT32 p, vector3_t gyro, vector3_t accel); +vector3_t G_GetGamepadGravity(INT32 p); +vector3_t G_GetGamepadShake(INT32 p); +fixed_t G_GetGamepadTilt(INT32 p); +vector3_t G_PlayerInputSensor(UINT8 p, motionsensortype_e sensor); fixed_t G_GetDeadZoneType(INT32 p, SINT8 type); INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, boolean digital, SINT8 type); diff --git a/src/g_input.c b/src/g_input.c index 805417e52..8ed61c255 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -491,19 +491,13 @@ static keyname_t keynames[] = {KEY_AXIS1+8, "L TRIGGER"}, {KEY_AXIS1+9, "R TRIGGER"}, - {KEY_ACCELEROMETER1+0, "TILT -X"}, - {KEY_ACCELEROMETER1+1, "TILT +X"}, - {KEY_ACCELEROMETER1+2, "TILT -Y"}, - {KEY_ACCELEROMETER1+3, "TILT +Y"}, - {KEY_ACCELEROMETER1+4, "TILT -Z"}, - {KEY_ACCELEROMETER1+5, "TILT +Z"}, + {KEY_ACCELEROMETER1+0, "TILT X"}, + {KEY_ACCELEROMETER1+1, "TILT Y"}, + {KEY_ACCELEROMETER1+2, "TILT Z"}, - {KEY_GYROSCOPE1+0, "ROTATE -X"}, - {KEY_GYROSCOPE1+1, "ROTATE +X"}, - {KEY_GYROSCOPE1+2, "ROTATE -Y"}, - {KEY_GYROSCOPE1+3, "ROTATE +Y"}, - {KEY_GYROSCOPE1+4, "ROTATE -Z"}, - {KEY_GYROSCOPE1+5, "ROTATE +Z"}, + {KEY_GYROSCOPE1+0, "ROTATE X"}, + {KEY_GYROSCOPE1+1, "ROTATE Y"}, + {KEY_GYROSCOPE1+2, "ROTATE Z"}, }; static const char *gamecontrolname[num_gamecontrols] = diff --git a/src/g_input.h b/src/g_input.h index e34e1c251..02bd76b7e 100644 --- a/src/g_input.h +++ b/src/g_input.h @@ -35,8 +35,9 @@ extern "C" { #define JOYANALOGS 4 // 4 analog stick axes (2 sticks * 2 axes) #define JOYTRIGGERS 2 // 2 trigger axes, positive only #define JOYIMUAXISES 6 // 3 accelerometer, 3 gyroscope axes (x, y, z) -#define JOYAXISES (JOYANALOGS + JOYTRIGGERS + JOYIMUAXISES) -#define JOYAXISKEYS ((2 * JOYANALOGS) + JOYTRIGGERS + (2 * JOYIMUAXISES)) +#define JOYAXISES (JOYANALOGS + JOYTRIGGERS + JOYIMUAXISES) +#define JOYAXISKEYS (2 * JOYANALOGS) + JOYTRIGGERS +#define JOYSTATEKEYS JOYAXISKEYS + JOYIMUAXISES #define MAXINPUTMAPPING 4 @@ -48,9 +49,9 @@ typedef enum KEY_JOY1 = NUMKEYS, KEY_HAT1 = KEY_JOY1 + JOY_DPAD_UP, KEY_AXIS1 = KEY_JOY1 + JOYBUTTONS, - KEY_ACCELEROMETER1 = KEY_AXIS1 + JOYANALOGS + JOYTRIGGERS, - KEY_GYROSCOPE1 = KEY_ACCELEROMETER1 + 6, - JOYINPUTEND = KEY_AXIS1 + JOYAXISKEYS, + KEY_ACCELEROMETER1 = KEY_AXIS1 + JOYAXISKEYS, + KEY_GYROSCOPE1 = KEY_ACCELEROMETER1 + 3, + JOYINPUTEND = KEY_AXIS1 + JOYSTATEKEYS, KEY_MOUSE1 = JOYINPUTEND, KEY_MOUSEMOVE = KEY_MOUSE1 + MOUSEBUTTONS, diff --git a/src/m_cheat.c b/src/m_cheat.c index 308356218..c22dac95e 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -723,6 +723,11 @@ struct debugFlagNames_s const debug_flag_names[] = {"Player", DBG_PLAYER}, {"Render", DBG_RENDER}, {"Renderer", DBG_RENDER}, // alt name + {"Music", DBG_MUSIC}, + {"IMU", DBG_IMU}, + {"Accelerometer", DBG_IMU}, // alt name + {"Gyro", DBG_IMU}, // alt name + {"Sensors", DBG_IMU}, // alt name {"Polyobj", DBG_POLYOBJ}, {"GameLogic", DBG_GAMELOGIC}, {"Game", DBG_GAMELOGIC}, // alt name diff --git a/src/sdl/i_gamepad.c b/src/sdl/i_gamepad.c index cad61b0ee..b2b43b472 100644 --- a/src/sdl/i_gamepad.c +++ b/src/sdl/i_gamepad.c @@ -95,7 +95,7 @@ void I_ShutdownController(UINT8 index) for (i = 0; i < 3; i++) { event.data1 = 0; - event.data2 = 0; + event.data2 = FLOAT_TO_FIXED(9.80665f); event.data3 = 0; D_PostEvent(&event); } @@ -250,6 +250,18 @@ void I_InitController(UINT8 playernum) controller->flipbuttons = SDL_GetGamepadButtonLabel(newcontroller, SDL_GAMEPAD_BUTTON_SOUTH) == SDL_GAMEPAD_BUTTON_LABEL_B; controller->flipgc = SDL_GetGamepadButtonLabel(newcontroller, SDL_GAMEPAD_BUTTON_WEST) == SDL_GAMEPAD_BUTTON_LABEL_B && SDL_GetGamepadButtonLabel(newcontroller, SDL_GAMEPAD_BUTTON_EAST) == SDL_GAMEPAD_BUTTON_LABEL_X; + + if (controller->hasaccelerometer) + { + CONS_Debug(DBG_GAMELOGIC, "Enabling accelerometer for controller %d\n", joystick_id); + SDL_SetGamepadSensorEnabled(controller->dev, SDL_SENSOR_ACCEL, true); + } + + if (controller->hasgyro) + { + CONS_Debug(DBG_GAMELOGIC, "Enabling gyro for controller %d\n", joystick_id); + SDL_SetGamepadSensorEnabled(controller->dev, SDL_SENSOR_GYRO, true); + } } void I_InitController1(void) diff --git a/src/sdl/i_video.cpp b/src/sdl/i_video.cpp index bf089779e..5ce00126b 100644 --- a/src/sdl/i_video.cpp +++ b/src/sdl/i_video.cpp @@ -1057,6 +1057,7 @@ static void Impl_HandleControllerSensorEvent(SDL_GamepadSensorEvent evt) // data[2]: z acceleration in m/s // we convert to gs before passing it to the event case SDL_SENSOR_ACCEL: + // CONS_Debug(DBG_IMU, "Got Accelerometer event %4.2f %4.2f %4.2f\n", evt.data[0]/SDL_STANDARD_GRAVITY, evt.data[1]/SDL_STANDARD_GRAVITY, evt.data[2]/SDL_STANDARD_GRAVITY); event.type = ev_accelerometer; event.data1 = FLOAT_TO_FIXED(evt.data[0] / SDL_STANDARD_GRAVITY); event.data2 = FLOAT_TO_FIXED(evt.data[1] / SDL_STANDARD_GRAVITY); @@ -1070,10 +1071,11 @@ static void Impl_HandleControllerSensorEvent(SDL_GamepadSensorEvent evt) // we convert to degrees per second before passing it to the event case SDL_SENSOR_GYRO: #define RAD2DEG 57.295779513f + // CONS_Debug(DBG_IMU, "Got Gyro event %4.2f %4.2f %4.2f\n", RAD2DEG * evt.data[0], RAD2DEG * evt.data[1], RAD2DEG * evt.data[2]); event.type = ev_gyroscope; - event.data1 = FixedAngle(FLOAT_TO_FIXED(RAD2DEG * evt.data[0])); - event.data2 = FixedAngle(FLOAT_TO_FIXED(RAD2DEG * evt.data[1])); - event.data3 = FixedAngle(FLOAT_TO_FIXED(RAD2DEG * evt.data[2])); + event.data1 = FLOAT_TO_FIXED(RAD2DEG * evt.data[0]); + event.data2 = FLOAT_TO_FIXED(RAD2DEG * evt.data[1]); + event.data3 = FLOAT_TO_FIXED(RAD2DEG * evt.data[2]); #undef RAD2DEG D_PostEvent(&event); return; diff --git a/src/st_stuff.c b/src/st_stuff.c index feb3b859e..eda5e70c5 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -474,6 +474,32 @@ static void ST_drawMusicDebug(INT32 *height) ST_pushDebugString(height, va(" Song: %8s", mname)); } +static INT32 ST_getFixedFrac(fixed_t val) +{ + return ((abs(val) * 100)>>FRACBITS) % 100; +} + +static void ST_drawImuDebug(INT32 *height) +{ + INT32 pnum = stplyrnum; + vector3_t accel, gyro, grav, shake; + if (demo.playback || (!P_IsMachineLocalPlayer(stplyr))) + { + ST_pushDebugString(height, va("Only for local player!!")); + return; + } + + accel = G_PlayerInputSensor(pnum, ACCELEROMETER); + gyro = G_PlayerInputSensor(pnum, GYROSCOPE); + grav = G_GetGamepadGravity(pnum); + shake = G_GetGamepadShake(pnum); + + ST_pushDebugString(height, va(" Gyro :%4.2f, %4.2f,%4.2f", FixedToFloat(gyro.x), FixedToFloat(gyro.y), FixedToFloat(gyro.z))); + ST_pushDebugString(height, va("Accel :%4.2f, %4.2f,%4.2f", FixedToFloat(accel.x), FixedToFloat(accel.y), FixedToFloat(accel.z))); + ST_pushDebugString(height, va("Shake :%4.2f, %4.2f,%4.2f", FixedToFloat(shake.x), FixedToFloat(shake.y), FixedToFloat(shake.z))); + ST_pushDebugString(height, va(" Grav :%4.2f, %4.2f,%4.2f", FixedToFloat(grav.x), FixedToFloat(grav.y), FixedToFloat(grav.z))); +} + static void ST_drawRenderDebug(INT32 *height) { const struct RenderStats *i = &g_renderstats; @@ -569,6 +595,11 @@ static void ST_drawDebugInfo(void) height -= 32; } + if (cht_debug & DBG_IMU) + { + ST_drawImuDebug(&height); + } + if (cht_debug & DBG_MUSIC) { ST_drawMusicDebug(&height); From dc04ac2f66749ef5830c2207e6aa284b5c2137e5 Mon Sep 17 00:00:00 2001 From: minenice55 Date: Thu, 26 Feb 2026 18:04:52 -0500 Subject: [PATCH 06/22] this feels quite good now --- src/d_main.cpp | 7 +++ src/g_game.c | 146 +++++++++++++++++++++++++++----------------- src/sdl/i_video.cpp | 2 +- 3 files changed, 97 insertions(+), 58 deletions(-) diff --git a/src/d_main.cpp b/src/d_main.cpp index 0362fddb7..63a725e63 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -220,6 +220,13 @@ void D_ProcessEvents(void) // i have to reset this somewhere or else your camera just glides away! for (size_t i = 0; i < 4; i++) gamekeydown[0][KEY_MOUSEMOVE + i] = 0; + + // for (size_t dev = 0; dev < MAXDEVICES; dev++) + // { + // gamekeydown[dev][KEY_GYROSCOPE1+0] = 0; + // gamekeydown[dev][KEY_GYROSCOPE1+1] = 0; + // gamekeydown[dev][KEY_GYROSCOPE1+2] = 0; + // } for (; eventtail != eventhead; eventtail = (eventtail+1) & (MAXEVENTS-1)) { diff --git a/src/g_game.c b/src/g_game.c index 4484dbd07..31b7216a8 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1450,6 +1450,16 @@ static vector3_t *QuaternionMulVec3(vector3_t *out, vector3_t *a, vector4_t *b) return out; } +static fixed_t FV3_LengthSquared(const vector3_t *vec) +{ + return FixedMul(vec->x, vec->x) + FixedMul(vec->y, vec->y) + FixedMul(vec->z, vec->z); +} + +static fixed_t FV3_Length(const vector3_t *vec) +{ + return FixedSqrt(FV3_LengthSquared(vec)); +} + static vector4_t *QuaternionInvert(vector4_t *out) { FV4_Load(out, @@ -1483,113 +1493,135 @@ inline static vector4_t AngleAxis(fixed_t angle, fixed_t x, fixed_t y, fixed_t z // math sourced from this article // http://gyrowiki.jibbsmart.com/blog:finding-gravity-with-sensor-fusion -#define Interpolator (FRACUNIT/4) +// state +vector3_t localgravityvectors[MAXSPLITSCREENPLAYERS]; +void G_UpdateGamepadGravity(INT32 p, vector3_t gyro, vector3_t accel) +{ + vector3_t invAccel = {-accel.x, -accel.y, -accel.z}; + fixed_t angleRate = FixedMul(FV3_Length(&gyro), FRACUNIT/TICRATE); + vector4_t invRotation = AngleAxis( + angleRate, + -gyro.x, + -gyro.y, + -gyro.z + ); + + // update gravity by rotation + QuaternionMulVec3(&localgravityvectors[p], &localgravityvectors[p], &invRotation); + + // nudge towards gravity according to current acceleration + FV3_Load(&localgravityvectors[p], + localgravityvectors[p].x + FixedMul(invAccel.x - localgravityvectors[p].x, FRACUNIT/10), + localgravityvectors[p].y + FixedMul(invAccel.y - localgravityvectors[p].y, FRACUNIT/10), + localgravityvectors[p].z + FixedMul(invAccel.z - localgravityvectors[p].z, FRACUNIT/10) + ); +} + +#if 0 // thresholds of trust for accel shakiness. less shakiness = more trust -#define ShakinessMaxThreshold (4*FRACUNIT/10) +#define ShakinessMaxThreshold (9*FRACUNIT/10) #define ShakinessMinThreshold (FRACUNIT/10) // when we trust the accel a lot (the controller is "still"), how quickly do we correct our gravity vector? #define CorrectionStillRate (FRACUNIT) // when we don't trust the accel (the controller is "shaky"), how quickly do we correct our gravity vector? #define CorrectionShakyRate (FRACUNIT/10) // if our old gravity vector is close enough to our new one, limit further corrections to this proportion of the rotation speed -#define CorrectionGyroFactor (FRACUNIT/10) +#define CorrectionGyroFactor (FRACUNIT/5) // thresholds for what's considered "close enough" #define CorrectionGyroMinThreshold (5*FRACUNIT/100) #define CorrectionGyroMaxThreshold (FRACUNIT/4) // no matter what, always apply a minimum of this much correction to our gravity vector #define CorrectionMinimumSpeed (FRACUNIT/10) -// state fixed_t localshakinessfac[MAXSPLITSCREENPLAYERS]; vector3_t localsmoothedaccel[MAXSPLITSCREENPLAYERS]; -vector3_t localgravityvectors[MAXSPLITSCREENPLAYERS]; void G_UpdateGamepadGravity(INT32 p, vector3_t gyro, vector3_t accel) { // convert gyro input to reverse rotation - const vector3_t invAccel = {-accel.x, -accel.y, -accel.z}; - fixed_t correctionRate = CorrectionMinimumSpeed; - fixed_t correctionMagnitude, gravityDeltaMagnitude, angleRate, correctionLimit; + const fixed_t smoothFactor = 924*FRACUNIT/1000; + vector3_t invAccelNorm = {-accel.x, -accel.y, -accel.z}; + fixed_t gravCorrectionRate = 0; + fixed_t angleRate; + fixed_t correctionLimit; vector4_t invRotation = AngleAxis( + FixedMul(FV3_Length(&gyro), FRACUNIT/TICRATE), -gyro.x, -gyro.y, - -gyro.z, - FixedMul(FV3_Magnitude(&gyro), FRACUNIT/TICRATE) + -gyro.z ); - vector3_t gravityDelta = {0}; - vector3_t gravityDeltaDirection = {0}; + vector3_t gravityToAccel = {0}; + vector3_t gravityToAccelDirection = {0}; vector3_t fv3_temp = {0}; - - // rotate gravity vector - QuaternionMulVec3(&localgravityvectors[p], &localgravityvectors[p], &invRotation); - QuaternionMulVec3(&localsmoothedaccel[p], &localsmoothedaccel[p], &invRotation); - FV3_SubEx(&accel, &localsmoothedaccel[p], &fv3_temp); - localshakinessfac[p] = max( - FixedMul(localshakinessfac[p], Interpolator), - FV3_Magnitude(&fv3_temp) - ); - FV3_Load(&localsmoothedaccel[p], - Easing_Linear(Interpolator, accel.x, localsmoothedaccel[p].x), - Easing_Linear(Interpolator, accel.y, localsmoothedaccel[p].y), - Easing_Linear(Interpolator, accel.z, localsmoothedaccel[p].z) - ); - FV3_SubEx(&invAccel, &localgravityvectors[p], &gravityDelta); - FV3_NormalizeEx(&gravityDelta, &gravityDeltaDirection); + if (FV3_Length(&accel) <= 0) + { + QuaternionMulVec3(&localgravityvectors[p], &localgravityvectors[p], &invRotation); + return; + } + + // rotate gravity vector + QuaternionMulVec3(&localsmoothedaccel[p], &localsmoothedaccel[p], &invRotation); + localshakinessfac[p] = FixedMul(localshakinessfac[p], smoothFactor), + FV3_SubEx(&accel, &localsmoothedaccel[p], &fv3_temp); + localshakinessfac[p] = max(localshakinessfac[p], FV3_Length(&fv3_temp)); + FV3_Load(&localsmoothedaccel[p], + Easing_Linear(smoothFactor, accel.x, localsmoothedaccel[p].x), + Easing_Linear(smoothFactor, accel.y, localsmoothedaccel[p].y), + Easing_Linear(smoothFactor, accel.z, localsmoothedaccel[p].z) + ); + + CONS_Debug(DBG_IMU, "Shakiness: %4.2f\n", FixedToFloat(localshakinessfac[p])); + + // update gravity by rotation + QuaternionMulVec3(&localgravityvectors[p], &localgravityvectors[p], &invRotation); + FV3_SubEx(&invAccelNorm, &localgravityvectors[p], &gravityToAccel); + FV3_NormalizeEx(&gravityToAccel, &gravityToAccelDirection); if (ShakinessMaxThreshold > ShakinessMinThreshold) { fixed_t stillness = CLAMP(FixedDiv((localshakinessfac[p] - ShakinessMinThreshold), (ShakinessMaxThreshold - ShakinessMinThreshold)), 0, FRACUNIT); - correctionRate = CorrectionStillRate + FixedMul(stillness, (CorrectionShakyRate - CorrectionStillRate)); - } - else if (localshakinessfac[p] > ShakinessMaxThreshold) - { - correctionRate = CorrectionShakyRate; + gravCorrectionRate = CorrectionStillRate + FixedMul((CorrectionShakyRate - CorrectionStillRate), stillness); } else { - correctionRate = CorrectionStillRate; + gravCorrectionRate = localshakinessfac[p] < ShakinessMaxThreshold ? CorrectionStillRate : CorrectionShakyRate; } // limit in proportion to rotation rate - angleRate = FixedMul(FV3_Magnitude(&gyro), M_PI_FIXED)/180; - correctionLimit = FixedMul(FixedMul(angleRate, FV3_Magnitude(&localgravityvectors[p])), CorrectionGyroFactor); - if (correctionRate > correctionLimit) { + angleRate = FixedMul(FixedMul(FV3_Length(&gyro), M_PI_FIXED)/180, FRACUNIT/TICRATE); + correctionLimit = max(FixedMul(angleRate, CorrectionGyroFactor), CorrectionMinimumSpeed); + if (gravCorrectionRate > correctionLimit) { fixed_t closeEnoughFactor; if (CorrectionGyroMaxThreshold > CorrectionGyroMinThreshold) { - closeEnoughFactor = CLAMP(FixedDiv((FV3_Magnitude(&gravityDelta) - CorrectionGyroMinThreshold), (CorrectionGyroMaxThreshold - CorrectionGyroMinThreshold)), 0, FRACUNIT); - } - else if (FV3_Magnitude(&gravityDelta) > CorrectionGyroMaxThreshold) - { - closeEnoughFactor = FRACUNIT; - } + closeEnoughFactor = CLAMP(FixedDiv((FV3_Length(&gravityToAccel) - CorrectionGyroMinThreshold), (CorrectionGyroMaxThreshold - CorrectionGyroMinThreshold)), 0, FRACUNIT); + } else { - closeEnoughFactor = 0; + closeEnoughFactor = FV3_Length(&gravityToAccel) < CorrectionGyroMaxThreshold ? 0 : FRACUNIT; } - correctionRate = correctionRate + FixedMul((correctionLimit - correctionRate), closeEnoughFactor); + gravCorrectionRate = correctionLimit + FixedMul((gravCorrectionRate - correctionLimit), closeEnoughFactor); } + + CONS_Debug(DBG_IMU, "Correction Rate: %4.2f\n", FixedToFloat(gravCorrectionRate)); - // finally, let's always allow a little bit of correction - correctionRate = max(correctionRate, CorrectionMinimumSpeed); - - FV3_Load(&gravityDeltaDirection, - FixedMul(FixedMul(correctionRate, FRACUNIT/TICRATE), gravityDeltaDirection.x), - FixedMul(FixedMul(correctionRate, FRACUNIT/TICRATE), gravityDeltaDirection.y), - FixedMul(FixedMul(correctionRate, FRACUNIT/TICRATE), gravityDeltaDirection.z) + FV3_Load(&fv3_temp, + FixedMul(gravityToAccelDirection.x, FixedMul(gravCorrectionRate, FRACUNIT/TICRATE)), + FixedMul(gravityToAccelDirection.y, FixedMul(gravCorrectionRate, FRACUNIT/TICRATE)), + FixedMul(gravityToAccelDirection.z, FixedMul(gravCorrectionRate, FRACUNIT/TICRATE)) ); - correctionMagnitude = FV3_Magnitude(&gravityDeltaDirection); - gravityDeltaMagnitude = FV3_Magnitude(&gravityDelta); - if (FixedMul(correctionMagnitude, correctionMagnitude) > FixedMul(gravityDeltaMagnitude, gravityDeltaMagnitude)) + if (FV3_LengthSquared(&fv3_temp) < FV3_LengthSquared(&gravityToAccel)) { - FV3_Add(&localgravityvectors[p], &gravityDeltaDirection); + FV3_Add(&localgravityvectors[p], &fv3_temp); } else { - FV3_Add(&localgravityvectors[p], &gravityDelta); + FV3_Normalize(&invAccelNorm); + FV3_Add(&localgravityvectors[p], &invAccelNorm); } } +#endif #define TILTRANGE 35*FRACUNIT fixed_t G_GetGamepadTilt(INT32 p) diff --git a/src/sdl/i_video.cpp b/src/sdl/i_video.cpp index 5ce00126b..2077b6fa9 100644 --- a/src/sdl/i_video.cpp +++ b/src/sdl/i_video.cpp @@ -1070,7 +1070,7 @@ static void Impl_HandleControllerSensorEvent(SDL_GamepadSensorEvent evt) // data[2]: delta roll in rad/s // we convert to degrees per second before passing it to the event case SDL_SENSOR_GYRO: -#define RAD2DEG 57.295779513f +#define RAD2DEG -57.295779513f // CONS_Debug(DBG_IMU, "Got Gyro event %4.2f %4.2f %4.2f\n", RAD2DEG * evt.data[0], RAD2DEG * evt.data[1], RAD2DEG * evt.data[2]); event.type = ev_gyroscope; event.data1 = FLOAT_TO_FIXED(RAD2DEG * evt.data[0]); From d3b02613f264a5f4312316e2dab6c4f21b17fbf5 Mon Sep 17 00:00:00 2001 From: minenice55 Date: Thu, 26 Feb 2026 21:22:43 -0500 Subject: [PATCH 07/22] fix some math issues --- src/g_game.c | 94 ++++++++++++++++++++++++---------------------------- 1 file changed, 43 insertions(+), 51 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 31b7216a8..0918ca8d5 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1495,38 +1495,37 @@ inline static vector4_t AngleAxis(fixed_t angle, fixed_t x, fixed_t y, fixed_t z // http://gyrowiki.jibbsmart.com/blog:finding-gravity-with-sensor-fusion // state vector3_t localgravityvectors[MAXSPLITSCREENPLAYERS]; -void G_UpdateGamepadGravity(INT32 p, vector3_t gyro, vector3_t accel) -{ - vector3_t invAccel = {-accel.x, -accel.y, -accel.z}; - fixed_t angleRate = FixedMul(FV3_Length(&gyro), FRACUNIT/TICRATE); - vector4_t invRotation = AngleAxis( - angleRate, - -gyro.x, - -gyro.y, - -gyro.z - ); +// void G_UpdateGamepadGravity(INT32 p, vector3_t gyro, vector3_t accel) +// { +// vector3_t invAccel = {-accel.x, -accel.y, -accel.z}; +// fixed_t angleRate = FixedMul(FV3_Length(&gyro), FRACUNIT/TICRATE); +// vector4_t invRotation = AngleAxis( +// angleRate, +// -gyro.x, +// -gyro.y, +// -gyro.z +// ); - // update gravity by rotation - QuaternionMulVec3(&localgravityvectors[p], &localgravityvectors[p], &invRotation); +// // update gravity by rotation +// QuaternionMulVec3(&localgravityvectors[p], &localgravityvectors[p], &invRotation); - // nudge towards gravity according to current acceleration - FV3_Load(&localgravityvectors[p], - localgravityvectors[p].x + FixedMul(invAccel.x - localgravityvectors[p].x, FRACUNIT/10), - localgravityvectors[p].y + FixedMul(invAccel.y - localgravityvectors[p].y, FRACUNIT/10), - localgravityvectors[p].z + FixedMul(invAccel.z - localgravityvectors[p].z, FRACUNIT/10) - ); -} +// // nudge towards gravity according to current acceleration +// FV3_Load(&localgravityvectors[p], +// localgravityvectors[p].x + FixedMul(invAccel.x - localgravityvectors[p].x, FRACUNIT/4), +// localgravityvectors[p].y + FixedMul(invAccel.y - localgravityvectors[p].y, FRACUNIT/4), +// localgravityvectors[p].z + FixedMul(invAccel.z - localgravityvectors[p].z, FRACUNIT/4) +// ); +// } -#if 0 // thresholds of trust for accel shakiness. less shakiness = more trust -#define ShakinessMaxThreshold (9*FRACUNIT/10) -#define ShakinessMinThreshold (FRACUNIT/10) +#define ShakinessMaxThreshold (4*FRACUNIT/10) +#define ShakinessMinThreshold (1*FRACUNIT/100) // when we trust the accel a lot (the controller is "still"), how quickly do we correct our gravity vector? #define CorrectionStillRate (FRACUNIT) // when we don't trust the accel (the controller is "shaky"), how quickly do we correct our gravity vector? #define CorrectionShakyRate (FRACUNIT/10) // if our old gravity vector is close enough to our new one, limit further corrections to this proportion of the rotation speed -#define CorrectionGyroFactor (FRACUNIT/5) +#define CorrectionGyroFactor (FRACUNIT/10) // thresholds for what's considered "close enough" #define CorrectionGyroMinThreshold (5*FRACUNIT/100) #define CorrectionGyroMaxThreshold (FRACUNIT/4) @@ -1538,8 +1537,8 @@ vector3_t localsmoothedaccel[MAXSPLITSCREENPLAYERS]; void G_UpdateGamepadGravity(INT32 p, vector3_t gyro, vector3_t accel) { // convert gyro input to reverse rotation - const fixed_t smoothFactor = 924*FRACUNIT/1000; - vector3_t invAccelNorm = {-accel.x, -accel.y, -accel.z}; + const fixed_t smoothFactor = 800*FRACUNIT/1000; + vector3_t invAccel = {-accel.x, -accel.y, -accel.z}; fixed_t gravCorrectionRate = 0; fixed_t angleRate; fixed_t correctionLimit; @@ -1551,19 +1550,14 @@ void G_UpdateGamepadGravity(INT32 p, vector3_t gyro, vector3_t accel) ); vector3_t gravityToAccel = {0}; vector3_t gravityToAccelDirection = {0}; - vector3_t fv3_temp = {0}; - - if (FV3_Length(&accel) <= 0) - { - QuaternionMulVec3(&localgravityvectors[p], &localgravityvectors[p], &invRotation); - return; - } + vector3_t correction = {0}; // rotate gravity vector + QuaternionMulVec3(&localgravityvectors[p], &localgravityvectors[p], &invRotation); QuaternionMulVec3(&localsmoothedaccel[p], &localsmoothedaccel[p], &invRotation); localshakinessfac[p] = FixedMul(localshakinessfac[p], smoothFactor), - FV3_SubEx(&accel, &localsmoothedaccel[p], &fv3_temp); - localshakinessfac[p] = max(localshakinessfac[p], FV3_Length(&fv3_temp)); + FV3_SubEx(&accel, &localsmoothedaccel[p], &correction); + localshakinessfac[p] = max(localshakinessfac[p], FV3_Length(&correction)); FV3_Load(&localsmoothedaccel[p], Easing_Linear(smoothFactor, accel.x, localsmoothedaccel[p].x), Easing_Linear(smoothFactor, accel.y, localsmoothedaccel[p].y), @@ -1572,9 +1566,7 @@ void G_UpdateGamepadGravity(INT32 p, vector3_t gyro, vector3_t accel) CONS_Debug(DBG_IMU, "Shakiness: %4.2f\n", FixedToFloat(localshakinessfac[p])); - // update gravity by rotation - QuaternionMulVec3(&localgravityvectors[p], &localgravityvectors[p], &invRotation); - FV3_SubEx(&invAccelNorm, &localgravityvectors[p], &gravityToAccel); + FV3_SubEx(&invAccel, &localgravityvectors[p], &gravityToAccel); FV3_NormalizeEx(&gravityToAccel, &gravityToAccelDirection); if (ShakinessMaxThreshold > ShakinessMinThreshold) @@ -1586,10 +1578,10 @@ void G_UpdateGamepadGravity(INT32 p, vector3_t gyro, vector3_t accel) { gravCorrectionRate = localshakinessfac[p] < ShakinessMaxThreshold ? CorrectionStillRate : CorrectionShakyRate; } - + // limit in proportion to rotation rate - angleRate = FixedMul(FixedMul(FV3_Length(&gyro), M_PI_FIXED)/180, FRACUNIT/TICRATE); - correctionLimit = max(FixedMul(angleRate, CorrectionGyroFactor), CorrectionMinimumSpeed); + angleRate = FixedMul(FV3_Length(&gyro), M_PI_FIXED)/180; + correctionLimit = FixedMul(FixedMul(angleRate, FV3_Length(&localgravityvectors[p])), CorrectionGyroFactor); if (gravCorrectionRate > correctionLimit) { fixed_t closeEnoughFactor; if (CorrectionGyroMaxThreshold > CorrectionGyroMinThreshold) @@ -1602,26 +1594,26 @@ void G_UpdateGamepadGravity(INT32 p, vector3_t gyro, vector3_t accel) } gravCorrectionRate = correctionLimit + FixedMul((gravCorrectionRate - correctionLimit), closeEnoughFactor); } - + + // finally, let's always allow a little bit of correction + gravCorrectionRate = max(gravCorrectionRate, CorrectionMinimumSpeed); + CONS_Debug(DBG_IMU, "Correction Rate: %4.2f\n", FixedToFloat(gravCorrectionRate)); - FV3_Load(&fv3_temp, - FixedMul(gravityToAccelDirection.x, FixedMul(gravCorrectionRate, FRACUNIT/TICRATE)), - FixedMul(gravityToAccelDirection.y, FixedMul(gravCorrectionRate, FRACUNIT/TICRATE)), - FixedMul(gravityToAccelDirection.z, FixedMul(gravCorrectionRate, FRACUNIT/TICRATE)) + FV3_Load(&correction, + FixedMul(gravityToAccel.x, gravCorrectionRate/TICRATE), + FixedMul(gravityToAccel.y, gravCorrectionRate/TICRATE), + FixedMul(gravityToAccel.z, gravCorrectionRate/TICRATE) ); - - if (FV3_LengthSquared(&fv3_temp) < FV3_LengthSquared(&gravityToAccel)) + if (FV3_LengthSquared(&correction) < FV3_LengthSquared(&gravityToAccel)) { - FV3_Add(&localgravityvectors[p], &fv3_temp); + FV3_Add(&localgravityvectors[p], &correction); } else { - FV3_Normalize(&invAccelNorm); - FV3_Add(&localgravityvectors[p], &invAccelNorm); + FV3_Add(&localgravityvectors[p], &gravityToAccel); } } -#endif #define TILTRANGE 35*FRACUNIT fixed_t G_GetGamepadTilt(INT32 p) From 3bda3ded660d4009f42c5afabae0bc9c4f5d8d5c Mon Sep 17 00:00:00 2001 From: minenice55 Date: Thu, 26 Feb 2026 22:49:27 -0500 Subject: [PATCH 08/22] properly implement tilt based axis --- src/g_game.c | 45 +++++++++++++++------------------------------ 1 file changed, 15 insertions(+), 30 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 0918ca8d5..5164adb98 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1494,28 +1494,9 @@ inline static vector4_t AngleAxis(fixed_t angle, fixed_t x, fixed_t y, fixed_t z // math sourced from this article // http://gyrowiki.jibbsmart.com/blog:finding-gravity-with-sensor-fusion // state +fixed_t localshakinessfac[MAXSPLITSCREENPLAYERS]; +vector3_t localsmoothedaccel[MAXSPLITSCREENPLAYERS]; vector3_t localgravityvectors[MAXSPLITSCREENPLAYERS]; -// void G_UpdateGamepadGravity(INT32 p, vector3_t gyro, vector3_t accel) -// { -// vector3_t invAccel = {-accel.x, -accel.y, -accel.z}; -// fixed_t angleRate = FixedMul(FV3_Length(&gyro), FRACUNIT/TICRATE); -// vector4_t invRotation = AngleAxis( -// angleRate, -// -gyro.x, -// -gyro.y, -// -gyro.z -// ); - -// // update gravity by rotation -// QuaternionMulVec3(&localgravityvectors[p], &localgravityvectors[p], &invRotation); - -// // nudge towards gravity according to current acceleration -// FV3_Load(&localgravityvectors[p], -// localgravityvectors[p].x + FixedMul(invAccel.x - localgravityvectors[p].x, FRACUNIT/4), -// localgravityvectors[p].y + FixedMul(invAccel.y - localgravityvectors[p].y, FRACUNIT/4), -// localgravityvectors[p].z + FixedMul(invAccel.z - localgravityvectors[p].z, FRACUNIT/4) -// ); -// } // thresholds of trust for accel shakiness. less shakiness = more trust #define ShakinessMaxThreshold (4*FRACUNIT/10) @@ -1530,10 +1511,8 @@ vector3_t localgravityvectors[MAXSPLITSCREENPLAYERS]; #define CorrectionGyroMinThreshold (5*FRACUNIT/100) #define CorrectionGyroMaxThreshold (FRACUNIT/4) // no matter what, always apply a minimum of this much correction to our gravity vector -#define CorrectionMinimumSpeed (FRACUNIT/10) +#define CorrectionMinimumSpeed (FRACUNIT/8) -fixed_t localshakinessfac[MAXSPLITSCREENPLAYERS]; -vector3_t localsmoothedaccel[MAXSPLITSCREENPLAYERS]; void G_UpdateGamepadGravity(INT32 p, vector3_t gyro, vector3_t accel) { // convert gyro input to reverse rotation @@ -1615,11 +1594,11 @@ void G_UpdateGamepadGravity(INT32 p, vector3_t gyro, vector3_t accel) } } -#define TILTRANGE 35*FRACUNIT +#define TILTRANGE (35*FRACUNIT/100) fixed_t G_GetGamepadTilt(INT32 p) { fixed_t tilt; - fixed_t axis; + fixed_t curve; if (p >= MAXSPLITSCREENPLAYERS) { #ifdef PARANOIA @@ -1628,10 +1607,16 @@ fixed_t G_GetGamepadTilt(INT32 p) return 0; } - tilt = CLAMP(G_GetGamepadGravity(p).x, -TILTRANGE, TILTRANGE); - //TODO: this is really dumb, pinch towards 0 and towards the farthest we can tilt comfortably - axis = Easing_Linear(FixedDiv(tilt + TILTRANGE, (2*TILTRANGE)), -32768, 32767); - return axis; + tilt = CLAMP(FixedDiv(G_GetGamepadGravity(p).x + TILTRANGE, (TILTRANGE + TILTRANGE)), 0, FRACUNIT); + CONS_Debug(DBG_IMU, "Tilt: %4.2f\n", FixedToFloat(tilt)); + return Easing_Linear(tilt, -JOYAXISRANGE, JOYAXISRANGE); + + // curve = FRACUNIT - FINECOSINE(FixedAngle(180*(tilt-FRACUNIT/2))>>ANGLETOFINESHIFT); + // curve = FixedMul(curve, curve); + // tilt = ((FixedMul(curve, FRACUNIT-curve) + curve) * intsign(G_GetGamepadGravity(p).x)); + // CONS_Debug(DBG_IMU, "Pinched Tilt: %4.2f\n", FixedToFloat(tilt)); + + // return (JOYAXISRANGE * tilt)/FRACUNIT; } #undef TILTRANGE From 2389b64803475c2a7f7263245ac105dda3996b50 Mon Sep 17 00:00:00 2001 From: minenice55 Date: Thu, 26 Feb 2026 22:52:32 -0500 Subject: [PATCH 09/22] add tiltcontrol cvar --- src/g_game.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/g_game.c b/src/g_game.c index 5164adb98..f2683e5ee 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1755,10 +1755,15 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) joystickactive[forplayer] = true; accelerometertilt = 0; } + + if (!cv_tiltcontrol[forplayer].value) + { + accelerometertilt = 0; + } else if (!joystickactive[forplayer]) { accelerometertilt = G_GetGamepadTilt(forplayer); - } + } // For kart, I've turned the aim axis into a digital axis because we only // use it for aiming to throw items forward/backward and the vote screen From 4bc182901fd4a4f42c6cab6b7552f510d6de8ace Mon Sep 17 00:00:00 2001 From: minenice55 Date: Thu, 26 Feb 2026 22:53:35 -0500 Subject: [PATCH 10/22] Update g_game.c --- src/g_game.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g_game.c b/src/g_game.c index f2683e5ee..e731c51d1 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1511,7 +1511,7 @@ vector3_t localgravityvectors[MAXSPLITSCREENPLAYERS]; #define CorrectionGyroMinThreshold (5*FRACUNIT/100) #define CorrectionGyroMaxThreshold (FRACUNIT/4) // no matter what, always apply a minimum of this much correction to our gravity vector -#define CorrectionMinimumSpeed (FRACUNIT/8) +#define CorrectionMinimumSpeed (FRACUNIT/6) void G_UpdateGamepadGravity(INT32 p, vector3_t gyro, vector3_t accel) { From a5b5955d28b69494f5650fd27bc44b1566099d3e Mon Sep 17 00:00:00 2001 From: minenice55 Date: Thu, 26 Feb 2026 23:21:49 -0500 Subject: [PATCH 11/22] pinch the tilt towards the extremes --- src/g_game.c | 11 ++++------- src/g_input.c | 8 ++++---- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index e731c51d1..bce70a43f 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1594,7 +1594,7 @@ void G_UpdateGamepadGravity(INT32 p, vector3_t gyro, vector3_t accel) } } -#define TILTRANGE (35*FRACUNIT/100) +#define TILTRANGE (40*FRACUNIT/100) fixed_t G_GetGamepadTilt(INT32 p) { fixed_t tilt; @@ -1609,14 +1609,11 @@ fixed_t G_GetGamepadTilt(INT32 p) tilt = CLAMP(FixedDiv(G_GetGamepadGravity(p).x + TILTRANGE, (TILTRANGE + TILTRANGE)), 0, FRACUNIT); CONS_Debug(DBG_IMU, "Tilt: %4.2f\n", FixedToFloat(tilt)); - return Easing_Linear(tilt, -JOYAXISRANGE, JOYAXISRANGE); - // curve = FRACUNIT - FINECOSINE(FixedAngle(180*(tilt-FRACUNIT/2))>>ANGLETOFINESHIFT); - // curve = FixedMul(curve, curve); - // tilt = ((FixedMul(curve, FRACUNIT-curve) + curve) * intsign(G_GetGamepadGravity(p).x)); - // CONS_Debug(DBG_IMU, "Pinched Tilt: %4.2f\n", FixedToFloat(tilt)); + curve = FINESINE(FixedAngle(180*(tilt-FRACUNIT/2))>>ANGLETOFINESHIFT); + CONS_Debug(DBG_IMU, "Pinched Tilt: %4.2f\n", FixedToFloat(curve)); - // return (JOYAXISRANGE * tilt)/FRACUNIT; + return (JOYAXISRANGE * curve)/FRACUNIT; } #undef TILTRANGE diff --git a/src/g_input.c b/src/g_input.c index 8ed61c255..a1548e2ae 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -91,10 +91,10 @@ consvar_t cv_controllerled[MAXSPLITSCREENPLAYERS] = { }; consvar_t cv_tiltcontrol[MAXSPLITSCREENPLAYERS] = { - CVAR_INIT ("tiltcontrol", "Off", CV_SAVE|CV_CALL|CV_NOINIT, CV_OnOff, NULL), - CVAR_INIT ("tiltcontrol2", "Off", CV_SAVE|CV_CALL|CV_NOINIT, CV_OnOff, NULL), - CVAR_INIT ("tiltcontrol3", "Off", CV_SAVE|CV_CALL|CV_NOINIT, CV_OnOff, NULL), - CVAR_INIT ("tiltcontrol4", "Off", CV_SAVE|CV_CALL|CV_NOINIT, CV_OnOff, NULL) + CVAR_INIT ("tiltcontrol", "Off", CV_SAVE, CV_OnOff, NULL), + CVAR_INIT ("tiltcontrol2", "Off", CV_SAVE, CV_OnOff, NULL), + CVAR_INIT ("tiltcontrol3", "Off", CV_SAVE, CV_OnOff, NULL), + CVAR_INIT ("tiltcontrol4", "Off", CV_SAVE, CV_OnOff, NULL) }; static void rumble_off_handle(void) From 0b28d8bf676158d1757c26aa47390c15b89edde5 Mon Sep 17 00:00:00 2001 From: minenice55 Date: Thu, 26 Feb 2026 23:46:23 -0500 Subject: [PATCH 12/22] rotated steering wheel display? needs some work --- src/k_hud.c | 41 +++++++++++------------------------------ 1 file changed, 11 insertions(+), 30 deletions(-) diff --git a/src/k_hud.c b/src/k_hud.c index 585049238..4d7483b32 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -5464,44 +5464,25 @@ static void K_drawInput(void) } else if (cv_showinput.value == 1) { - if (!stplyr->cmd.turning) // no turn - target = 0; - else // turning of multiple strengths! + patch_t *workingPic = kp_inputwheel[0]; + INT16 turning = encoremode ? -stplyr->cmd.turning : stplyr->cmd.turning; + angle_t rotate = FixedAngle(45 * FixedDiv(abs(turning), 1024) * intsign(turning)); + INT32 rot = R_GetRollAngle(rotate); + + if (rot) { - target = ((abs(stplyr->cmd.turning) - 1)/200)+1; // was 125, do we need another toggle for this? Zzz... - if (target > 4) - target = 4; - if (stplyr->cmd.turning < 0) - target = -target; + workingPic = Patch_GetRotated(workingPic, rot, false); } - if (pn != target) - { - if (abs(pn - target) == 1) - pn = target; - else if (pn < target) - pn += 2; - else //if (pn > target) - pn -= 2; - } - - if (pn < 0) - { - splitflags |= V_FLIP; // right turn - x -= FRACUNIT; - } - - target = abs(pn); - if (target > 4) - target = 4; - if (!K_GetHudColor()) - V_DrawFixedPatch(x, y, FRACUNIT, splitflags, kp_inputwheel[target], NULL); + { + V_DrawFixedPatch(x, y, FRACUNIT, splitflags, workingPic, NULL); + } else { UINT8 *colormap; colormap = R_GetTranslationColormap(0, K_GetHudColor(), GTC_CACHE); - V_DrawFixedPatch(x, y, FRACUNIT, splitflags, kp_inputwheel[target], colormap); + V_DrawFixedPatch(x, y, FRACUNIT, splitflags, workingPic, colormap); } } } From 2010b0376d17e21989549670e8e2d1372bbcc6d9 Mon Sep 17 00:00:00 2001 From: minenice55 Date: Sat, 28 Feb 2026 00:27:30 -0500 Subject: [PATCH 13/22] implement new wheel gfx --- src/d_main.cpp | 2 +- src/d_ticcmd.h | 1 - src/k_hud.c | 36 +++++++++++++++++++++++++----------- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/d_main.cpp b/src/d_main.cpp index 63a725e63..fda0121ef 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -93,7 +93,7 @@ #define ASSET_HASH_TEXTURES_KART 0xb4211b2f32b6a291 #define ASSET_HASH_CHARS_KART 0x1e68a3e01aa5c68b #define ASSET_HASH_MAPS_KART 0x38558ed00da41ce9 -#define ASSET_HASH_MAIN_PK3 0x348af552ccc104d2 +#define ASSET_HASH_MAIN_PK3 0xac00f1a74761e9bd #define ASSET_HASH_MAPPATCH_PK3 0x1745690024efbaf8 #define ASSET_HASH_BONUSCHARS_KART 0x60e6f13d822a7461 #ifdef USE_PATCH_FILE diff --git a/src/d_ticcmd.h b/src/d_ticcmd.h index 398a816a6..59a3422e7 100644 --- a/src/d_ticcmd.h +++ b/src/d_ticcmd.h @@ -68,7 +68,6 @@ struct ticcmd_t INT16 angle; // Predicted angle, use me if you can! INT16 throwdir; // Aiming direction INT16 aiming; // vertical aiming, see G_BuildTicCmd - INT16 tilt; // controller tilt UINT16 buttons; UINT8 latency; // Netgames: how many tics ago was this ticcmd generated from this player's end? UINT8 flags; diff --git a/src/k_hud.c b/src/k_hud.c index 4d7483b32..23e00ab63 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -190,7 +190,8 @@ static patch_t *kp_check[11]; static patch_t *kp_eggnum[4]; static patch_t *kp_fpview[3]; -static patch_t *kp_inputwheel[5]; +static patch_t *kp_inputwheel; +static patch_t *kp_inputwheel_shadow; static patch_t *kp_challenger[25]; @@ -592,12 +593,21 @@ void K_LoadKartHUDGraphics(void) HU_UpdatePatch(&joyshadow, "JOYSHD"); // Input UI Wheel - sprintf(buffer, "K_WHEELx"); - for (i = 0; i < 5; i++) - { - buffer[7] = '0'+i; - HU_UpdatePatch(&kp_inputwheel[i], "%s", buffer); - } + HU_UpdatePatch(&kp_inputwheel, "K_WHEEL0"); + kp_inputwheel->pivot.x = + (kp_inputwheel->width / 2) + kp_inputwheel->leftoffset; + kp_inputwheel->pivot.y = + (kp_inputwheel->height / 2) + kp_inputwheel->topoffset; + kp_inputwheel->alignflags |= + (PATCHALIGN_AUTOCENTER | PATCHALIGN_USEPIVOTS); + + HU_UpdatePatch(&kp_inputwheel_shadow, "K_WHEEL1"); + kp_inputwheel_shadow->pivot.x = + (kp_inputwheel_shadow->width / 2) + kp_inputwheel_shadow->leftoffset; + kp_inputwheel_shadow->pivot.y = + (kp_inputwheel_shadow->height / 2) + kp_inputwheel_shadow->topoffset; + kp_inputwheel_shadow->alignflags |= + (PATCHALIGN_AUTOCENTER | PATCHALIGN_USEPIVOTS); // HERE COMES A NEW CHALLENGER sprintf(buffer, "K_CHALxx"); @@ -5464,25 +5474,29 @@ static void K_drawInput(void) } else if (cv_showinput.value == 1) { - patch_t *workingPic = kp_inputwheel[0]; + patch_t *workingPic = kp_inputwheel; + patch_t *shadowPic = kp_inputwheel_shadow; INT16 turning = encoremode ? -stplyr->cmd.turning : stplyr->cmd.turning; - angle_t rotate = FixedAngle(45 * FixedDiv(abs(turning), 1024) * intsign(turning)); + angle_t rotate = FixedAngle(60 * FixedDiv(abs(turning), 1024) * intsign(turning)); INT32 rot = R_GetRollAngle(rotate); if (rot) { workingPic = Patch_GetRotated(workingPic, rot, false); + shadowPic = Patch_GetRotated(shadowPic, rot, false); } if (!K_GetHudColor()) { - V_DrawFixedPatch(x, y, FRACUNIT, splitflags, workingPic, NULL); + V_DrawFixedPatch(x-(17*FRACUNIT), y-(38*FRACUNIT)+(FRACUNIT*2), FRACUNIT/3, splitflags|V_SUBTRACT|V_10TRANS, shadowPic, NULL); + V_DrawFixedPatch(x-(17*FRACUNIT), y-(38*FRACUNIT), FRACUNIT/3, splitflags, workingPic, NULL); } else { UINT8 *colormap; colormap = R_GetTranslationColormap(0, K_GetHudColor(), GTC_CACHE); - V_DrawFixedPatch(x, y, FRACUNIT, splitflags, workingPic, colormap); + V_DrawFixedPatch(x-(17*FRACUNIT), y-(38*FRACUNIT)+(FRACUNIT*2), FRACUNIT/3, splitflags|V_SUBTRACT|V_10TRANS, shadowPic, colormap); + V_DrawFixedPatch(x-(17*FRACUNIT), y-(38*FRACUNIT), FRACUNIT/3, splitflags, workingPic, colormap); } } } From d5fc5ab7f0890a410f02c8550c66e844b0c5de01 Mon Sep 17 00:00:00 2001 From: minenice55 Date: Sat, 28 Feb 2026 01:31:38 -0500 Subject: [PATCH 14/22] tune correction a bit --- src/g_game.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index bce70a43f..9265dcd06 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1506,7 +1506,7 @@ vector3_t localgravityvectors[MAXSPLITSCREENPLAYERS]; // when we don't trust the accel (the controller is "shaky"), how quickly do we correct our gravity vector? #define CorrectionShakyRate (FRACUNIT/10) // if our old gravity vector is close enough to our new one, limit further corrections to this proportion of the rotation speed -#define CorrectionGyroFactor (FRACUNIT/10) +#define CorrectionGyroFactor (FRACUNIT/4) // thresholds for what's considered "close enough" #define CorrectionGyroMinThreshold (5*FRACUNIT/100) #define CorrectionGyroMaxThreshold (FRACUNIT/4) @@ -1594,7 +1594,7 @@ void G_UpdateGamepadGravity(INT32 p, vector3_t gyro, vector3_t accel) } } -#define TILTRANGE (40*FRACUNIT/100) +#define TILTRANGE (45*FRACUNIT/100) fixed_t G_GetGamepadTilt(INT32 p) { fixed_t tilt; From db0afed3fd2156f50df68a65fa4877effd864797 Mon Sep 17 00:00:00 2001 From: minenice55 Date: Sun, 1 Mar 2026 00:24:55 -0500 Subject: [PATCH 15/22] implement BT_SHAKE and code cleanup --- src/d_ticcmd.h | 5 ++- src/deh_tables.c | 1 + src/g_game.c | 105 ++++++++++++++++++-------------------------- src/g_game.h | 9 ++-- src/g_input.c | 9 ++-- src/p_mobj.c | 18 ++++++++ src/sdl/i_gamepad.c | 4 ++ 7 files changed, 78 insertions(+), 73 deletions(-) diff --git a/src/d_ticcmd.h b/src/d_ticcmd.h index 59a3422e7..404356995 100644 --- a/src/d_ticcmd.h +++ b/src/d_ticcmd.h @@ -34,8 +34,9 @@ typedef enum BT_BACKWARD = 1<<6, // Aim Item Backward BT_LOOKBACK = 1<<7, // Look Backward BT_HORN = 1<<8, // Honk your (follower's) horn + BT_SHAKE = 1<<9, // Shake controller (if using IMU) - // free: 1<<9 to 1<<12 + // free: 1<<10 to 1<<12 // Lua garbage BT_CUSTOM1 = 1<<13, @@ -58,7 +59,7 @@ typedef enum #define TICCMD_RECEIVED (0x01) /* Actual tic recieved from client */ #define TICCMD_TYPING (0x02) /* chat window or console open */ #define TICCMD_KEYSTROKE (0x04) /* chat character input */ -#define TICCMD_EXCESSTILT (0x08) /* player tilting controller too far */ +#define TICCMD_USINGTILT (0x08) /* player is using tilt control */ struct ticcmd_t { diff --git a/src/deh_tables.c b/src/deh_tables.c index 72c259f3c..7783f9466 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -1383,6 +1383,7 @@ struct int_const_s const INT_CONST[] = { {"BT_BACKWARD",BT_BACKWARD}, {"BT_LOOKBACK",BT_LOOKBACK}, {"BT_HORN",BT_HORN}, + {"BT_SHAKE",BT_SHAKE}, {"BT_CUSTOM1",BT_CUSTOM1}, // Lua customizable {"BT_CUSTOM2",BT_CUSTOM2}, // Lua customizable {"BT_CUSTOM3",BT_CUSTOM3}, // Lua customizable diff --git a/src/g_game.c b/src/g_game.c index 9265dcd06..0d7ddb1f3 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1421,22 +1421,7 @@ static void G_HandleAxisDeadZone(UINT8 splitnum, joystickvector2_t *joystickvect } // copy/pasted from the lua version of these routines -static vector4_t *QuaternionMul(vector4_t *out, vector4_t *a, vector4_t *b) -{ - fixed_t ax = a->x, ay = a->y, az = a->z, aw = a->a; - fixed_t bx = b->x, by = b->y, bz = b->z, bw = b->a; - - FV4_NormalizeEx(out, FV4_Load(out, - FixedMul(aw, bx) + FixedMul(ax, bw) + FixedMul(ay, bz) - FixedMul(az, by), - FixedMul(aw, by) - FixedMul(ax, bz) + FixedMul(ay, bw) + FixedMul(az, bx), - FixedMul(aw, bz) + FixedMul(ax, by) - FixedMul(ay, bx) + FixedMul(az, bw), - FixedMul(aw, bw) - FixedMul(ax, bx) - FixedMul(ay, by) - FixedMul(az, bz) - )); - - return out; -} - -static vector3_t *QuaternionMulVec3(vector3_t *out, vector3_t *a, vector4_t *b) +inline static vector3_t *QuaternionMulVec3(vector3_t *out, vector3_t *a, vector4_t *b) { fixed_t ax = a->x, ay = a->y, az = a->z, aw = 0; fixed_t bx = b->x, by = b->y, bz = b->z, bw = b->a; @@ -1450,28 +1435,16 @@ static vector3_t *QuaternionMulVec3(vector3_t *out, vector3_t *a, vector4_t *b) return out; } -static fixed_t FV3_LengthSquared(const vector3_t *vec) +inline static fixed_t FV3_LengthSquared(const vector3_t *vec) { return FixedMul(vec->x, vec->x) + FixedMul(vec->y, vec->y) + FixedMul(vec->z, vec->z); } -static fixed_t FV3_Length(const vector3_t *vec) +inline static fixed_t FV3_Length(const vector3_t *vec) { return FixedSqrt(FV3_LengthSquared(vec)); } -static vector4_t *QuaternionInvert(vector4_t *out) -{ - FV4_Load(out, - -out->x, - -out->y, - -out->z, - out->a - ); - - return out; -} - inline static vector4_t AngleAxis(fixed_t angle, fixed_t x, fixed_t y, fixed_t z) { fixed_t sinangle = FINESINE(FixedAngle(angle/2) >> ANGLETOFINESHIFT); @@ -1531,6 +1504,8 @@ void G_UpdateGamepadGravity(INT32 p, vector3_t gyro, vector3_t accel) vector3_t gravityToAccelDirection = {0}; vector3_t correction = {0}; + if (!G_GetGamepadCanUseTilt(p)) return; + // rotate gravity vector QuaternionMulVec3(&localgravityvectors[p], &localgravityvectors[p], &invRotation); QuaternionMulVec3(&localsmoothedaccel[p], &localsmoothedaccel[p], &invRotation); @@ -1594,20 +1569,12 @@ void G_UpdateGamepadGravity(INT32 p, vector3_t gyro, vector3_t accel) } } -#define TILTRANGE (45*FRACUNIT/100) -fixed_t G_GetGamepadTilt(INT32 p) +INT32 G_GetGamepadTilt(INT32 p) { fixed_t tilt; fixed_t curve; - if (p >= MAXSPLITSCREENPLAYERS) - { -#ifdef PARANOIA - CONS_Debug(DBG_GAMELOGIC, "G_PlayerInputAnalog: Invalid player ID %d\n", p); -#endif - return 0; - } - - tilt = CLAMP(FixedDiv(G_GetGamepadGravity(p).x + TILTRANGE, (TILTRANGE + TILTRANGE)), 0, FRACUNIT); + if (!G_GetGamepadCanUseTilt(p)) return 0; + tilt = CLAMP(FixedDiv(G_GetGamepadGravity(p).x + MAXGAMEPADTILT, 2*MAXGAMEPADTILT), 0, FRACUNIT); CONS_Debug(DBG_IMU, "Tilt: %4.2f\n", FixedToFloat(tilt)); curve = FINESINE(FixedAngle(180*(tilt-FRACUNIT/2))>>ANGLETOFINESHIFT); @@ -1615,39 +1582,35 @@ fixed_t G_GetGamepadTilt(INT32 p) return (JOYAXISRANGE * curve)/FRACUNIT; } -#undef TILTRANGE vector3_t G_GetGamepadGravity(INT32 p) { - const vector3_t zero = {0}; - - if (p >= MAXSPLITSCREENPLAYERS) - { -#ifdef PARANOIA - CONS_Debug(DBG_GAMELOGIC, "G_PlayerInputAnalog: Invalid player ID %d\n", p); -#endif - return zero; - } - + const vector3_t zero = {0, -ACCELEROMETERGRAVITY, 0}; + if (!G_GetGamepadCanUseTilt(p)) return zero; return localgravityvectors[p]; } vector3_t G_GetGamepadShake(INT32 p) { - const vector3_t zero = {0}; - vector3_t accel; - + vector3_t accel = {0}; + if (!G_GetGamepadCanUseTilt(p)) return accel; + accel = G_PlayerInputSensor(p, ACCELEROMETER); + FV3_Add(&accel, &localgravityvectors[p]); + + return accel; +} + +boolean G_GetGamepadCanUseTilt(INT32 p) +{ if (p >= MAXSPLITSCREENPLAYERS) { #ifdef PARANOIA - CONS_Debug(DBG_GAMELOGIC, "G_PlayerInputAnalog: Invalid player ID %d\n", p); + CONS_Debug(DBG_GAMELOGIC, "G_GetGamepadCanUseTilt: Invalid player ID %d\n", p); #endif - return zero; + return false; } - accel = G_PlayerInputSensor(p, ACCELEROMETER); - FV3_Add(&accel, &localgravityvectors[p]); - return accel; + return (I_ControllerSupportsSensorAccelerometer(p) && I_ControllerSupportsSensorGyro(p)); } #undef Interpolator @@ -1696,7 +1659,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) INT32 forward, side, tspeed; joystickvector2_t joystickvector; - INT16 accelerometertilt; + INT32 accelerometertilt; // you'd BETTER not touch the player while freecamming... player_t *player = &players[g_localplayers[forplayer]]; @@ -1723,7 +1686,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) // update our gamepad gravity when applicable // this should always execute so we have an accurate state - if (true) //todo: check if gamepad supports accel or accel and gyro + if (G_GetGamepadCanUseTilt(forplayer)) { vector3_t accel = G_PlayerInputSensor(forplayer, ACCELEROMETER); vector3_t gyro = G_PlayerInputSensor(forplayer, GYROSCOPE); @@ -1753,7 +1716,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) accelerometertilt = 0; } - if (!cv_tiltcontrol[forplayer].value) + if (cv_tiltcontrol[forplayer].value != 1) { accelerometertilt = 0; } @@ -1920,6 +1883,17 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) cmd->buttons |= BT_HORN; } + // shaking controller shakes + if (G_GetGamepadCanUseTilt(forplayer) && cv_tiltcontrol[forplayer].value > 0) + { + vector3_t shake = G_GetGamepadShake(forplayer); + CONS_Debug(DBG_IMU, "Shake: %4.2f\n", FixedToFloat(FV3_Length(&shake))); + if (FV3_Length(&shake) >= FRACUNIT) + { + cmd->buttons |= BT_SHAKE; + } + } + // Lua scriptable buttons if (G_PlayerInputDown(forplayer, gc_custom1, false, DEADZONE_BUTTON)) cmd->buttons |= BT_CUSTOM1; @@ -1972,6 +1946,11 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) } } + if (G_GetGamepadCanUseTilt(forplayer) && (cv_tiltcontrol[forplayer].value == 1)) + { + cmd->flags |= TICCMD_USINGTILT; + } + /* Lua: Allow this hook to overwrite ticcmd. We check if we're actually in a level because for some reason this Hook would run in menus and on the titlescreen otherwise. Be aware that within this hook, nothing but this player's cmd can be edited (otherwise we'd run in some pretty bad synching problems since this is clientsided, or something) diff --git a/src/g_game.h b/src/g_game.h index bddde7852..f07ac3dfd 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -141,12 +141,13 @@ typedef enum GYROSCOPE, } motionsensortype_e; -#define MAXGAMEPADTILT (ANG30) -#define ACCEL_GRAVITY (FLOAT_TO_FIXED(9.80665f)) +#define MAXGAMEPADTILT (45*FRACUNIT/100) +#define ACCELEROMETERGRAVITY FLOAT_TO_FIXED(9.80665f) +boolean G_GetGamepadCanUseTilt(INT32 p); void G_UpdateGamepadGravity(INT32 p, vector3_t gyro, vector3_t accel); -vector3_t G_GetGamepadGravity(INT32 p); +INT32 G_GetGamepadTilt(INT32 p); vector3_t G_GetGamepadShake(INT32 p); -fixed_t G_GetGamepadTilt(INT32 p); +vector3_t G_GetGamepadGravity(INT32 p); vector3_t G_PlayerInputSensor(UINT8 p, motionsensortype_e sensor); fixed_t G_GetDeadZoneType(INT32 p, SINT8 type); diff --git a/src/g_input.c b/src/g_input.c index a1548e2ae..e23f3615a 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -90,11 +90,12 @@ consvar_t cv_controllerled[MAXSPLITSCREENPLAYERS] = { CVAR_INIT ("gamepadled4", "Skincolor", CV_SAVE|CV_CALL|CV_NOINIT, controllerled_cons_t, led_off_handle4) }; +static CV_PossibleValue_t tiltcontrol_cons_t[] = {{0, "Off"}, {1, "On"}, {2, "Shakes Only"}, {0, NULL}}; consvar_t cv_tiltcontrol[MAXSPLITSCREENPLAYERS] = { - CVAR_INIT ("tiltcontrol", "Off", CV_SAVE, CV_OnOff, NULL), - CVAR_INIT ("tiltcontrol2", "Off", CV_SAVE, CV_OnOff, NULL), - CVAR_INIT ("tiltcontrol3", "Off", CV_SAVE, CV_OnOff, NULL), - CVAR_INIT ("tiltcontrol4", "Off", CV_SAVE, CV_OnOff, NULL) + CVAR_INIT ("tiltcontrol", "Off", CV_SAVE, tiltcontrol_cons_t, NULL), + CVAR_INIT ("tiltcontrol2", "Off", CV_SAVE, tiltcontrol_cons_t, NULL), + CVAR_INIT ("tiltcontrol3", "Off", CV_SAVE, tiltcontrol_cons_t, NULL), + CVAR_INIT ("tiltcontrol4", "Off", CV_SAVE, tiltcontrol_cons_t, NULL) }; static void rumble_off_handle(void) diff --git a/src/p_mobj.c b/src/p_mobj.c index f55f8f95b..8f79e6009 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -10036,6 +10036,24 @@ static boolean P_MobjRegularThink(mobj_t *mobj) mobj->lastlook = player->cmd.turning; } + if (player->cmd.buttons & BT_SHAKE) + { + if (mobj->extravalue3 == 0) mobj->extravalue3 = 1; + + if (mobj->extravalue2 == 0) + { + mobj->movecount += (TICRATE/2); + mobj->threshold = 16*mobj->extravalue3; + S_StartSound(mobj, sfx_s1ab); + } + mobj->extravalue2 = 1; + mobj->extravalue3 *= -1; + } + else + { + mobj->extravalue2 = 0; + } + mobj->movecount++; } else if (mobj->extravalue1) // lost your player somehow, DIE diff --git a/src/sdl/i_gamepad.c b/src/sdl/i_gamepad.c index b2b43b472..fa46597c1 100644 --- a/src/sdl/i_gamepad.c +++ b/src/sdl/i_gamepad.c @@ -172,6 +172,10 @@ void I_InitController(UINT8 playernum) if (M_CheckParm("-nolibusb")) SDL_SetHintWithPriority("SDL_HIDAPI_LIBUSB", "0", SDL_HINT_OVERRIDE); + + // need to figure out how to get it to recognize Motion Plus + // if (!M_CheckParm("-nohidapiwii")) + // SDL_SetHintWithPriority("SDL_JOYSTICK_HIDAPI_WII", "1", SDL_HINT_OVERRIDE); if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0) { From 093a4fb96912371b977cb6c472ec70f05db5b596 Mon Sep 17 00:00:00 2001 From: minenice55 Date: Sun, 1 Mar 2026 00:43:01 -0500 Subject: [PATCH 16/22] add tilt control cvar to menus --- src/d_main.cpp | 2 +- src/m_menu.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/d_main.cpp b/src/d_main.cpp index fda0121ef..a20a80b27 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -93,7 +93,7 @@ #define ASSET_HASH_TEXTURES_KART 0xb4211b2f32b6a291 #define ASSET_HASH_CHARS_KART 0x1e68a3e01aa5c68b #define ASSET_HASH_MAPS_KART 0x38558ed00da41ce9 -#define ASSET_HASH_MAIN_PK3 0xac00f1a74761e9bd +#define ASSET_HASH_MAIN_PK3 0x77c6be959d682d85 #define ASSET_HASH_MAPPATCH_PK3 0x1745690024efbaf8 #define ASSET_HASH_BONUSCHARS_KART 0x60e6f13d822a7461 #ifdef USE_PATCH_FILE diff --git a/src/m_menu.c b/src/m_menu.c index ac1365536..d26ec3ba2 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -8592,6 +8592,7 @@ INT32 MR_SetupControlsMenu(INT32 arg) M_SetItemCvar(MN_OP_CHANGECONTROLS, "LEDCOLOR", &cv_controllerled[arg]); M_SetItemCvar(MN_OP_CHANGECONTROLS, "RUMBLE", &cv_controllerrumble[arg]); M_SetItemCvar(MN_OP_CHANGECONTROLS, "RUMBLESTR", &cv_rumblestrength[arg]); + M_SetItemCvar(MN_OP_CHANGECONTROLS, "TILTCONTROL", &cv_tiltcontrol[arg]); M_SetItemVisible(MN_OP_CHANGECONTROLS, "TALK", player1); // Chat //M_SetItemVisible(MN_OP_CHANGECONTROLS, "TEAM", player1); // Team-chat From d4b62c7fa2e7547252741e02e1b15db3b2086a22 Mon Sep 17 00:00:00 2001 From: minenice55 Date: Mon, 2 Mar 2026 00:48:04 -0500 Subject: [PATCH 17/22] replace BT_SHAKE with a dedicated shake value also adds a tilt value implements a GT7-style wheel dot when tilt steering is being used --- src/d_ticcmd.h | 9 ++++++--- src/deh_tables.c | 12 +++++++++++- src/g_demo.c | 20 +++++++++++++++++++ src/g_game.c | 48 +++++++++++++++++++++++++++------------------ src/g_game.h | 5 +++-- src/k_hud.c | 23 +++++++++++++++++++++- src/lua_playerlib.c | 14 +++++++++++++ src/p_mobj.c | 2 +- 8 files changed, 106 insertions(+), 27 deletions(-) diff --git a/src/d_ticcmd.h b/src/d_ticcmd.h index 404356995..8075344ef 100644 --- a/src/d_ticcmd.h +++ b/src/d_ticcmd.h @@ -34,9 +34,8 @@ typedef enum BT_BACKWARD = 1<<6, // Aim Item Backward BT_LOOKBACK = 1<<7, // Look Backward BT_HORN = 1<<8, // Honk your (follower's) horn - BT_SHAKE = 1<<9, // Shake controller (if using IMU) - // free: 1<<10 to 1<<12 + // free: 1<<9 to 1<<12 // Lua garbage BT_CUSTOM1 = 1<<13, @@ -59,8 +58,10 @@ typedef enum #define TICCMD_RECEIVED (0x01) /* Actual tic recieved from client */ #define TICCMD_TYPING (0x02) /* chat window or console open */ #define TICCMD_KEYSTROKE (0x04) /* chat character input */ -#define TICCMD_USINGTILT (0x08) /* player is using tilt control */ +#define TICCMD_USINGTILT (0x08) /* player is using tilt control instead of the stick/buttons to turn */ +#define TICCMD_EXCESSTILT (0x10) /* player is tilting gamepad farther than the max range */ +// 16 bytes long now! struct ticcmd_t { SINT8 forwardmove; // -MAXPLMOVE to MAXPLMOVE (50) @@ -72,6 +73,8 @@ struct ticcmd_t UINT16 buttons; UINT8 latency; // Netgames: how many tics ago was this ticcmd generated from this player's end? UINT8 flags; + SINT8 tilt; + UINT8 shake; } ATTRPACK; #ifdef __cplusplus diff --git a/src/deh_tables.c b/src/deh_tables.c index 7783f9466..8a4e7aa81 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -1383,7 +1383,6 @@ struct int_const_s const INT_CONST[] = { {"BT_BACKWARD",BT_BACKWARD}, {"BT_LOOKBACK",BT_LOOKBACK}, {"BT_HORN",BT_HORN}, - {"BT_SHAKE",BT_SHAKE}, {"BT_CUSTOM1",BT_CUSTOM1}, // Lua customizable {"BT_CUSTOM2",BT_CUSTOM2}, // Lua customizable {"BT_CUSTOM3",BT_CUSTOM3}, // Lua customizable @@ -1843,6 +1842,17 @@ struct int_const_s const INT_CONST[] = { {"TIMER_PINK", TIMER_PINK}, {"TIMER_BROWN", TIMER_BROWN}, {"TIMER_TAN", TIMER_TAN}, + + // motion sensor related constants + {"MAXGAMEPADTILT", MAXGAMEPADTILT}, + {"GAMEPADSHAKETHRESHOLD", GAMEPADSHAKETHRESHOLD}, + {"ACCELEROMETERGRAVITY", ACCELEROMETERGRAVITY}, + + //ticcmd constants + {"TICCMD_TYPING", TICCMD_TYPING}, + {"TICCMD_KEYSTROKE", TICCMD_KEYSTROKE}, + {"TICCMD_USINGTILT", TICCMD_USINGTILT}, + {"TICCMD_EXCESSTILT", TICCMD_EXCESSTILT}, {NULL,0} }; diff --git a/src/g_demo.c b/src/g_demo.c index b4c9b0b9d..2864c223f 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -144,6 +144,8 @@ demoghost *ghosts = NULL; #define ZT_AIMING 0x0040 #define ZT_LATENCY 0x0080 #define ZT_FLAGS 0x0100 +#define ZT_IMUTILT 0x0200 +#define ZT_IMUSHAKE 0x0400 // Ziptics are UINT16 now, go nuts #define DEMOMARKER 0x80 // demobuf.end @@ -400,6 +402,10 @@ static UINT8 *G_ReadZipTic(ticcmd_t *cmd, UINT8 *dp, UINT16 version) cmd->latency = READUINT8(dp); if (ziptic & ZT_FLAGS) cmd->flags = READUINT8(dp); + if (ziptic & ZT_IMUTILT) + cmd->tilt = READSINT8(dp); + if (ziptic & ZT_IMUSHAKE) + cmd->shake = READUINT8(dp); if (version < 0x000a && ziptic & 0x8000) // ZT_BOT { @@ -1583,6 +1589,20 @@ void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum) ziptic |= ZT_FLAGS; } + if (cmd->tilt != oldcmd[playernum].tilt) + { + WRITESINT8(demobuf.p,cmd->tilt); + oldcmd[playernum].tilt = cmd->tilt; + ziptic |= ZT_IMUTILT; + } + + if (cmd->shake != oldcmd[playernum].shake) + { + WRITESINT8(demobuf.p,cmd->shake); + oldcmd[playernum].shake = cmd->shake; + ziptic |= ZT_IMUSHAKE; + } + WRITEUINT16(ziptic_p, ziptic); // attention here for the ticcmd size! diff --git a/src/g_game.c b/src/g_game.c index 0d7ddb1f3..3b76b7ed6 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1472,19 +1472,19 @@ vector3_t localsmoothedaccel[MAXSPLITSCREENPLAYERS]; vector3_t localgravityvectors[MAXSPLITSCREENPLAYERS]; // thresholds of trust for accel shakiness. less shakiness = more trust -#define ShakinessMaxThreshold (4*FRACUNIT/10) +#define ShakinessMaxThreshold (6*FRACUNIT/10) #define ShakinessMinThreshold (1*FRACUNIT/100) // when we trust the accel a lot (the controller is "still"), how quickly do we correct our gravity vector? #define CorrectionStillRate (FRACUNIT) // when we don't trust the accel (the controller is "shaky"), how quickly do we correct our gravity vector? #define CorrectionShakyRate (FRACUNIT/10) // if our old gravity vector is close enough to our new one, limit further corrections to this proportion of the rotation speed -#define CorrectionGyroFactor (FRACUNIT/4) +#define CorrectionGyroFactor (FRACUNIT/6) // thresholds for what's considered "close enough" #define CorrectionGyroMinThreshold (5*FRACUNIT/100) #define CorrectionGyroMaxThreshold (FRACUNIT/4) // no matter what, always apply a minimum of this much correction to our gravity vector -#define CorrectionMinimumSpeed (FRACUNIT/6) +#define CorrectionMinimumSpeed (FRACUNIT/100) void G_UpdateGamepadGravity(INT32 p, vector3_t gyro, vector3_t accel) { @@ -1686,6 +1686,9 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) // update our gamepad gravity when applicable // this should always execute so we have an accurate state + // interesting minutae: + // while every IMU event is sent to our event system only the last one before a tick is run seems to be the sample used + // seems OK for now, may be an issue later? could look into averaging out samples later if we need to improve feel if (G_GetGamepadCanUseTilt(forplayer)) { vector3_t accel = G_PlayerInputSensor(forplayer, ACCELEROMETER); @@ -1883,17 +1886,6 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) cmd->buttons |= BT_HORN; } - // shaking controller shakes - if (G_GetGamepadCanUseTilt(forplayer) && cv_tiltcontrol[forplayer].value > 0) - { - vector3_t shake = G_GetGamepadShake(forplayer); - CONS_Debug(DBG_IMU, "Shake: %4.2f\n", FixedToFloat(FV3_Length(&shake))); - if (FV3_Length(&shake) >= FRACUNIT) - { - cmd->buttons |= BT_SHAKE; - } - } - // Lua scriptable buttons if (G_PlayerInputDown(forplayer, gc_custom1, false, DEADZONE_BUTTON)) cmd->buttons |= BT_CUSTOM1; @@ -1946,11 +1938,6 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) } } - if (G_GetGamepadCanUseTilt(forplayer) && (cv_tiltcontrol[forplayer].value == 1)) - { - cmd->flags |= TICCMD_USINGTILT; - } - /* Lua: Allow this hook to overwrite ticcmd. We check if we're actually in a level because for some reason this Hook would run in menus and on the titlescreen otherwise. Be aware that within this hook, nothing but this player's cmd can be edited (otherwise we'd run in some pretty bad synching problems since this is clientsided, or something) @@ -1995,6 +1982,27 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) else if (cmd->throwdir < -KART_FULLTURN) cmd->throwdir = -KART_FULLTURN; + if (G_GetGamepadCanUseTilt(forplayer) && cv_tiltcontrol[forplayer].value) + { + fixed_t tilt = G_GetGamepadGravity(forplayer).x; + vector3_t shake = G_GetGamepadShake(forplayer); + + if (cv_tiltcontrol[forplayer].value == 1) + { + cmd->tilt = INT8_MAX*tilt/FRACUNIT; + + if (!joystickactive[forplayer]) + cmd->flags |= TICCMD_USINGTILT; + if (abs(tilt) > MAXGAMEPADTILT) + cmd->flags |= TICCMD_EXCESSTILT; + } + + CONS_Debug(DBG_IMU, "Shake: %4.2f\n", FixedToFloat(FV3_Length(&shake))); + cmd->shake = CLAMP((GAMEPADSHAKETHRESHOLD*FV3_Length(&shake))/FRACUNIT, 0, UINT8_MAX); + + CONS_Debug(DBG_IMU, "CMD Tilt: %d, Shake: %d\n", cmd->tilt, cmd->shake); + } + G_DoCameraTurn(cmd, realtics, ssplayer, player); // Reset away view if a command is given. @@ -2027,6 +2035,8 @@ ticcmd_t *G_MoveTiccmd(ticcmd_t* dest, const ticcmd_t* src, const size_t n) dest[i].buttons = (UINT16)SHORT(src[i].buttons); dest[i].latency = src[i].latency; dest[i].flags = src[i].flags; + dest[i].tilt = src[i].tilt; + dest[i].shake = src[i].shake; } return dest; } diff --git a/src/g_game.h b/src/g_game.h index f07ac3dfd..7ef3b66c6 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -141,8 +141,9 @@ typedef enum GYROSCOPE, } motionsensortype_e; -#define MAXGAMEPADTILT (45*FRACUNIT/100) -#define ACCELEROMETERGRAVITY FLOAT_TO_FIXED(9.80665f) +#define MAXGAMEPADTILT (50*FRACUNIT/100) +#define ACCELEROMETERGRAVITY ((fixed_t)(9.80665f * ((float)FRACUNIT))) +#define GAMEPADSHAKETHRESHOLD (UINT8_MAX/2) boolean G_GetGamepadCanUseTilt(INT32 p); void G_UpdateGamepadGravity(INT32 p, vector3_t gyro, vector3_t accel); INT32 G_GetGamepadTilt(INT32 p); diff --git a/src/k_hud.c b/src/k_hud.c index 23e00ab63..34fa2503d 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -5476,9 +5476,12 @@ static void K_drawInput(void) { patch_t *workingPic = kp_inputwheel; patch_t *shadowPic = kp_inputwheel_shadow; + INT16 turning = encoremode ? -stplyr->cmd.turning : stplyr->cmd.turning; + SINT8 tilt = encoremode ? -stplyr->cmd.tilt : stplyr->cmd.tilt; angle_t rotate = FixedAngle(60 * FixedDiv(abs(turning), 1024) * intsign(turning)); INT32 rot = R_GetRollAngle(rotate); + UINT8 *colormap; if (rot) { @@ -5493,11 +5496,29 @@ static void K_drawInput(void) } else { - UINT8 *colormap; colormap = R_GetTranslationColormap(0, K_GetHudColor(), GTC_CACHE); V_DrawFixedPatch(x-(17*FRACUNIT), y-(38*FRACUNIT)+(FRACUNIT*2), FRACUNIT/3, splitflags|V_SUBTRACT|V_10TRANS, shadowPic, colormap); V_DrawFixedPatch(x-(17*FRACUNIT), y-(38*FRACUNIT), FRACUNIT/3, splitflags, workingPic, colormap); } + + // a la GT7 + if (stplyr->cmd.flags & TICCMD_USINGTILT) + { + rot = FixedAngle(60 * ((tilt < 0) ? FixedDiv(-tilt, -(50*INT8_MIN/100)) : FixedDiv(tilt, (50*INT8_MAX/100)))); + x += 17*FINESINE(rot>>ANGLETOFINESHIFT) * intsign(tilt); + y -= 17*FINECOSINE(rot>>ANGLETOFINESHIFT); + splitflags |= (((leveltime % 3 == 0) && (stplyr->cmd.flags & TICCMD_EXCESSTILT)) ? V_ADD|V_20TRANS : 0); + + if (!K_GetHudColor()) + { + V_DrawFixedPatch(x-(2*FRACUNIT)-(FRACUNIT/2), y-(23*FRACUNIT), FRACUNIT, splitflags, kp_minimapdot, NULL); + } + else + { + colormap = R_GetTranslationColormap(0, K_GetHudColor(), GTC_CACHE); + V_DrawFixedPatch(x-(2*FRACUNIT)-(FRACUNIT/2), y-(23*FRACUNIT), FRACUNIT, splitflags, kp_minimapdot, colormap); + } + } } } diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index f85e658af..338b9a280 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -2664,6 +2664,8 @@ enum ticcmd_e ticcmd_buttons, ticcmd_latency, ticcmd_flags, + ticcmd_tilt, + ticcmd_shake, }; static const char *const ticcmd_opt[] = { @@ -2678,6 +2680,8 @@ static const char *const ticcmd_opt[] = { "buttons", "latency", "flags", + "tilt", + "shake", NULL, }; @@ -2722,6 +2726,12 @@ static int ticcmd_get(lua_State *L) case ticcmd_flags: lua_pushinteger(L, cmd->flags); break; + case ticcmd_tilt: + lua_pushinteger(L, cmd->tilt); + break; + case ticcmd_shake: + lua_pushinteger(L, cmd->shake); + break; default: return NOFIELD; } @@ -2768,6 +2778,10 @@ static int ticcmd_set(lua_State *L) return NOSET; case ticcmd_flags: return NOSET; + case ticcmd_tilt: + cmd->tilt = (SINT8)luaL_checkinteger(L, 3); + case ticcmd_shake: + cmd->shake = (UINT8)luaL_checkinteger(L, 3); default: return NOFIELD; } diff --git a/src/p_mobj.c b/src/p_mobj.c index 8f79e6009..b7757184e 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -10036,7 +10036,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj) mobj->lastlook = player->cmd.turning; } - if (player->cmd.buttons & BT_SHAKE) + if (player->cmd.shake > GAMEPADSHAKETHRESHOLD) { if (mobj->extravalue3 == 0) mobj->extravalue3 = 1; From f0e5b426196a94b5a40842d18f6ed3bac3540aad Mon Sep 17 00:00:00 2001 From: minenice55 Date: Mon, 2 Mar 2026 17:11:05 -0500 Subject: [PATCH 18/22] average out all IMU samples received for a tick instead of only using the last one --- src/d_main.cpp | 15 +++++++++------ src/g_game.c | 17 ++++++----------- src/g_input.c | 14 ++++++++------ 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/d_main.cpp b/src/d_main.cpp index a20a80b27..53a400ff0 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -221,12 +221,15 @@ void D_ProcessEvents(void) for (size_t i = 0; i < 4; i++) gamekeydown[0][KEY_MOUSEMOVE + i] = 0; - // for (size_t dev = 0; dev < MAXDEVICES; dev++) - // { - // gamekeydown[dev][KEY_GYROSCOPE1+0] = 0; - // gamekeydown[dev][KEY_GYROSCOPE1+1] = 0; - // gamekeydown[dev][KEY_GYROSCOPE1+2] = 0; - // } + for (size_t dev = 0; dev < MAXDEVICES; dev++) + { + gamekeydown[dev][KEY_ACCELEROMETER1+0] = 0; + gamekeydown[dev][KEY_ACCELEROMETER1+1] = 0; + gamekeydown[dev][KEY_ACCELEROMETER1+2] = 0; + gamekeydown[dev][KEY_GYROSCOPE1+0] = 0; + gamekeydown[dev][KEY_GYROSCOPE1+1] = 0; + gamekeydown[dev][KEY_GYROSCOPE1+2] = 0; + } for (; eventtail != eventhead; eventtail = (eventtail+1) & (MAXEVENTS-1)) { diff --git a/src/g_game.c b/src/g_game.c index 3b76b7ed6..93bc615a1 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1472,19 +1472,19 @@ vector3_t localsmoothedaccel[MAXSPLITSCREENPLAYERS]; vector3_t localgravityvectors[MAXSPLITSCREENPLAYERS]; // thresholds of trust for accel shakiness. less shakiness = more trust -#define ShakinessMaxThreshold (6*FRACUNIT/10) -#define ShakinessMinThreshold (1*FRACUNIT/100) +#define ShakinessMaxThreshold (80*FRACUNIT/100) +#define ShakinessMinThreshold (32*FRACUNIT/100) // when we trust the accel a lot (the controller is "still"), how quickly do we correct our gravity vector? #define CorrectionStillRate (FRACUNIT) // when we don't trust the accel (the controller is "shaky"), how quickly do we correct our gravity vector? -#define CorrectionShakyRate (FRACUNIT/10) +#define CorrectionShakyRate (FRACUNIT/8) // if our old gravity vector is close enough to our new one, limit further corrections to this proportion of the rotation speed -#define CorrectionGyroFactor (FRACUNIT/6) +#define CorrectionGyroFactor (FRACUNIT/5) // thresholds for what's considered "close enough" -#define CorrectionGyroMinThreshold (5*FRACUNIT/100) +#define CorrectionGyroMinThreshold (2*FRACUNIT/100) #define CorrectionGyroMaxThreshold (FRACUNIT/4) // no matter what, always apply a minimum of this much correction to our gravity vector -#define CorrectionMinimumSpeed (FRACUNIT/100) +#define CorrectionMinimumSpeed (9*FRACUNIT/100) void G_UpdateGamepadGravity(INT32 p, vector3_t gyro, vector3_t accel) { @@ -1612,8 +1612,6 @@ boolean G_GetGamepadCanUseTilt(INT32 p) return (I_ControllerSupportsSensorAccelerometer(p) && I_ControllerSupportsSensorGyro(p)); } - -#undef Interpolator #undef ShakinessMaxThreshold #undef ShakinessMinThreshold #undef CorrectionStillRate @@ -1686,9 +1684,6 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) // update our gamepad gravity when applicable // this should always execute so we have an accurate state - // interesting minutae: - // while every IMU event is sent to our event system only the last one before a tick is run seems to be the sample used - // seems OK for now, may be an issue later? could look into averaging out samples later if we need to improve feel if (G_GetGamepadCanUseTilt(forplayer)) { vector3_t accel = G_PlayerInputSensor(forplayer, ACCELEROMETER); diff --git a/src/g_input.c b/src/g_input.c index e23f3615a..993679ed0 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -343,15 +343,17 @@ void G_MapEventsToControls(event_t *ev) break; case ev_accelerometer: - gamekeydown[device][KEY_ACCELEROMETER1+0] = ev->data1; - gamekeydown[device][KEY_ACCELEROMETER1+1] = ev->data2; - gamekeydown[device][KEY_ACCELEROMETER1+2] = ev->data3; + // average out accel events recieved for this tick (favouring the newer data) + gamekeydown[device][KEY_ACCELEROMETER1+0] = (gamekeydown[device][KEY_ACCELEROMETER1+0] + (3*ev->data1))/4; + gamekeydown[device][KEY_ACCELEROMETER1+1] = (gamekeydown[device][KEY_ACCELEROMETER1+1] + (3*ev->data2))/4; + gamekeydown[device][KEY_ACCELEROMETER1+2] = (gamekeydown[device][KEY_ACCELEROMETER1+2] + (3*ev->data3))/4; break; case ev_gyroscope: - gamekeydown[device][KEY_GYROSCOPE1+0] = ev->data1; - gamekeydown[device][KEY_GYROSCOPE1+1] = ev->data2; - gamekeydown[device][KEY_GYROSCOPE1+2] = ev->data3; + // average out gyro events recieved for this tick + gamekeydown[device][KEY_GYROSCOPE1+0] = (gamekeydown[device][KEY_GYROSCOPE1+0] + ev->data1)/2; + gamekeydown[device][KEY_GYROSCOPE1+1] = (gamekeydown[device][KEY_GYROSCOPE1+1] + ev->data2)/2; + gamekeydown[device][KEY_GYROSCOPE1+2] = (gamekeydown[device][KEY_GYROSCOPE1+2] + ev->data3)/2; break; default: From d03f65c5f6d5ec35134dd0496b92b4162e26114d Mon Sep 17 00:00:00 2001 From: minenice55 Date: Wed, 4 Mar 2026 00:06:42 -0500 Subject: [PATCH 19/22] small deadzone for player sprite turndir --- src/d_main.cpp | 2 +- src/k_kart.c | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/d_main.cpp b/src/d_main.cpp index 53a400ff0..c67f48c61 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -93,7 +93,7 @@ #define ASSET_HASH_TEXTURES_KART 0xb4211b2f32b6a291 #define ASSET_HASH_CHARS_KART 0x1e68a3e01aa5c68b #define ASSET_HASH_MAPS_KART 0x38558ed00da41ce9 -#define ASSET_HASH_MAIN_PK3 0x77c6be959d682d85 +#define ASSET_HASH_MAIN_PK3 0x19ee86358d2cd9e9 #define ASSET_HASH_MAPPATCH_PK3 0x1745690024efbaf8 #define ASSET_HASH_BONUSCHARS_KART 0x60e6f13d822a7461 #ifdef USE_PATCH_FILE diff --git a/src/k_kart.c b/src/k_kart.c index e42b0cde9..347e26cbf 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1813,7 +1813,7 @@ void K_KartMoveAnimation(player_t *player) const boolean lookback = ((buttons & BT_LOOKBACK) == BT_LOOKBACK); const boolean skincompat = wadfiles[((skin_t *)player->mo->skin)->wadnum]->compatmode; - SINT8 turndir = intsign(player->cmd.turning); + SINT8 turndir = 0; SINT8 destGlanceDir = 0; SINT8 drift = player->drift; UINT8 spr2, glanceofs; @@ -1823,6 +1823,11 @@ void K_KartMoveAnimation(player_t *player) if (!lookback) player->pflags &= ~PF_GAINAX; + + if (drift || (abs(player->cmd.turning) > (10*KART_FULLTURN/100))) + { + turndir = intsign(player->cmd.turning); + } // Sliptides: drift -> lookback frames if (abs(player->aizdriftturn) >= ANGLE_90 && !skincompat) From 668f31ca164f87762d77f59af5c6fc7690b8a2e0 Mon Sep 17 00:00:00 2001 From: minenice55 Date: Sat, 7 Mar 2026 00:54:22 -0500 Subject: [PATCH 20/22] make wheel display use affine transform for rotation --- src/d_main.cpp | 2 +- src/k_hud.c | 21 +++++++-------------- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/src/d_main.cpp b/src/d_main.cpp index c67f48c61..fdc656bd8 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -93,7 +93,7 @@ #define ASSET_HASH_TEXTURES_KART 0xb4211b2f32b6a291 #define ASSET_HASH_CHARS_KART 0x1e68a3e01aa5c68b #define ASSET_HASH_MAPS_KART 0x38558ed00da41ce9 -#define ASSET_HASH_MAIN_PK3 0x19ee86358d2cd9e9 +#define ASSET_HASH_MAIN_PK3 0xceb926d00133de51 #define ASSET_HASH_MAPPATCH_PK3 0x1745690024efbaf8 #define ASSET_HASH_BONUSCHARS_KART 0x60e6f13d822a7461 #ifdef USE_PATCH_FILE diff --git a/src/k_hud.c b/src/k_hud.c index c70256c86..2766f357c 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -5455,33 +5455,26 @@ static void K_drawInput(void) INT16 turning = encoremode ? -stplyr->cmd.turning : stplyr->cmd.turning; SINT8 tilt = encoremode ? -stplyr->cmd.tilt : stplyr->cmd.tilt; angle_t rotate = FixedAngle(60 * FixedDiv(abs(turning), 1024) * intsign(turning)); - INT32 rot = R_GetRollAngle(rotate); UINT8 *colormap; - - if (rot) - { - workingPic = Patch_GetRotated(workingPic, rot, false); - shadowPic = Patch_GetRotated(shadowPic, rot, false); - } if (!K_GetHudColor()) { - V_DrawFixedPatch(x-(17*FRACUNIT), y-(38*FRACUNIT)+(FRACUNIT*2), FRACUNIT/3, splitflags|V_SUBTRACT|V_10TRANS, shadowPic, NULL); - V_DrawFixedPatch(x-(17*FRACUNIT), y-(38*FRACUNIT), FRACUNIT/3, splitflags, workingPic, NULL); + V_DrawRotatedPatch(x-(17*FRACUNIT), y-(38*FRACUNIT)+(FRACUNIT*2), rotate, FRACUNIT, FRACUNIT, splitflags|V_SUBTRACT|V_10TRANS, shadowPic, NULL); + V_DrawRotatedPatch(x-(17*FRACUNIT), y-(38*FRACUNIT), rotate, FRACUNIT, FRACUNIT, splitflags, workingPic, NULL); } else { colormap = R_GetTranslationColormap(0, K_GetHudColor(), GTC_CACHE); - V_DrawFixedPatch(x-(17*FRACUNIT), y-(38*FRACUNIT)+(FRACUNIT*2), FRACUNIT/3, splitflags|V_SUBTRACT|V_10TRANS, shadowPic, colormap); - V_DrawFixedPatch(x-(17*FRACUNIT), y-(38*FRACUNIT), FRACUNIT/3, splitflags, workingPic, colormap); + V_DrawRotatedPatch(x-(17*FRACUNIT), y-(38*FRACUNIT)+(FRACUNIT*2), rotate, FRACUNIT, FRACUNIT, splitflags|V_SUBTRACT|V_10TRANS, shadowPic, colormap); + V_DrawRotatedPatch(x-(17*FRACUNIT), y-(38*FRACUNIT), rotate, FRACUNIT, FRACUNIT, splitflags, workingPic, colormap); } // a la GT7 if (stplyr->cmd.flags & TICCMD_USINGTILT) { - rot = FixedAngle(60 * ((tilt < 0) ? FixedDiv(-tilt, -(50*INT8_MIN/100)) : FixedDiv(tilt, (50*INT8_MAX/100)))); - x += 17*FINESINE(rot>>ANGLETOFINESHIFT) * intsign(tilt); - y -= 17*FINECOSINE(rot>>ANGLETOFINESHIFT); + rotate = FixedAngle(60 * ((tilt < 0) ? FixedDiv(-tilt, -(50*INT8_MIN/100)) : FixedDiv(tilt, (50*INT8_MAX/100)))); + x += 17*FINESINE(rotate>>ANGLETOFINESHIFT) * intsign(tilt); + y -= 17*FINECOSINE(rotate>>ANGLETOFINESHIFT); splitflags |= (((leveltime % 3 == 0) && (stplyr->cmd.flags & TICCMD_EXCESSTILT)) ? V_ADD|V_20TRANS : 0); if (!K_GetHudColor()) From 32e3c3175091015712bd0df6cdec589c082f106a Mon Sep 17 00:00:00 2001 From: minenice55 Date: Sat, 7 Mar 2026 03:02:02 -0500 Subject: [PATCH 21/22] stick-and-tilt blending wip --- src/g_game.c | 45 +++++++++++++++++++-------------------------- src/g_game.h | 1 + 2 files changed, 20 insertions(+), 26 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index bd2aadf73..8773765f6 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1652,7 +1652,6 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) static INT32 turnheld[MAXSPLITSCREENPLAYERS]; // for accelerative turning static boolean resetdown[MAXSPLITSCREENPLAYERS]; // don't cam reset every frame - static boolean joystickactive[MAXSPLITSCREENPLAYERS]; // don't use accelerometer tilt if we're alrady turning with stick INT32 forward, side, tspeed; @@ -1717,19 +1716,15 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) } G_HandleAxisDeadZone(forplayer, &joystickvector); - if (joystickvector.xaxis != 0) + // tilt control never has deadzone + // if stick input is used gradually cancel out tilt based on stick intensity + if (cv_tiltcontrol[forplayer].value == 1) { - joystickactive[forplayer] = true; - accelerometertilt = 0; - } - - if (cv_tiltcontrol[forplayer].value != 1) - { - accelerometertilt = 0; - } - else if (!joystickactive[forplayer]) - { - accelerometertilt = G_GetGamepadTilt(forplayer); + accelerometertilt = joystickvector.xaxis + + FixedMul( + FRACUNIT-FixedDiv(joystickvector.xaxis, JOYAXISRANGE), + G_GetGamepadTilt(forplayer)*FRACUNIT + )/FRACUNIT; } // For kart, I've turned the aim axis into a digital axis because we only @@ -1750,13 +1745,13 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) } forward = side = 0; - if (joystickactive[forplayer]) + if (cv_tiltcontrol[forplayer].value == 1) { - tspeed = joystickvector.xaxis; + tspeed = accelerometertilt; } else { - tspeed = accelerometertilt; + tspeed = joystickvector.xaxis; } // use two stage accelerative turning @@ -1779,23 +1774,22 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) else { turnheld[forplayer] = 0; - joystickactive[forplayer] = false; } cmd->turning = 0; - if (joystickvector.xaxis != 0) - { - cmd->turning -= (tspeed * KART_FULLTURN) / JOYAXISRANGE; - cmd->angle -= (tspeed * KART_FULLTURN) / JOYAXISRANGE; - side += (joystickvector.xaxis * 4) / JOYAXISRANGE; - } - else if (accelerometertilt != 0) + if (cv_tiltcontrol[forplayer].value == 1) { cmd->turning -= (tspeed * KART_FULLTURN) / JOYAXISRANGE; cmd->angle -= (tspeed * KART_FULLTURN) / JOYAXISRANGE; side += (accelerometertilt * 4) / JOYAXISRANGE; } + else if (joystickvector.xaxis != 0) + { + cmd->turning -= (tspeed * KART_FULLTURN) / JOYAXISRANGE; + cmd->angle -= (tspeed * KART_FULLTURN) / JOYAXISRANGE; + side += (joystickvector.xaxis * 4) / JOYAXISRANGE; + } // Specator mouse turning if (spectating) @@ -2000,8 +1994,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) { cmd->tilt = INT8_MAX*tilt/FRACUNIT; - if (!joystickactive[forplayer]) - cmd->flags |= TICCMD_USINGTILT; + cmd->flags |= TICCMD_USINGTILT; if (abs(tilt) > MAXGAMEPADTILT) cmd->flags |= TICCMD_EXCESSTILT; } diff --git a/src/g_game.h b/src/g_game.h index 7ef3b66c6..d976277c4 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -144,6 +144,7 @@ typedef enum #define MAXGAMEPADTILT (50*FRACUNIT/100) #define ACCELEROMETERGRAVITY ((fixed_t)(9.80665f * ((float)FRACUNIT))) #define GAMEPADSHAKETHRESHOLD (UINT8_MAX/2) +#define TILTTOSTICKEASE 6 boolean G_GetGamepadCanUseTilt(INT32 p); void G_UpdateGamepadGravity(INT32 p, vector3_t gyro, vector3_t accel); INT32 G_GetGamepadTilt(INT32 p); From 02fffa75dea3f97a415de3af8506e4cd3c4d23a8 Mon Sep 17 00:00:00 2001 From: minenice55 Date: Sat, 7 Mar 2026 23:04:52 -0500 Subject: [PATCH 22/22] fix stick-tilt blending --- src/d_main.cpp | 2 +- src/g_game.c | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/d_main.cpp b/src/d_main.cpp index fdc656bd8..96f8865aa 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -93,7 +93,7 @@ #define ASSET_HASH_TEXTURES_KART 0xb4211b2f32b6a291 #define ASSET_HASH_CHARS_KART 0x1e68a3e01aa5c68b #define ASSET_HASH_MAPS_KART 0x38558ed00da41ce9 -#define ASSET_HASH_MAIN_PK3 0xceb926d00133de51 +#define ASSET_HASH_MAIN_PK3 0x9a54c263d9f325cd #define ASSET_HASH_MAPPATCH_PK3 0x1745690024efbaf8 #define ASSET_HASH_BONUSCHARS_KART 0x60e6f13d822a7461 #ifdef USE_PATCH_FILE diff --git a/src/g_game.c b/src/g_game.c index 8773765f6..78f982309 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1720,12 +1720,11 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) // if stick input is used gradually cancel out tilt based on stick intensity if (cv_tiltcontrol[forplayer].value == 1) { - accelerometertilt = joystickvector.xaxis + - FixedMul( - FRACUNIT-FixedDiv(joystickvector.xaxis, JOYAXISRANGE), - G_GetGamepadTilt(forplayer)*FRACUNIT - )/FRACUNIT; - } + fixed_t rate = FixedDiv(abs(joystickvector.xaxis), JOYAXISRANGE); + accelerometertilt = + (FixedMul(rate, joystickvector.xaxis*FRACUNIT)/FRACUNIT) + + (FixedMul(FRACUNIT-rate, G_GetGamepadTilt(forplayer)*FRACUNIT)/FRACUNIT); + } // For kart, I've turned the aim axis into a digital axis because we only // use it for aiming to throw items forward/backward and the vote screen