diff --git a/crates/bevy_openxr/examples/overlay.rs b/crates/bevy_openxr/examples/overlay.rs new file mode 100644 index 0000000..589bdf1 --- /dev/null +++ b/crates/bevy_openxr/examples/overlay.rs @@ -0,0 +1,95 @@ +//! A simple 3D scene with light shining over a cube sitting on a plane. + +use bevy::prelude::*; +use bevy_openxr::{add_xr_plugins, init::OxrInitPlugin, types::OxrExtensions}; +use bevy_xr::session::XrStatus; +use openxr::EnvironmentBlendMode; +use wgpu::TextureFormat; + +fn main() { + App::new() + .add_plugins(add_xr_plugins(DefaultPlugins).build().set(OxrInitPlugin { + exts: { + let mut exts = OxrExtensions::default(); + exts.enable_hand_tracking(); + exts.other.push("XR_EXTX_overlay\0".into()); + exts + }, + // blend_modes: Some({ + // let mut v = Vec::new(); + // v.push(EnvironmentBlendMode::ALPHA_BLEND); + // v.push(EnvironmentBlendMode::ADDITIVE); + // v.push(EnvironmentBlendMode::OPAQUE); + // v + // }), + // formats: Some({ + // let mut v = Vec::new(); + // // v.push(TextureFormat::Rgba8Uint); + // v.push(TextureFormat::Rgba8Unorm); + // v.push(TextureFormat::Rgba8UnormSrgb); + // v + // }), + ..OxrInitPlugin::default() + })) + .insert_resource(ClearColor(Color::NONE)) + .add_plugins(bevy_xr_utils::hand_gizmos::HandGizmosPlugin) + .add_systems(Startup, setup) + .add_systems(Update, handle_input) + .run(); +} + +fn handle_input( + keys: Res>, + mut end: EventWriter, + mut _destroy: EventWriter, + mut _begin: EventWriter, + mut create: EventWriter, + state: Res, +) { + if keys.just_pressed(KeyCode::KeyE) { + info!("sending end"); + end.send_default(); + } + if keys.just_pressed(KeyCode::KeyC) { + info!("sending create"); + create.send_default(); + } + if keys.just_pressed(KeyCode::KeyI) { + info!("current state: {:?}", *state); + } +} + +/// set up a simple 3D scene +fn setup( + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, +) { + // circular base + // commands.spawn(PbrBundle { + // mesh: meshes.add(Circle::new(4.0)), + // material: materials.add(Color::WHITE), + // transform: Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)), + // ..default() + // }); + // cube + commands.spawn(PbrBundle { + mesh: meshes.add(Cuboid::new(1.0, 1.0, 1.0)), + material: materials.add(Color::rgb_u8(124, 144, 255)), + transform: Transform::from_xyz(0.0, 2.5, 0.0), + ..default() + }); + // light + commands.spawn(PointLightBundle { + point_light: PointLight { + shadows_enabled: true, + ..default() + }, + transform: Transform::from_xyz(4.0, 8.0, 4.0), + ..default() + }); + commands.spawn(Camera3dBundle { + transform: Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y), + ..default() + }); +} diff --git a/crates/bevy_openxr/src/openxr/exts.rs b/crates/bevy_openxr/src/openxr/exts.rs index 42a665b..26e87ec 100644 --- a/crates/bevy_openxr/src/openxr/exts.rs +++ b/crates/bevy_openxr/src/openxr/exts.rs @@ -1,6 +1,9 @@ -use bevy::prelude::{Deref, DerefMut}; +use bevy::{ecs::system::Resource, prelude::{Deref, DerefMut}}; use openxr::ExtensionSet; +#[derive(Clone, Debug, Eq, PartialEq, Deref, DerefMut, Resource)] +pub struct OxrEnabledExtensions(pub OxrExtensions); + #[derive(Clone, Debug, Eq, PartialEq, Deref, DerefMut)] pub struct OxrExtensions(ExtensionSet); impl OxrExtensions { diff --git a/crates/bevy_openxr/src/openxr/features/mod.rs b/crates/bevy_openxr/src/openxr/features/mod.rs index ce58e91..b5d55e3 100644 --- a/crates/bevy_openxr/src/openxr/features/mod.rs +++ b/crates/bevy_openxr/src/openxr/features/mod.rs @@ -1,3 +1,4 @@ pub mod handtracking; #[cfg(feature = "passthrough")] pub mod passthrough; +pub mod overlay; diff --git a/crates/bevy_openxr/src/openxr/features/overlay.rs b/crates/bevy_openxr/src/openxr/features/overlay.rs new file mode 100644 index 0000000..20e0d7e --- /dev/null +++ b/crates/bevy_openxr/src/openxr/features/overlay.rs @@ -0,0 +1,95 @@ +use std::{mem, ptr}; + +use bevy::prelude::*; +use openxr::sys; + +use crate::{ + next_chain::{OxrNextChainStructBase, OxrNextChainStructProvider}, + openxr::exts::OxrEnabledExtensions, + session::{OxrSessionCreateNextChain, OxrSessionCreateNextProvider}, +}; + +pub struct OxrOverlayPlugin; + +impl Plugin for OxrOverlayPlugin { + fn build(&self, app: &mut bevy::prelude::App) { + app.add_event::(); + app.init_resource::(); + app.add_systems(First, add_overlay_info_to_chain); + } +} + +#[derive(Resource)] +pub struct OxrOverlaySettings { + pub session_layer_placement: u32, + pub flags: openxr::OverlaySessionCreateFlagsEXTX, +} + +impl Default for OxrOverlaySettings { + fn default() -> Self { + OxrOverlaySettings { + session_layer_placement: 0, + flags: openxr::OverlaySessionCreateFlagsEXTX::EMPTY, + } + } +} + +fn add_overlay_info_to_chain( + mut chain: NonSendMut, + exts: Res, + settings: Res, +) { + if exts.other.contains(&"XR_EXTX_overlay\0".to_string()) { + chain.push(OxrSessionCreateInfoOverlay::new( + settings.flags, + settings.session_layer_placement, + )); + } +} + +#[derive(Event, Clone, Copy, Debug)] +pub enum OxrOverlaySessionEvent { + MainSessionVisibilityChanged { + visible: bool, + flags: openxr::OverlayMainSessionFlagsEXTX, + }, +} + +pub struct OxrSessionCreateInfoOverlay { + inner: sys::SessionCreateInfoOverlayEXTX, +} +impl OxrSessionCreateInfoOverlay { + pub const fn new( + flags: openxr::OverlaySessionCreateFlagsEXTX, + session_layers_placement: u32, + ) -> Self { + Self { + inner: sys::SessionCreateInfoOverlayEXTX { + ty: sys::SessionCreateInfoOverlayEXTX::TYPE, + next: ptr::null(), + create_flags: flags, + session_layers_placement, + }, + } + } +} +impl Default for OxrSessionCreateInfoOverlay { + fn default() -> Self { + Self::new(openxr::OverlaySessionCreateFlagsEXTX::EMPTY, 0) + } +} + +impl OxrNextChainStructProvider for OxrSessionCreateInfoOverlay { + fn header(&self) -> &OxrNextChainStructBase { + unsafe { mem::transmute(&self.inner) } + } + + fn set_next(&mut self, next: &OxrNextChainStructBase) { + self.inner.next = next as *const _ as *const _; + } + fn clear_next(&mut self) { + self.inner.next = ptr::null(); + } +} + +impl OxrSessionCreateNextProvider for OxrSessionCreateInfoOverlay {} diff --git a/crates/bevy_openxr/src/openxr/init.rs b/crates/bevy_openxr/src/openxr/init.rs index e216249..c3ef274 100644 --- a/crates/bevy_openxr/src/openxr/init.rs +++ b/crates/bevy_openxr/src/openxr/init.rs @@ -28,7 +28,9 @@ use bevy_xr::session::XrStatus; use bevy_xr::session::XrStatusChanged; use crate::error::OxrError; +use crate::features::overlay::OxrOverlaySessionEvent; use crate::graphics::*; +use crate::openxr::exts::OxrEnabledExtensions; use crate::resources::*; use crate::session::OxrSession; use crate::session::OxrSessionCreateNextChain; @@ -97,7 +99,9 @@ impl Plugin for OxrInitPlugin { system_id, WgpuGraphics(device, queue, adapter_info, adapter, wgpu_instance), session_create_info, + enabled_exts, )) => { + app.insert_resource(enabled_exts); app.add_plugins(( RenderPlugin { render_creation: RenderCreation::manual( @@ -197,7 +201,15 @@ pub fn update_root_transform( } impl OxrInitPlugin { - fn init_xr(&self) -> Result<(OxrInstance, OxrSystemId, WgpuGraphics, SessionConfigInfo)> { + fn init_xr( + &self, + ) -> Result<( + OxrInstance, + OxrSystemId, + WgpuGraphics, + SessionConfigInfo, + OxrEnabledExtensions, + )> { #[cfg(windows)] let entry = OxrEntry(openxr::Entry::linked()); #[cfg(not(windows))] @@ -236,7 +248,7 @@ impl OxrInitPlugin { let instance = entry.create_instance( self.app_info.clone(), - exts, + exts.clone(), // &["XR_APILAYER_LUNARG_api_dump"], &[], backend, @@ -274,6 +286,7 @@ impl OxrInitPlugin { OxrSystemId(system_id), graphics, session_create_info, + OxrEnabledExtensions(exts), )) } } @@ -495,6 +508,7 @@ pub fn poll_events( mut status: ResMut, mut changed_event: EventWriter, mut session_status_events: EventWriter, + mut overlay_writer: Option>>, ) { let _span = info_span!("xr_poll_events"); let mut buffer = Default::default(); @@ -534,6 +548,16 @@ pub fn poll_events( } InstanceLossPending(_) => {} EventsLost(e) => warn!("lost {} XR events", e.lost_event_count()), + MainSessionVisibilityChangedEXTX(d) => { + if let Some(writer) = overlay_writer.as_mut() { + writer.send(OxrOverlaySessionEvent::MainSessionVisibilityChanged { + visible: d.visible(), + flags: d.flags(), + }); + } else { + warn!("Overlay Event Recieved without the OverlayPlugin being added!"); + } + } _ => {} } } diff --git a/crates/bevy_openxr/src/openxr/mod.rs b/crates/bevy_openxr/src/openxr/mod.rs index 82eae03..95fb677 100644 --- a/crates/bevy_openxr/src/openxr/mod.rs +++ b/crates/bevy_openxr/src/openxr/mod.rs @@ -26,12 +26,12 @@ pub mod graphics; pub mod helper_traits; pub mod init; pub mod layer_builder; +pub mod next_chain; pub mod reference_space; pub mod render; pub mod resources; pub mod session; pub mod types; -pub mod next_chain; pub fn add_xr_plugins(plugins: G) -> PluginGroupBuilder { plugins @@ -49,6 +49,7 @@ pub fn add_xr_plugins(plugins: G) -> PluginGroupBuilder { .add(action_set_attaching::OxrActionAttachingPlugin) .add(action_binding::OxrActionBindingPlugin) .add(action_set_syncing::OxrActionSyncingPlugin) + .add(features::overlay::OxrOverlayPlugin) // .add(XrActionPlugin) // we should probably handle the exiting ourselfs so that we can correctly end the // session and instance