Merge pull request #990 from protocultor/old_sdl_gyro_switch

Gyro aiming for Switch controllers with SDL < 2.0.14
This commit is contained in:
Yamagi 2023-04-15 18:23:28 +02:00 committed by GitHub
commit 11bea2a68b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -167,12 +167,25 @@ static qboolean gyro_active = false;
// Gyro calibration // Gyro calibration
static float gyro_accum[3]; static float gyro_accum[3];
static unsigned int num_samples;
static cvar_t *gyro_calibration_x; static cvar_t *gyro_calibration_x;
static cvar_t *gyro_calibration_y; static cvar_t *gyro_calibration_y;
static cvar_t *gyro_calibration_z; static cvar_t *gyro_calibration_z;
#if SDL_VERSION_ATLEAST(2, 0, 14) // support for controller sensors (gyro, accelerometer)
static unsigned int num_samples;
#define NATIVE_SDL_GYRO // uses SDL_CONTROLLERSENSORUPDATE to read gyro
#else // for SDL < 2.0.14, gyro can be read as a "secondary joystick" exposed by dkms-hid-nintendo
static unsigned int num_samples[3];
static SDL_Joystick *imu_joystick = NULL; // gyro "joystick"
#define IMU_JOY_AXIS_GYRO_ROLL 3
#define IMU_JOY_AXIS_GYRO_PITCH 4
#define IMU_JOY_AXIS_GYRO_YAW 5
#endif
// To ignore SDL_JOYDEVICEADDED at game init. Allows for hot plugging of game controller afterwards. // To ignore SDL_JOYDEVICEADDED at game init. Allows for hot plugging of game controller afterwards.
static qboolean first_init = true; static qboolean first_init = true;
@ -766,7 +779,7 @@ IN_Update(void)
break; break;
} }
#if SDL_VERSION_ATLEAST(2, 0, 16) // support for controller sensors (gyro, accelerometer) #ifdef NATIVE_SDL_GYRO // controller sensors' reading supported (gyro, accelerometer)
case SDL_CONTROLLERSENSORUPDATE: case SDL_CONTROLLERSENSORUPDATE:
if (event.csensor.sensor != SDL_SENSOR_GYRO) if (event.csensor.sensor != SDL_SENSOR_GYRO)
{ {
@ -781,9 +794,39 @@ IN_Update(void)
break; break;
} }
#else // gyro read as "secondary joystick"
case SDL_JOYAXISMOTION:
if ( !imu_joystick || event.cdevice.which != SDL_JoystickInstanceID(imu_joystick) )
{
break; // controller axes handled by SDL_CONTROLLERAXISMOTION
}
int axis_value = event.caxis.value;
if (countdown_reason == REASON_GYROCALIBRATION && updates_countdown)
{
switch (event.caxis.axis)
{
case IMU_JOY_AXIS_GYRO_PITCH:
gyro_accum[0] += axis_value;
num_samples[0]++;
break;
case IMU_JOY_AXIS_GYRO_YAW:
gyro_accum[1] += axis_value;
num_samples[1]++;
break;
case IMU_JOY_AXIS_GYRO_ROLL:
gyro_accum[2] += axis_value;
num_samples[2]++;
}
break;
}
#endif // NATIVE_SDL_GYRO
if (gyro_active && gyro_mode->value && if (gyro_active && gyro_mode->value &&
!cl_paused->value && cls.key_dest == key_game) !cl_paused->value && cls.key_dest == key_game)
{ {
#ifdef NATIVE_SDL_GYRO
if (!gyro_turning_axis->value) if (!gyro_turning_axis->value)
{ {
gyro_yaw = event.csensor.data[1] - gyro_calibration_y->value; // yaw gyro_yaw = event.csensor.data[1] - gyro_calibration_y->value; // yaw
@ -793,13 +836,31 @@ IN_Update(void)
gyro_yaw = -(event.csensor.data[2] - gyro_calibration_z->value); // roll gyro_yaw = -(event.csensor.data[2] - gyro_calibration_z->value); // roll
} }
gyro_pitch = event.csensor.data[0] - gyro_calibration_x->value; gyro_pitch = event.csensor.data[0] - gyro_calibration_x->value;
#else // old "joystick" gyro
switch (event.caxis.axis) // inside "case SDL_JOYAXISMOTION" here
{
case IMU_JOY_AXIS_GYRO_PITCH:
gyro_pitch = -(axis_value - gyro_calibration_x->value);
break;
case IMU_JOY_AXIS_GYRO_YAW:
if (!gyro_turning_axis->value)
{
gyro_yaw = axis_value - gyro_calibration_y->value;
}
break;
case IMU_JOY_AXIS_GYRO_ROLL:
if (gyro_turning_axis->value)
{
gyro_yaw = axis_value - gyro_calibration_z->value;
}
}
#endif // NATIVE_SDL_GYRO
} }
else else
{ {
gyro_yaw = gyro_pitch = 0; gyro_yaw = gyro_pitch = 0;
} }
break; break;
#endif // SDL_VERSION_ATLEAST(2, 0, 16)
case SDL_CONTROLLERDEVICEREMOVED: case SDL_CONTROLLERDEVICEREMOVED:
if (!controller) if (!controller)
@ -889,10 +950,23 @@ IN_Update(void)
case REASON_GYROCALIBRATION: // finish and save calibration case REASON_GYROCALIBRATION: // finish and save calibration
{ {
#ifdef NATIVE_SDL_GYRO
const float inverseSamples = 1.f / num_samples; const float inverseSamples = 1.f / num_samples;
Cvar_SetValue("gyro_calibration_x", gyro_accum[0] * inverseSamples); Cvar_SetValue("gyro_calibration_x", gyro_accum[0] * inverseSamples);
Cvar_SetValue("gyro_calibration_y", gyro_accum[1] * inverseSamples); Cvar_SetValue("gyro_calibration_y", gyro_accum[1] * inverseSamples);
Cvar_SetValue("gyro_calibration_z", gyro_accum[2] * inverseSamples); Cvar_SetValue("gyro_calibration_z", gyro_accum[2] * inverseSamples);
#else
if (!num_samples[0] || !num_samples[1] || !num_samples[2])
{
Com_Printf("Calibration failed, please retry inside a level after having moved your controller a little.\n");
}
else
{
Cvar_SetValue("gyro_calibration_x", gyro_accum[0] / num_samples[0]);
Cvar_SetValue("gyro_calibration_y", gyro_accum[1] / num_samples[1]);
Cvar_SetValue("gyro_calibration_z", gyro_accum[2] / num_samples[2]);
}
#endif
Com_Printf("Calibration results:\n X=%f Y=%f Z=%f\n", Com_Printf("Calibration results:\n X=%f Y=%f Z=%f\n",
gyro_calibration_x->value, gyro_calibration_y->value, gyro_calibration_z->value); gyro_calibration_x->value, gyro_calibration_y->value, gyro_calibration_z->value);
CalibrationFinishedCallback(); CalibrationFinishedCallback();
@ -1269,7 +1343,11 @@ IN_Move(usercmd_t *cmd)
// //
// For movement this is not needed, as those are absolute values independent of framerate // For movement this is not needed, as those are absolute values independent of framerate
float joyViewFactor = cls.rframetime/0.01666f; float joyViewFactor = cls.rframetime/0.01666f;
#ifdef NATIVE_SDL_GYRO
float gyroViewFactor = (1.0f / M_PI) * joyViewFactor; float gyroViewFactor = (1.0f / M_PI) * joyViewFactor;
#else
float gyroViewFactor = (1.0f / 2560.0f) * joyViewFactor; // normalized for Switch gyro
#endif
if (joystick_yaw) if (joystick_yaw)
{ {
@ -1915,10 +1993,14 @@ Controller_Rumble(const char *name, vec3_t source, qboolean from_player,
void void
StartCalibration(void) StartCalibration(void)
{ {
#ifdef NATIVE_SDL_GYRO
num_samples = 0;
#else
num_samples[0] = num_samples[1] = num_samples[2] = 0;
#endif
gyro_accum[0] = 0.0; gyro_accum[0] = 0.0;
gyro_accum[1] = 0.0; gyro_accum[1] = 0.0;
gyro_accum[2] = 0.0; gyro_accum[2] = 0.0;
num_samples = 0;
updates_countdown = 300; updates_countdown = 300;
countdown_reason = REASON_GYROCALIBRATION; countdown_reason = REASON_GYROCALIBRATION;
} }
@ -2033,9 +2115,26 @@ IN_Controller_Init(qboolean notify_user)
// Ugly hack to detect IMU-only devices - works for Switch controllers at least // Ugly hack to detect IMU-only devices - works for Switch controllers at least
if (name_len > 4 && !strncmp(joystick_name + name_len - 4, " IMU", 4)) if (name_len > 4 && !strncmp(joystick_name + name_len - 4, " IMU", 4))
{ {
Com_Printf ("Skipping IMU device.\n");
SDL_JoystickClose(joystick); SDL_JoystickClose(joystick);
joystick = NULL; joystick = NULL;
#ifdef NATIVE_SDL_GYRO
Com_Printf ("Skipping IMU device.\n");
#else // if it's not a Left JoyCon, use it as Gyro
Com_Printf ("IMU device found.\n");
if ( !imu_joystick && name_len > 16 && strncmp(joystick_name + name_len - 16, "Left Joy-Con IMU", 16) != 0 )
{
imu_joystick = SDL_JoystickOpen(i);
if (imu_joystick)
{
show_gyro = true;
Com_Printf ("Using this device as Gyro sensor.\n");
}
else
{
Com_Printf ("Couldn't open IMU: %s.\n", SDL_GetError());
}
}
#endif
continue; continue;
} }
@ -2059,7 +2158,7 @@ IN_Controller_Init(qboolean notify_user)
SDL_JoystickClose(joystick); SDL_JoystickClose(joystick);
joystick = NULL; joystick = NULL;
if (is_controller) if (is_controller && !controller)
{ {
controller = SDL_GameControllerOpen(i); controller = SDL_GameControllerOpen(i);
if (!controller) if (!controller)
@ -2071,7 +2170,7 @@ IN_Controller_Init(qboolean notify_user)
show_gamepad = true; show_gamepad = true;
Com_Printf("Enabled as Game Controller, settings:\n%s\n", SDL_GameControllerMapping(controller)); Com_Printf("Enabled as Game Controller, settings:\n%s\n", SDL_GameControllerMapping(controller));
#if SDL_VERSION_ATLEAST(2, 0, 16) // support for controller sensors #ifdef NATIVE_SDL_GYRO
if ( SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO) if ( SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO)
&& !SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_TRUE) ) && !SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_TRUE) )
@ -2090,7 +2189,7 @@ IN_Controller_Init(qboolean notify_user)
SDL_GameControllerSetLED(controller, 0, 80, 0); // green light SDL_GameControllerSetLED(controller, 0, 80, 0); // green light
} }
#endif // SDL_VERSION_ATLEAST(2, 0, 16) #endif // NATIVE_SDL_GYRO
joystick_haptic = SDL_HapticOpenFromJoystick(SDL_GameControllerGetJoystick(controller)); joystick_haptic = SDL_HapticOpenFromJoystick(SDL_GameControllerGetJoystick(controller));
@ -2124,7 +2223,9 @@ IN_Controller_Init(qboolean notify_user)
Com_Printf("Controller doesn't support rumble.\n"); Com_Printf("Controller doesn't support rumble.\n");
} }
#ifdef NATIVE_SDL_GYRO // "native" exits when finding a single working controller
break; break;
#endif
} }
} }
} }
@ -2236,6 +2337,14 @@ IN_Controller_Shutdown(qboolean notify_user)
show_gamepad = show_gyro = show_haptic = false; show_gamepad = show_gyro = show_haptic = false;
joystick_left_x = joystick_left_y = joystick_right_x = joystick_right_y = 0; joystick_left_x = joystick_left_y = joystick_right_x = joystick_right_y = 0;
gyro_yaw = gyro_pitch = 0; gyro_yaw = gyro_pitch = 0;
#ifndef NATIVE_SDL_GYRO
if (imu_joystick)
{
SDL_JoystickClose(imu_joystick);
imu_joystick = NULL;
}
#endif
} }
/* /*