Generalized the sensor coordinate transform for wraparound gamepads

This commit is contained in:
Sam Lantinga 2023-06-17 08:28:37 -07:00
parent 9eb5eab0ad
commit 610c31c7b7
3 changed files with 65 additions and 34 deletions

View file

@ -370,33 +370,19 @@ static void RecenterGamepad(SDL_Gamepad *gamepad)
} }
} }
/* SDL defines sensor orientation for phones relative to the natural /* SDL defines sensor orientation relative to the device natural
orientation, and for gamepads relative to being held in front of you. orientation, so when it's changed orientation to be used as a
When a phone is being used as a gamepad, its orientation changes, gamepad, change the sensor orientation to match.
so adjust sensor axes to match.
*/ */
static void AdjustSensorOrientation(float *src, float *dst) static void AdjustSensorOrientation(SDL_Joystick *joystick, float *src, float *dst)
{ {
if (SDL_GetDisplayNaturalOrientation(SDL_GetPrimaryDisplay()) == SDL_ORIENTATION_LANDSCAPE) { unsigned int i, j;
/* When a device in landscape orientation is laid flat, the axes change
orientation as follows: for (i = 0; i < 3; ++i) {
-X to +X becomes -X to +X dst[i] = 0.0f;
-Y to +Y becomes +Z to -Z for (j = 0; j < 3; ++j) {
-Z to +Z becomes -Y to +Y dst[i] += joystick->sensor_transform[i][j] * src[j];
*/ }
dst[0] = src[0];
dst[1] = src[2];
dst[2] = -src[1];
} else {
/* When a device in portrait orientation is rotated left and laid flat,
the axes change orientation as follows:
-X to +X becomes +Z to -Z
-Y to +Y becomes +X to -X
-Z to +Z becomes -Y to +Y
*/
dst[0] = -src[1];
dst[1] = src[2];
dst[2] = -src[0];
} }
} }
@ -480,12 +466,12 @@ static int SDLCALL SDL_GamepadEventWatcher(void *userdata, SDL_Event *event)
for (gamepad = SDL_gamepads; gamepad; gamepad = gamepad->next) { for (gamepad = SDL_gamepads; gamepad; gamepad = gamepad->next) {
if (gamepad->joystick->accel && gamepad->joystick->accel_sensor == event->sensor.which) { if (gamepad->joystick->accel && gamepad->joystick->accel_sensor == event->sensor.which) {
float data[3]; float data[3];
AdjustSensorOrientation(event->sensor.data, data); AdjustSensorOrientation(gamepad->joystick, event->sensor.data, data);
SDL_SendJoystickSensor(event->common.timestamp, gamepad->joystick, SDL_SENSOR_ACCEL, event->sensor.sensor_timestamp, data, SDL_arraysize(data)); SDL_SendJoystickSensor(event->common.timestamp, gamepad->joystick, SDL_SENSOR_ACCEL, event->sensor.sensor_timestamp, data, SDL_arraysize(data));
} }
if (gamepad->joystick->gyro && gamepad->joystick->gyro_sensor == event->sensor.which) { if (gamepad->joystick->gyro && gamepad->joystick->gyro_sensor == event->sensor.which) {
float data[3]; float data[3];
AdjustSensorOrientation(event->sensor.data, data); AdjustSensorOrientation(gamepad->joystick, event->sensor.data, data);
SDL_SendJoystickSensor(event->common.timestamp, gamepad->joystick, SDL_SENSOR_GYRO, event->sensor.sensor_timestamp, data, SDL_arraysize(data)); SDL_SendJoystickSensor(event->common.timestamp, gamepad->joystick, SDL_SENSOR_GYRO, event->sensor.sensor_timestamp, data, SDL_arraysize(data));
} }
} }

View file

@ -562,7 +562,7 @@ static SDL_bool IsROGAlly(SDL_Joystick *joystick)
return SDL_FALSE; return SDL_FALSE;
} }
static SDL_bool ShouldAttemptSensorFusion(SDL_Joystick *joystick) static SDL_bool ShouldAttemptSensorFusion(SDL_Joystick *joystick, SDL_bool *invert_sensors)
{ {
static Uint32 wraparound_gamepads[] = { static Uint32 wraparound_gamepads[] = {
MAKE_VIDPID(0x1532, 0x0709), /* Razer Junglecat (L) */ MAKE_VIDPID(0x1532, 0x0709), /* Razer Junglecat (L) */
@ -578,6 +578,8 @@ static SDL_bool ShouldAttemptSensorFusion(SDL_Joystick *joystick)
int i; int i;
int hint; int hint;
*invert_sensors = SDL_FALSE;
/* The SDL controller sensor API is only available for gamepads (at the moment) */ /* The SDL controller sensor API is only available for gamepads (at the moment) */
if (!joystick->is_gamepad) { if (!joystick->is_gamepad) {
return SDL_FALSE; return SDL_FALSE;
@ -607,17 +609,23 @@ static SDL_bool ShouldAttemptSensorFusion(SDL_Joystick *joystick)
} }
/* See if this is another known wraparound gamepad */ /* See if this is another known wraparound gamepad */
if (IsBackboneOne(joystick) || IsROGAlly(joystick)) { if (IsBackboneOne(joystick)) {
return SDL_TRUE;
}
if (IsROGAlly(joystick)) {
/* I'm not sure if this is a Windows thing, or a quirk for ROG Ally,
* but we need to invert the sensor data on all axes.
*/
*invert_sensors = SDL_TRUE;
return SDL_TRUE; return SDL_TRUE;
} }
return SDL_FALSE; return SDL_FALSE;
} }
static void AttemptSensorFusion(SDL_Joystick *joystick) static void AttemptSensorFusion(SDL_Joystick *joystick, SDL_bool invert_sensors)
{ {
SDL_SensorID *sensors; SDL_SensorID *sensors;
int i; unsigned int i, j;
if (SDL_InitSubSystem(SDL_INIT_SENSOR) < 0) { if (SDL_InitSubSystem(SDL_INIT_SENSOR) < 0) {
return; return;
@ -646,6 +654,41 @@ static void AttemptSensorFusion(SDL_Joystick *joystick)
SDL_free(sensors); SDL_free(sensors);
} }
SDL_QuitSubSystem(SDL_INIT_SENSOR); SDL_QuitSubSystem(SDL_INIT_SENSOR);
/* SDL defines sensor orientation for phones relative to the natural
orientation, and for gamepads relative to being held in front of you.
When a phone is being used as a gamepad, its orientation changes,
so adjust sensor axes to match.
*/
if (SDL_GetDisplayNaturalOrientation(SDL_GetPrimaryDisplay()) == SDL_ORIENTATION_LANDSCAPE) {
/* When a device in landscape orientation is laid flat, the axes change
orientation as follows:
-X to +X becomes -X to +X
-Y to +Y becomes +Z to -Z
-Z to +Z becomes -Y to +Y
*/
joystick->sensor_transform[0][0] = 1.0f;
joystick->sensor_transform[1][2] = 1.0f;
joystick->sensor_transform[2][1] = -1.0f;
} else {
/* When a device in portrait orientation is rotated left and laid flat,
the axes change orientation as follows:
-X to +X becomes +Z to -Z
-Y to +Y becomes +X to -X
-Z to +Z becomes -Y to +Y
*/
joystick->sensor_transform[0][1] = -1.0f;
joystick->sensor_transform[1][2] = 1.0f;
joystick->sensor_transform[2][0] = -1.0f;
}
if (invert_sensors) {
for (i = 0; i < SDL_arraysize(joystick->sensor_transform); ++i) {
for (j = 0; j < SDL_arraysize(joystick->sensor_transform[i]); ++j) {
joystick->sensor_transform[i][j] *= -1.0f;
}
}
}
} }
static void CleanupSensorFusion(SDL_Joystick *joystick) static void CleanupSensorFusion(SDL_Joystick *joystick)
@ -690,6 +733,7 @@ SDL_Joystick *SDL_OpenJoystick(SDL_JoystickID instance_id)
const char *joystickname = NULL; const char *joystickname = NULL;
const char *joystickpath = NULL; const char *joystickpath = NULL;
SDL_JoystickPowerLevel initial_power_level; SDL_JoystickPowerLevel initial_power_level;
SDL_bool invert_sensors = SDL_FALSE;
SDL_LockJoysticks(); SDL_LockJoysticks();
@ -776,8 +820,8 @@ SDL_Joystick *SDL_OpenJoystick(SDL_JoystickID instance_id)
joystick->is_gamepad = SDL_IsGamepad(instance_id); joystick->is_gamepad = SDL_IsGamepad(instance_id);
/* Use system gyro and accelerometer if the gamepad doesn't have built-in sensors */ /* Use system gyro and accelerometer if the gamepad doesn't have built-in sensors */
if (ShouldAttemptSensorFusion(joystick)) { if (ShouldAttemptSensorFusion(joystick, &invert_sensors)) {
AttemptSensorFusion(joystick); AttemptSensorFusion(joystick, invert_sensors);
} }
/* Add joystick to list */ /* Add joystick to list */

View file

@ -117,6 +117,7 @@ struct SDL_Joystick
SDL_Sensor *accel _guarded; SDL_Sensor *accel _guarded;
SDL_SensorID gyro_sensor _guarded; SDL_SensorID gyro_sensor _guarded;
SDL_Sensor *gyro _guarded; SDL_Sensor *gyro _guarded;
float sensor_transform[3][3] _guarded;
struct SDL_JoystickDriver *driver _guarded; struct SDL_JoystickDriver *driver _guarded;