mirror of
https://github.com/mitch030504/Wiicompiled_VR_Frame.git
synced 2026-10-06 10:00:27 +02:00
Implement OpenXR Settings Panel and Stereo Overlay
This commit is contained in:
1 parent
f222eedf6b
commit
1346e53cd6
22 files changed
+1466
-101
No files matched your search
@@ -5,6 +5,7 @@
|
||||
#if defined(MKW_ENABLE_OPENXR)
|
||||
|
||||
#include "vr/openxr_runtime.h"
|
||||
#include "vr/openxr_settings_panel.h"
|
||||
#include "vr/openxr_wii_remote.h"
|
||||
|
||||
#include <array>
|
||||
@@ -54,6 +55,11 @@ struct OpenXRPointerScreen {
|
||||
// Both are bound for the Oculus Touch profile; khr/simple_controller gets
|
||||
// select/menu and the poses so an unknown runtime still offers something.
|
||||
//
|
||||
// Clicking both thumbsticks opens the in-headset settings panel
|
||||
// (openxr_settings_panel.h). While it is open, and until every button has been
|
||||
// released after it closes, the game sees idle controllers: the chord, the
|
||||
// pointer and the triggers belong to the panel.
|
||||
//
|
||||
// Lifetime: Create after the session exists (attaches the action set, which
|
||||
// OpenXR permits once per session), Sync once per xrWaitFrame, Idle while the
|
||||
// session is not running, Destroy before the session is destroyed. All of them
|
||||
@@ -75,8 +81,10 @@ public:
|
||||
|
||||
// xrSyncActions + state reads, then publishes to the virtual gamepad and
|
||||
// the Wii Remote bridge. predicted_display_time is the frame's XrTime;
|
||||
// screen is where the pointer can land this frame.
|
||||
void Sync(XrTime predicted_display_time, const OpenXRPointerScreen& screen);
|
||||
// screen is where the Wii Remote pointer can land this frame, and
|
||||
// settings_panel where the settings panel is (its whole rectangle).
|
||||
void Sync(XrTime predicted_display_time, const OpenXRPointerScreen& screen,
|
||||
const OpenXRPointerScreen& settings_panel);
|
||||
|
||||
// Publishes a remote with nothing held, at rest and not pointing, and stops
|
||||
// the haptics, for frames without focused input.
|
||||
@@ -100,8 +108,13 @@ private:
|
||||
XrTime InputSampleTime(XrTime predicted_display_time) const;
|
||||
bool AttachVirtualGamepad();
|
||||
void DetachVirtualGamepad();
|
||||
void PublishWiiRemote(XrTime predicted_display_time, const OpenXRPointerScreen& screen,
|
||||
const std::array<wii_remote::HandInputs, kHands>& hands, uint32_t injected_buttons);
|
||||
// `withheld` publishes a remote at rest with nothing held and no pointer,
|
||||
// while still tracking motion so releasing it does not read as a jolt.
|
||||
void PublishWiiRemote(XrTime input_time, const OpenXRPointerScreen& screen,
|
||||
const std::array<wii_remote::HandInputs, kHands>& hands, uint32_t injected_buttons,
|
||||
bool withheld);
|
||||
void PublishSettingsPanel(XrTime input_time, const OpenXRPointerScreen& panel,
|
||||
const settings_panel::Frame& frame);
|
||||
void UpdateRumble();
|
||||
void StopRumble();
|
||||
bool Check(XrResult result, const char* operation);
|
||||
@@ -128,6 +141,9 @@ private:
|
||||
PFN_xrVoidFunction m_convert_now_to_xr_time = nullptr;
|
||||
wii_remote::MotionTracker m_motion[kHands];
|
||||
wii_remote::PointerFilter m_pointer;
|
||||
settings_panel::Controls m_panel_controls;
|
||||
XrTime m_last_input_time = 0;
|
||||
bool m_panel_select_held = false;
|
||||
std::array<float, 2> m_horizon{1.0f, 0.0f};
|
||||
bool m_haptics_active[kHands]{};
|
||||
uint32_t m_joystick_id = 0; // SDL_JoystickID; 0 when detached
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vr/openxr_wii_remote.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
|
||||
namespace mkw::vr {
|
||||
|
||||
// The settings panel shown inside the headset: the F10 settings, drawn by the
|
||||
// settings overlay with an ImGui context of its own and laid over the eyes by
|
||||
// Aurora (aurora_imgui_set_stereo_overlay). It sits on the virtual screen, the
|
||||
// menu quad or the race's 2D screen, and is operated with the tracked
|
||||
// controllers, which are withheld from the game while it is open.
|
||||
//
|
||||
// The XR pacing thread owns the controllers: it opens and closes the panel from
|
||||
// the controller chord, aims the pointer and publishes it here. The game thread
|
||||
// draws the panel, may close it (its Close button) or open it (the F10 bar), and
|
||||
// reads the pointer once per presented frame. Nothing in this header depends on
|
||||
// OpenXR, so the settings overlay compiles the same in builds without it.
|
||||
|
||||
// The panel's canvas in ImGui pixels, drawn at twice the desktop menu's scale.
|
||||
inline constexpr float kSettingsPanelWidthPixels = 1440.0f;
|
||||
inline constexpr float kSettingsPanelHeightPixels = 1080.0f;
|
||||
inline constexpr float kSettingsPanelUiScale = 2.0f;
|
||||
// Its width as a fraction of the virtual screen's.
|
||||
inline constexpr float kSettingsPanelWidthFraction = 0.75f;
|
||||
|
||||
struct OpenXRSettingsPanelPointer {
|
||||
bool valid = false;
|
||||
float x = 0.0f; // canvas pixels, +x right
|
||||
float y = 0.0f; // canvas pixels, +y down
|
||||
bool select = false;
|
||||
float wheel = 0.0f; // ImGui wheel steps since the last read, +up
|
||||
};
|
||||
|
||||
// Either thread.
|
||||
void OpenXRSetSettingsPanelOpen(bool open) noexcept;
|
||||
bool OpenXRSettingsPanelOpen() noexcept;
|
||||
// XR thread: this frame's pointer, and wheel steps to add to what is pending.
|
||||
void OpenXRPublishSettingsPanelPointer(bool valid, float x, float y, bool select, float wheel) noexcept;
|
||||
// Game thread: the latest pointer, taking the wheel steps accumulated since the last call.
|
||||
OpenXRSettingsPanelPointer OpenXRTakeSettingsPanelPointer() noexcept;
|
||||
|
||||
namespace settings_panel {
|
||||
|
||||
using wii_remote::HandInputs;
|
||||
|
||||
// Thumbstick deflection below this does not scroll.
|
||||
inline constexpr float kScrollDeadzone = 0.25f;
|
||||
// Wheel steps per second at full deflection. ImGui scrolls about five lines a step.
|
||||
inline constexpr float kScrollStepsPerSecond = 8.0f;
|
||||
|
||||
// The panel's half extents, in metres, on a virtual screen `screen_width` metres across.
|
||||
inline std::array<float, 2> HalfExtents(float screen_width) noexcept {
|
||||
const float half_width = 0.5f * screen_width * kSettingsPanelWidthFraction;
|
||||
return {half_width, half_width * kSettingsPanelHeightPixels / kSettingsPanelWidthPixels};
|
||||
}
|
||||
|
||||
// A hit on the panel (-1..1 across it, +v up) in canvas pixels.
|
||||
inline std::array<float, 2> CanvasPoint(const wii_remote::ScreenHit& hit) noexcept {
|
||||
return {0.5f * (hit.u + 1.0f) * kSettingsPanelWidthPixels, 0.5f * (1.0f - hit.v) * kSettingsPanelHeightPixels};
|
||||
}
|
||||
|
||||
// What the controllers do this frame.
|
||||
struct Frame {
|
||||
bool open = false; // the panel is showing
|
||||
bool withheld = false; // the game must not see the controllers
|
||||
uint32_t pointing_hand = 1;
|
||||
bool select = false;
|
||||
float wheel = 0.0f;
|
||||
};
|
||||
|
||||
// The controller side of the panel, one Update per XR frame:
|
||||
//
|
||||
// - Clicking both thumbsticks together opens or closes it. Nothing else opens
|
||||
// it from the controllers: every other button already means something to the
|
||||
// game.
|
||||
// - While it is open, the left menu button also closes it, both triggers and
|
||||
// the A / X buttons select, the thumbsticks scroll, and the hand whose trigger
|
||||
// was pulled last is the one pointing.
|
||||
// - The game gets the controllers back only once everything is released, so the
|
||||
// press that closed the panel never lands in the game as well.
|
||||
class Controls {
|
||||
public:
|
||||
// `open` is the shared flag: the chord flips it, and the game thread may
|
||||
// have changed it since the last frame. `dt_seconds` is the time since the
|
||||
// previous frame.
|
||||
Frame Update(const std::array<HandInputs, 2>& hands, bool& open, float dt_seconds) noexcept {
|
||||
const bool chord = hands[0].thumbstick_click && hands[1].thumbstick_click;
|
||||
if (chord && !m_chord_held) {
|
||||
open = !open;
|
||||
}
|
||||
m_chord_held = chord;
|
||||
|
||||
if (open && hands[0].menu && !m_menu_held && m_was_open) {
|
||||
open = false;
|
||||
}
|
||||
m_menu_held = hands[0].menu;
|
||||
|
||||
for (uint32_t hand = 0; hand < 2; ++hand) {
|
||||
const bool pulled = hands[hand].trigger > wii_remote::kPressThreshold;
|
||||
if (pulled && !m_trigger_held[hand]) {
|
||||
m_pointing_hand = hand;
|
||||
}
|
||||
m_trigger_held[hand] = pulled;
|
||||
}
|
||||
|
||||
if (open != m_was_open) {
|
||||
// Whatever is held across the change belongs to that change.
|
||||
m_release_pending = true;
|
||||
}
|
||||
if (m_release_pending && !AnyHeld(hands)) {
|
||||
m_release_pending = false;
|
||||
}
|
||||
m_was_open = open;
|
||||
|
||||
Frame frame{};
|
||||
frame.open = open;
|
||||
frame.withheld = open || m_release_pending;
|
||||
frame.pointing_hand = m_pointing_hand;
|
||||
if (!open) {
|
||||
return frame;
|
||||
}
|
||||
// Opening held nothing that selects, but a trigger still down from the
|
||||
// game must not click the first thing under the pointer.
|
||||
frame.select = !m_release_pending && (m_trigger_held[0] || m_trigger_held[1] || hands[0].primary ||
|
||||
hands[1].primary);
|
||||
const float stick = std::fabs(hands[1].stick_y) >= std::fabs(hands[0].stick_y) ? hands[1].stick_y
|
||||
: hands[0].stick_y;
|
||||
if (std::fabs(stick) > kScrollDeadzone) {
|
||||
const float magnitude = (std::fabs(stick) - kScrollDeadzone) / (1.0f - kScrollDeadzone);
|
||||
frame.wheel = std::copysign(magnitude, stick) * kScrollStepsPerSecond * std::clamp(dt_seconds, 0.0f, 0.1f);
|
||||
}
|
||||
return frame;
|
||||
}
|
||||
|
||||
void Reset() noexcept { *this = Controls{}; }
|
||||
|
||||
static bool AnyHeld(const std::array<HandInputs, 2>& hands) noexcept {
|
||||
for (const HandInputs& hand : hands) {
|
||||
if (hand.primary || hand.secondary || hand.menu || hand.thumbstick_click ||
|
||||
hand.trigger > wii_remote::kPressThreshold || hand.squeeze > wii_remote::kPressThreshold) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_chord_held = false;
|
||||
bool m_menu_held = false;
|
||||
std::array<bool, 2> m_trigger_held{};
|
||||
bool m_was_open = false;
|
||||
bool m_release_pending = false;
|
||||
uint32_t m_pointing_hand = 1;
|
||||
};
|
||||
|
||||
} // namespace settings_panel
|
||||
|
||||
} // namespace mkw::vr
|
||||
Reference in new issue
Block a user