Add Flat Screen mode for VR races and update settings accordingly

This commit is contained in:
iChris4 committed 2026-09-23 17:15:53 +02:00
1 parent 2a89fac79d
commit 2a4686191e
16 files changed
+161 -50

No files matched your search

+30 -6
View File
@@ -141,6 +141,7 @@ bool g_vrEnabled = RuntimeConfigFile::VrEnabled(true);
bool g_vrStopAtDisplayCopy = RuntimeConfigFile::VrStopAtDisplayCopy(true);
bool g_vrSkipCopyClears = RuntimeConfigFile::VrSkipCopyClears(true);
bool g_vrHudVirtualScreen = RuntimeConfigFile::VrHudVirtualScreen(true);
bool g_vrFlatScreen = RuntimeConfigFile::VrFlatScreen();
#if defined(__ANDROID__)
bool g_vrPassthrough = RuntimeConfigFile::VrPassthrough();
#endif
@@ -1354,8 +1355,9 @@ void DrawVrSettings() {
ImGui::SetTooltip(
"Wii Remote + Nunchuk: the right controller is a Wii Remote, with motion and a pointer "
"that lands where you aim on the virtual screen; the left one is the Nunchuk.\n"
" Right: A = A, trigger = B, stick up/down = 1/2\n"
" Left: stick = Nunchuk stick, trigger = Z, grip = C, X = -, menu = +, Y = settings panel\n"
" Right: A = A, trigger = B, B = C (look behind), stick up/down = 1/2\n"
" Left: stick = Nunchuk stick, trigger = Z, X = -, menu = +, Y = settings panel\n"
" The grips press nothing; they take hold of the wheel with hand steering.\n"
"Gamepad: both controllers are one ordinary controller, read as a GameCube pad.\n"
"Applies immediately; the game sees the controller change as a reconnection.");
}
@@ -1424,11 +1426,13 @@ void DrawVrSettings() {
ImGui::PopTextWrapPos();
ImGui::Separator();
ImGui::Text("VR 2D layer");
ImGui::BeginDisabled(g_vrFlatScreen);
if (ImGui::Checkbox("2D layer on a virtual screen", &g_vrHudVirtualScreen)) {
ApplyVrHudVirtualScreen();
RuntimeConfigFile::SetVrHudVirtualScreen(g_vrHudVirtualScreen);
}
if (ImGui::IsItemHovered()) {
ImGui::EndDisabled();
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
ImGui::SetTooltip(
"Puts the minimap, race position, item roulette and the rest of the race HUD on a "
"screen fixed ahead of the kart camera. Turn off to leave them stretched across "
@@ -1467,13 +1471,15 @@ void DrawVrSettings() {
ImGui::SetTooltip("Click to rebind. Esc cancels, Backspace unbinds.");
}
}
ImGui::BeginDisabled(g_vrFlatScreen);
if (ImGui::SliderFloat("Lean back angle (deg)", &g_vrLeanBackDegrees,
-RuntimeConfigFile::kVrLeanBackDegreesLimit,
RuntimeConfigFile::kVrLeanBackDegreesLimit, "%.1f")) {
mkw::vr::OpenXRSetLeanBackDegrees(g_vrLeanBackDegrees);
RuntimeConfigFile::SetVrLeanBackDegrees(g_vrLeanBackDegrees);
}
if (ImGui::IsItemHovered()) {
ImGui::EndDisabled();
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
ImGui::SetTooltip(
"Tilts the game camera back with you when you play reclined, so set it to "
"roughly how far back your seat is and the track comes back in front of you "
@@ -1489,12 +1495,26 @@ void DrawVrSettings() {
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip(
"Shows your room through the headset's cameras around the menu screen and every "
"other screen outside a race, instead of black. Races stay fully virtual. "
"other screen outside an immersive race, instead of black. Immersive races stay "
"fully virtual; the Flat Screen race has the room around it too. "
"Applies immediately.");
}
#endif
ImGui::Separator();
ImGui::Text("VR camera");
if (ImGui::Checkbox("Flat Screen mode", &g_vrFlatScreen)) {
RuntimeConfigFile::SetVrFlatScreen(g_vrFlatScreen);
mkw::vr::MkwVRPolicySetImmersiveRaces(!g_vrFlatScreen);
mkw::vr::MkwVRFirstPersonApplyConfiguredSettings();
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip(
"Plays races on the same flat screen as the menus, through the game's own camera, "
"instead of all around you in stereo. The first-person camera, hand steering and "
"the race view settings do not apply while it is on. Applies immediately.");
}
// Everything below shapes the immersive race view, which Flat Screen mode replaces.
ImGui::BeginDisabled(g_vrFlatScreen);
if (ImGui::Checkbox("First-person camera", &g_vrFirstPerson)) {
RuntimeConfigFile::SetVrFirstPerson(g_vrFirstPerson);
mkw::vr::MkwVRFirstPersonApplyConfiguredSettings();
@@ -1643,11 +1663,15 @@ void DrawVrSettings() {
mkw::vr::MkwVRFirstPersonApplyConfiguredSettings();
ApplyVrHudVirtualScreen();
}
ImGui::EndDisabled();
}
// The right-thumbstick click: flips the first-person camera exactly as its
// checkbox does. Game thread.
// checkbox does, so it does nothing in Flat Screen mode either. Game thread.
void ToggleFirstPersonCamera() {
if (g_vrFlatScreen) {
return;
}
g_vrFirstPerson = !g_vrFirstPerson;
RuntimeConfigFile::SetVrFirstPerson(g_vrFirstPerson);
mkw::vr::MkwVRFirstPersonApplyConfiguredSettings();
+5 -2
View File
@@ -1213,8 +1213,11 @@ void MkwVRFirstPersonApplyConfiguredSettings() noexcept {
const FirstPersonRotation rotation = mode == "full" ? FirstPersonRotation::Full
: mode == "yaw_pitch" ? FirstPersonRotation::YawPitch
: FirstPersonRotation::YawOnly;
MkwVRFirstPersonConfigure(RuntimeConfigFile::VrFirstPerson(false), offsets, units_per_meter,
rotation);
// Flat Screen mode shows the race on the menu screen through the game's own
// camera, where a hidden driver would only be missing from the kart.
MkwVRFirstPersonConfigure(RuntimeConfigFile::VrFirstPerson(false) &&
!RuntimeConfigFile::VrFlatScreen(),
offsets, units_per_meter, rotation);
MkwVRPolicySetFirstPersonUnitsPerMeter(units_per_meter);
const bool cockpit = RuntimeConfigFile::VrFirstPersonSeat() != "custom";
const float cockpit_units = RuntimeConfigFile::VrCockpitUnitsPerMeter();
+5
View File
@@ -257,6 +257,11 @@ void MkwVRPolicySetFirstPersonUnitsPerMeter(float units_per_meter) noexcept {
}
}
void MkwVRPolicySetImmersiveRaces(bool enabled) noexcept {
std::lock_guard<std::mutex> lock(g_policy_mutex);
ApplyPolicyMutation([&] { g_policy.config.immersive_races = enabled; });
}
MkwVRPolicySnapshot MkwVRPolicyGetSnapshot() noexcept {
std::lock_guard<std::mutex> lock(g_policy_mutex);
MkwVRPolicySnapshot snapshot;
+12 -9
View File
@@ -67,7 +67,7 @@ void ConfigurePolicy(bool enabled) noexcept {
MkwVRPolicyReset();
MkwVRPolicyConfig config{};
config.enabled = enabled;
config.immersive_races = true;
config.immersive_races = !RuntimeConfigFile::VrFlatScreen();
config.world_units_per_meter = RuntimeConfigFile::VrWorldUnitsPerMeter(500.0f);
config.hud_distance_meters = RuntimeConfigFile::VrHudDistanceMeters(2.0f);
config.hud_width_meters = RuntimeConfigFile::VrHudWidthMeters(2.4f);
@@ -753,7 +753,7 @@ private:
bool fatal = false;
uint32_t consecutive_skips = 0;
bool store_gate_set = false;
bool store_gate_immersive = false;
bool store_gate_racing = false;
bool presentation_logged = false;
VRPresentationMode logged_presentation = VRPresentationMode::Desktop;
uint32_t presentation_log_count = 0;
@@ -840,8 +840,9 @@ private:
aurora_get_stereo_screen_aspects(&picture_aspect, &snapshot_aspect)) {
presentation.quad_content_aspect = snapshot_aspect;
}
// The room around the menu screen and every other virtual screen; a race is
// fully virtual, and the cameras are paused for it.
// The room around the menu screen and every other virtual screen, a
// Flat Screen race included; an immersive race is fully virtual, and the
// cameras are paused for it.
presentation.passthrough = !immersive && passthrough_.load(std::memory_order_relaxed);
// The settings panel gets a compositor layer of its own while it is
// open, and Aurora leaves it out of the eyes. A backend that could
@@ -852,12 +853,14 @@ private:
// Pipeline caches are stored where their stall is invisible: once when a race ends,
// and by the compiler itself while the headset shows the virtual screen. Never
// mid-race.
if (!store_gate_set || immersive != store_gate_immersive) {
const bool left_race = store_gate_set && store_gate_immersive && !immersive;
// mid-race, and a race on the virtual screen (Flat Screen mode) is still a race.
const bool racing = immersive || (!policy.config.immersive_races &&
policy.scene.mode == VRSceneMode::Race);
if (!store_gate_set || racing != store_gate_racing) {
const bool left_race = store_gate_set && store_gate_racing && !racing;
store_gate_set = true;
store_gate_immersive = immersive;
aurora_set_pipeline_cache_idle_store(!immersive);
store_gate_racing = racing;
aurora_set_pipeline_cache_idle_store(!racing);
if (left_race) {
aurora_store_pipeline_caches();
}