From c7d9a8733ff79bbef162ffe56b61b9962a7be593 Mon Sep 17 00:00:00 2001 From: Irastris Date: Thu, 16 Apr 2026 15:31:58 -0400 Subject: [PATCH] Gyro: Revisions & Rollgoal Mirror Mode --- include/dusk/gyro.h | 4 +- include/dusk/settings.h | 12 +++--- src/d/actor/d_a_alink_link.inc | 12 +----- src/dusk/gyro.cpp | 54 ++++++++++++------------ src/dusk/imgui/ImGuiMenuEnhancements.cpp | 36 ++++++++++------ src/dusk/settings.cpp | 24 ++++++----- 6 files changed, 74 insertions(+), 68 deletions(-) diff --git a/include/dusk/gyro.h b/include/dusk/gyro.h index 5636d34b05..4ee1c61737 100644 --- a/include/dusk/gyro.h +++ b/include/dusk/gyro.h @@ -3,11 +3,11 @@ namespace dusk::gyro { void read(float dt); -void consumeAimDeltas(float& out_yaw_rad, float& out_pitch_rad); +void getAimDeltas(float& out_yaw, float& out_pitch); bool queryGyroAimItemContext(); void rollgoalTick(bool play_active, s16 camera_yaw); -void rollgoalTableOffset(s16& out_add_x, s16& out_add_z); +void rollgoalTableOffset(s16& out_ax, s16& out_az); extern bool s_sensor_keep_alive; bool get_sensor_keep_alive(); diff --git a/include/dusk/settings.h b/include/dusk/settings.h index 7bfc65a312..ea3d090e6b 100644 --- a/include/dusk/settings.h +++ b/include/dusk/settings.h @@ -82,11 +82,13 @@ struct UserSettings { // Input ConfigVar enableGyroAim; ConfigVar enableGyroRollgoal; - ConfigVar gyroAimSensitivityX; - ConfigVar gyroAimSensitivityY; - ConfigVar gyroRollgoalSensitivity; - ConfigVar gyroAimInvertPitch; - ConfigVar gyroAimInvertYaw; + ConfigVar gyroSensitivityX; + ConfigVar gyroSensitivityY; + ConfigVar gyroSensitivityRollgoal; + ConfigVar gyroSmoothing; + ConfigVar gyroDeadband; + ConfigVar gyroInvertPitch; + ConfigVar gyroInvertYaw; // Cheats ConfigVar enableFastIronBoots; diff --git a/src/d/actor/d_a_alink_link.inc b/src/d/actor/d_a_alink_link.inc index 6bf9ae23dc..6de02a7598 100644 --- a/src/d/actor/d_a_alink_link.inc +++ b/src/d/actor/d_a_alink_link.inc @@ -142,17 +142,7 @@ BOOL daAlink_c::setBodyAngleToCamera() { f32 gy_yaw = 0.f; f32 gy_pitch = 0.f; - dusk::gyro::consumeAimDeltas(gy_yaw, gy_pitch); - - if (dusk::getSettings().game.gyroAimInvertPitch) { - gy_pitch = -gy_pitch; - } - if (dusk::getSettings().game.gyroAimInvertYaw) { - gy_yaw = -gy_yaw; - } - if (dusk::getSettings().game.enableMirrorMode) { - gy_yaw = -gy_yaw; - } + dusk::gyro::getAimDeltas(gy_yaw, gy_pitch); shape_angle.y = shape_angle.y + cM_rad2s(gy_yaw * gyro_scale); sp8 = sp8 + cM_rad2s(gy_pitch * gyro_scale); diff --git a/src/dusk/gyro.cpp b/src/dusk/gyro.cpp index a2b026713a..0905bd59fd 100644 --- a/src/dusk/gyro.cpp +++ b/src/dusk/gyro.cpp @@ -3,31 +3,28 @@ namespace dusk::gyro { namespace { -// TODO: Make deadband and smoothing configurable -constexpr float kDeadbandRadS = 0.04f; -constexpr float kSmoothAlpha = 0.35f; constexpr s32 kRollgoalTableMaxOffset = 12000; +constexpr float kGyroEmaAlphaMin = 0.05f; +constexpr float kGyroEmaAlphaMax = 1.0f; bool s_sensor_enabled = false; float s_smooth_gx = 0.0f; float s_smooth_gy = 0.0f; float s_smooth_gz = 0.0f; float s_yaw_rad = 0.0f; -float s_yaw_rad_pending = 0.0f; float s_pitch_rad = 0.0f; -float s_pitch_rad_pending = 0.0f; float s_roll_rad = 0.0f; s32 s_rollgoal_ax = 0; s32 s_rollgoal_az = 0; void reset_filter_state() { s_smooth_gx = s_smooth_gy = s_smooth_gz = 0.0f; - s_yaw_rad_pending = s_pitch_rad_pending = s_roll_rad = 0.0f; + s_yaw_rad = s_pitch_rad = s_roll_rad = 0.0f; s_rollgoal_ax = s_rollgoal_az = 0; } -float apply_deadband(float v) { - if (v > -kDeadbandRadS && v < kDeadbandRadS) { +float apply_deadband(float v, float deadband_rad_s) { + if (v > -deadband_rad_s && v < deadband_rad_s) { return 0.0f; } return v; @@ -35,7 +32,6 @@ float apply_deadband(float v) { } // namespace bool s_sensor_keep_alive = false; - bool get_sensor_keep_alive() { return s_sensor_keep_alive; } void set_sensor_keep_alive(bool value) { s_sensor_keep_alive = value; } @@ -77,22 +73,25 @@ void read(float dt) { return; } - s_smooth_gx += kSmoothAlpha * (gyro[0] - s_smooth_gx); - s_smooth_gy += kSmoothAlpha * (gyro[1] - s_smooth_gy); - s_smooth_gz += kSmoothAlpha * (gyro[2] - s_smooth_gz); + const float smooth_alpha = kGyroEmaAlphaMax + dusk::getSettings().game.gyroSmoothing * (kGyroEmaAlphaMin - kGyroEmaAlphaMax); + const float deadband = dusk::getSettings().game.gyroDeadband; - s_pitch_rad = apply_deadband(s_smooth_gx) * dt * dusk::getSettings().game.gyroAimSensitivityX; - s_yaw_rad = apply_deadband(s_smooth_gy) * dt * dusk::getSettings().game.gyroAimSensitivityY; - s_roll_rad = apply_deadband(s_smooth_gz) * dt * dusk::getSettings().game.gyroAimSensitivityX; // GYRO NOTE: Exposing Z sensitivity seems unusual, so I'm just using X + s_smooth_gx += smooth_alpha * (gyro[0] - s_smooth_gx); + s_smooth_gy += smooth_alpha * (gyro[1] - s_smooth_gy); + s_smooth_gz += smooth_alpha * (gyro[2] - s_smooth_gz); - s_pitch_rad_pending += s_pitch_rad; - s_yaw_rad_pending += s_yaw_rad; + s_pitch_rad = -apply_deadband(s_smooth_gx, deadband) * dt * dusk::getSettings().game.gyroSensitivityX; + s_yaw_rad = apply_deadband(s_smooth_gy, deadband) * dt * dusk::getSettings().game.gyroSensitivityY; + s_roll_rad = apply_deadband(s_smooth_gz, deadband) * dt * dusk::getSettings().game.gyroSensitivityX; // GYRO NOTE: Exposing Z sensitivity seems unusual, so I'm just using X + + s_pitch_rad = dusk::getSettings().game.gyroInvertPitch ? -s_pitch_rad : s_pitch_rad; + s_yaw_rad = dusk::getSettings().game.gyroInvertYaw ? -s_yaw_rad : s_yaw_rad; + s_yaw_rad = dusk::getSettings().game.enableMirrorMode ? -s_yaw_rad : s_yaw_rad; } -void consumeAimDeltas(float& out_yaw_rad, float& out_pitch_rad) { - out_yaw_rad = s_yaw_rad_pending; - out_pitch_rad = s_pitch_rad_pending; - s_yaw_rad_pending = s_pitch_rad_pending = 0.0f; +void getAimDeltas(float& out_yaw, float& out_pitch) { + out_yaw = s_yaw_rad; + out_pitch = s_pitch_rad; } void rollgoalTick(bool play_active, s16 camera_yaw) { @@ -101,13 +100,14 @@ void rollgoalTick(bool play_active, s16 camera_yaw) { return; } - const float pitch_rad = s_pitch_rad * dusk::getSettings().game.gyroRollgoalSensitivity; - const float roll_rad = s_roll_rad * dusk::getSettings().game.gyroRollgoalSensitivity; + float pitch_rad = -s_pitch_rad * dusk::getSettings().game.gyroSensitivityRollgoal; + float roll_rad = s_roll_rad * dusk::getSettings().game.gyroSensitivityRollgoal; + roll_rad = dusk::getSettings().game.enableMirrorMode ? -roll_rad : roll_rad; s_rollgoal_az += cM_rad2s(roll_rad); cXyz in(roll_rad, 0.0f, pitch_rad); cXyz out; - cMtx_YrotS(*calc_mtx, static_cast(-camera_yaw)); + cMtx_YrotS(*calc_mtx, -camera_yaw); MtxPosition(&in, &out); s_rollgoal_ax += cM_rad2s(out.z); @@ -116,8 +116,8 @@ void rollgoalTick(bool play_active, s16 camera_yaw) { s_rollgoal_az = std::clamp(s_rollgoal_az, -kRollgoalTableMaxOffset, kRollgoalTableMaxOffset); } -void rollgoalTableOffset(s16& out_add_x, s16& out_add_z) { - out_add_x = static_cast(s_rollgoal_ax); - out_add_z = static_cast(s_rollgoal_az); +void rollgoalTableOffset(s16& out_ax, s16& out_az) { + out_ax = static_cast(s_rollgoal_ax); + out_az = static_cast(s_rollgoal_az); } } // namespace dusk::gyro diff --git a/src/dusk/imgui/ImGuiMenuEnhancements.cpp b/src/dusk/imgui/ImGuiMenuEnhancements.cpp index 516b924680..d47b4e8d4b 100644 --- a/src/dusk/imgui/ImGuiMenuEnhancements.cpp +++ b/src/dusk/imgui/ImGuiMenuEnhancements.cpp @@ -154,22 +154,32 @@ namespace dusk { "tilt the Rollgoal table in Hena's Cabin."); } - if (getSettings().game.enableGyroAim) { - config::ImGuiSliderFloat("Gyro Pitch Sensitivity", getSettings().game.gyroAimSensitivityY, 0.25f, 4.0f, "%.2f"); - config::ImGuiSliderFloat("Gyro Yaw Sensitivity", getSettings().game.gyroAimSensitivityX, 0.25f, 4.0f, "%.2f"); - } + if (getSettings().game.enableGyroAim || getSettings().game.enableGyroRollgoal) { + config::ImGuiSliderFloat("Gyro Pitch Sensitivity", getSettings().game.gyroSensitivityY, 0.25f, 4.0f, "%.2f"); + config::ImGuiSliderFloat("Gyro Yaw Sensitivity", getSettings().game.gyroSensitivityX, 0.25f, 4.0f, "%.2f"); - if (getSettings().game.enableGyroRollgoal) { - config::ImGuiSliderFloat("Rollgoal Sensitivity", getSettings().game.gyroRollgoalSensitivity, 0.25f, 4.0f, "%.2f"); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Additional multiplier for scaling how strongly\n" - "the gyroscope affects the Rollgoal table."); + if (getSettings().game.enableGyroRollgoal) { + config::ImGuiSliderFloat("Rollgoal Sensitivity", getSettings().game.gyroSensitivityRollgoal, 0.25f, 4.0f, "%.2f"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Additional multiplier for scaling how strongly\n" + "the gyroscope affects the Rollgoal table."); + } } - } - if (getSettings().game.enableGyroAim) { - config::ImGuiCheckbox("Invert Gyro Pitch", getSettings().game.gyroAimInvertPitch); - config::ImGuiCheckbox("Invert Gyro Yaw", getSettings().game.gyroAimInvertYaw); + config::ImGuiSliderFloat("Gyro Deadband", getSettings().game.gyroDeadband, 0.0f, 0.5f, "%.3f"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Angular rates below this magnitude are treated as zero,\n" + "reducing drift and jitter when the controller is still."); + } + + config::ImGuiSliderFloat("Gyro Smoothing", getSettings().game.gyroSmoothing, 0.0f, 1.0f, "%.2f"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Low values track raw gyro input more closely,\n" + "while higher values smooth out input over time."); + } + + config::ImGuiCheckbox("Invert Gyro Pitch", getSettings().game.gyroInvertPitch); + config::ImGuiCheckbox("Invert Gyro Yaw", getSettings().game.gyroInvertYaw); } ImGui::SeparatorText("Tools"); diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index fd6569ed2c..6718297f9a 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -56,11 +56,13 @@ UserSettings g_userSettings = { // Input .enableGyroAim {"game.enableGyroAim", false}, .enableGyroRollgoal {"game.enableGyroRollgoal", false}, - .gyroAimSensitivityX {"game.gyroAimSensitivityX", 1.0f}, - .gyroAimSensitivityY {"game.gyroAimSensitivityY", 1.0f}, - .gyroRollgoalSensitivity {"game.gyroRollgoalSensitivity", 1.0f}, - .gyroAimInvertPitch {"game.gyroAimInvertPitch", false}, - .gyroAimInvertYaw {"game.gyroAimInvertYaw", false}, + .gyroSensitivityX {"game.gyroSensitivityX", 1.0f}, + .gyroSensitivityY {"game.gyroSensitivityY", 1.0f}, + .gyroSensitivityRollgoal {"game.gyroSensitivityRollgoal", 1.0f}, + .gyroSmoothing {"game.gyroSmoothing", 0.65f}, + .gyroDeadband {"game.gyroDeadband", 0.04f}, + .gyroInvertPitch {"game.gyroInvertPitch", false}, + .gyroInvertYaw {"game.gyroInvertYaw", false}, // Cheats .enableFastIronBoots {"game.enableFastIronBoots", false}, @@ -137,11 +139,13 @@ void registerSettings() { Register(g_userSettings.game.enableFrameInterpolation); Register(g_userSettings.game.enableGyroAim); Register(g_userSettings.game.enableGyroRollgoal); - Register(g_userSettings.game.gyroAimSensitivityX); - Register(g_userSettings.game.gyroAimSensitivityY); - Register(g_userSettings.game.gyroRollgoalSensitivity); - Register(g_userSettings.game.gyroAimInvertPitch); - Register(g_userSettings.game.gyroAimInvertYaw); + Register(g_userSettings.game.gyroSensitivityX); + Register(g_userSettings.game.gyroSensitivityY); + Register(g_userSettings.game.gyroSensitivityRollgoal); + Register(g_userSettings.game.gyroDeadband); + Register(g_userSettings.game.gyroSmoothing); + Register(g_userSettings.game.gyroInvertPitch); + Register(g_userSettings.game.gyroInvertYaw); Register(g_userSettings.backend.isoPath); Register(g_userSettings.backend.graphicsBackend);