This commit is contained in:
Porkyoot 2026-06-29 09:27:58 +02:00 committed by GitHub
commit 6c21451bde
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 128 additions and 6 deletions

View File

@ -6,6 +6,7 @@ use std::{
};
use anyhow::{Result, anyhow};
use glam::{Affine3A, Vec3};
use ovr_overlay::{
TrackedDeviceIndex,
sys::{ETrackedDeviceProperty, EVRApplicationType, EVREventType},
@ -35,6 +36,7 @@ use crate::{
subsystem::notifications::NotificationManager,
windowing::{
backend::{RenderResources, RenderTarget, ShouldRender},
get_flat_hmd,
manager::OverlayWindowManager,
},
};
@ -257,6 +259,19 @@ pub fn openvr_run(
);
app.input_state.post_update(&app.session);
let hmd_is_valid = app.input_state.hmd.translation.length_squared() > 1e-6;
if !app.anchor_initialized && hmd_is_valid {
log::info!("HMD tracking has started! Initializing app.anchor flat in front of user.");
let hmd = get_flat_hmd(&app.input_state.hmd);
let anchor_local = Affine3A::from_translation(Vec3::new(0.0, 0.0, -1.0));
app.anchor = hmd * anchor_local;
app.anchor_initialized = true;
for o in overlays.values_mut() {
o.config.reset(&mut app, true);
}
}
if app
.input_state
.pointers

View File

@ -27,6 +27,7 @@ use crate::{
subsystem::notifications::NotificationManager,
windowing::{
backend::{RenderResources, RenderTarget, ShouldRender},
get_flat_hmd,
manager::OverlayWindowManager,
},
};
@ -277,6 +278,19 @@ pub fn openxr_run(
input_source.update(&xr_state, &mut app)?;
app.input_state.post_update(&app.session);
let hmd_is_valid = app.input_state.hmd.translation.length_squared() > 1e-6;
if !app.anchor_initialized && hmd_is_valid {
log::info!("HMD tracking has started! Initializing app.anchor flat in front of user.");
let hmd = get_flat_hmd(&app.input_state.hmd);
let anchor_local = Affine3A::from_translation(Vec3::new(0.0, 0.0, -1.0));
app.anchor = hmd * anchor_local;
app.anchor_initialized = true;
for o in overlays.values_mut() {
o.config.reset(&mut app, true);
}
}
if let Some(ref mut blocker) = blocker {
blocker.update(&mut app);
}

View File

@ -105,13 +105,13 @@ pub fn create_keyboard(app: &mut AppState, wayland: bool) -> anyhow::Result<Over
category: OverlayCategory::Keyboard,
default_state: OverlayWindowState {
grabbable: true,
positioning: Positioning::Anchored,
positioning: Positioning::Floating,
interactable: true,
curvature: Some(0.15),
transform: Affine3A::from_scale_rotation_translation(
Vec3::ONE * width,
Quat::from_rotation_x(-10f32.to_radians()),
vec3(0.0, -0.65, -0.5),
vec3(0.0, -0.4, -0.95),
),
..OverlayWindowState::default()
},

View File

@ -51,6 +51,7 @@ pub struct AppState {
pub screens: SmallVec<[ScreenMeta; 8]>,
pub anchor: Affine3A,
pub anchor_grabbed: bool,
pub anchor_initialized: bool,
pub wgui_globals: WguiGlobals,
pub wgui_theme: Rc<WguiTheme>,
@ -170,6 +171,7 @@ impl AppState {
screens: smallvec![],
anchor: Affine3A::IDENTITY,
anchor_grabbed: false,
anchor_initialized: false,
wgui_globals: WguiGlobals::new(
assets,
&lang_provider,

View File

@ -45,3 +45,24 @@ pub fn snap_upright(transform: Affine3A, up_dir: Vec3A) -> Affine3A {
transform
}
}
pub fn get_flat_hmd(hmd: &Affine3A) -> Affine3A {
let forward = -hmd.z_axis;
let forward_h = Vec3A::new(forward.x, 0.0, forward.z);
let forward_h = if forward_h.length_squared() > 1e-6 {
forward_h.normalize()
} else {
Vec3A::new(0.0, 0.0, -1.0)
};
let col_z = -forward_h;
let col_y = Vec3A::Y;
let col_x = col_y.cross(col_z).normalize();
Affine3A::from_cols(
col_x,
col_y,
col_z,
hmd.translation,
)
}

View File

@ -8,7 +8,7 @@ use crate::{
subsystem::input::KeyboardFocus,
windowing::{
backend::{FrameMeta, OverlayBackend, RenderResources, ShouldRender},
snap_upright,
snap_upright, get_flat_hmd,
},
};
@ -218,19 +218,33 @@ impl OverlayWindowConfig {
return;
};
if self.category == OverlayCategory::Keyboard
|| self.category == OverlayCategory::Screen
|| self.category == OverlayCategory::WayVR
{
state.positioning = Positioning::Floating;
state.saved_transform = None;
}
let cur_transform = state
.saved_transform
.unwrap_or(self.default_state.transform);
let (parent_transform, align_to_hmd) = match state.positioning {
Positioning::Floating | Positioning::FollowHead { .. } => (app.input_state.hmd, false),
Positioning::Floating | Positioning::FollowHead { .. } => {
if hard_reset {
(get_flat_hmd(&app.input_state.hmd), false)
} else {
(app.input_state.hmd, false)
}
}
Positioning::FollowHand {
hand, align_to_hmd, ..
} => (app.input_state.pointers[hand as usize].pose, align_to_hmd),
Positioning::Anchored => (app.anchor, false),
Positioning::Static => {
if hard_reset {
(app.input_state.hmd, false)
(get_flat_hmd(&app.input_state.hmd), false)
} else {
return;
}
@ -244,7 +258,12 @@ impl OverlayWindowConfig {
state.transform = parent_transform * cur_transform;
if align_to_hmd || (state.grabbable && hard_reset) {
realign(&mut state.transform, &app.input_state.hmd);
let hmd = if hard_reset {
get_flat_hmd(&app.input_state.hmd)
} else {
app.input_state.hmd
};
realign(&mut state.transform, &hmd);
}
self.dirty = true;
}
@ -252,6 +271,14 @@ impl OverlayWindowConfig {
pub fn realign(transform: &mut Affine3A, hmd: &Affine3A) {
let to_hmd = hmd.translation - transform.translation;
if to_hmd.length_squared() < 1e-6 {
log::warn!("realign: to_hmd is too short, using direct HMD alignment");
let scale = transform.x_axis.length();
let rot = Mat3A::from_quat(Quat::from_axis_angle(Vec3::Y, PI));
transform.matrix3 = Mat3A::from_cols(hmd.x_axis, hmd.y_axis, hmd.z_axis).mul_scalar(scale) * rot;
return;
}
let up_dir: Vec3A;
if hmd.x_axis.dot(Vec3A::Y).abs() > 0.2 {
@ -300,3 +327,46 @@ pub fn save_transform(state: &mut OverlayWindowState, app: &mut AppState) {
state.saved_transform = Some(parent_transform.inverse() * state.transform);
}
#[cfg(test)]
mod tests {
use super::*;
use crate::windowing::get_flat_hmd;
use glam::{Affine3A, Quat, Vec3, Vec3A};
#[test]
fn test_get_flat_hmd_removes_pitch_roll() {
let translation = Vec3::new(1.0, 2.0, 3.0);
let rotation = Quat::from_euler(glam::EulerRot::YXZ, 0.5, 0.3, 0.2);
let hmd = Affine3A::from_rotation_translation(rotation, translation);
let flat_hmd = get_flat_hmd(&hmd);
assert_eq!(flat_hmd.translation, Vec3A::from(translation));
assert!((flat_hmd.y_axis - Vec3A::Y).length() < 1e-5);
assert!((flat_hmd.x_axis.length() - 1.0).abs() < 1e-5);
assert!((flat_hmd.y_axis.length() - 1.0).abs() < 1e-5);
assert!((flat_hmd.z_axis.length() - 1.0).abs() < 1e-5);
assert!(flat_hmd.x_axis.dot(flat_hmd.y_axis).abs() < 1e-5);
assert!(flat_hmd.y_axis.dot(flat_hmd.z_axis).abs() < 1e-5);
assert!(flat_hmd.z_axis.dot(flat_hmd.x_axis).abs() < 1e-5);
}
#[test]
fn test_realign_safety_guard() {
let hmd = Affine3A::from_translation(Vec3::new(0.5, 1.0, -1.0));
let mut transform = Affine3A::from_scale_rotation_translation(
Vec3::splat(1.5),
Quat::IDENTITY,
Vec3::new(0.5, 1.0, -1.0),
);
realign(&mut transform, &hmd);
assert!(transform.x_axis.x.is_finite());
assert!(transform.y_axis.y.is_finite());
assert!(transform.z_axis.z.is_finite());
}
}