Toggle the first-person camera with a right thumbstick click

Clicking the right thumbstick flips first person exactly as the F10
checkbox does, and saves it the same way. It works on the VR controllers
in either presentation (with a short tick on the right controller) and on
any other gamepad while VR is running. It never reached the game: SDL's
stick click only lands in Aurora's extended buttons.

A click counts on release, and only if the left thumbstick stayed up and
the headset settings panel stayed closed throughout, so the two-thumbstick
panel chord in gamepad mode never toggles the camera. A gamepad whose
right stick click is bound to a GameCube control on its port, as a button
or in an input expression, keeps it for the game. The XR side only posts
a request; the toggle itself runs on the game thread with the checkbox.

[vr] first_person_toggle_click (default true) and a checkbox under the
camera toggle turn the click off. mkw_vr_camera_toggle_tests covers the
click rule.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
iChris4andClaude Opus 5 committed 2026-09-22 15:03:17 +02:00
1 parent c11a7c9a61
commit dd7f046214
11 files changed
+245 -4

No files matched your search

+14
View File
@@ -63,6 +63,7 @@ struct RuntimeUserConfig {
std::optional<std::string> vrControllerMode;
std::optional<uint32_t> vrFrameInterpolationFps;
std::optional<bool> vrFirstPerson;
std::optional<bool> vrFirstPersonToggleClick;
std::optional<float> vrFirstPersonUnitsPerMeter;
std::optional<float> vrFirstPersonHeadUpMeters;
std::optional<float> vrFirstPersonHeadForwardMeters;
@@ -502,6 +503,9 @@ inline void EnsureConfigFile() {
"# the kart, with the horizon kept level. Changeable live from the\n"
"# F10 menu, and only during a single-screen race.\n"
"first_person = false\n"
"# Clicking the right thumbstick, on the VR controllers or on any\n"
"# gamepad while VR runs, toggles first_person as the F10 checkbox does.\n"
"first_person_toggle_click = true\n"
"# Where the head sits: \"cockpit\" puts it at the driver's own eyes,\n"
"# behind the steering wheel, at a life-size scale that allows for\n"
"# the character's height, so the wheel is within reach.\n"
@@ -742,6 +746,7 @@ inline RuntimeUserConfig ParseConfigDocument(const toml::value& document) {
config.vrStopAtDisplayCopy = FindConfigValue<bool>(document, "vr", "stop_at_display_copy");
config.vrSkipCopyClears = FindConfigValue<bool>(document, "vr", "skip_copy_clears");
config.vrFirstPerson = FindConfigValue<bool>(document, "vr", "first_person");
config.vrFirstPersonToggleClick = FindConfigValue<bool>(document, "vr", "first_person_toggle_click");
if (auto value = FindConfigFloat(document, "vr", "first_person_units_per_meter");
value && *value >= 1.0f && *value <= 10000.0f) {
config.vrFirstPersonUnitsPerMeter = *value;
@@ -1066,6 +1071,11 @@ inline bool SetVrFirstPerson(bool value) {
return WriteSetting("vr", "first_person", value ? "true" : "false");
}
inline bool SetVrFirstPersonToggleClick(bool value) {
Mutable().vrFirstPersonToggleClick = value;
return WriteSetting("vr", "first_person_toggle_click", value ? "true" : "false");
}
inline bool SetVrFirstPersonUnitsPerMeter(float value) {
value = std::clamp(value, 1.0f, 10000.0f);
Mutable().vrFirstPersonUnitsPerMeter = value;
@@ -1510,6 +1520,10 @@ inline bool VrFirstPerson(bool fallback = false) {
return Get().vrFirstPerson.value_or(fallback);
}
inline bool VrFirstPersonToggleClick(bool fallback = true) {
return Get().vrFirstPersonToggleClick.value_or(fallback);
}
inline float VrFirstPersonUnitsPerMeter(float fallback = kVrFirstPersonUnitsPerMeterDefault) {
return std::clamp(Get().vrFirstPersonUnitsPerMeter.value_or(fallback), 1.0f, 10000.0f);
}