diff --git a/src/main.rs b/src/main.rs index ca32613..ca449b9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -52,6 +52,7 @@ use nodes::drawable::lines::LinesNodePlugin; use nodes::drawable::model::ModelNodePlugin; use nodes::drawable::text::TextNodePlugin; use nodes::spatial::SpatialNodePlugin; +use objects::hmd::HmdPlugin; use objects::input::mouse_pointer::FlatscreenInputPlugin; use objects::input::oxr_controller::ControllerPlugin; use objects::input::oxr_hand::HandPlugin; @@ -173,6 +174,8 @@ async fn main() -> Result { let dbus_connection = Connection::session() .await .expect("Could not open dbus session"); + // why is this requested here? should there be a specific server bus name that we check + // instead? dbus_connection .request_name("org.stardustxr.HMD") .await @@ -393,7 +396,7 @@ fn bevy_loop( AudioNodePlugin, )); // object plugins - app.add_plugins((PlaySpacePlugin, HandPlugin, ControllerPlugin)); + app.add_plugins((PlaySpacePlugin, HandPlugin, ControllerPlugin, HmdPlugin)); // feature plugins app.add_plugins(WaylandPlugin); app.add_systems(PostStartup, move || { diff --git a/src/objects/hmd.rs b/src/objects/hmd.rs new file mode 100644 index 0000000..6169054 --- /dev/null +++ b/src/objects/hmd.rs @@ -0,0 +1,108 @@ +use std::sync::Arc; + +use bevy::prelude::*; +use bevy_mod_openxr::{ + helper_traits::{ToQuat as _, ToVec3 as _}, + resources::OxrFrameState, + session::OxrSession, +}; +use bevy_mod_xr::{ + session::{XrPreDestroySession, XrSessionCreated, session_running}, + spaces::{XrPrimaryReferenceSpace, XrSpace}, +}; +use openxr::SpaceLocationFlags; + +use crate::{DbusConnection, PreFrameWait, nodes::spatial::Spatial}; + +use super::{ObjectHandle, SpatialRef, input::mouse_pointer::FlatscreenCam}; + +pub struct HmdPlugin; +impl Plugin for HmdPlugin { + fn build(&self, app: &mut App) { + app.add_systems(XrPreDestroySession, destroy_view_space); + app.add_systems(XrSessionCreated, create_view_space); + app.add_systems(PreFrameWait, update_xr.run_if(session_running)); + app.add_systems(PreFrameWait, update_flat.run_if(not(session_running))); + app.add_systems(Startup, setup); + } +} + +fn setup(connection: Res, mut cmds: Commands) { + let (spatial, _spatial_handle) = SpatialRef::create(&connection, "/org/stardustxr/HMD"); + let hmd = Hmd { + spatial, + _spatial_handle, + space: None, + }; + cmds.insert_resource(hmd); +} + +fn create_view_space(session: Res, mut hmd: ResMut) { + let space = session + .create_reference_space(openxr::ReferenceSpaceType::VIEW, Transform::IDENTITY) + .inspect_err(|err| error!("failed to create View XrSpace")) + .ok(); + hmd.space = space.map(|v| v.0); +} +fn destroy_view_space(session: Res, mut cmds: Commands, mut hmd: ResMut) { + let Some(space) = hmd.space.take() else { + return; + }; + session.destroy_space(space); +} + +#[derive(Resource)] +struct Hmd { + spatial: Arc, + _spatial_handle: ObjectHandle, + space: Option, +} + +fn update_flat(cam: Single<&GlobalTransform, With>, hmd: Res) { + // this shouldn't be parented to anything, so global and local spaces should be the same + hmd.spatial.set_local_transform(cam.compute_matrix()); +} + +fn update_xr( + session: Option>, + ref_space: Option>, + hmd: Res, + state: Option>, +) { + let (Some(session), Some(view), Some(ref_space), Some(state)) = + (session, hmd.space, ref_space, state) + else { + // tokio::task::spawn({ + // let handle = hmd.tracked_handle.clone(); + // async move { + // handle.set_tracked(false); + // } + // }); + return; + }; + // this won't be correct with pipelined rendering + let location = session + .locate_space(&view, &ref_space, state.predicted_display_time) + .inspect_err(|err| error!("Error while Locating OpenXR Stage Space {err}")); + if let Ok(location) = location { + let is_tracked = location + .location_flags + .contains(SpaceLocationFlags::POSITION_VALID | SpaceLocationFlags::POSITION_TRACKED) + || location.location_flags.contains( + SpaceLocationFlags::ORIENTATION_VALID | SpaceLocationFlags::ORIENTATION_TRACKED, + ); + // tokio::task::spawn({ + // let handle = play_space.tracked_handle.clone(); + // async move { + // handle.set_tracked(is_tracked); + // } + // }); + if is_tracked { + hmd.spatial + .set_local_transform(Mat4::from_rotation_translation( + location.pose.orientation.to_quat(), + location.pose.position.to_vec3(), + )); + } + } +} diff --git a/src/objects/input/mouse_pointer.rs b/src/objects/input/mouse_pointer.rs index fe29b5d..096a4fe 100644 --- a/src/objects/input/mouse_pointer.rs +++ b/src/objects/input/mouse_pointer.rs @@ -46,7 +46,7 @@ impl Plugin for FlatscreenInputPlugin { #[derive(Component)] #[require(Camera3d)] -struct FlatscreenCam; +pub struct FlatscreenCam; fn setup(mut cmds: Commands) { let Ok(pointer) = diff --git a/src/objects/mod.rs b/src/objects/mod.rs index 6c9ecae..98a658a 100644 --- a/src/objects/mod.rs +++ b/src/objects/mod.rs @@ -24,6 +24,7 @@ use zbus::{Connection, interface, object_server::Interface, zvariant::OwnedObjec pub mod input; pub mod play_space; +pub mod hmd; pub struct ObjectHandle(Connection, OwnedObjectPath, PhantomData);