Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 54 additions & 5 deletions src/mods/VR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1396,6 +1396,11 @@ void VR::on_pre_calculate_stereo_view_offset(void* stereo_device, const int32_t
return;
}

// Refresh the plane cache on the game thread; render-thread consumers must not take the pose locks.
if (is_using_spatial()) {
update_spatial_ui_plane();
}

const auto now = std::chrono::high_resolution_clock::now();
const auto delta = std::chrono::duration<float, std::chrono::seconds::period>(now - m_last_lerp_update).count();

Expand Down Expand Up @@ -1752,6 +1757,11 @@ void VR::on_config_load(const utility::Config& cfg, bool set_defaults) {
option.config_load(cfg, set_defaults);
}

// 2D screen and spatial are mutually exclusive; spatial wins if a config has both set.
if (m_spatial_mode->value() && m_2d_screen_mode->value()) {
m_2d_screen_mode->value() = false;
}

if (get_runtime() != nullptr && get_runtime()->loaded) {
get_runtime()->on_config_load(cfg, set_defaults);

Expand All @@ -1772,8 +1782,13 @@ void VR::on_config_load(const utility::Config& cfg, bool set_defaults) {

m_overlay_component.on_config_load(cfg, set_defaults);

// Seed the aperture plane if spatial loaded enabled, before anything consumes the default plane.
if (m_spatial_mode->value()) {
update_spatial_ui_plane();
}

if (m_cvar_manager != nullptr) {
m_cvar_manager->on_config_load(cfg, set_defaults);
m_cvar_manager->on_config_load(cfg, set_defaults);
}

// Load camera offsets
Expand Down Expand Up @@ -1950,6 +1965,19 @@ void VR::handle_keybinds() {

if (m_keybind_toggle_2d_screen->is_key_down_once()) {
m_2d_screen_mode->toggle();
if (m_2d_screen_mode->value() && m_spatial_mode->value()) {
m_spatial_mode->value() = false; // mutually exclusive
get_runtime()->should_recalculate_eye_projections = true; // drop the stale off-axis projection
}
}

if (m_keybind_toggle_spatial->is_key_down_once()) {
m_spatial_mode->toggle();
if (m_spatial_mode->value()) {
m_2d_screen_mode->value() = false; // mutually exclusive
update_spatial_ui_plane(); // seed the aperture now; consumers must never see the default plane
}
get_runtime()->should_recalculate_eye_projections = true;
}

if (m_keybind_disable_vr->is_key_down_once()) {
Expand Down Expand Up @@ -2250,7 +2278,7 @@ void VR::on_post_present() {
}

uint32_t VR::get_hmd_width() const {
if (m_2d_screen_mode->value()) {
if (is_using_2d_screen()) {
if (get_runtime()->is_openxr()) {
return g_framework->get_rt_size().x * m_openxr->resolution_scale->value();
}
Expand All @@ -2266,7 +2294,7 @@ uint32_t VR::get_hmd_width() const {
}

uint32_t VR::get_hmd_height() const {
if (m_2d_screen_mode->value()) {
if (is_using_2d_screen()) {
if (get_runtime()->is_openxr()) {
return g_framework->get_rt_size().y * m_openxr->resolution_scale->value();
}
Expand Down Expand Up @@ -2394,7 +2422,26 @@ void VR::on_draw_sidebar_entry(std::string_view name) {

m_desktop_fix->draw("Desktop Spectator View");
ImGui::SameLine();
m_2d_screen_mode->draw("2D Screen Mode");
if (m_2d_screen_mode->draw("2D Screen Mode") && m_2d_screen_mode->value()) {
// 2D screen and spatial mode are mutually exclusive
if (m_spatial_mode->value()) {
get_runtime()->should_recalculate_eye_projections = true; // drop the stale off-axis projection
}
m_spatial_mode->value() = false;
}

bool spatial_changed = m_spatial_mode->draw("Spatial Mode");
if (spatial_changed && m_spatial_mode->value()) {
m_2d_screen_mode->value() = false;
update_spatial_ui_plane(); // seed the aperture now; consumers must never see the default plane
}
if (m_spatial_mode->value()) {
spatial_changed |= m_spatial_size->draw("Spatial Size");
}
if (spatial_changed) {
// regenerate eye projection matrices so the new framing takes effect
get_runtime()->should_recalculate_eye_projections = true;
}

ImGui::TextWrapped("Render Resolution (per-eye): %d x %d", get_runtime()->get_width(), get_runtime()->get_height());
ImGui::TextWrapped("Total Render Resolution: %d x %d", get_runtime()->get_width() * 2, get_runtime()->get_height());
Expand Down Expand Up @@ -2578,7 +2625,8 @@ void VR::on_draw_sidebar_entry(std::string_view name) {

ImGui::SetNextItemOpen(true, ImGuiCond_::ImGuiCond_Once);
if (ImGui::TreeNode("Decoupled Pitch")) {
m_decoupled_pitch->draw("Enabled");
m_decoupled_pitch->draw("Enabled VR");
m_spatial_decoupled_pitch->draw("Enabled Spatial");
m_decoupled_pitch_ui_adjust->draw("Auto Adjust UI");

ImGui::TreePop();
Expand Down Expand Up @@ -2607,6 +2655,7 @@ void VR::on_draw_sidebar_entry(std::string_view name) {
ImGui::SetNextItemOpen(true, ImGuiCond_::ImGuiCond_Once);
if (ImGui::TreeNode("Overlay/Runtime Keys")) {
m_keybind_toggle_2d_screen->draw("Toggle 2D Screen Mode Key");
m_keybind_toggle_spatial->draw("Toggle Spatial Mode Key");
m_keybind_toggle_gui->draw("Toggle In-Game UI Key");
m_keybind_disable_vr->draw("Disable VR Key");

Expand Down
53 changes: 52 additions & 1 deletion src/mods/VR.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,11 @@ class VR : public Mod {
}

bool is_decoupled_pitch_enabled() const {
// Spatial mode gets its own decoupled-pitch toggle, independent of the global VR setting.
if (is_using_spatial()) {
return m_spatial_decoupled_pitch->value();
}

return m_decoupled_pitch->value();
}

Expand Down Expand Up @@ -573,7 +578,43 @@ class VR : public Mod {
}

bool is_using_2d_screen() const {
return m_2d_screen_mode->value();
// 2D screen and spatial mode are mutually exclusive; spatial takes precedence
// if both flags are somehow set (e.g. a config saved before the toggles were exclusive).
return m_2d_screen_mode->value() && !m_spatial_mode->value();
}

bool is_using_spatial() const {
if (!m_spatial_mode->value()) {
return false;
}

// Combos with no spatial display backend no-op to normal VR: OpenVR+D3D12 (the capture /
// black eye submits are D3D11-only) and extreme compatibility mode (single-eye frame layout).
if (is_extreme_compatibility_mode_enabled()) {
return false;
}

if (get_runtime()->is_openvr() && g_framework->is_dx12()) {
return false;
}

return true;
}

// Spatial Size: display-only shrink of the aperture quad (1.0 = life-size). The same content on
// a smaller quad shrinks world + UI together into a coherent miniature; the render is untouched.
float get_spatial_size() const { return m_spatial_size->value(); }

// Cached UI/slate plane. Written on the game thread; read by render/present threads which hold
// pose_mtx and must NOT re-lock it. Its own mutex prevents torn reads while the plane moves.
vrmod::OverlayComponent::UIPlaneTransform get_spatial_ui_plane() const {
std::shared_lock _{m_spatial_ui_plane_mtx};
return m_spatial_ui_plane;
}
void update_spatial_ui_plane() {
const auto plane = m_overlay_component.get_ui_plane(); // takes pose/rotation locks; keep outside ours
std::unique_lock _{m_spatial_ui_plane_mtx};
m_spatial_ui_plane = plane;
}

bool is_roomscale_enabled() const {
Expand Down Expand Up @@ -895,6 +936,11 @@ class VR : public Mod {
const ModToggle::Ptr m_decoupled_pitch_ui_adjust{ ModToggle::create(generate_name("DecoupledPitchUIAdjust"), true) };
const ModToggle::Ptr m_load_blueprint_code{ ModToggle::create(generate_name("LoadBlueprintCode"), false, true) };
const ModToggle::Ptr m_2d_screen_mode{ ModToggle::create(generate_name("2DScreenMode"), false) };
const ModToggle::Ptr m_spatial_mode{ ModToggle::create(generate_name("SpatialMode"), false) };
const ModSlider::Ptr m_spatial_size{ ModSlider::create(generate_name("SpatialSize"), 0.1f, 1.0f, 1.0f) };
const ModToggle::Ptr m_spatial_decoupled_pitch{ ModToggle::create(generate_name("SpatialDecoupledPitch"), false) };
vrmod::OverlayComponent::UIPlaneTransform m_spatial_ui_plane{};
mutable std::shared_mutex m_spatial_ui_plane_mtx{};
const ModToggle::Ptr m_roomscale_movement{ ModToggle::create(generate_name("RoomscaleMovement"), false) };
const ModToggle::Ptr m_roomscale_sweep{ ModToggle::create(generate_name("RoomscaleMovementSweep"), true) };
const ModToggle::Ptr m_swap_controllers{ ModToggle::create(generate_name("SwapControllerInputs"), false) };
Expand Down Expand Up @@ -978,6 +1024,7 @@ class VR : public Mod {
const ModKey::Ptr m_keybind_load_camera_2{ ModKey::create(generate_name("LoadCamera2Key")) };

const ModKey::Ptr m_keybind_toggle_2d_screen{ ModKey::create(generate_name("Toggle2DScreenKey")) };
const ModKey::Ptr m_keybind_toggle_spatial{ ModKey::create(generate_name("ToggleSpatialModeKey")) };
const ModKey::Ptr m_keybind_disable_vr{ ModKey::create(generate_name("DisableVRKey")) };
bool m_disable_vr{false}; // definitely should not be persistent

Expand Down Expand Up @@ -1041,6 +1088,9 @@ class VR : public Mod {
*m_decoupled_pitch_ui_adjust,
*m_load_blueprint_code,
*m_2d_screen_mode,
*m_spatial_mode,
*m_spatial_size,
*m_spatial_decoupled_pitch,
*m_roomscale_movement,
*m_roomscale_sweep,
*m_swap_controllers,
Expand Down Expand Up @@ -1085,6 +1135,7 @@ class VR : public Mod {
*m_keybind_load_camera_1,
*m_keybind_load_camera_2,
*m_keybind_toggle_2d_screen,
*m_keybind_toggle_spatial,
*m_keybind_disable_vr,
*m_keybind_toggle_gui,
*m_requested_runtime_name,
Expand Down
Loading
Loading