mirror of
https://github.com/mitch030504/Wiicompiled_VR_Frame.git
synced 2026-10-06 08:00:25 +02:00
Anchored Main Menu Quad
This commit is contained in:
1 parent
913eda2b3a
commit
bd106dd1fe
6 files changed
+94
-30
No files matched your search
@@ -386,11 +386,11 @@ inline void EnsureConfigFile() {
|
|||||||
"# model of your kart, the vehicle included.\n"
|
"# model of your kart, the vehicle included.\n"
|
||||||
"first_person_hidden_model = 0\n"
|
"first_person_hidden_model = 0\n"
|
||||||
"# Keyboard shortcut that recenters the VR view, naming the key the\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"
|
"# way SDL does (F9, Home, Keypad 5, ...). It moves the race view to\n"
|
||||||
"# you are sitting now and nothing else: forward and the horizon come\n"
|
"# where you are sitting now and brings the menu screen back upright in\n"
|
||||||
"# from the headset's own reference space, so recentering cannot tilt\n"
|
"# front of you. The race view moves in position only: forward and the\n"
|
||||||
"# the game. Applies during an immersive race; the menu screen is\n"
|
"# horizon come from the headset's own reference space, so recentering\n"
|
||||||
"# head-locked already. Rebindable from the F10 menu under VR. Leave\n"
|
"# cannot tilt the game. Rebindable from the F10 menu under VR. Leave\n"
|
||||||
"# empty to unbind.\n"
|
"# empty to unbind.\n"
|
||||||
"recenter_key = \"F9\"\n"
|
"recenter_key = \"F9\"\n"
|
||||||
"# Fixed pitch of the game camera, in degrees, for a player sitting\n"
|
"# Fixed pitch of the game camera, in degrees, for a player sitting\n"
|
||||||
|
|||||||
@@ -41,10 +41,17 @@ struct OpenXRD3D12GraphicsRequirements {
|
|||||||
struct OpenXRD3D12Presentation {
|
struct OpenXRD3D12Presentation {
|
||||||
OpenXRD3D12FrameMode mode = OpenXRD3D12FrameMode::ImmersiveProjection;
|
OpenXRD3D12FrameMode mode = OpenXRD3D12FrameMode::ImmersiveProjection;
|
||||||
|
|
||||||
// Used only by VirtualScreen. The quad is head-locked in XR_VIEW_SPACE
|
// Used only by VirtualScreen.
|
||||||
// and centered straight ahead at -Z.
|
|
||||||
float quad_distance_meters = 2.0f;
|
float quad_distance_meters = 2.0f;
|
||||||
float quad_width_meters = 2.4f;
|
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 {
|
struct OpenXRD3D12Frame {
|
||||||
@@ -96,8 +103,9 @@ public:
|
|||||||
|
|
||||||
// Releases acquired images and calls xrEndFrame. submit_layer must only be
|
// Releases acquired images and calls xrEndFrame. submit_layer must only be
|
||||||
// true after WaitForSubmission returned Success. Immersive frames submit
|
// true after WaitForSubmission returned Success. Immersive frames submit
|
||||||
// XrCompositionLayerProjection; virtual-screen frames submit a head-locked
|
// XrCompositionLayerProjection; virtual-screen frames submit an
|
||||||
// XrCompositionLayerQuad using the single mono target.
|
// XrCompositionLayerQuad using the single mono target, placed as the
|
||||||
|
// presentation's quad_anchored/quad_pose describe.
|
||||||
bool FinishFrame(OpenXRD3D12Frame& frame, bool submit_layer);
|
bool FinishFrame(OpenXRD3D12Frame& frame, bool submit_layer);
|
||||||
|
|
||||||
// Call on the XR owner thread after Aurora's worker is idle and before
|
// Call on the XR owner thread after Aurora's worker is idle and before
|
||||||
|
|||||||
@@ -36,12 +36,13 @@ void OpenXRServiceProducerFrameBoundary() noexcept;
|
|||||||
bool OpenXRIsRunning() noexcept;
|
bool OpenXRIsRunning() noexcept;
|
||||||
std::string OpenXRLastError();
|
std::string OpenXRLastError();
|
||||||
|
|
||||||
// Recenters the view on where the player is sitting now. Position only: the
|
// Recenters on where the player is sitting now: it moves the immersive race
|
||||||
// forward direction and the horizon come from the OpenXR reference space and
|
// view's origin, and re-places the anchored menu screen upright in front of
|
||||||
// are never relatched from the headset, so recentering cannot tilt the game's
|
// them. Position only for the race view: the forward direction and the horizon
|
||||||
// horizon. Use the runtime's own recenter gesture to change forward. Callable
|
// come from the OpenXR reference space and are never relatched from the
|
||||||
// from any thread; serviced on the XR pacing thread and ignored outside an
|
// headset, so recentering cannot tilt the game's horizon. Use the runtime's own
|
||||||
// immersive race, where the head-locked menu screen has no origin to recenter.
|
// recenter gesture to change forward. Callable from any thread; serviced once
|
||||||
|
// per frame on the XR pacing thread.
|
||||||
void OpenXRRequestRecenter() noexcept;
|
void OpenXRRequestRecenter() noexcept;
|
||||||
|
|
||||||
// Sets a fixed pitch of the game camera for a player sitting reclined, in
|
// Sets a fixed pitch of the game camera for a player sitting reclined, in
|
||||||
|
|||||||
@@ -885,10 +885,10 @@ void DrawVrSettings() {
|
|||||||
}
|
}
|
||||||
if (ImGui::IsItemHovered()) {
|
if (ImGui::IsItemHovered()) {
|
||||||
ImGui::SetTooltip(
|
ImGui::SetTooltip(
|
||||||
"Makes where you are sitting right now the centre of the view. Position "
|
"Makes where you are sitting right now the centre of the view, and brings "
|
||||||
"only, so the horizon stays level and forward is unchanged; use your "
|
"the menu screen back upright in front of you. The race view moves in "
|
||||||
"headset's own recenter to change forward. Applies during an immersive "
|
"position only, so the horizon stays level and forward is unchanged; use "
|
||||||
"race, as the menu screen already follows your head.");
|
"your headset's own recenter to change forward.");
|
||||||
}
|
}
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
// Click to arm, then the next key press is captured in HandleEvents.
|
// Click to arm, then the next key press is captured in HandleEvents.
|
||||||
|
|||||||
@@ -404,15 +404,25 @@ public:
|
|||||||
} else if (frame.presentation.mode == OpenXRD3D12FrameMode::VirtualScreen) {
|
} else if (frame.presentation.mode == OpenXRD3D12FrameMode::VirtualScreen) {
|
||||||
XrCompositionLayerQuad quad{XR_TYPE_COMPOSITION_LAYER_QUAD};
|
XrCompositionLayerQuad quad{XR_TYPE_COMPOSITION_LAYER_QUAD};
|
||||||
quad.layerFlags = 0;
|
quad.layerFlags = 0;
|
||||||
quad.space = runtime_->ViewSpace();
|
|
||||||
quad.eyeVisibility = XR_EYE_VISIBILITY_BOTH;
|
quad.eyeVisibility = XR_EYE_VISIBILITY_BOTH;
|
||||||
quad.subImage.swapchain = eye_swapchains_[0].handle;
|
quad.subImage.swapchain = eye_swapchains_[0].handle;
|
||||||
quad.subImage.imageRect = {{0, 0},
|
quad.subImage.imageRect = {{0, 0},
|
||||||
{static_cast<int32_t>(eye_swapchains_[0].width),
|
{static_cast<int32_t>(eye_swapchains_[0].width),
|
||||||
static_cast<int32_t>(eye_swapchains_[0].height)}};
|
static_cast<int32_t>(eye_swapchains_[0].height)}};
|
||||||
quad.subImage.imageArrayIndex = 0;
|
quad.subImage.imageArrayIndex = 0;
|
||||||
quad.pose.orientation = {0.0f, 0.0f, 0.0f, 1.0f};
|
if (frame.presentation.quad_anchored) {
|
||||||
quad.pose.position = {0.0f, 0.0f, -std::max(0.25f, frame.presentation.quad_distance_meters)};
|
// 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.size.width = std::max(0.25f, frame.presentation.quad_width_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) /
|
quad.size.height = quad.size.width * static_cast<float>(eye_swapchains_[0].height) /
|
||||||
static_cast<float>(eye_swapchains_[0].width);
|
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 {
|
void IdentityEye(AuroraStereoEye& eye) noexcept {
|
||||||
std::fill(std::begin(eye.projection), std::end(eye.projection), 0.0f);
|
std::fill(std::begin(eye.projection), std::end(eye.projection), 0.0f);
|
||||||
eye.projection[0] = 1.0f;
|
eye.projection[0] = 1.0f;
|
||||||
@@ -492,6 +507,11 @@ private:
|
|||||||
break;
|
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 (!frame.expects_gpu_submission) {
|
||||||
if (!backend_->FinishFrame(frame, false)) {
|
if (!backend_->FinishFrame(frame, false)) {
|
||||||
SetError(backend_->LastError());
|
SetError(backend_->LastError());
|
||||||
@@ -603,20 +623,12 @@ private:
|
|||||||
IdentityEye(destination.eyes[eye]);
|
IdentityEye(destination.eyes[eye]);
|
||||||
}
|
}
|
||||||
if (!immersive) {
|
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;
|
last_immersive_ = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool position_valid =
|
const bool position_valid =
|
||||||
(source.xr_frame.view_state_flags & XR_VIEW_STATE_POSITION_VALID_BIT) != 0;
|
(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
|
// 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
|
// change, and on re-entry from the virtual screen so a race start
|
||||||
// recenters a player who shifted during the menus. Position only: the
|
// 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 {
|
void ApplyPendingReferenceSpaceChange(const OpenXRFrame& frame) noexcept {
|
||||||
if (runtime_->ConsumeAppSpaceChangesThrough(frame.predicted_display_time)) {
|
if (runtime_->ConsumeAppSpaceChangesThrough(frame.predicted_display_time)) {
|
||||||
ResetTrackingOrigin();
|
ResetTrackingOrigin();
|
||||||
@@ -650,6 +690,9 @@ private:
|
|||||||
base_position_ = {};
|
base_position_ = {};
|
||||||
base_position_valid_ = false;
|
base_position_valid_ = false;
|
||||||
last_immersive_ = 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) {
|
void WaitForStopOrDelay(std::chrono::milliseconds delay) {
|
||||||
@@ -689,6 +732,8 @@ private:
|
|||||||
std::string last_error_;
|
std::string last_error_;
|
||||||
std::array<float, 3> base_position_{};
|
std::array<float, 3> base_position_{};
|
||||||
bool base_position_valid_ = false;
|
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;
|
bool last_immersive_ = false;
|
||||||
uint64_t applied_session_run_serial_ = 0;
|
uint64_t applied_session_run_serial_ = 0;
|
||||||
bool session_was_active_ = false;
|
bool session_was_active_ = false;
|
||||||
|
|||||||
Reference in new issue
Block a user