Merge pull request '[FEAT] Tilt Steering, GamepadMotion Sensor Access' (#222) from accelerometer into next

Reviewed-on: https://codeberg.org/NepDisk/blankart/pulls/222
This commit is contained in:
NepDisk 2026-03-11 02:40:24 +01:00
commit 2c6605440d
19 changed files with 554 additions and 69 deletions

View file

@ -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 0x9a54c263d9f325cd
#define ASSET_HASH_MAPPATCH_PK3 0x1745690024efbaf8
#define ASSET_HASH_BONUSCHARS_KART 0x60e6f13d822a7461
#ifdef USE_PATCH_FILE
@ -220,6 +220,16 @@ 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_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))
{

View file

@ -1405,6 +1405,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

View file

@ -58,7 +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 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)
@ -70,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

View file

@ -1851,6 +1851,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}
};

View file

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

View file

@ -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!

View file

@ -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"
@ -1306,6 +1307,46 @@ 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]
);
break;
case GYROSCOPE:
FV3_Load(&out,
gamekeydown[deviceID][KEY_GYROSCOPE1+0],
gamekeydown[deviceID][KEY_GYROSCOPE1+1],
gamekeydown[deviceID][KEY_GYROSCOPE1+2]
);
break;
}
return out;
}
boolean G_PlayerInputDown(UINT8 p, INT32 gc, boolean digital, SINT8 type)
{
return (G_PlayerInputAnalog(p, gc, digital, type) != 0);
@ -1379,6 +1420,207 @@ static void G_HandleAxisDeadZone(UINT8 splitnum, joystickvector2_t *joystickvect
}
}
// copy/pasted from the lua version of these routines
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;
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;
}
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);
}
inline static fixed_t FV3_Length(const vector3_t *vec)
{
return FixedSqrt(FV3_LengthSquared(vec));
}
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(axis.x, sinangle),
FixedMul(axis.y, sinangle),
FixedMul(axis.z, sinangle),
cosangle
);
return result;
}
// 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];
// thresholds of trust for accel shakiness. less shakiness = more trust
#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/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/5)
// thresholds for what's considered "close enough"
#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 (9*FRACUNIT/100)
void G_UpdateGamepadGravity(INT32 p, vector3_t gyro, vector3_t accel)
{
// convert gyro input to reverse rotation
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;
vector4_t invRotation = AngleAxis(
FixedMul(FV3_Length(&gyro), FRACUNIT/TICRATE),
-gyro.x,
-gyro.y,
-gyro.z
);
vector3_t gravityToAccel = {0};
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);
localshakinessfac[p] = FixedMul(localshakinessfac[p], smoothFactor),
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),
Easing_Linear(smoothFactor, accel.z, localsmoothedaccel[p].z)
);
CONS_Debug(DBG_IMU, "Shakiness: %4.2f\n", FixedToFloat(localshakinessfac[p]));
FV3_SubEx(&invAccel, &localgravityvectors[p], &gravityToAccel);
FV3_NormalizeEx(&gravityToAccel, &gravityToAccelDirection);
if (ShakinessMaxThreshold > ShakinessMinThreshold)
{
fixed_t stillness = CLAMP(FixedDiv((localshakinessfac[p] - ShakinessMinThreshold), (ShakinessMaxThreshold - ShakinessMinThreshold)), 0, FRACUNIT);
gravCorrectionRate = CorrectionStillRate + FixedMul((CorrectionShakyRate - CorrectionStillRate), stillness);
}
else
{
gravCorrectionRate = localshakinessfac[p] < ShakinessMaxThreshold ? CorrectionStillRate : CorrectionShakyRate;
}
// limit in proportion to rotation rate
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)
{
closeEnoughFactor = CLAMP(FixedDiv((FV3_Length(&gravityToAccel) - CorrectionGyroMinThreshold), (CorrectionGyroMaxThreshold - CorrectionGyroMinThreshold)), 0, FRACUNIT);
}
else
{
closeEnoughFactor = FV3_Length(&gravityToAccel) < CorrectionGyroMaxThreshold ? 0 : FRACUNIT;
}
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(&correction,
FixedMul(gravityToAccel.x, gravCorrectionRate/TICRATE),
FixedMul(gravityToAccel.y, gravCorrectionRate/TICRATE),
FixedMul(gravityToAccel.z, gravCorrectionRate/TICRATE)
);
if (FV3_LengthSquared(&correction) < FV3_LengthSquared(&gravityToAccel))
{
FV3_Add(&localgravityvectors[p], &correction);
}
else
{
FV3_Add(&localgravityvectors[p], &gravityToAccel);
}
}
INT32 G_GetGamepadTilt(INT32 p)
{
fixed_t tilt;
fixed_t curve;
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);
CONS_Debug(DBG_IMU, "Pinched Tilt: %4.2f\n", FixedToFloat(curve));
return (JOYAXISRANGE * curve)/FRACUNIT;
}
vector3_t G_GetGamepadGravity(INT32 p)
{
const vector3_t zero = {0, -ACCELEROMETERGRAVITY, 0};
if (!G_GetGamepadCanUseTilt(p)) return zero;
return localgravityvectors[p];
}
vector3_t G_GetGamepadShake(INT32 p)
{
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_GetGamepadCanUseTilt: Invalid player ID %d\n", p);
#endif
return false;
}
return (I_ControllerSupportsSensorAccelerometer(p) && I_ControllerSupportsSensorGyro(p));
}
#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
@ -1414,6 +1656,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
INT32 forward, side, tspeed;
joystickvector2_t joystickvector;
INT32 accelerometertilt;
// you'd BETTER not touch the player while freecamming...
player_t *player = &players[g_localplayers[forplayer]];
@ -1438,6 +1681,15 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
break;
}
// update our gamepad gravity when applicable
// this should always execute so we have an accurate state
if (G_GetGamepadCanUseTilt(forplayer))
{
vector3_t accel = G_PlayerInputSensor(forplayer, ACCELEROMETER);
vector3_t gyro = G_PlayerInputSensor(forplayer, GYROSCOPE);
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)))
@ -1455,6 +1707,16 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
joystickvector.yaxis = 0;
G_HandleAxisDeadZone(forplayer, &joystickvector);
// tilt control never has deadzone
// if stick input is used gradually cancel out tilt based on stick intensity
if (cv_tiltcontrol[forplayer].value == 1)
{
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
// This mean that the turn axis will still be gradient but up/down will be 0
@ -1465,10 +1727,18 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
if (encoremode)
{
joystickvector.xaxis = -joystickvector.xaxis;
accelerometertilt = -accelerometertilt;
}
forward = side = 0;
tspeed = joystickvector.xaxis;
if (cv_tiltcontrol[forplayer].value == 1)
{
tspeed = accelerometertilt;
}
else
{
tspeed = joystickvector.xaxis;
}
// use two stage accelerative turning
// on the keyboard and (NOT!) joystick
@ -1488,11 +1758,19 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
}
}
else
{
turnheld[forplayer] = 0;
}
cmd->turning = 0;
if (joystickvector.xaxis != 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;
@ -1693,6 +1971,26 @@ 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;
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.
@ -1725,6 +2023,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;
}

View file

@ -135,6 +135,23 @@ typedef enum
DEADZONE_BUTTON,
} analogdeadzone_e;
typedef enum
{
ACCELEROMETER,
GYROSCOPE,
} motionsensortype_e;
#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);
vector3_t G_GetGamepadShake(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);
INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, boolean digital, SINT8 type);
boolean G_PlayerInputDown(UINT8 p, INT32 gc, boolean digital, SINT8 type);

View file

@ -90,6 +90,14 @@ 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, 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)
{
if (cv_controllerrumble[0].value == 0)
@ -333,6 +341,20 @@ void G_MapEventsToControls(event_t *ev)
gamekeydown[device][i] = max(0, ev->data2);
break;
case ev_accelerometer:
// 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:
// 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:
break;
@ -472,19 +494,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] =

View file

@ -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,
@ -106,6 +107,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

View file

@ -189,7 +189,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];
@ -600,12 +601,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");
@ -5439,44 +5449,43 @@ static void K_drawInput(void)
}
else if (cv_showinput.value == 1)
{
if (!stplyr->cmd.turning) // no turn
target = 0;
else // turning of multiple strengths!
{
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;
}
patch_t *workingPic = kp_inputwheel;
patch_t *shadowPic = kp_inputwheel_shadow;
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;
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));
UINT8 *colormap;
if (!K_GetHudColor())
V_DrawFixedPatch(x, y, FRACUNIT, splitflags, kp_inputwheel[target], 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
{
UINT8 *colormap;
colormap = R_GetTranslationColormap(0, K_GetHudColor(), GTC_CACHE);
V_DrawFixedPatch(x, y, FRACUNIT, splitflags, kp_inputwheel[target], 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)
{
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())
{
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);
}
}
}
}

View file

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

View file

@ -2678,6 +2678,8 @@ enum ticcmd_e
ticcmd_buttons,
ticcmd_latency,
ticcmd_flags,
ticcmd_tilt,
ticcmd_shake,
};
static const char *const ticcmd_opt[] = {
@ -2692,6 +2694,8 @@ static const char *const ticcmd_opt[] = {
"buttons",
"latency",
"flags",
"tilt",
"shake",
NULL,
};
@ -2736,6 +2740,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;
}
@ -2782,6 +2792,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;
}

View file

@ -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

View file

@ -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

View file

@ -10000,6 +10000,24 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
mobj->lastlook = player->cmd.turning;
}
if (player->cmd.shake > GAMEPADSHAKETHRESHOLD)
{
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

View file

@ -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);
}
@ -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)
{
@ -250,6 +254,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)

View file

@ -1055,23 +1055,27 @@ 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:
// 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]);
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
#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;

View file

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