diff --git a/crates/bevy_openxr/src/render.rs b/crates/bevy_openxr/src/render.rs index 41741e8..38c8199 100644 --- a/crates/bevy_openxr/src/render.rs +++ b/crates/bevy_openxr/src/render.rs @@ -7,7 +7,7 @@ use bevy::{ }, }; use bevy_xr::camera::{XrCameraBundle, XrProjection, XrView}; -use openxr::CompositionLayerFlags; +use openxr::{CompositionLayerFlags, ViewStateFlags}; use crate::resources::*; use crate::{init::begin_xr_session, layer_builder::*}; @@ -51,6 +51,7 @@ pub fn init_views( swapchain_images: Res, mut commands: Commands, ) { + let _span = info_span!("xr_init_views"); let temp_tex = swapchain_images.first().unwrap(); // this for loop is to easily add support for quad or mono views in the future. let mut views = vec![]; @@ -74,6 +75,7 @@ pub fn init_views( } pub fn wait_frame(mut frame_waiter: ResMut, mut commands: Commands) { + let _span = info_span!("xr_wait_frame"); let state = frame_waiter.wait().expect("Failed to wait frame"); // Here we insert the predicted display time for when this frame will be displayed. // TODO: don't add predicted_display_period if pipelined rendering plugin not enabled @@ -89,6 +91,7 @@ pub fn update_views( stage: Res, time: Res, ) { + let _span = info_span!("xr_wait_frame"); let (_flags, xr_views) = session .locate_views( openxr::ViewConfigurationType::PRIMARY_STEREO, @@ -222,6 +225,7 @@ pub fn insert_texture_views( mut manual_texture_views: ResMut, graphics_info: Res, ) { + let _span = info_span!("xr_insert_texture_views"); let index = swapchain.acquire_image().expect("Failed to acquire image"); swapchain .wait_image(openxr::Duration::INFINITE) @@ -262,16 +266,37 @@ pub fn end_frame( stage: Res, display_time: Res, graphics_info: Res, + mut openxr_views: Local>, ) { + let _span = info_span!("xr_end_frame"); swapchain.release_image().unwrap(); - let (_flags, views) = session + let (flags, views) = session .locate_views( openxr::ViewConfigurationType::PRIMARY_STEREO, **display_time, &stage, ) .expect("Failed to locate views"); - + if openxr_views.len() != views.len() { + openxr_views.resize(views.len(), default()); + } + match ( + flags & ViewStateFlags::ORIENTATION_VALID == ViewStateFlags::ORIENTATION_VALID, + flags & ViewStateFlags::POSITION_VALID == ViewStateFlags::POSITION_VALID, + ) { + (true, true) => *openxr_views = views, + (true, false) => { + for (i, view) in openxr_views.iter_mut().enumerate() { + view.pose.orientation = views[i].pose.orientation; + } + } + (false, true) => { + for (i, view) in openxr_views.iter_mut().enumerate() { + view.pose.position = views[i].pose.position; + } + } + (false, false) => {} + } let rect = openxr::Rect2Di { offset: openxr::Offset2Di { x: 0, y: 0 }, extent: openxr::Extent2Di { @@ -288,8 +313,8 @@ pub fn end_frame( .space(&stage) .views(&[ CompositionLayerProjectionView::new() - .pose(views[0].pose) - .fov(views[0].fov) + .pose(openxr_views[0].pose) + .fov(openxr_views[0].fov) .sub_image( SwapchainSubImage::new() .swapchain(&swapchain) @@ -297,8 +322,8 @@ pub fn end_frame( .image_rect(rect), ), CompositionLayerProjectionView::new() - .pose(views[1].pose) - .fov(views[1].fov) + .pose(openxr_views[1].pose) + .fov(openxr_views[1].fov) .sub_image( SwapchainSubImage::new() .swapchain(&swapchain) diff --git a/crates/bevy_openxr/src/resources.rs b/crates/bevy_openxr/src/resources.rs index 6fec233..2f9d148 100644 --- a/crates/bevy_openxr/src/resources.rs +++ b/crates/bevy_openxr/src/resources.rs @@ -277,5 +277,5 @@ pub struct XrGraphicsInfo { pub format: wgpu::TextureFormat, } -#[derive(Clone, Resource)] +#[derive(Clone, Resource, ExtractResource)] pub struct XrViews(pub Vec); diff --git a/crates/bevy_xr/src/actions.rs b/crates/bevy_xr/src/actions.rs new file mode 100644 index 0000000..288c8b5 --- /dev/null +++ b/crates/bevy_xr/src/actions.rs @@ -0,0 +1,97 @@ +use std::marker::PhantomData; + +use bevy::{ + app::{App, First, Plugin}, + ecs::system::{ResMut, Resource}, +}; + +pub struct ActionPlugin(PhantomData); + +impl Default for ActionPlugin { + fn default() -> Self { + Self(Default::default()) + } +} + +impl Plugin for ActionPlugin { + fn build(&self, app: &mut App) { + app.add_systems(First, reset_action_state::) + .init_resource::(); + app.world.resource_mut::().0.push(A::INFO); + } +} + +pub enum ActionType { + Bool, +} + +pub trait ActionTy: Send + Sync + Default + Clone + Copy { + const TYPE: ActionType; +} + +impl ActionTy for bool { + const TYPE: ActionType = ActionType::Bool; +} + +pub trait Action: Send + Sync + 'static { + type ActionType: ActionTy; + + const INFO: ActionInfo; +} + +pub struct ActionInfo { + pub pretty_name: &'static str, + pub name: &'static str, + pub action_type: ActionType, +} + +#[derive(Resource, Default)] +pub struct Actions(pub Vec); + +#[derive(Resource, Default)] +pub struct ActionState { + previous_state: A::ActionType, + current_state: A::ActionType, +} + +impl ActionState { + pub fn current_state(&self) -> A::ActionType { + self.current_state + } + + pub fn previous_state(&self) -> A::ActionType { + self.previous_state + } +} + +impl> ActionState { + pub fn pressed(&self) -> bool { + self.current_state + } + + pub fn just_pressed(&self) -> bool { + self.previous_state == false && self.current_state == true + } + + pub fn just_released(&self) -> bool { + self.previous_state == true && self.current_state == false + } + + pub fn press(&mut self) { + self.current_state = true + } +} + +pub fn reset_action_state(mut action_state: ResMut>) { + action_state.previous_state = std::mem::take(&mut action_state.current_state); +} + +pub trait ActionApp { + fn register_action(&mut self) -> &mut Self; +} + +impl ActionApp for App { + fn register_action(&mut self) -> &mut Self { + self.add_plugins(ActionPlugin::::default()) + } +} diff --git a/crates/bevy_xr/src/lib.rs b/crates/bevy_xr/src/lib.rs index 4ad0204..551636d 100644 --- a/crates/bevy_xr/src/lib.rs +++ b/crates/bevy_xr/src/lib.rs @@ -1,2 +1,3 @@ +pub mod actions; pub mod camera; pub mod session;