| 416 | } |
| 417 | |
| 418 | static void ReadGamepad() |
| 419 | { |
| 420 | if (InputLocked || OisGamepad == nullptr) |
| 421 | return; |
| 422 | |
| 423 | try |
| 424 | { |
| 425 | OisGamepad->capture(); |
| 426 | const auto& state = OisGamepad->getJoyStickState(); |
| 427 | |
| 428 | // Poll buttons. |
| 429 | for (int keyID = 0; keyID < state.mButtons.size(); keyID++) |
| 430 | KeyMap[KEY_OFFSET_GAMEPAD + keyID] = state.mButtons[keyID] ? 1.0f : 0.0f; |
| 431 | |
| 432 | // Poll axes. |
| 433 | for (int axis = 0; axis < state.mAxes.size(); axis++) |
| 434 | { |
| 435 | // NOTE: Anything above 6 existing XBOX/PS controller axes not supported (2 sticks + 2 triggers). |
| 436 | if (axis >= GAMEPAD_AXIS_COUNT) |
| 437 | break; |
| 438 | |
| 439 | // Filter out deadzone. |
| 440 | if (abs(state.mAxes[axis].abs) < AXIS_DEADZONE) |
| 441 | continue; |
| 442 | |
| 443 | // Calculate raw normalized analog value (for camera). |
| 444 | float normalizedValue = float(state.mAxes[axis].abs + (state.mAxes[axis].abs > 0 ? -AXIS_DEADZONE : AXIS_DEADZONE)) / |
| 445 | float(SHRT_MAX - AXIS_DEADZONE); |
| 446 | |
| 447 | // Calculate scaled analog value for movement. |
| 448 | // NOTE: [0.2f, 1.7f] range gives most organic rates. |
| 449 | float scaledValue = (abs(normalizedValue) * AXIS_SCALE) + AXIS_OFFSET; |
| 450 | |
| 451 | // Calculate and reset discrete input slots. |
| 452 | int negKeyID = (KEY_OFFSET_GAMEPAD + GAMEPAD_BUTTON_COUNT) + (axis * 2); |
| 453 | int posKeyID = (KEY_OFFSET_GAMEPAD + GAMEPAD_BUTTON_COUNT) + (axis * 2) + 1; |
| 454 | KeyMap[negKeyID] = (normalizedValue > 0) ? abs(normalizedValue) : 0.0f; |
| 455 | KeyMap[posKeyID] = (normalizedValue < 0) ? abs(normalizedValue) : 0.0f; |
| 456 | |
| 457 | // Determine discrete input registering based on analog value. |
| 458 | int usedKeyID = (normalizedValue > 0) ? negKeyID : posKeyID; |
| 459 | |
| 460 | // Register analog input in certain direction. |
| 461 | // If axis is bound as directional controls, register axis as directional input. |
| 462 | // Otherwise, register as camera movement input (for future). |
| 463 | // NOTE: `abs()` operations are needed to avoid issues with inverted axes on different controllers. |
| 464 | |
| 465 | if (g_Bindings.GetBoundKeyID(BindingProfileID::Custom, In::Forward) == usedKeyID) |
| 466 | { |
| 467 | AxisMap[AxisID::Move].y = abs(scaledValue); |
| 468 | } |
| 469 | else if (g_Bindings.GetBoundKeyID(BindingProfileID::Custom, In::Back) == usedKeyID) |
| 470 | { |
| 471 | AxisMap[AxisID::Move].y = -abs(scaledValue); |
| 472 | } |
| 473 | else if (g_Bindings.GetBoundKeyID(BindingProfileID::Custom, In::Left) == usedKeyID) |
| 474 | { |
| 475 | AxisMap[AxisID::Move].x = -abs(scaledValue); |
no test coverage detected