mirror of
https://github.com/mitch030504/Wiicompiled_VR_Frame.git
synced 2026-10-06 06:00:25 +02:00
Anchored Main Menu Quad
This commit is contained in:
1 parent
913eda2b3a
commit
bd106dd1fe
6 files changed
+93
-29
No files matched your search
@@ -386,11 +386,11 @@ inline void EnsureConfigFile() {
|
||||
"# model of your kart, the vehicle included.\n"
|
||||
"first_person_hidden_model = 0\n"
|
||||
"# Keyboard shortcut that recenters the VR view, naming the key the\n"
|
||||
"# way SDL does (F9, Home, Keypad 5, ...). It moves the view to where\n"
|
||||
"# you are sitting now and nothing else: forward and the horizon come\n"
|
||||
"# from the headset's own reference space, so recentering cannot tilt\n"
|
||||
"# the game. Applies during an immersive race; the menu screen is\n"
|
||||
"# head-locked already. Rebindable from the F10 menu under VR. Leave\n"
|
||||
"# way SDL does (F9, Home, Keypad 5, ...). It moves the race view to\n"
|
||||
"# where you are sitting now and brings the menu screen back upright in\n"
|
||||
"# front of you. The race view moves in position only: forward and the\n"
|
||||
"# horizon come from the headset's own reference space, so recentering\n"
|
||||
"# cannot tilt the game. Rebindable from the F10 menu under VR. Leave\n"
|
||||
"# empty to unbind.\n"
|
||||
"recenter_key = \"F9\"\n"
|
||||
"# Fixed pitch of the game camera, in degrees, for a player sitting\n"
|
||||
|
||||
@@ -41,10 +41,17 @@ struct OpenXRD3D12GraphicsRequirements {
|
||||
struct OpenXRD3D12Presentation {
|
||||
OpenXRD3D12FrameMode mode = OpenXRD3D12FrameMode::ImmersiveProjection;
|
||||
|
||||
// Used only by VirtualScreen. The quad is head-locked in XR_VIEW_SPACE
|
||||
// and centered straight ahead at -Z.
|
||||
// Used only by VirtualScreen.
|
||||
float quad_distance_meters = 2.0f;
|
||||
float quad_width_meters = 2.4f;
|
||||
|
||||
// When quad_anchored is set, the quad is placed at quad_pose in the
|
||||
// application reference space and stays put as the player looks around.
|
||||
// Otherwise it falls back to being head-locked in XR_VIEW_SPACE, centered
|
||||
// straight ahead at -Z, which is what happens until tracking has produced a
|
||||
// head pose good enough to anchor against.
|
||||
bool quad_anchored = false;
|
||||
XrPosef quad_pose{{0.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 0.0f}};
|
||||
};
|
||||
|
||||
struct OpenXRD3D12Frame {
|
||||
@@ -96,8 +103,9 @@ public:
|
||||
|
||||
// Releases acquired images and calls xrEndFrame. submit_layer must only be
|
||||
// true after WaitForSubmission returned Success. Immersive frames submit
|
||||
// XrCompositionLayerProjection; virtual-screen frames submit a head-locked
|
||||
// XrCompositionLayerQuad using the single mono target.
|
||||
// XrCompositionLayerProjection; virtual-screen frames submit an
|
||||
// XrCompositionLayerQuad using the single mono target, placed as the
|
||||
// presentation's quad_anchored/quad_pose describe.
|
||||
bool FinishFrame(OpenXRD3D12Frame& frame, bool submit_layer);
|
||||
|
||||
// Call on the XR owner thread after Aurora's worker is idle and before
|
||||
|
||||
@@ -36,12 +36,13 @@ void OpenXRServiceProducerFrameBoundary() noexcept;
|
||||
bool OpenXRIsRunning() noexcept;
|
||||
std::string OpenXRLastError();
|
||||
|
||||
// Recenters the view on where the player is sitting now. Position only: the
|
||||
// forward direction and the horizon come from the OpenXR reference space and
|
||||
// are never relatched from the headset, so recentering cannot tilt the game's
|
||||
// horizon. Use the runtime's own recenter gesture to change forward. Callable
|
||||
// from any thread; serviced on the XR pacing thread and ignored outside an
|
||||
// immersive race, where the head-locked menu screen has no origin to recenter.
|
||||
// Recenters on where the player is sitting now: it moves the immersive race
|
||||
// view's origin, and re-places the anchored menu screen upright in front of
|
||||
// them. Position only for the race view: the forward direction and the horizon
|
||||
// come from the OpenXR reference space and are never relatched from the
|
||||
// headset, so recentering cannot tilt the game's horizon. Use the runtime's own
|
||||
// recenter gesture to change forward. Callable from any thread; serviced once
|
||||
// per frame on the XR pacing thread.
|
||||
void OpenXRRequestRecenter() noexcept;
|
||||
|
||||
// Sets a fixed pitch of the game camera for a player sitting reclined, in
|
||||
|
||||
@@ -885,10 +885,10 @@ void DrawVrSettings() {
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(
|
||||
"Makes where you are sitting right now the centre of the view. Position "
|
||||
"only, so the horizon stays level and forward is unchanged; use your "
|
||||
"headset's own recenter to change forward. Applies during an immersive "
|
||||
"race, as the menu screen already follows your head.");
|
||||
"Makes where you are sitting right now the centre of the view, and brings "
|
||||
"the menu screen back upright in front of you. The race view moves in "
|
||||
"position only, so the horizon stays level and forward is unchanged; use "
|
||||
"your headset's own recenter to change forward.");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
// Click to arm, then the next key press is captured in HandleEvents.
|
||||
|
||||
@@ -404,15 +404,25 @@ public:
|
||||
} else if (frame.presentation.mode == OpenXRD3D12FrameMode::VirtualScreen) {
|
||||
XrCompositionLayerQuad quad{XR_TYPE_COMPOSITION_LAYER_QUAD};
|
||||
quad.layerFlags = 0;
|
||||
quad.space = runtime_->ViewSpace();
|
||||
quad.eyeVisibility = XR_EYE_VISIBILITY_BOTH;
|
||||
quad.subImage.swapchain = eye_swapchains_[0].handle;
|
||||
quad.subImage.imageRect = {{0, 0},
|
||||
{static_cast<int32_t>(eye_swapchains_[0].width),
|
||||
static_cast<int32_t>(eye_swapchains_[0].height)}};
|
||||
quad.subImage.imageArrayIndex = 0;
|
||||
if (frame.presentation.quad_anchored) {
|
||||
// Placed in the application space, so the screen keeps its place
|
||||
// in the room while the player looks around it.
|
||||
quad.space = runtime_->AppSpace();
|
||||
quad.pose = frame.presentation.quad_pose;
|
||||
} else {
|
||||
// No head pose to anchor against yet: keep it in front of the
|
||||
// player so the menus are never left stranded behind them.
|
||||
quad.space = runtime_->ViewSpace();
|
||||
quad.pose.orientation = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
quad.pose.position = {0.0f, 0.0f, -std::max(0.25f, frame.presentation.quad_distance_meters)};
|
||||
quad.pose.position = {
|
||||
0.0f, 0.0f, -std::max(0.25f, frame.presentation.quad_distance_meters)};
|
||||
}
|
||||
quad.size.width = std::max(0.25f, frame.presentation.quad_width_meters);
|
||||
quad.size.height = quad.size.width * static_cast<float>(eye_swapchains_[0].height) /
|
||||
static_cast<float>(eye_swapchains_[0].width);
|
||||
|
||||
@@ -135,6 +135,21 @@ std::array<float, 3> CenterPosition(const OpenXRFrame& frame) noexcept {
|
||||
};
|
||||
}
|
||||
|
||||
// Places an upright screen `distance` metres ahead of the head. Only the
|
||||
// head's yaw is used, so the screen is never pitched or rolled by whatever the
|
||||
// player's head happened to be doing when it was anchored.
|
||||
XrPosef ScreenPoseAhead(const OpenXRFrame& frame, float distance) noexcept {
|
||||
const auto& q = frame.views[0].pose.orientation;
|
||||
const float yaw =
|
||||
std::atan2(2.0f * (q.x * q.z + q.w * q.y), 1.0f - 2.0f * (q.x * q.x + q.y * q.y));
|
||||
const std::array<float, 3> center = CenterPosition(frame);
|
||||
XrPosef pose{};
|
||||
pose.orientation = {0.0f, std::sin(yaw * 0.5f), 0.0f, std::cos(yaw * 0.5f)};
|
||||
pose.position = {center[0] - std::sin(yaw) * distance, center[1],
|
||||
center[2] - std::cos(yaw) * distance};
|
||||
return pose;
|
||||
}
|
||||
|
||||
void IdentityEye(AuroraStereoEye& eye) noexcept {
|
||||
std::fill(std::begin(eye.projection), std::end(eye.projection), 0.0f);
|
||||
eye.projection[0] = 1.0f;
|
||||
@@ -492,6 +507,11 @@ private:
|
||||
break;
|
||||
}
|
||||
|
||||
// Both of these read this frame's located head pose and must run
|
||||
// before FinishFrame submits a layer built from it.
|
||||
ServiceRecenterRequest();
|
||||
UpdateVirtualScreenPose(frame);
|
||||
|
||||
if (!frame.expects_gpu_submission) {
|
||||
if (!backend_->FinishFrame(frame, false)) {
|
||||
SetError(backend_->LastError());
|
||||
@@ -603,20 +623,12 @@ private:
|
||||
IdentityEye(destination.eyes[eye]);
|
||||
}
|
||||
if (!immersive) {
|
||||
// The virtual screen is submitted in view space, so it is
|
||||
// head-locked and has no origin to recenter. Drop the request rather
|
||||
// than leaving it queued for a race start that latches the position
|
||||
// on its own anyway.
|
||||
recenter_requested_.store(false, std::memory_order_relaxed);
|
||||
last_immersive_ = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const bool position_valid =
|
||||
(source.xr_frame.view_state_flags & XR_VIEW_STATE_POSITION_VALID_BIT) != 0;
|
||||
if (recenter_requested_.exchange(false, std::memory_order_acq_rel)) {
|
||||
ResetTrackingOrigin();
|
||||
}
|
||||
// Latch on the first immersive frame, after a recenter or an origin
|
||||
// change, and on re-entry from the virtual screen so a race start
|
||||
// recenters a player who shifted during the menus. Position only: the
|
||||
@@ -640,6 +652,34 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
// Runs once per located frame, before the virtual screen is placed and
|
||||
// before the immersive origin is latched, so a recenter reaches both from
|
||||
// this frame's head pose rather than the next one's.
|
||||
void ServiceRecenterRequest() noexcept {
|
||||
if (recenter_requested_.exchange(false, std::memory_order_acq_rel)) {
|
||||
ResetTrackingOrigin();
|
||||
}
|
||||
}
|
||||
|
||||
// Anchors the menu screen in the application space and holds it there. The
|
||||
// pose is captured once, from the first frame whose head pose is good enough
|
||||
// to place it, and released again by a recenter or an origin change.
|
||||
void UpdateVirtualScreenPose(OpenXRD3D12Frame& frame) noexcept {
|
||||
if (frame.presentation.mode != OpenXRD3D12FrameMode::VirtualScreen) {
|
||||
return;
|
||||
}
|
||||
constexpr XrViewStateFlags kPoseUsable =
|
||||
XR_VIEW_STATE_ORIENTATION_VALID_BIT | XR_VIEW_STATE_POSITION_VALID_BIT;
|
||||
if (!virtual_screen_pose_valid_ && frame.xr_frame.views_valid &&
|
||||
(frame.xr_frame.view_state_flags & kPoseUsable) == kPoseUsable) {
|
||||
virtual_screen_pose_ = ScreenPoseAhead(
|
||||
frame.xr_frame, std::max(0.25f, frame.presentation.quad_distance_meters));
|
||||
virtual_screen_pose_valid_ = true;
|
||||
}
|
||||
frame.presentation.quad_anchored = virtual_screen_pose_valid_;
|
||||
frame.presentation.quad_pose = virtual_screen_pose_;
|
||||
}
|
||||
|
||||
void ApplyPendingReferenceSpaceChange(const OpenXRFrame& frame) noexcept {
|
||||
if (runtime_->ConsumeAppSpaceChangesThrough(frame.predicted_display_time)) {
|
||||
ResetTrackingOrigin();
|
||||
@@ -650,6 +690,9 @@ private:
|
||||
base_position_ = {};
|
||||
base_position_valid_ = false;
|
||||
last_immersive_ = false;
|
||||
// The anchored menu screen is placed in the same space, so it is stale
|
||||
// for exactly the same reasons and is re-placed on the next frame.
|
||||
virtual_screen_pose_valid_ = false;
|
||||
}
|
||||
|
||||
void WaitForStopOrDelay(std::chrono::milliseconds delay) {
|
||||
@@ -689,6 +732,8 @@ private:
|
||||
std::string last_error_;
|
||||
std::array<float, 3> base_position_{};
|
||||
bool base_position_valid_ = false;
|
||||
XrPosef virtual_screen_pose_{{0.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 0.0f}};
|
||||
bool virtual_screen_pose_valid_ = false;
|
||||
bool last_immersive_ = false;
|
||||
uint64_t applied_session_run_serial_ = 0;
|
||||
bool session_was_active_ = false;
|
||||
|
||||
Reference in new issue
Block a user