pub mod schedules; pub use schedules::*; use bevy::{ prelude::*, render::{ camera::{ManualTextureView, ManualTextureViews}, extract_resource::{ExtractResource, ExtractResourcePlugin}, renderer::{RenderAdapter, RenderDevice, RenderInstance}, }, window::{PrimaryWindow, RawHandleWrapper}, }; use crate::{ graphics, resources::{ OXrSessionSetupInfo, XrFormat, XrInstance, XrResolution, XrSession, XrSessionRunning, XrSwapchain, }, LEFT_XR_TEXTURE_HANDLE, RIGHT_XR_TEXTURE_HANDLE, }; #[derive(Resource, Event, Clone, Copy, PartialEq, Eq, Reflect, Debug, ExtractResource)] pub enum XrStatus { NoInstance, Enabled, Enabling, Disabled, Disabling, } #[derive( Resource, Clone, Copy, PartialEq, Eq, Reflect, Debug, ExtractResource, Default, Deref, DerefMut, )] pub struct XrShouldRender(bool); #[derive( Resource, Clone, Copy, PartialEq, Eq, Reflect, Debug, ExtractResource, Default, Deref, DerefMut, )] pub struct XrHasWaited(bool); pub struct XrEarlyInitPlugin; pub struct XrInitPlugin; pub fn xr_only() -> impl FnMut(Res) -> bool { resource_equals(XrStatus::Enabled) } pub fn xr_render_only() -> impl FnMut(Res) -> bool { resource_equals(XrShouldRender(true)) } pub fn xr_after_wait_only() -> impl FnMut(Res) -> bool { resource_equals(XrHasWaited(true)) } impl Plugin for XrEarlyInitPlugin { fn build(&self, app: &mut App) { app.add_event::() .add_event::() .add_event::() .add_event::(); } } impl Plugin for XrInitPlugin { fn build(&self, app: &mut App) { add_schedules(app); app.add_plugins(ExtractResourcePlugin::::default()); app.add_plugins(ExtractResourcePlugin::::default()); app.add_plugins(ExtractResourcePlugin::::default()); app.init_resource::(); app.init_resource::(); app.add_systems(PreUpdate, setup_xr.run_if(on_event::())) .add_systems(PreUpdate, cleanup_xr.run_if(on_event::())); app.add_systems( PostUpdate, start_xr_session.run_if(on_event::()), ); app.add_systems( PostUpdate, stop_xr_session.run_if(on_event::()), ); app.add_systems(XrSetup, setup_manual_texture_views); } } fn setup_manual_texture_views( mut manual_texture_views: ResMut, swapchain: Res, xr_resolution: Res, xr_format: Res, ) { info!("Creating Texture views"); let (left, right) = 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, }; manual_texture_views.insert(LEFT_XR_TEXTURE_HANDLE, left); manual_texture_views.insert(RIGHT_XR_TEXTURE_HANDLE, right); } pub fn setup_xr(world: &mut World) { world.run_schedule(XrPreSetup); world.run_schedule(XrSetup); world.run_schedule(XrPrePostSetup); world.run_schedule(XrPostSetup); *world.resource_mut::() = XrStatus::Enabled; } fn cleanup_xr(world: &mut World) { world.run_schedule(XrPreCleanup); world.run_schedule(XrCleanup); world.run_schedule(XrPostCleanup); *world.resource_mut::() = XrStatus::Disabled; } #[derive(Event, Clone, Copy, Default)] pub struct StartXrSession; #[derive(Event, Clone, Copy, Default)] pub struct EndXrSession; #[derive(Event, Clone, Copy, Default)] pub(crate) struct SetupXrData; #[derive(Event, Clone, Copy, Default)] pub(crate) struct CleanupXrData; #[allow(clippy::too_many_arguments)] fn start_xr_session( mut commands: Commands, mut setup_xr: EventWriter, mut status: ResMut, instance: Res, primary_window: Query<&RawHandleWrapper, With>, setup_info: NonSend, render_device: Res, render_adapter: Res, render_instance: Res, ) { info!("start Session"); match *status { XrStatus::Disabled => {} XrStatus::NoInstance => { warn!("Trying to start OpenXR Session without instance, ignoring"); return; } XrStatus::Enabled | XrStatus::Enabling => { warn!("Trying to start OpenXR Session while one already exists, ignoring"); return; } XrStatus::Disabling => { warn!("Trying to start OpenXR Session while one is stopping, ignoring"); return; } } 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); *status = XrStatus::Enabling; } fn stop_xr_session(session: ResMut, mut status: ResMut) { match session.request_exit() { Ok(_) => {} Err(err) => { error!("Error while trying to request session exit: {}", err) } } *status = XrStatus::Enabling; }