diff --git a/examples/demo/manifest.yaml b/examples/demo/manifest.yaml index 730187a..6cba5b8 100644 --- a/examples/demo/manifest.yaml +++ b/examples/demo/manifest.yaml @@ -13,6 +13,7 @@ android: # required: true uses_permission: - name: "com.oculus.permission.HAND_TRACKING" + - name: "android.permission.INTERNET" application: label: "Bevy Openxr Android" theme: "@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen" diff --git a/examples/demo/src/lib.rs b/examples/demo/src/lib.rs index 2867a23..e7feb94 100644 --- a/examples/demo/src/lib.rs +++ b/examples/demo/src/lib.rs @@ -19,7 +19,7 @@ use bevy_oxr::{ graphics::{extensions::XrExtensions, XrAppInfo, XrPreferdBlendMode}, input::XrInput, resources::{XrFrameState, XrInstance, XrSession}, - xr_init::{xr_only, XrEnableRequest, XrEnableStatus}, + xr_init::{xr_only, XrStatus}, xr_input::{ actions::XrActionSets, debug_gizmos::OpenXrDebugRenderer, @@ -41,18 +41,18 @@ use bevy_oxr::{ DefaultXrPlugins, }; -fn input_stuff( - keys: Res>, - status: Res, - mut request: EventWriter, -) { - if keys.just_pressed(KeyCode::Space) { - match status.into_inner() { - XrEnableStatus::Enabled => request.send(XrEnableRequest::TryDisable), - XrEnableStatus::Disabled => request.send(XrEnableRequest::TryEnable), - } - } -} +// fn input_stuff( +// keys: Res>, +// status: Res, +// mut request: EventWriter, +// ) { +// if keys.just_pressed(KeyCode::Space) { +// match status.into_inner() { +// XrEnableStatus::Enabled => request.send(XrEnableRequest::TryDisable), +// XrEnableStatus::Disabled => request.send(XrEnableRequest::TryEnable), +// } +// } +// } mod setup; use crate::setup::setup_scene; @@ -66,7 +66,7 @@ pub fn main() { let mut app = App::new(); let mut xr_extensions = XrExtensions::default(); - app.add_systems(Update, input_stuff) + app //lets get the usual diagnostic stuff added .add_plugins(LogDiagnosticsPlugin::default()) .add_plugins(FrameTimeDiagnosticsPlugin) diff --git a/src/graphics/mod.rs b/src/graphics/mod.rs index 24e80ff..33dc317 100644 --- a/src/graphics/mod.rs +++ b/src/graphics/mod.rs @@ -103,7 +103,7 @@ pub fn try_full_init( )> { let mut system_state: SystemState>> = SystemState::new(world); - let primary_window = system_state.get(&world).get_single().ok().cloned(); + let primary_window = system_state.get(world).get_single().ok().cloned(); let ( xr_instance, setup_info, diff --git a/src/lib.rs b/src/lib.rs index 4705e54..3b52be6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,10 +6,13 @@ pub mod resources; pub mod xr_init; pub mod xr_input; +use std::fs::File; +use std::io::{BufWriter, Write}; +use std::net::TcpStream; use std::sync::atomic::AtomicBool; use std::sync::{Arc, Mutex}; -use crate::xr_init::RenderRestartPlugin; +use crate::xr_init::{StartXrSession, XrInitPlugin}; use crate::xr_input::hands::hand_tracking::DisableHandTracking; use crate::xr_input::oculus_touch::ActionSets; use bevy::app::{AppExit, PluginGroupBuilder}; @@ -18,22 +21,25 @@ use bevy::prelude::*; use bevy::render::camera::{ManualTextureView, ManualTextureViewHandle, ManualTextureViews}; use bevy::render::extract_resource::ExtractResourcePlugin; use bevy::render::pipelined_rendering::PipelinedRenderingPlugin; -use bevy::render::renderer::{render_system, RenderInstance}; +use bevy::render::renderer::RenderInstance; use bevy::render::settings::RenderCreation; use bevy::render::{Render, RenderApp, RenderPlugin, RenderSet}; use bevy::window::{PresentMode, PrimaryWindow, RawHandleWrapper}; -use eyre::anyhow; use graphics::extensions::XrExtensions; use graphics::{XrAppInfo, XrPreferdBlendMode}; use input::XrInput; use openxr as xr; // use passthrough::{start_passthrough, supports_passthrough, XrPassthroughLayer}; use resources::*; -use xr::{FormFactor, FrameWaiter}; -use xr_init::{xr_only, XrEnableStatus, XrRenderData}; +use xr::FormFactor; +use xr_init::{ + xr_only, xr_render_only, CleanupXrData, XrEarlyInitPlugin, XrShouldRender, XrStatus, +}; use xr_input::controllers::XrControllerType; use xr_input::hands::emulated::HandEmulationPlugin; use xr_input::hands::hand_tracking::{HandTrackingData, HandTrackingPlugin}; +use xr_input::hands::XrHandPlugins; +use xr_input::xr_camera::XrCameraType; use xr_input::OpenXrInput; const VIEW_TYPE: xr::ViewConfigurationType = xr::ViewConfigurationType::PRIMARY_STEREO; @@ -49,141 +55,119 @@ pub struct OpenXrPlugin { app_info: XrAppInfo, } -#[derive(Resource)] -pub struct FutureXrResources( - pub Arc< - Mutex< - Option<( - XrInstance, - XrSession, - XrEnvironmentBlendMode, - XrResolution, - XrFormat, - XrSessionRunning, - XrSwapchain, - XrInput, - XrViews, - XrFrameState, - )>, - >, - >, -); - impl Plugin for OpenXrPlugin { fn build(&self, app: &mut App) { app.insert_resource(XrSessionRunning::new(AtomicBool::new(false))); - // #[cfg(target_os = "android")] - // { - // let ctx = ndk_context::android_context(); - // let vm = unsafe { jni::JavaVM::from_raw(ctx.vm().cast()) }.unwrap(); - // let env = vm.attach_current_thread_permanently(); - // } #[cfg(not(target_arch = "wasm32"))] - match graphics::try_full_init( - &mut app.world, + match graphics::initialize_xr_instance( + SystemState::>>::new(&mut app.world) + .get(&app.world) + .get_single() + .ok() + .cloned(), self.reqeusted_extensions.clone(), self.prefered_blend_mode, self.app_info.clone(), ) { - Ok((device, queue, adapter_info, render_adapter, instance)) => { + Ok(( + xr_instance, + oxr_session_setup_info, + blend_mode, + device, + queue, + adapter_info, + render_adapter, + instance, + )) => { debug!("Configured wgpu adapter Limits: {:#?}", device.limits()); debug!("Configured wgpu adapter Features: {:#?}", device.features()); - warn!("Starting in Xr"); + warn!("Starting with OpenXR Instance"); app.insert_resource(ActionSets(vec![])); + app.insert_resource(xr_instance); + app.insert_resource(blend_mode); + app.insert_non_send_resource(oxr_session_setup_info); + let render_instance = RenderInstance(instance.into()); + app.insert_resource(render_instance.clone()); app.add_plugins(RenderPlugin { render_creation: RenderCreation::Manual( device, queue, adapter_info, render_adapter, - instance, + render_instance, ), }); - app.insert_resource(XrEnableStatus::Disabled); + app.insert_resource(XrStatus::Disabled); + app.world.send_event(StartXrSession); } Err(err) => { - warn!("OpenXR Failed to initialize: {}", err); + warn!("OpenXR Instance Failed to initialize: {}", err); app.add_plugins(RenderPlugin::default()); - app.add_plugins(ExtractResourcePlugin::::default()); - app.insert_resource(XrEnableStatus::Disabled); + app.insert_resource(XrStatus::NoInstance); } } - // app.add_systems(PreUpdate, mr_test); #[cfg(target_arch = "wasm32")] { app.add_plugins(RenderPlugin::default()); - app.insert_resource(XrEnableStatus::Disabled); + app.insert_resource(XrStatus::Disabled); } - app.add_plugins(ExtractResourcePlugin::::default()); app.add_systems( PreUpdate, - (xr_poll_events, xr_begin_frame.run_if(xr_only())).chain(), + xr_poll_events.run_if(|status: Res| *status != XrStatus::NoInstance), ); - } - - fn finish(&self, app: &mut App) { - // TODO: Split this up into the indevidual resources - // app.world.get_resource::() == Some(&XrEnableStatus::Enabled) - if true { - warn!("finished xr init"); - let xr_instance = app - .world - .get_resource::() - .expect("should exist"); - let xr_session = app.world.get_resource::().expect("should exist"); - let hands = xr_instance.exts().ext_hand_tracking.is_some() - && xr_instance - .supports_hand_tracking( - xr_instance - .system(FormFactor::HEAD_MOUNTED_DISPLAY) - .unwrap(), - ) - .is_ok_and(|v| v); - if hands { - app.insert_resource(HandTrackingData::new(xr_session).unwrap()); - } else { - app.insert_resource(DisableHandTracking::Both); - } - let xr_swapchain = app - .world - .get_resource::() - .expect("should exist"); - let xr_resolution = app - .world - .get_resource::() - .expect("should exist"); - let xr_format = app.world.get_resource::().expect("should exist"); - - let (left, right) = xr_swapchain.get_render_views(); - let left = ManualTextureView { - texture_view: left.into(), - size: **xr_resolution, - format: **xr_format, - }; - let right = ManualTextureView { - texture_view: right.into(), - size: **xr_resolution, - format: **xr_format, - }; - let mut manual_texture_views = app.world.resource_mut::(); - manual_texture_views.insert(LEFT_XR_TEXTURE_HANDLE, left); - manual_texture_views.insert(RIGHT_XR_TEXTURE_HANDLE, right); - drop(manual_texture_views); - let render_app = app.sub_app_mut(RenderApp); - render_app.add_systems( - Render, - ( - post_frame - .run_if(xr_only()) - .before(render_system) - .after(RenderSet::ExtractCommands), - end_frame.run_if(xr_only()).after(render_system), - ), - ); - } + app.add_systems( + PreUpdate, + ( + xr_reset_should_render, + apply_deferred, + xr_wait_frame.run_if(xr_only()), + apply_deferred, + locate_views.run_if(xr_only()), + apply_deferred, + ) + .chain() + .after(xr_poll_events), + ); + let render_app = app.sub_app_mut(RenderApp); + render_app.add_systems( + Render, + xr_begin_frame + .run_if(xr_only()) + .run_if(xr_render_only()) + .after(RenderSet::ExtractCommands) + .before(xr_pre_frame), + ); + render_app.add_systems( + Render, + xr_pre_frame + .run_if(xr_only()) + .run_if(xr_render_only()) + .in_set(RenderSet::Prepare), + ); + render_app.add_systems( + Render, + (locate_views, xr_input::xr_camera::xr_camera_head_sync) + .chain() + .run_if(xr_only()) + .run_if(xr_render_only()) + .in_set(RenderSet::Prepare), + ); + render_app.add_systems( + Render, + xr_end_frame + .run_if(xr_only()) + .run_if(xr_render_only()) + .after(RenderSet::Render), + ); + render_app.insert_resource(TcpConnection( + TcpStream::connect("192.168.2.100:6969").unwrap(), + )); } } +#[derive(Resource)] +struct TcpConnection(TcpStream); + #[derive(Default)] pub struct DefaultXrPlugins { pub reqeusted_extensions: XrExtensions, @@ -203,11 +187,10 @@ impl PluginGroup for DefaultXrPlugins { app_info: self.app_info.clone(), }) .add_after::(OpenXrInput::new(XrControllerType::OculusTouch)) - .add_before::(RenderRestartPlugin) - .add(HandEmulationPlugin) - .add(HandTrackingPlugin) + .add_after::(XrInitPlugin) + .add_before::(XrEarlyInitPlugin) + .add(XrHandPlugins) .add(XrResourcePlugin) - // .add(xr_init::RenderRestartPlugin) .set(WindowPlugin { #[cfg(not(target_os = "android"))] primary_window: Some(Window { @@ -227,11 +210,16 @@ impl PluginGroup for DefaultXrPlugins { } } -pub fn xr_poll_events( +fn xr_reset_should_render(mut should: ResMut) { + **should = false; +} + +fn xr_poll_events( instance: Option>, session: Option>, session_running: Res, mut app_exit: EventWriter, + mut cleanup_xr: EventWriter, ) { if let (Some(instance), Some(session)) = (instance, session) { let _span = info_span!("xr_poll_events"); @@ -251,6 +239,7 @@ pub fn xr_poll_events( xr::SessionState::STOPPING => { session.end().unwrap(); session_running.store(false, std::sync::atomic::Ordering::Relaxed); + cleanup_xr.send_default(); } xr::SessionState::EXITING | xr::SessionState::LOSS_PENDING => { // app_exit.send(AppExit); @@ -260,7 +249,9 @@ pub fn xr_poll_events( _ => {} } } - InstanceLossPending(_) => return, + InstanceLossPending(_) => { + app_exit.send_default(); + } EventsLost(e) => { warn!("lost {} XR events", e.lost_event_count()); } @@ -270,13 +261,15 @@ pub fn xr_poll_events( } } -pub fn xr_begin_frame( - session: Res, +fn xr_begin_frame(swapchain: Res) { + let _span = info_span!("xr_begin_frame").entered(); + swapchain.begin().unwrap() +} + +pub fn xr_wait_frame( mut frame_state: ResMut, mut frame_waiter: ResMut, - swapchain: Res, - mut views: ResMut, - input: Res, + mut should_render: ResMut, ) { { let _span = info_span!("xr_wait_frame").entered(); @@ -287,21 +280,11 @@ pub fn xr_begin_frame( return; } }; - } - { - let _span = info_span!("xr_begin_frame").entered(); - swapchain.begin().unwrap() - } - { - let _span = info_span!("xr_locate_views").entered(); - **views = session - .locate_views(VIEW_TYPE, frame_state.predicted_display_time, &input.stage) - .unwrap() - .1; + **should_render = frame_state.should_render; } } -pub fn post_frame( +pub fn xr_pre_frame( resolution: Res, format: Res, swapchain: Res, @@ -313,7 +296,9 @@ pub fn post_frame( } { let _span = info_span!("xr_wait_image").entered(); + info!("wait image"); swapchain.wait_image().unwrap(); + info!("waited image"); } { let _span = info_span!("xr_update_manual_texture_views").entered(); @@ -333,36 +318,51 @@ pub fn post_frame( } } -pub fn end_frame( - xr_frame_state: Option>, - views: Option>, - input: Option>, - swapchain: Option>, - resolution: Option>, - environment_blend_mode: Option>, - // _main_thread: NonSend<()>, - #[cfg(target_os = "android")] mut attached: Local, - // passthrough_layer: Option>, +pub fn xr_end_frame( + xr_frame_state: Res, + views: Res, + input: Res, + swapchain: Res, + resolution: Res, + environment_blend_mode: Res, + mut connection: ResMut, + cams: Query<(&Transform, &XrCameraType)>, ) { - let xr_frame_state = xr_frame_state.unwrap(); - let views = views.unwrap(); - let input = input.unwrap(); - let swapchain = swapchain.unwrap(); - let resolution = resolution.unwrap(); - let environment_blend_mode = environment_blend_mode.unwrap(); - #[cfg(target_os = "android")] - // if !*attached { { let ctx = ndk_context::android_context(); let vm = unsafe { jni::JavaVM::from_raw(ctx.vm().cast()) }.unwrap(); let env = vm.attach_current_thread_as_daemon(); - *attached = true; } { let _span = info_span!("xr_release_image").entered(); swapchain.release_image().unwrap(); } + let mut cam = None; + for (t, c) in &cams { + if *c == XrCameraType::Xr(xr_input::xr_camera::Eye::Left) { + cam = Some(*t); + break; + } + } + let _ = std::writeln!( + &mut connection.0, + "{},{},{},{},{},{},{},{},{},{},{},{},{},{}", + views[0].pose.position.x, + views[0].pose.position.y, + views[0].pose.position.z, + views[0].pose.orientation.x, + views[0].pose.orientation.y, + views[0].pose.orientation.z, + views[0].pose.orientation.w, + cam.unwrap().translation.x, + cam.unwrap().translation.y, + cam.unwrap().translation.z, + cam.unwrap().rotation.x, + cam.unwrap().rotation.y, + cam.unwrap().rotation.z, + cam.unwrap().rotation.w, + ); { let _span = info_span!("xr_end_frame").entered(); let result = swapchain.end( diff --git a/src/resources.rs b/src/resources.rs index 584cb16..da72cf6 100644 --- a/src/resources.rs +++ b/src/resources.rs @@ -5,7 +5,7 @@ use std::sync::Mutex; use crate::input::XrInput; // use crate::passthrough::XrPassthroughLayer; use crate::resource_macros::*; -use crate::xr_init::XrEnableStatus; +use crate::xr_init::XrStatus; use bevy::prelude::*; use bevy::render::extract_resource::ExtractResourcePlugin; use openxr as xr; @@ -27,7 +27,6 @@ pub(crate) struct VulkanOXrSessionSetupInfo { pub(crate) vk_instance_ptr: *const c_void, pub(crate) queue_family_index: u32, pub(crate) xr_system_id: xr::SystemId, - // pub(crate) swapchain_create_info: xr::SwapchainCreateInfo } pub(crate) enum OXrSessionSetupInfo { @@ -46,6 +45,7 @@ impl Plugin for XrResourcePlugin { app.add_plugins(ExtractResourcePlugin::::default()); app.add_plugins(ExtractResourcePlugin::::default()); app.add_plugins(ExtractResourcePlugin::::default()); + app.add_plugins(ExtractResourcePlugin::::default()); } } @@ -219,7 +219,6 @@ impl SwapchainInner { // } // None => - info!("swapchain stream lock"); let r = self.stream.lock().unwrap().end( predicted_display_time, environment_blend_mode, @@ -244,7 +243,6 @@ impl SwapchainInner { ), ])], ); - info!("swapchain stream done"); r // } } diff --git a/src/xr_init.rs b/src/xr_init.rs deleted file mode 100644 index fb4917a..0000000 --- a/src/xr_init.rs +++ /dev/null @@ -1,433 +0,0 @@ -// Just a lot of code that is meant for something way more complex but hey. -// maybe will work on that soon - -use std::sync::Arc; - -use bevy::{ - ecs::schedule::{ExecutorKind, ScheduleLabel}, - prelude::*, - render::{ - extract_resource::{ExtractResource, ExtractResourcePlugin}, - renderer::{ - self, RenderAdapter, RenderAdapterInfo, RenderDevice, RenderInstance, RenderQueue, - }, - settings::WgpuSettings, - }, - window::{PrimaryWindow, RawHandleWrapper}, -}; -use wgpu::Instance; - -use crate::{ - graphics, - input::XrInput, - resources::{ - OXrSessionSetupInfo, XrEnvironmentBlendMode, XrFormat, XrFrameState, XrInstance, - XrResolution, XrSession, XrSessionRunning, XrSwapchain, XrViews, - }, -}; - -#[derive(Resource, Clone)] -pub struct RenderCreationData { - pub device: RenderDevice, - pub queue: RenderQueue, - pub adapter_info: RenderAdapterInfo, - pub render_adapter: RenderAdapter, - pub instance: Arc, -} - -#[derive(Resource, Clone, ExtractResource)] -pub struct XrRenderData { - pub xr_instance: XrInstance, - pub xr_session: XrSession, - pub xr_blend_mode: XrEnvironmentBlendMode, - pub xr_resolution: XrResolution, - pub xr_format: XrFormat, - pub xr_session_running: XrSessionRunning, - // pub xr_frame_waiter: XrFrameWaiter, - pub xr_swapchain: XrSwapchain, - pub xr_input: XrInput, - pub xr_views: XrViews, - pub xr_frame_state: XrFrameState, -} - -#[derive(Event, Clone, Copy, Debug)] -pub enum XrEnableRequest { - TryEnable, - TryDisable, -} -#[derive(Resource, Event, Copy, Clone, PartialEq, Eq, Reflect, ExtractResource)] -pub enum XrEnableStatus { - Enabled, - Disabled, -} - -#[derive(Resource, Event, Copy, Clone, PartialEq, Eq, Debug, ExtractResource)] -pub enum XrNextEnabledState { - Enabled, - Disabled, -} - -pub struct RenderRestartPlugin; - -#[derive(Debug, ScheduleLabel, Clone, Copy, Hash, PartialEq, Eq)] -pub struct XrPreSetup; - -#[derive(Debug, ScheduleLabel, Clone, Copy, Hash, PartialEq, Eq)] -pub struct XrSetup; - -#[derive(Debug, ScheduleLabel, Clone, Copy, Hash, PartialEq, Eq)] -pub struct XrPrePostSetup; - -#[derive(Debug, ScheduleLabel, Clone, Copy, Hash, PartialEq, Eq)] -pub struct XrPostSetup; - -#[derive(Debug, ScheduleLabel, Clone, Copy, Hash, PartialEq, Eq)] -pub struct XrPreCleanup; - -#[derive(Debug, ScheduleLabel, Clone, Copy, Hash, PartialEq, Eq)] -pub struct XrCleanup; - -#[derive(Debug, ScheduleLabel, Clone, Copy, Hash, PartialEq, Eq)] -pub struct XrPostCleanup; - -#[derive(Debug, ScheduleLabel, Clone, Copy, Hash, PartialEq, Eq)] -pub struct XrPreRenderUpdate; - -#[derive(Debug, ScheduleLabel, Clone, Copy, Hash, PartialEq, Eq)] -pub struct XrRenderUpdate; - -#[derive(Debug, ScheduleLabel, Clone, Copy, Hash, PartialEq, Eq)] -pub struct XrPostRenderUpdate; - -pub fn xr_only() -> impl FnMut(Option>, Res) -> bool { - |status, running| { - resource_exists_and_equals(XrEnableStatus::Enabled)(status) - && running.load(std::sync::atomic::Ordering::Relaxed) - } -} - -impl Plugin for RenderRestartPlugin { - fn build(&self, app: &mut App) { - add_schedules(app); - app.add_plugins(ExtractResourcePlugin::::default()) - .add_event::() - .add_event::() - .add_event::() - .add_event::() - .add_systems( - PreUpdate, - setup_xr - .run_if(|running: Res| { - running.load(std::sync::atomic::Ordering::Relaxed) - }) - .run_if(run_once()), - ) - .add_systems( - PostUpdate, - update_xr_stuff.run_if(on_event::()), - ) - .add_systems(XrPreRenderUpdate, decide_next_xr_state) - .add_systems(XrPostRenderUpdate, clear_events) - .add_systems( - XrRenderUpdate, - ( - cleanup_xr.run_if(resource_exists_and_equals(XrNextEnabledState::Disabled)), - // handle_xr_enable_requests, - apply_deferred, - setup_xr, /* .run_if(resource_exists_and_equals(XrEnableStatus::Enabled)) */ - ) - .chain(), - ) - .add_systems(XrCleanup, cleanup_oxr_session); - app.add_systems( - PostUpdate, - start_xr_session.run_if(on_event::()), - ); - app.add_systems( - PostUpdate, - stop_xr_session.run_if(on_event::()), - ); - } -} - -fn clear_events(mut events: ResMut>) { - events.clear(); -} - -fn add_schedules(app: &mut App) { - let schedules = [ - Schedule::new(XrPreSetup), - Schedule::new(XrSetup), - Schedule::new(XrPrePostSetup), - Schedule::new(XrPostSetup), - Schedule::new(XrPreRenderUpdate), - Schedule::new(XrRenderUpdate), - Schedule::new(XrPostRenderUpdate), - Schedule::new(XrPreCleanup), - Schedule::new(XrCleanup), - Schedule::new(XrPostCleanup), - ]; - for mut schedule in schedules { - schedule.set_executor_kind(ExecutorKind::SingleThreaded); - schedule.set_apply_final_deferred(true); - app.add_schedule(schedule); - } -} - -pub fn setup_xr(world: &mut World) { - info!("running setup schedule :3"); - world.insert_resource(XrEnableStatus::Enabled); - world.run_schedule(XrPreSetup); - world.run_schedule(XrSetup); - world.run_schedule(XrPrePostSetup); - world.run_schedule(XrPostSetup); -} -fn cleanup_xr(world: &mut World) { - world.run_schedule(XrPreCleanup); - world.run_schedule(XrCleanup); - world.run_schedule(XrPostCleanup); -} - -fn cleanup_oxr_session(xr_status: Option>, session: Option>) { - if let (Some(XrEnableStatus::Disabled), Some(s)) = (xr_status.map(|v| v.into_inner()), session) - { - s.into_inner().request_exit().unwrap(); - } -} - -pub fn update_xr_stuff(world: &mut World) { - world.run_schedule(XrPreRenderUpdate); - world.run_schedule(XrRenderUpdate); - world.run_schedule(XrPostRenderUpdate); -} - -#[derive(Event, Clone, Copy, Default)] -pub struct StartXrSession; - -#[derive(Event, Clone, Copy, Default)] -pub struct EndXrSession; - -fn start_xr_session( - mut commands: Commands, - instance: Res, - primary_window: Query<&RawHandleWrapper, With>, - setup_info: NonSend, - render_device: Res, - render_adapter: Res, - render_instance: Res, -) { - let ( - xr_session, - xr_resolution, - xr_format, - xr_session_running, - xr_frame_waiter, - xr_swapchain, - xr_input, - xr_views, - xr_frame_state, - ) = match graphics::start_xr_session( - primary_window.get_single().cloned().ok(), - &setup_info, - &instance, - &render_device, - &render_adapter, - &render_instance, - ) { - Ok(data) => data, - Err(err) => { - error!("Unable to start OpenXR Session: {}", err); - return; - } - }; - commands.insert_resource(xr_session); - commands.insert_resource(xr_resolution); - commands.insert_resource(xr_format); - commands.insert_resource(xr_session_running); - commands.insert_resource(xr_frame_waiter); - commands.insert_resource(xr_swapchain); - commands.insert_resource(xr_input); - commands.insert_resource(xr_views); - commands.insert_resource(xr_frame_state); -} - -fn stop_xr_session(mut commands: Commands, session: ResMut) { - match session.request_exit() { - Ok(_) => {} - Err(err) => { - error!("Error while trying to request session exit: {}", err) - } - } -} - -// fn handle_xr_enable_requests( -// primary_window: Query<&RawHandleWrapper, With>, -// mut commands: Commands, -// next_state: Res, -// on_main: Option>, -// ) { -// // Just to force this system onto the main thread because of unsafe code -// let _ = on_main; -// -// let (creation_data, xr_data) = match next_state.into_inner() { -// XrNextEnabledState::Enabled => { -// let ( -// device, -// queue, -// adapter_info, -// render_adapter, -// instance, -// xr_instance, -// session, -// blend_mode, -// resolution, -// format, -// session_running, -// frame_waiter, -// swapchain, -// input, -// views, -// frame_state, -// ) = graphics::initialize_xr_graphics(primary_window.get_single().ok().cloned()) -// .unwrap(); -// -// commands.insert_resource(XrEnableStatus::Enabled); -// ( -// RenderCreationData { -// device, -// queue, -// adapter_info, -// render_adapter, -// instance: Arc::new(instance), -// }, -// Some(XrRenderData { -// xr_instance, -// xr_session: session, -// xr_blend_mode: blend_mode, -// xr_resolution: resolution, -// xr_format: format, -// xr_session_running: session_running, -// xr_frame_waiter: frame_waiter, -// xr_swapchain: swapchain, -// xr_input: input, -// xr_views: views, -// xr_frame_state: frame_state, -// }), -// ) -// } -// XrNextEnabledState::Disabled => ( -// init_non_xr_graphics(primary_window.get_single().ok().cloned()), -// None, -// ), -// }; -// -// commands.insert_resource(creation_data.device); -// commands.insert_resource(creation_data.queue); -// commands.insert_resource(RenderInstance(creation_data.instance)); -// commands.insert_resource(creation_data.adapter_info); -// commands.insert_resource(creation_data.render_adapter); -// -// if let Some(xr_data) = xr_data { -// // TODO: Remove this lib.rs:144 -// commands.insert_resource(xr_data.clone()); -// -// commands.insert_resource(xr_data.xr_instance); -// commands.insert_resource(xr_data.xr_session); -// commands.insert_resource(xr_data.xr_blend_mode); -// commands.insert_resource(xr_data.xr_resolution); -// commands.insert_resource(xr_data.xr_format); -// commands.insert_resource(xr_data.xr_session_running); -// commands.insert_resource(xr_data.xr_frame_waiter); -// commands.insert_resource(xr_data.xr_input); -// commands.insert_resource(xr_data.xr_views); -// commands.insert_resource(xr_data.xr_frame_state); -// commands.insert_resource(xr_data.xr_swapchain); -// } else { -// commands.remove_resource::(); -// -// commands.remove_resource::(); -// commands.remove_resource::(); -// commands.remove_resource::(); -// commands.remove_resource::(); -// commands.remove_resource::(); -// commands.remove_resource::(); -// commands.remove_resource::(); -// commands.remove_resource::(); -// commands.remove_resource::(); -// commands.remove_resource::(); -// commands.remove_resource::(); -// } -// } - -fn decide_next_xr_state( - mut commands: Commands, - mut events: EventReader, - xr_status: Option>, -) { - info!("hm"); - let request = match events.read().next() { - Some(v) => v, - None => return, - }; - info!("ok"); - match (request, xr_status.as_deref()) { - (XrEnableRequest::TryEnable, Some(XrEnableStatus::Enabled)) => { - info!("Xr Already Enabled! ignoring request"); - return; - } - (XrEnableRequest::TryDisable, Some(XrEnableStatus::Disabled)) => { - info!("Xr Already Disabled! ignoring request"); - return; - } - // (_, Some(XrEnableStatus::Waiting)) => { - // info!("Already Handling Request! ignoring request"); - // return; - // } - _ => {} - } - let r = match request { - XrEnableRequest::TryEnable => XrNextEnabledState::Enabled, - XrEnableRequest::TryDisable => XrNextEnabledState::Disabled, - }; - info!("{:#?}", r); - commands.insert_resource(r); -} - -pub fn init_non_xr_graphics(primary_window: Option) -> RenderCreationData { - let settings = WgpuSettings::default(); - - let async_renderer = async move { - let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { - // Probably a bad idea unwraping here but on the other hand no backends - backends: settings.backends.unwrap(), - dx12_shader_compiler: settings.dx12_shader_compiler.clone(), - }); - let surface = primary_window.map(|wrapper| unsafe { - // SAFETY: Plugins should be set up on the main thread. - let handle = wrapper.get_handle(); - instance - .create_surface(&handle) - .expect("Failed to create wgpu surface") - }); - - let request_adapter_options = wgpu::RequestAdapterOptions { - power_preference: settings.power_preference, - compatible_surface: surface.as_ref(), - ..Default::default() - }; - - let (device, queue, adapter_info, render_adapter) = - renderer::initialize_renderer(&instance, &settings, &request_adapter_options).await; - debug!("Configured wgpu adapter Limits: {:#?}", device.limits()); - debug!("Configured wgpu adapter Features: {:#?}", device.features()); - RenderCreationData { - device, - queue, - adapter_info, - render_adapter, - instance: Arc::new(instance), - } - }; - // No need for wasm in bevy_oxr web xr would be a different crate - futures_lite::future::block_on(async_renderer) -} diff --git a/src/xr_input/hands/mod.rs b/src/xr_input/hands/mod.rs index 06eb5c1..fa85cf8 100644 --- a/src/xr_input/hands/mod.rs +++ b/src/xr_input/hands/mod.rs @@ -1,6 +1,15 @@ use bevy::{app::PluginGroupBuilder, prelude::*}; +use openxr::FormFactor; -use self::{emulated::HandEmulationPlugin, hand_tracking::HandTrackingPlugin}; +use crate::{ + resources::{XrInstance, XrSession}, + xr_init::XrPreSetup, +}; + +use self::{ + emulated::HandEmulationPlugin, + hand_tracking::{DisableHandTracking, HandTrackingData, HandTrackingPlugin}, +}; pub mod common; pub mod emulated; @@ -8,12 +17,35 @@ pub mod hand_tracking; pub struct XrHandPlugins; -impl PluginGroup for XrHandPlugins { - fn build(self) -> PluginGroupBuilder { - PluginGroupBuilder::start::() - .add(HandTrackingPlugin) - .add(HandEmulationPlugin) - .build() +impl Plugin for XrHandPlugins { + fn build(&self, app: &mut App) { + app.add_plugins(HandTrackingPlugin) + .add_plugins(HandPlugin) + .add_plugins(HandEmulationPlugin); + } +} + +pub struct HandPlugin; + +impl Plugin for HandPlugin { + fn build(&self, app: &mut App) { + app.add_systems(XrPreSetup, check_for_handtracking); + } +} + +fn check_for_handtracking( + mut commands: Commands, + instance: Res, + session: Res, +) { + let hands = instance.exts().ext_hand_tracking.is_some() + && instance + .supports_hand_tracking(instance.system(FormFactor::HEAD_MOUNTED_DISPLAY).unwrap()) + .is_ok_and(|v| v); + if hands { + commands.insert_resource(HandTrackingData::new(&session).unwrap()); + } else { + commands.insert_resource(DisableHandTracking::Both); } } diff --git a/src/xr_input/mod.rs b/src/xr_input/mod.rs index 540f2e2..ac2e13b 100644 --- a/src/xr_input/mod.rs +++ b/src/xr_input/mod.rs @@ -10,11 +10,11 @@ pub mod trackers; pub mod xr_camera; use crate::resources::{XrInstance, XrSession}; -use crate::xr_begin_frame; -use crate::xr_init::{xr_only, XrPostSetup, XrSetup, XrPreSetup}; +use crate::xr_init::{xr_only, XrPostSetup, XrPreSetup, XrSetup}; use crate::xr_input::controllers::XrControllerType; use crate::xr_input::oculus_touch::setup_oculus_controller; use crate::xr_input::xr_camera::{xr_camera_head_sync, Eye, XRProjection, XrCameraBundle}; +use crate::{locate_views, xr_wait_frame}; use bevy::app::{App, PostUpdate, Startup}; use bevy::ecs::entity::Entity; use bevy::ecs::query::With; @@ -24,17 +24,19 @@ use bevy::math::Vec2; use bevy::prelude::{BuildChildren, Component, Deref, DerefMut, IntoSystemConfigs, Resource}; use bevy::prelude::{Commands, Plugin, PreUpdate, Quat, Res, SpatialBundle, Update, Vec3}; use bevy::render::camera::CameraProjectionPlugin; +use bevy::render::extract_component::ExtractComponentPlugin; use bevy::render::view::{update_frusta, VisibilitySystems}; use bevy::transform::TransformSystem; use bevy::utils::HashMap; use openxr::Binding; use self::actions::{setup_oxr_actions, OpenXrActionsPlugin}; -use self::oculus_touch::{post_action_setup_oculus_controller, ActionSets, init_subaction_path}; +use self::oculus_touch::{init_subaction_path, post_action_setup_oculus_controller, ActionSets}; use self::trackers::{ adopt_open_xr_trackers, update_open_xr_controllers, OpenXRLeftEye, OpenXRRightEye, OpenXRTrackingRoot, }; +use self::xr_camera::{XrCameraType, TransformExtract}; #[derive(Copy, Clone)] pub struct OpenXrInput { @@ -67,7 +69,10 @@ impl Plugin for OpenXrInput { app.add_systems(PreUpdate, action_set_system.run_if(xr_only())); app.add_systems( PreUpdate, - xr_camera_head_sync.run_if(xr_only()).after(xr_begin_frame), + xr_camera_head_sync + .run_if(xr_only()) + .after(xr_wait_frame) + .after(locate_views), ); //update controller trackers app.add_systems(Update, update_open_xr_controllers.run_if(xr_only())); @@ -79,6 +84,9 @@ impl Plugin for OpenXrInput { ); app.add_systems(XrPreSetup, init_subaction_path); app.add_systems(XrSetup, setup_xr_cameras); + app.add_plugins(ExtractComponentPlugin::::default()); + app.add_plugins(ExtractComponentPlugin::::default()); + app.add_plugins(ExtractComponentPlugin::::default()); } } diff --git a/src/xr_input/xr_camera.rs b/src/xr_input/xr_camera.rs index 210feaa..1d143c3 100644 --- a/src/xr_input/xr_camera.rs +++ b/src/xr_input/xr_camera.rs @@ -1,9 +1,11 @@ use crate::xr_input::{QuatConv, Vec3Conv}; use crate::{LEFT_XR_TEXTURE_HANDLE, RIGHT_XR_TEXTURE_HANDLE}; use bevy::core_pipeline::tonemapping::{DebandDither, Tonemapping}; +use bevy::ecs::system::lifetimeless::Read; use bevy::math::Vec3A; use bevy::prelude::*; use bevy::render::camera::{CameraProjection, CameraRenderGraph, RenderTarget}; +use bevy::render::extract_component::ExtractComponent; use bevy::render::primitives::Frustum; use bevy::render::view::{ColorGrading, VisibleEntities}; use openxr::Fovf; @@ -48,6 +50,35 @@ pub enum XrCameraType { Flatscreen, } + +#[derive(Component)] +pub(super) struct TransformExtract; + + +impl ExtractComponent for TransformExtract { + type Query = Read; + + type Filter = (); + + type Out = Transform; + + fn extract_component(item: bevy::ecs::query::QueryItem<'_, Self::Query>) -> Option { + Some(*item) + } +} + +impl ExtractComponent for XrCameraType { + type Query = Read; + + type Filter = (); + + type Out = Self; + + fn extract_component(item: bevy::ecs::query::QueryItem<'_, Self::Query>) -> Option { + Some(*item) + } +} + #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] pub enum Eye { Left = 0, @@ -81,7 +112,7 @@ impl XrCameraBundle { } } -#[derive(Debug, Clone, Component, Reflect)] +#[derive(Debug, Clone, Component, Reflect, ExtractComponent)] #[reflect(Component, Default)] pub struct XRProjection { pub near: f32, @@ -241,24 +272,21 @@ impl CameraProjection for XRProjection { pub fn xr_camera_head_sync( views: ResMut, - query: Query<(&mut Transform, &XrCameraType, &mut XRProjection)>, + mut query: Query<(&mut Transform, &XrCameraType, &mut XRProjection)>, ) { - fn f( - views: ResMut, - mut query: Query<(&mut Transform, &XrCameraType, &mut XRProjection)>, - ) -> Option<()> { - //TODO calculate HMD position - for (mut transform, camera_type, mut xr_projection) in query.iter_mut() { - let view_idx = match camera_type { - XrCameraType::Xr(eye) => *eye as usize, - XrCameraType::Flatscreen => return None, - }; - let view = views.get(view_idx)?; - xr_projection.fov = view.fov; - transform.rotation = view.pose.orientation.to_quat(); - transform.translation = view.pose.position.to_vec3(); - } - Some(()) + //TODO calculate HMD position + for (mut transform, camera_type, mut xr_projection) in query.iter_mut() { + let view_idx = match camera_type { + XrCameraType::Xr(eye) => *eye as usize, + // I don't belive we need a flatscrenn cam, that's just a cam without this component + XrCameraType::Flatscreen => continue, + }; + let view = match views.get(view_idx) { + Some(views) => views, + None => continue, + }; + xr_projection.fov = view.fov; + transform.rotation = view.pose.orientation.to_quat(); + transform.translation = view.pose.position.to_vec3(); } - let _ = f(views, query); }