This commit is contained in:
TakaRikka
2026-04-16 03:33:24 -07:00
13 changed files with 240 additions and 133 deletions
+123
View File
@@ -0,0 +1,123 @@
#include "dusk/gyro.h"
#include "d/actor/d_a_alink.h"
namespace dusk::gyro {
namespace {
// TODO: Make deadband and smoothing configurable
constexpr float kDeadbandRadS = 0.04f;
constexpr float kSmoothAlpha = 0.35f;
constexpr s32 kRollgoalTableMaxOffset = 12000;
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_rollgoal_ax = s_rollgoal_az = 0;
}
float apply_deadband(float v) {
if (v > -kDeadbandRadS && v < kDeadbandRadS) {
return 0.0f;
}
return 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; }
bool queryGyroAimItemContext() {
if (!static_cast<bool>(dusk::getSettings().game.enableGyroAim)) {
return false;
}
daAlink_c* link = daAlink_getAlinkActorClass();
if (link == nullptr) {
return false;
}
return link->checkGyroAimItemContext() && dComIfGp_checkCameraAttentionStatus(link->field_0x317c, 0x10);
}
void read(float dt) {
if (!s_sensor_keep_alive && !(dusk::getSettings().game.enableGyroAim && queryGyroAimItemContext())) {
if (s_sensor_enabled) {
PADSetSensorEnabled(PAD_CHAN0, PAD_SENSOR_GYRO, FALSE);
s_sensor_enabled = false;
}
reset_filter_state();
return;
}
if (!s_sensor_enabled) {
if (!PADHasSensor(PAD_CHAN0, PAD_SENSOR_GYRO)) {
return;
}
if (!PADSetSensorEnabled(PAD_CHAN0, PAD_SENSOR_GYRO, TRUE)) {
return;
}
s_sensor_enabled = true;
}
f32 gyro[3];
if (!PADGetSensorData(PAD_CHAN0, PAD_SENSOR_GYRO, gyro, 3)) {
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);
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_pitch_rad_pending += s_pitch_rad;
s_yaw_rad_pending += 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 rollgoalTick(bool play_active, s16 camera_yaw) {
if (!play_active) {
reset_filter_state();
return;
}
const float pitch_rad = s_pitch_rad * dusk::getSettings().game.gyroRollgoalSensitivity;
const float roll_rad = s_roll_rad * dusk::getSettings().game.gyroRollgoalSensitivity;
s_rollgoal_az += cM_rad2s(roll_rad);
cXyz in(roll_rad, 0.0f, pitch_rad);
cXyz out;
cMtx_YrotS(*calc_mtx, static_cast<s16>(-camera_yaw));
MtxPosition(&in, &out);
s_rollgoal_ax += cM_rad2s(out.z);
s_rollgoal_az += cM_rad2s(out.x);
s_rollgoal_ax = std::clamp(s_rollgoal_ax, -kRollgoalTableMaxOffset, kRollgoalTableMaxOffset);
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<s16>(s_rollgoal_ax);
out_add_z = static_cast<s16>(s_rollgoal_az);
}
} // namespace dusk::gyro
-87
View File
@@ -1,87 +0,0 @@
#include "dusk/gyro_aim.h"
#include <SDL3/SDL.h>
#include "d/actor/d_a_alink.h"
namespace dusk::gyro_aim {
namespace {
// TODO: Make deadband and smoothing configurable
constexpr float kDeadbandRadS = 0.04f;
constexpr float kSmoothAlpha = 0.35f;
bool s_sensor_enabled = false;
float s_smooth_gx = 0.0f;
float s_smooth_gy = 0.0f;
float s_pending_yaw_rad = 0.0f;
float s_pending_pitch_rad = 0.0f;
void reset_filter_state() {
s_smooth_gx = s_smooth_gy = 0.0f;
s_pending_yaw_rad = s_pending_pitch_rad = 0.0f;
}
float apply_deadband(float v) {
if (v > -kDeadbandRadS && v < kDeadbandRadS) {
return 0.0f;
}
return v;
}
} // namespace
void read(float dt, bool context_active) {
if (!context_active || !static_cast<bool>(dusk::getSettings().game.enableGyroAim)) {
SDL_Gamepad* pad = SDL_GetGamepadFromPlayerIndex(0);
if (pad != nullptr && s_sensor_enabled) {
SDL_SetGamepadSensorEnabled(pad, SDL_SENSOR_GYRO, false);
s_sensor_enabled = false;
}
reset_filter_state();
return;
}
SDL_Gamepad* pad = SDL_GetGamepadFromPlayerIndex(0);
if (pad == nullptr || !SDL_GamepadHasSensor(pad, SDL_SENSOR_GYRO)) {
return;
}
if (!s_sensor_enabled) {
if (!SDL_SetGamepadSensorEnabled(pad, SDL_SENSOR_GYRO, true)) {
return;
}
s_sensor_enabled = true;
reset_filter_state();
}
float gyro[3];
if (!SDL_GetGamepadSensorData(pad, SDL_SENSOR_GYRO, gyro, 3)) {
return;
}
s_smooth_gx += kSmoothAlpha * (gyro[0] - s_smooth_gx);
s_smooth_gy += kSmoothAlpha * (gyro[1] - s_smooth_gy);
float yaw_rate = apply_deadband(s_smooth_gy);
float pitch_rate = apply_deadband(s_smooth_gx);
s_pending_yaw_rad += yaw_rate * dt * dusk::getSettings().game.gyroAimSensitivityX;
s_pending_pitch_rad += pitch_rate * dt * dusk::getSettings().game.gyroAimSensitivityY;
}
void consumeAimDeltas(float& out_yaw_rad, float& out_pitch_rad) {
out_yaw_rad = s_pending_yaw_rad;
out_pitch_rad = s_pending_pitch_rad;
s_pending_yaw_rad = 0.0f;
s_pending_pitch_rad = 0.0f;
}
bool queryGyroAimItemContext() {
if (!static_cast<bool>(dusk::getSettings().game.enableGyroAim)) {
return false;
}
daAlink_c* link = daAlink_getAlinkActorClass();
if (link == nullptr) {
return false;
}
return link->checkGyroAimItemContext() && dComIfGp_checkCameraAttentionStatus(link->field_0x317c, 0x10);
}
} // namespace dusk::gyro_aim
+24 -22
View File
@@ -5,7 +5,7 @@
#include "ImGuiConsole.hpp"
#include "ImGuiMenuGame.hpp"
#include <SDL3/SDL.h>
#include <dolphin/pad.h>
namespace dusk {
void ImGuiMenuGame::windowInputViewer() {
@@ -260,31 +260,33 @@ namespace dusk {
size.y = 130 * scale;
ImGui::Dummy(size);
SDL_Gamepad* pad = SDL_GetGamepadFromPlayerIndex(0);
if (pad != nullptr && SDL_GamepadHasSensor(pad, SDL_SENSOR_GYRO)) {
if (PADHasSensor(PAD_1, PAD_SENSOR_GYRO) == TRUE) {
ImGui::Separator();
ImGui::TextUnformatted("Gyro");
ImGui::Checkbox("Gyro Values", &m_showInputViewerGyro);
if (m_showInputViewerGyro) {
ImGui::TextUnformatted("Gyro");
constexpr float kBarScale = 4.0f;
auto bar = [kBarScale](const char* label, float v) {
const float a = std::fabs(v);
const float t = std::min(1.f, a / kBarScale);
char overlay[32];
snprintf(overlay, sizeof(overlay), "%s %+.3f", label, v);
ImGui::ProgressBar(t, ImVec2(-1, 0), overlay);
};
constexpr float kBarScale = 4.0f;
auto bar = [kBarScale](const char* label, float v) {
const float a = std::fabs(v);
const float t = std::min(1.f, a / kBarScale);
char overlay[32];
snprintf(overlay, sizeof(overlay), "%s %+.3f", label, v);
ImGui::ProgressBar(t, ImVec2(-1, 0), overlay);
};
if (SDL_SetGamepadSensorEnabled(pad, SDL_SENSOR_GYRO, true)) {
float gyro[3];
if (SDL_GetGamepadSensorData(pad, SDL_SENSOR_GYRO, gyro, 3)) {
bar("X", gyro[0]);
bar("Y", gyro[1]);
bar("Z", gyro[2]);
if (PADSetSensorEnabled(PAD_1, PAD_SENSOR_GYRO, TRUE) == TRUE) {
f32 gyro[3];
if (PADGetSensorData(PAD_1, PAD_SENSOR_GYRO, gyro, 3) == TRUE) {
bar("X", gyro[0]);
bar("Y", gyro[1]);
bar("Z", gyro[2]);
}
} else {
bar("X", 0.f);
bar("Y", 0.f);
bar("Z", 0.f);
}
} else {
bar("X", 0.f);
bar("Y", 0.f);
bar("Z", 0.f);
}
}
+23 -4
View File
@@ -148,10 +148,29 @@ namespace dusk {
"Slingshot, Gale Boomerang, Hero's Bow, Clawshot(s), Ball and Chain, and Dominion Rod.");
}
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");
config::ImGuiCheckbox("Invert Gyro Pitch", getSettings().game.gyroAimInvertPitch);
config::ImGuiCheckbox("Invert Gyro Yaw", getSettings().game.gyroAimInvertYaw);
config::ImGuiCheckbox("Gyro Rollgoal", getSettings().game.enableGyroRollgoal);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Enables the gyroscope on supported controllers to\n"
"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.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.enableGyroAim) {
config::ImGuiCheckbox("Invert Gyro Pitch", getSettings().game.gyroAimInvertPitch);
config::ImGuiCheckbox("Invert Gyro Yaw", getSettings().game.gyroAimInvertYaw);
}
ImGui::SeparatorText("Tools");
+1
View File
@@ -30,6 +30,7 @@ namespace dusk {
bool m_showControllerConfig = false;
bool m_showInputViewer = false;
bool m_showInputViewerGyro = false;
int m_inputOverlayCorner = 3;
std::string m_controllerName;
};
+4
View File
@@ -55,8 +55,10 @@ 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},
@@ -134,8 +136,10 @@ void registerSettings() {
Register(g_userSettings.game.fastSpinner);
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);