pull webxr-refactor and make needed changes
Signed-off-by: Schmarni <marnistromer@gmail.com>
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
use bevy::prelude::*;
|
||||
use bevy_openxr::add_xr_plugins;
|
||||
use bevy_xr::session::{XrSharedStatus, XrStatus};
|
||||
use bevy_xr::session::{ XrStatus};
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
@@ -20,7 +20,7 @@ fn handle_input(
|
||||
mut destroy: EventWriter<bevy_xr::session::DestroyXrSession>,
|
||||
mut begin: EventWriter<bevy_xr::session::BeginXrSession>,
|
||||
mut create: EventWriter<bevy_xr::session::CreateXrSession>,
|
||||
state: Res<XrSharedStatus>,
|
||||
state: Res<XrStatus>,
|
||||
) {
|
||||
if keys.just_pressed(KeyCode::KeyE) {
|
||||
info!("sending end");
|
||||
@@ -39,7 +39,7 @@ fn handle_input(
|
||||
create.send_default();
|
||||
}
|
||||
if keys.just_pressed(KeyCode::KeyI) {
|
||||
info!("current state: {:?}", state.get());
|
||||
info!("current state: {:?}", *state);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ use bevy::prelude::*;
|
||||
use bevy::utils::HashMap;
|
||||
use openxr::sys::ActionSuggestedBinding;
|
||||
|
||||
use crate::{init::OxrPreUpdateSet, resources::OxrInstance, session::OxrSessionStatusEvent};
|
||||
use crate::{resources::OxrInstance, session::OxrSessionStatusEvent};
|
||||
|
||||
impl Plugin for OxrActionBindingPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
@@ -15,13 +15,13 @@ impl Plugin for OxrActionBindingPlugin {
|
||||
app.add_event::<OxrSuggestActionBinding>();
|
||||
app.add_systems(
|
||||
PostUpdate,
|
||||
run_action_binding_sugestion
|
||||
.run_if(|mut session_state: EventReader<OxrSessionStatusEvent>| {
|
||||
run_action_binding_sugestion.run_if(
|
||||
|mut session_state: EventReader<OxrSessionStatusEvent>| {
|
||||
session_state
|
||||
.read()
|
||||
.any(|s| *s == OxrSessionStatusEvent::Created)
|
||||
})
|
||||
.after(OxrPreUpdateSet::HandleEvents),
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{init::OxrPreUpdateSet, session::OxrSession};
|
||||
use crate::session::OxrSession;
|
||||
use bevy::prelude::*;
|
||||
use bevy_xr::session::session_running;
|
||||
|
||||
@@ -7,9 +7,7 @@ impl Plugin for OxrActionSyncingPlugin {
|
||||
app.add_event::<OxrSyncActionSet>();
|
||||
app.add_systems(
|
||||
PreUpdate,
|
||||
sync_sets
|
||||
.run_if(session_running)
|
||||
.in_set(OxrPreUpdateSet::SyncActions),
|
||||
sync_sets.run_if(session_running), // .in_set(OxrPreUpdateSet::SyncActions),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,10 +6,11 @@ use bevy_xr::{
|
||||
};
|
||||
use openxr::SpaceLocationFlags;
|
||||
|
||||
use crate::resources::Pipelined;
|
||||
use crate::{
|
||||
init::OxrTrackingRoot,
|
||||
reference_space::{OxrPrimaryReferenceSpace, OxrReferenceSpace},
|
||||
resources::OxrTime,
|
||||
resources::OxrFrameState,
|
||||
session::OxrSession,
|
||||
};
|
||||
|
||||
@@ -130,18 +131,29 @@ pub struct OxrHandTracker(pub openxr::HandTracker);
|
||||
|
||||
fn locate_hands(
|
||||
default_ref_space: Res<OxrPrimaryReferenceSpace>,
|
||||
time: Res<OxrTime>,
|
||||
frame_state: Res<OxrFrameState>,
|
||||
tracker_query: Query<(
|
||||
&OxrHandTracker,
|
||||
Option<&OxrReferenceSpace>,
|
||||
&OxrHandBoneEntities,
|
||||
)>,
|
||||
mut bone_query: Query<(&HandBone, &mut HandBoneRadius, &mut Transform)>,
|
||||
pipelined: Option<Res<Pipelined>>,
|
||||
) {
|
||||
for (tracker, ref_space, hand_entities) in &tracker_query {
|
||||
let ref_space = ref_space.map(|v| &v.0).unwrap_or(&default_ref_space.0);
|
||||
// relate_hand_joints also provides velocities
|
||||
let joints = match ref_space.locate_hand_joints(tracker, **time) {
|
||||
let joints = match ref_space.locate_hand_joints(
|
||||
tracker,
|
||||
if pipelined.is_some() {
|
||||
openxr::Time::from_nanos(
|
||||
frame_state.predicted_display_time.as_nanos()
|
||||
+ frame_state.predicted_display_period.as_nanos(),
|
||||
)
|
||||
} else {
|
||||
frame_state.predicted_display_time
|
||||
},
|
||||
) {
|
||||
Ok(Some(v)) => v,
|
||||
Ok(None) => continue,
|
||||
Err(openxr::sys::Result::ERROR_EXTENSION_NOT_PRESENT) => {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use bevy::app::MainScheduleOrder;
|
||||
use bevy::ecs::schedule::ScheduleLabel;
|
||||
use bevy::ecs::system::RunSystemOnce;
|
||||
use bevy::prelude::*;
|
||||
use bevy::render::extract_resource::ExtractResourcePlugin;
|
||||
@@ -15,8 +17,6 @@ use bevy::render::RenderSet;
|
||||
use bevy::transform::TransformSystem;
|
||||
use bevy::winit::UpdateMode;
|
||||
use bevy::winit::WinitSettings;
|
||||
use bevy_xr::session::handle_session;
|
||||
use bevy_xr::session::session_available;
|
||||
use bevy_xr::session::session_running;
|
||||
use bevy_xr::session::status_equals;
|
||||
use bevy_xr::session::BeginXrSession;
|
||||
@@ -24,31 +24,31 @@ use bevy_xr::session::CreateXrSession;
|
||||
use bevy_xr::session::DestroyXrSession;
|
||||
use bevy_xr::session::EndXrSession;
|
||||
use bevy_xr::session::XrSessionExiting;
|
||||
use bevy_xr::session::XrSharedStatus;
|
||||
use bevy_xr::session::XrStatus;
|
||||
use bevy_xr::session::XrStatusChanged;
|
||||
|
||||
use crate::error::OxrError;
|
||||
use crate::graphics::*;
|
||||
use crate::reference_space::OxrPrimaryReferenceSpace;
|
||||
use crate::resources::*;
|
||||
use crate::session::OxrSession;
|
||||
use crate::session::OxrSessionStatusEvent;
|
||||
use crate::types::*;
|
||||
|
||||
pub fn session_started(started: Option<Res<OxrSessionStarted>>) -> bool {
|
||||
started.is_some_and(|started| started.get())
|
||||
started.is_some_and(|started| started.0)
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, SystemSet)]
|
||||
pub enum OxrPreUpdateSet {
|
||||
PollEvents,
|
||||
HandleEvents,
|
||||
UpdateCriticalComponents,
|
||||
UpdateNonCriticalComponents,
|
||||
SyncActions,
|
||||
pub fn should_render(frame_state: Option<Res<OxrFrameState>>) -> bool {
|
||||
frame_state.is_some_and(|frame_state| frame_state.should_render)
|
||||
}
|
||||
|
||||
/// TODO!() better name pls
|
||||
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct OxrLast;
|
||||
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, SystemSet)]
|
||||
pub struct OxrHandleEvents;
|
||||
|
||||
pub struct OxrInitPlugin {
|
||||
/// Information about the app this is being used to build.
|
||||
pub app_info: AppInfo,
|
||||
@@ -97,8 +97,6 @@ impl Plugin for OxrInitPlugin {
|
||||
WgpuGraphics(device, queue, adapter_info, adapter, wgpu_instance),
|
||||
session_create_info,
|
||||
)) => {
|
||||
let status = XrSharedStatus::new(XrStatus::Available);
|
||||
|
||||
app.add_plugins((
|
||||
RenderPlugin {
|
||||
render_creation: RenderCreation::manual(
|
||||
@@ -111,30 +109,30 @@ impl Plugin for OxrInitPlugin {
|
||||
synchronous_pipeline_compilation: self.synchronous_pipeline_compilation,
|
||||
},
|
||||
ExtractResourcePlugin::<OxrCleanupSession>::default(),
|
||||
ExtractResourcePlugin::<OxrTime>::default(),
|
||||
ExtractResourcePlugin::<OxrRootTransform>::default(),
|
||||
ExtractResourcePlugin::<XrStatus>::default(),
|
||||
ExtractResourcePlugin::<OxrSessionStarted>::default(),
|
||||
))
|
||||
.add_systems(First, reset_per_frame_resources)
|
||||
.init_schedule(OxrLast)
|
||||
.add_systems(
|
||||
PreUpdate,
|
||||
OxrLast,
|
||||
(
|
||||
poll_events
|
||||
.run_if(session_available)
|
||||
.in_set(OxrPreUpdateSet::PollEvents),
|
||||
(
|
||||
(create_xr_session, apply_deferred)
|
||||
.chain()
|
||||
.run_if(on_event::<CreateXrSession>())
|
||||
.run_if(status_equals(XrStatus::Available)),
|
||||
begin_xr_session
|
||||
.run_if(on_event::<BeginXrSession>())
|
||||
.run_if(status_equals(XrStatus::Ready)),
|
||||
end_xr_session
|
||||
.run_if(on_event::<EndXrSession>())
|
||||
.run_if(status_equals(XrStatus::Running)),
|
||||
)
|
||||
.in_set(OxrPreUpdateSet::HandleEvents),
|
||||
),
|
||||
reset_per_frame_resources,
|
||||
poll_events,
|
||||
create_xr_session
|
||||
.run_if(on_event::<CreateXrSession>())
|
||||
.run_if(status_equals(XrStatus::Available)),
|
||||
begin_xr_session
|
||||
.run_if(on_event::<BeginXrSession>())
|
||||
.run_if(status_equals(XrStatus::Ready)),
|
||||
end_xr_session
|
||||
.run_if(on_event::<EndXrSession>())
|
||||
.run_if(status_equals(XrStatus::Running)),
|
||||
destroy_xr_session
|
||||
.run_if(on_event::<DestroyXrSession>())
|
||||
.run_if(status_equals(XrStatus::Exiting)),
|
||||
)
|
||||
.chain()
|
||||
.in_set(OxrHandleEvents),
|
||||
)
|
||||
.add_systems(XrSessionExiting, destroy_xr_session)
|
||||
.add_systems(
|
||||
@@ -143,25 +141,25 @@ impl Plugin for OxrInitPlugin {
|
||||
)
|
||||
.insert_resource(instance.clone())
|
||||
.insert_resource(system_id)
|
||||
.insert_resource(status.clone())
|
||||
.insert_resource(XrStatus::Available)
|
||||
.insert_resource(WinitSettings {
|
||||
focused_mode: UpdateMode::Continuous,
|
||||
unfocused_mode: UpdateMode::Continuous,
|
||||
})
|
||||
.init_resource::<OxrCleanupSession>()
|
||||
.init_resource::<OxrRootTransform>()
|
||||
.insert_non_send_resource(session_create_info);
|
||||
.insert_non_send_resource(session_create_info)
|
||||
.configure_sets(OxrLast, OxrHandleEvents);
|
||||
|
||||
app.world
|
||||
.resource_mut::<MainScheduleOrder>()
|
||||
.insert_after(Last, OxrLast);
|
||||
|
||||
app.world
|
||||
.spawn((TransformBundle::default(), OxrTrackingRoot));
|
||||
|
||||
let render_app = app.sub_app_mut(RenderApp);
|
||||
render_app
|
||||
.insert_resource(instance)
|
||||
.insert_resource(system_id)
|
||||
.insert_resource(status)
|
||||
.init_resource::<OxrRootTransform>()
|
||||
.init_resource::<OxrCleanupSession>()
|
||||
.add_systems(
|
||||
Render,
|
||||
destroy_xr_session_render
|
||||
@@ -171,39 +169,20 @@ impl Plugin for OxrInitPlugin {
|
||||
.add_systems(
|
||||
ExtractSchedule,
|
||||
transfer_xr_resources.run_if(not(session_running)),
|
||||
);
|
||||
)
|
||||
.insert_resource(instance)
|
||||
.insert_resource(system_id)
|
||||
.init_resource::<OxrRootTransform>()
|
||||
.init_resource::<OxrCleanupSession>();
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to initialize openxr: {e}");
|
||||
let status = XrSharedStatus::new(XrStatus::Unavailable);
|
||||
|
||||
app.add_plugins(RenderPlugin::default())
|
||||
.insert_resource(status.clone());
|
||||
|
||||
let render_app = app.sub_app_mut(RenderApp);
|
||||
|
||||
render_app.insert_resource(status);
|
||||
.insert_resource(XrStatus::Unavailable);
|
||||
}
|
||||
};
|
||||
|
||||
app.configure_sets(
|
||||
PreUpdate,
|
||||
(
|
||||
OxrPreUpdateSet::PollEvents.before(handle_session),
|
||||
OxrPreUpdateSet::HandleEvents.after(handle_session),
|
||||
OxrPreUpdateSet::UpdateCriticalComponents,
|
||||
OxrPreUpdateSet::UpdateNonCriticalComponents,
|
||||
)
|
||||
.chain(),
|
||||
);
|
||||
|
||||
let session_started = OxrSessionStarted::default();
|
||||
|
||||
app.insert_resource(session_started.clone());
|
||||
|
||||
let render_app = app.sub_app_mut(RenderApp);
|
||||
|
||||
render_app.insert_resource(session_started);
|
||||
app.insert_resource(OxrSessionStarted(false));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,17 +195,12 @@ pub fn update_root_transform(
|
||||
root_transform.0 = *transform;
|
||||
}
|
||||
|
||||
fn xr_entry() -> Result<OxrEntry> {
|
||||
#[cfg(windows)]
|
||||
let entry = openxr::Entry::linked();
|
||||
#[cfg(not(windows))]
|
||||
let entry = unsafe { openxr::Entry::load()? };
|
||||
Ok(OxrEntry(entry))
|
||||
}
|
||||
|
||||
impl OxrInitPlugin {
|
||||
fn init_xr(&self) -> Result<(OxrInstance, OxrSystemId, WgpuGraphics, SessionConfigInfo)> {
|
||||
let entry = xr_entry()?;
|
||||
#[cfg(windows)]
|
||||
let entry = OxrEntry(openxr::Entry::linked());
|
||||
#[cfg(not(windows))]
|
||||
let entry = OxrEntry(unsafe { openxr::Entry::load()? });
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
entry.initialize_android_loader()?;
|
||||
@@ -437,6 +411,20 @@ fn init_xr_session(
|
||||
))
|
||||
}
|
||||
|
||||
pub fn begin_xr_session(session: Res<OxrSession>, mut session_started: ResMut<OxrSessionStarted>) {
|
||||
let _span = info_span!("xr_begin_session");
|
||||
session
|
||||
.begin(openxr::ViewConfigurationType::PRIMARY_STEREO)
|
||||
.expect("Failed to begin session");
|
||||
session_started.0 = true;
|
||||
}
|
||||
|
||||
pub fn end_xr_session(session: Res<OxrSession>, mut session_started: ResMut<OxrSessionStarted>) {
|
||||
let _span = info_span!("xr_end_session");
|
||||
session.request_exit().expect("Failed to end session");
|
||||
session_started.0 = false;
|
||||
}
|
||||
|
||||
/// This is used solely to transport resources from the main world to the render world.
|
||||
#[derive(Resource)]
|
||||
struct OxrRenderResources {
|
||||
@@ -477,21 +465,6 @@ pub fn create_xr_session(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn begin_xr_session(session: Res<OxrSession>, session_started: Res<OxrSessionStarted>) {
|
||||
let _span = info_span!("xr_begin_session");
|
||||
session
|
||||
.begin(openxr::ViewConfigurationType::PRIMARY_STEREO)
|
||||
.expect("Failed to begin session");
|
||||
session_started.set(true);
|
||||
}
|
||||
|
||||
pub fn end_xr_session(session: Res<OxrSession>, session_started: Res<OxrSessionStarted>) {
|
||||
let _span = info_span!("xr_end_session");
|
||||
session
|
||||
.request_exit()
|
||||
.expect("Failed to request session exit");
|
||||
}
|
||||
|
||||
/// This system transfers important render resources from the main world to the render world when a session is created.
|
||||
pub fn transfer_xr_resources(mut commands: Commands, mut world: ResMut<MainWorld>) {
|
||||
let Some(OxrRenderResources {
|
||||
@@ -515,7 +488,7 @@ pub fn transfer_xr_resources(mut commands: Commands, mut world: ResMut<MainWorld
|
||||
/// Polls any OpenXR events and handles them accordingly
|
||||
pub fn poll_events(
|
||||
instance: Res<OxrInstance>,
|
||||
status: Res<XrSharedStatus>,
|
||||
mut status: ResMut<XrStatus>,
|
||||
mut changed_event: EventWriter<XrStatusChanged>,
|
||||
mut session_status_events: EventWriter<OxrSessionStatusEvent>,
|
||||
) {
|
||||
@@ -536,7 +509,7 @@ pub fn poll_events(
|
||||
|
||||
let new_status = match state {
|
||||
SessionState::IDLE => {
|
||||
if status.get() == XrStatus::Available {
|
||||
if *status == XrStatus::Available {
|
||||
session_status_events.send(OxrSessionStatusEvent::Created);
|
||||
info!("sending create info");
|
||||
}
|
||||
@@ -555,7 +528,7 @@ pub fn poll_events(
|
||||
_ => unreachable!(),
|
||||
};
|
||||
changed_event.send(XrStatusChanged(new_status));
|
||||
status.set(new_status);
|
||||
*status = new_status;
|
||||
}
|
||||
InstanceLossPending(_) => {}
|
||||
EventsLost(e) => warn!("lost {} XR events", e.lost_event_count()),
|
||||
@@ -579,7 +552,10 @@ pub fn destroy_xr_session_render(world: &mut World) {
|
||||
world.remove_resource::<OxrFrameStream>();
|
||||
world.remove_resource::<OxrSwapchainImages>();
|
||||
world.remove_resource::<OxrGraphicsInfo>();
|
||||
world.run_system_once(apply_deferred);
|
||||
world.run_schedule(XrSessionExiting);
|
||||
world.run_system_once(apply_deferred);
|
||||
world.remove_resource::<OxrSession>();
|
||||
world.insert_resource(OxrSessionStarted(false));
|
||||
info!("Render App destroy");
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::reference_space::OxrPrimaryReferenceSpace;
|
||||
use crate::resources::*;
|
||||
|
||||
pub trait LayerProvider {
|
||||
fn get<'a>(&'a self, world: &'a World) -> Box<dyn CompositionLayer + '_>;
|
||||
fn get<'a>(&'a self, world: &'a World) -> Option<Box<dyn CompositionLayer + '_>>;
|
||||
}
|
||||
|
||||
pub struct ProjectionLayer;
|
||||
@@ -16,11 +16,11 @@ pub struct ProjectionLayer;
|
||||
pub struct PassthroughLayer;
|
||||
|
||||
impl LayerProvider for ProjectionLayer {
|
||||
fn get<'a>(&self, world: &'a World) -> Box<dyn CompositionLayer<'a> + 'a> {
|
||||
let stage = world.resource::<OxrPrimaryReferenceSpace>();
|
||||
let openxr_views = world.resource::<OxrViews>();
|
||||
let swapchain = world.resource::<OxrSwapchain>();
|
||||
let graphics_info = world.resource::<OxrGraphicsInfo>();
|
||||
fn get<'a>(&self, world: &'a World) -> Option<Box<dyn CompositionLayer<'a> + 'a>> {
|
||||
let stage = world.get_resource::<OxrPrimaryReferenceSpace>()?;
|
||||
let openxr_views = world.get_resource::<OxrViews>()?;
|
||||
let swapchain = world.get_resource::<OxrSwapchain>()?;
|
||||
let graphics_info = world.get_resource::<OxrGraphicsInfo>()?;
|
||||
let rect = openxr::Rect2Di {
|
||||
offset: openxr::Offset2Di { x: 0, y: 0 },
|
||||
extent: openxr::Extent2Di {
|
||||
@@ -29,7 +29,11 @@ impl LayerProvider for ProjectionLayer {
|
||||
},
|
||||
};
|
||||
|
||||
Box::new(
|
||||
if openxr_views.len() < 2 {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(Box::new(
|
||||
CompositionLayerProjection::new()
|
||||
.layer_flags(CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA)
|
||||
.space(&stage)
|
||||
@@ -53,17 +57,17 @@ impl LayerProvider for ProjectionLayer {
|
||||
.image_rect(rect),
|
||||
),
|
||||
]),
|
||||
)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl LayerProvider for PassthroughLayer {
|
||||
fn get<'a>(&'a self, world: &'a World) -> Box<dyn CompositionLayer + '_> {
|
||||
Box::new(
|
||||
fn get<'a>(&'a self, world: &'a World) -> Option<Box<dyn CompositionLayer + '_>> {
|
||||
Some(Box::new(
|
||||
CompositionLayerPassthrough::new()
|
||||
.layer_handle(world.resource::<OxrPassthroughLayer>())
|
||||
.layer_handle(world.get_resource::<OxrPassthroughLayer>()?)
|
||||
.layer_flags(CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA),
|
||||
)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,9 +7,9 @@ use bevy::{
|
||||
RenderApp,
|
||||
},
|
||||
};
|
||||
use bevy_xr::session::{status_changed_to, XrSessionCreated, XrSessionExiting, XrStatus};
|
||||
use bevy_xr::session::{XrSessionCreated, XrSessionExiting};
|
||||
|
||||
use crate::{init::OxrPreUpdateSet, session::OxrSession};
|
||||
use crate::session::OxrSession;
|
||||
|
||||
pub struct OxrReferenceSpacePlugin {
|
||||
pub default_primary_ref_space: openxr::ReferenceSpaceType,
|
||||
|
||||
@@ -4,7 +4,7 @@ use bevy::{
|
||||
render::{
|
||||
camera::{ManualTextureView, ManualTextureViewHandle, ManualTextureViews, RenderTarget},
|
||||
extract_resource::ExtractResourcePlugin,
|
||||
renderer::render_system,
|
||||
pipelined_rendering::PipelinedRenderingPlugin,
|
||||
view::ExtractedView,
|
||||
Render, RenderApp, RenderSet,
|
||||
},
|
||||
@@ -17,57 +17,144 @@ use bevy_xr::{
|
||||
use openxr::ViewStateFlags;
|
||||
|
||||
use crate::{
|
||||
init::{session_started, OxrPreUpdateSet, OxrTrackingRoot},
|
||||
layer_builder::ProjectionLayer, session::OxrSession,
|
||||
init::{session_started, OxrHandleEvents, OxrLast, OxrTrackingRoot},
|
||||
layer_builder::ProjectionLayer,
|
||||
session::OxrSession,
|
||||
};
|
||||
use crate::{reference_space::OxrPrimaryReferenceSpace, resources::*};
|
||||
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, SystemSet)]
|
||||
pub struct OxrRenderBegin;
|
||||
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, SystemSet)]
|
||||
pub struct OxrRenderEnd;
|
||||
|
||||
pub struct OxrRenderPlugin;
|
||||
|
||||
impl Plugin for OxrRenderPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_plugins((ExtractResourcePlugin::<OxrViews>::default(),))
|
||||
.add_systems(
|
||||
PreUpdate,
|
||||
(
|
||||
init_views.run_if(resource_added::<OxrGraphicsInfo>),
|
||||
locate_views.run_if(session_running),
|
||||
update_views.run_if(session_running),
|
||||
)
|
||||
.chain()
|
||||
.after(OxrPreUpdateSet::UpdateNonCriticalComponents),
|
||||
if app.is_plugin_added::<PipelinedRenderingPlugin>() {
|
||||
app.init_resource::<Pipelined>();
|
||||
}
|
||||
|
||||
app.add_plugins((
|
||||
ExtractResourcePlugin::<OxrFrameState>::default(),
|
||||
ExtractResourcePlugin::<OxrRootTransform>::default(),
|
||||
ExtractResourcePlugin::<OxrGraphicsInfo>::default(),
|
||||
ExtractResourcePlugin::<OxrSwapchainImages>::default(),
|
||||
ExtractResourcePlugin::<OxrViews>::default(),
|
||||
))
|
||||
.add_systems(
|
||||
PreUpdate,
|
||||
(locate_views, update_views)
|
||||
.chain()
|
||||
// .run_if(should_render)
|
||||
.run_if(session_started),
|
||||
)
|
||||
.add_systems(
|
||||
OxrLast,
|
||||
(
|
||||
wait_frame.run_if(session_started),
|
||||
update_cameras.run_if(session_started),
|
||||
init_views.run_if(resource_added::<OxrSession>),
|
||||
apply_deferred,
|
||||
)
|
||||
.add_systems(
|
||||
PostUpdate,
|
||||
(locate_views, update_views)
|
||||
.chain()
|
||||
.run_if(session_running)
|
||||
.before(TransformSystem::TransformPropagate),
|
||||
.chain()
|
||||
.after(OxrHandleEvents),
|
||||
)
|
||||
.add_systems(XrSessionExiting, clean_views)
|
||||
.init_resource::<OxrViews>();
|
||||
|
||||
let render_app = app.sub_app_mut(RenderApp);
|
||||
render_app
|
||||
.configure_sets(
|
||||
Render,
|
||||
OxrRenderBegin
|
||||
.after(RenderSet::ExtractCommands)
|
||||
.before(RenderSet::PrepareAssets)
|
||||
.before(RenderSet::ManageViews),
|
||||
)
|
||||
.add_systems(Last, wait_frame.run_if(session_started))
|
||||
.add_systems(XrSessionExiting, clean_views);
|
||||
.configure_sets(
|
||||
Render,
|
||||
OxrRenderEnd
|
||||
.after(RenderSet::Render)
|
||||
.before(RenderSet::Cleanup),
|
||||
)
|
||||
.add_systems(Last, wait_frame.run_if(session_started));
|
||||
app.sub_app_mut(RenderApp)
|
||||
.add_systems(
|
||||
Render,
|
||||
(|q: Query<&XrCamera>| info!("cams render: {}", q.iter().len()))
|
||||
.in_set(OxrRenderBegin),
|
||||
)
|
||||
.add_systems(
|
||||
Render,
|
||||
(
|
||||
(
|
||||
insert_texture_views,
|
||||
locate_views.run_if(resource_exists::<OxrPrimaryReferenceSpace>),
|
||||
update_views_render_world,
|
||||
)
|
||||
.chain()
|
||||
.in_set(RenderSet::PrepareAssets),
|
||||
begin_frame
|
||||
.before(RenderSet::Queue)
|
||||
.before(insert_texture_views),
|
||||
wait_image.in_set(RenderSet::Render).before(render_system),
|
||||
(release_image, end_frame)
|
||||
.chain()
|
||||
.in_set(RenderSet::Cleanup),
|
||||
begin_frame,
|
||||
insert_texture_views,
|
||||
locate_views,
|
||||
update_views_render_world,
|
||||
wait_image,
|
||||
)
|
||||
.run_if(session_started),
|
||||
.chain()
|
||||
.run_if(session_started)
|
||||
.in_set(OxrRenderBegin),
|
||||
)
|
||||
.add_systems(
|
||||
Render,
|
||||
(release_image, end_frame)
|
||||
.chain()
|
||||
.run_if(session_started)
|
||||
.in_set(OxrRenderEnd),
|
||||
)
|
||||
.insert_resource(OxrRenderLayers(vec![Box::new(ProjectionLayer)]));
|
||||
// .add_systems(
|
||||
// XrSessionExiting,
|
||||
// (
|
||||
// |mut cmds: Commands| cmds.remove_resource::<OxrRenderLayers>(),
|
||||
// clean_views,
|
||||
// ),
|
||||
// );
|
||||
|
||||
// app.add_systems(
|
||||
// PreUpdate,
|
||||
// (
|
||||
// init_views.run_if(resource_added::<OxrGraphicsInfo>),
|
||||
// locate_views.run_if(session_running),
|
||||
// update_views.run_if(session_running),
|
||||
// )
|
||||
// .chain(), // .after(OxrPreUpdateSet::UpdateNonCriticalComponents),
|
||||
// )
|
||||
// .add_systems(
|
||||
// PostUpdate,
|
||||
// (locate_views, update_views)
|
||||
// .chain()
|
||||
// .run_if(session_running)
|
||||
// .before(TransformSystem::TransformPropagate),
|
||||
// )
|
||||
// .add_systems(Last, wait_frame.run_if(session_started));
|
||||
// app.sub_app_mut(RenderApp)
|
||||
// .add_systems(
|
||||
// Render,
|
||||
// (
|
||||
// (
|
||||
// insert_texture_views,
|
||||
// locate_views.run_if(resource_exists::<OxrPrimaryReferenceSpace>),
|
||||
// update_views_render_world,
|
||||
// )
|
||||
// .chain()
|
||||
// .in_set(RenderSet::PrepareAssets),
|
||||
// begin_frame
|
||||
// .before(RenderSet::Queue)
|
||||
// .before(insert_texture_views),
|
||||
// wait_image.in_set(RenderSet::Render).before(render_system),
|
||||
// (release_image, end_frame)
|
||||
// .chain()
|
||||
// .in_set(RenderSet::Cleanup),
|
||||
// )
|
||||
// .run_if(session_started),
|
||||
// )
|
||||
// .insert_resource(OxrRenderLayers(vec![Box::new(ProjectionLayer)]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +168,7 @@ pub fn clean_views(
|
||||
for (e, cam) in &cam_query {
|
||||
manual_texture_views.remove(&ManualTextureViewHandle(XR_TEXTURE_INDEX + cam.0));
|
||||
commands.entity(e).despawn_recursive();
|
||||
info!("removing cam")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +184,6 @@ pub fn init_views(
|
||||
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::with_capacity(2);
|
||||
for index in 0..2 {
|
||||
info!("{}", graphics_info.resolution);
|
||||
let view_handle =
|
||||
@@ -127,10 +214,7 @@ pub fn init_views(
|
||||
warn!("Multiple OxrTrackingRoots! this is not allowed");
|
||||
}
|
||||
}
|
||||
|
||||
views.push(default());
|
||||
}
|
||||
commands.insert_resource(OxrViews(views));
|
||||
}
|
||||
|
||||
pub fn wait_frame(mut frame_waiter: ResMut<OxrFrameWaiter>, mut commands: Commands) {
|
||||
@@ -138,26 +222,43 @@ pub fn wait_frame(mut frame_waiter: ResMut<OxrFrameWaiter>, mut commands: Comman
|
||||
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
|
||||
commands.insert_resource(OxrTime(state.predicted_display_time));
|
||||
commands.insert_resource(OxrFrameState(state));
|
||||
}
|
||||
|
||||
pub fn update_cameras(
|
||||
frame_state: Res<OxrFrameState>,
|
||||
mut cameras: Query<&mut Camera, With<XrCamera>>,
|
||||
) {
|
||||
if frame_state.is_changed() {
|
||||
for mut camera in &mut cameras {
|
||||
camera.is_active = frame_state.should_render
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn locate_views(
|
||||
session: Res<OxrSession>,
|
||||
ref_space: Res<OxrPrimaryReferenceSpace>,
|
||||
time: Res<OxrTime>,
|
||||
frame_state: Res<OxrFrameState>,
|
||||
mut openxr_views: ResMut<OxrViews>,
|
||||
pipelined: Option<Res<Pipelined>>,
|
||||
) {
|
||||
let _span = info_span!("xr_locate_views");
|
||||
let time = if pipelined.is_some() {
|
||||
openxr::Time::from_nanos(
|
||||
frame_state.predicted_display_time.as_nanos()
|
||||
+ frame_state.predicted_display_period.as_nanos(),
|
||||
)
|
||||
} else {
|
||||
frame_state.predicted_display_time
|
||||
};
|
||||
let (flags, xr_views) = session
|
||||
.locate_views(
|
||||
openxr::ViewConfigurationType::PRIMARY_STEREO,
|
||||
**time,
|
||||
time,
|
||||
&ref_space,
|
||||
)
|
||||
.expect("Failed to locate views");
|
||||
if openxr_views.len() != xr_views.len() {
|
||||
openxr_views.resize(xr_views.len(), default());
|
||||
}
|
||||
|
||||
match (
|
||||
flags & ViewStateFlags::ORIENTATION_VALID == ViewStateFlags::ORIENTATION_VALID,
|
||||
flags & ViewStateFlags::POSITION_VALID == ViewStateFlags::POSITION_VALID,
|
||||
@@ -165,12 +266,18 @@ pub fn locate_views(
|
||||
(true, true) => *openxr_views = OxrViews(xr_views),
|
||||
(true, false) => {
|
||||
for (i, view) in openxr_views.iter_mut().enumerate() {
|
||||
view.pose.orientation = xr_views[i].pose.orientation;
|
||||
let Some(xr_view) = xr_views.get(i) else {
|
||||
break;
|
||||
};
|
||||
view.pose.orientation = xr_view.pose.orientation;
|
||||
}
|
||||
}
|
||||
(false, true) => {
|
||||
for (i, view) in openxr_views.iter_mut().enumerate() {
|
||||
view.pose.position = xr_views[i].pose.position;
|
||||
let Some(xr_view) = xr_views.get(i) else {
|
||||
break;
|
||||
};
|
||||
view.pose.position = xr_view.pose.position;
|
||||
}
|
||||
}
|
||||
(false, false) => {}
|
||||
@@ -384,64 +491,21 @@ pub fn end_frame(world: &mut World) {
|
||||
}
|
||||
world.resource_scope::<OxrFrameStream, ()>(|world, mut frame_stream| {
|
||||
let mut layers = vec![];
|
||||
for layer in world.resource::<OxrRenderLayers>().iter() {
|
||||
layers.push(layer.get(world));
|
||||
let frame_state = world.resource::<OxrFrameState>();
|
||||
if frame_state.should_render {
|
||||
for layer in world.resource::<OxrRenderLayers>().iter() {
|
||||
if let Some(layer) = layer.get(world) {
|
||||
layers.push(layer);
|
||||
}
|
||||
}
|
||||
}
|
||||
let layers: Vec<_> = layers.iter().map(Box::as_ref).collect();
|
||||
frame_stream
|
||||
.end(
|
||||
**world.resource::<OxrTime>(),
|
||||
frame_state.predicted_display_time,
|
||||
world.resource::<OxrGraphicsInfo>().blend_mode,
|
||||
&layers,
|
||||
)
|
||||
.expect("Failed to end frame");
|
||||
});
|
||||
}
|
||||
|
||||
// pub fn end_frame(
|
||||
// mut frame_stream: ResMut<OxrFrameStream>,
|
||||
// mut swapchain: ResMut<OxrSwapchain>,
|
||||
// stage: Res<OxrStage>,
|
||||
// display_time: Res<OxrTime>,
|
||||
// graphics_info: Res<OxrGraphicsInfo>,
|
||||
// openxr_views: Res<OxrViews>,
|
||||
// ) {
|
||||
// let _span = info_span!("xr_end_frame");
|
||||
// swapchain.release_image().unwrap();
|
||||
// let rect = openxr::Rect2Di {
|
||||
// offset: openxr::Offset2Di { x: 0, y: 0 },
|
||||
// extent: openxr::Extent2Di {
|
||||
// width: graphics_info.resolution.x as _,
|
||||
// height: graphics_info.resolution.y as _,
|
||||
// },
|
||||
// };
|
||||
// frame_stream
|
||||
// .end(
|
||||
// **display_time,
|
||||
// graphics_info.blend_mode,
|
||||
// &[&CompositionLayerProjection::new()
|
||||
// .layer_flags(CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA)
|
||||
// .space(&stage)
|
||||
// .views(&[
|
||||
// CompositionLayerProjectionView::new()
|
||||
// .pose(openxr_views.0[0].pose)
|
||||
// .fov(openxr_views.0[0].fov)
|
||||
// .sub_image(
|
||||
// SwapchainSubImage::new()
|
||||
// .swapchain(&swapchain)
|
||||
// .image_array_index(0)
|
||||
// .image_rect(rect),
|
||||
// ),
|
||||
// CompositionLayerProjectionView::new()
|
||||
// .pose(openxr_views.0[1].pose)
|
||||
// .fov(openxr_views.0[1].fov)
|
||||
// .sub_image(
|
||||
// SwapchainSubImage::new()
|
||||
// .swapchain(&swapchain)
|
||||
// .image_array_index(1)
|
||||
// .image_rect(rect),
|
||||
// ),
|
||||
// ])],
|
||||
// )
|
||||
// .expect("Failed to end frame");
|
||||
// }
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
use bevy::prelude::*;
|
||||
@@ -283,7 +282,7 @@ impl OxrSwapchain {
|
||||
}
|
||||
|
||||
/// Stores the generated swapchain images.
|
||||
#[derive(Debug, Deref, Resource, Clone, Copy)]
|
||||
#[derive(Debug, Deref, Resource, Clone, Copy, ExtractResource)]
|
||||
pub struct OxrSwapchainImages(pub &'static [wgpu::Texture]);
|
||||
|
||||
/// Thread safe wrapper around [openxr::Space] representing the stage.
|
||||
@@ -291,7 +290,7 @@ pub struct OxrSwapchainImages(pub &'static [wgpu::Texture]);
|
||||
// pub struct OxrStage(pub Arc<openxr::Space>);
|
||||
|
||||
/// Stores the latest generated [OxrViews]
|
||||
#[derive(Clone, Resource, ExtractResource, Deref, DerefMut)]
|
||||
#[derive(Clone, Resource, ExtractResource, Deref, DerefMut, Default)]
|
||||
pub struct OxrViews(pub Vec<openxr::View>);
|
||||
|
||||
/// Wrapper around [openxr::SystemId] to allow it to be stored as a resource.
|
||||
@@ -330,7 +329,7 @@ pub struct OxrPassthroughLayer(pub openxr::PassthroughLayer);
|
||||
pub struct OxrRenderLayers(pub Vec<Box<dyn LayerProvider + Send + Sync>>);
|
||||
|
||||
/// Resource storing graphics info for the currently running session.
|
||||
#[derive(Clone, Copy, Resource)]
|
||||
#[derive(Clone, Copy, Resource, ExtractResource)]
|
||||
pub struct OxrGraphicsInfo {
|
||||
pub blend_mode: EnvironmentBlendMode,
|
||||
pub resolution: UVec2,
|
||||
@@ -350,22 +349,12 @@ pub struct SessionConfigInfo {
|
||||
pub graphics_info: SessionCreateInfo,
|
||||
}
|
||||
|
||||
#[derive(Resource, Clone, Default)]
|
||||
pub struct OxrSessionStarted(Arc<AtomicBool>);
|
||||
#[derive(ExtractResource, Resource, Clone, Default)]
|
||||
pub struct OxrSessionStarted(pub bool);
|
||||
|
||||
impl OxrSessionStarted {
|
||||
pub fn set(&self, val: bool) {
|
||||
self.0.store(val, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
pub fn get(&self) -> bool {
|
||||
self.0.load(Ordering::SeqCst)
|
||||
}
|
||||
}
|
||||
|
||||
/// The calculated display time for the app. Passed through the pipeline.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Deref, DerefMut, Resource, ExtractResource)]
|
||||
pub struct OxrTime(pub openxr::Time);
|
||||
/// The frame state returned from [FrameWaiter::wait_frame](openxr::FrameWaiter::wait)
|
||||
#[derive(Clone, Deref, DerefMut, Resource, ExtractResource)]
|
||||
pub struct OxrFrameState(pub openxr::FrameState);
|
||||
|
||||
/// The root transform's global position for late latching in the render world.
|
||||
#[derive(ExtractResource, Resource, Clone, Copy, Default)]
|
||||
@@ -374,3 +363,7 @@ pub struct OxrRootTransform(pub GlobalTransform);
|
||||
#[derive(ExtractResource, Resource, Clone, Copy, Default, Deref, DerefMut, PartialEq)]
|
||||
/// This is inserted into the world to signify if the session should be cleaned up.
|
||||
pub struct OxrCleanupSession(pub bool);
|
||||
|
||||
/// Instructs systems to add display period
|
||||
#[derive(Clone, Copy, Default, Resource)]
|
||||
pub struct Pipelined;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::init::OxrPreUpdateSet;
|
||||
use crate::init::{OxrHandleEvents, OxrLast};
|
||||
use crate::resources::{
|
||||
OxrCleanupSession, OxrPassthrough, OxrPassthroughLayer, OxrSessionStarted, OxrSwapchain,
|
||||
};
|
||||
@@ -7,9 +7,7 @@ use bevy::ecs::event::ManualEventReader;
|
||||
use bevy::ecs::system::RunSystemOnce;
|
||||
use bevy::prelude::*;
|
||||
use bevy::render::RenderApp;
|
||||
use bevy_xr::session::{
|
||||
status_changed_to, XrSessionCreated, XrSessionExiting, XrSharedStatus, XrStatus,
|
||||
};
|
||||
use bevy_xr::session::{status_changed_to, XrSessionCreated, XrSessionExiting, XrStatus};
|
||||
use openxr::AnyGraphics;
|
||||
|
||||
use crate::graphics::{graphics_match, GraphicsExt, GraphicsType, GraphicsWrap};
|
||||
@@ -25,10 +23,7 @@ pub struct OxrSessionPlugin;
|
||||
impl Plugin for OxrSessionPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_event::<OxrSessionStatusEvent>();
|
||||
app.add_systems(
|
||||
PreUpdate,
|
||||
run_session_status_schedules.in_set(OxrPreUpdateSet::HandleEvents),
|
||||
);
|
||||
app.add_systems(OxrLast, run_session_status_schedules.after(OxrHandleEvents));
|
||||
app.add_systems(XrSessionExiting, clean_session);
|
||||
app.sub_app_mut(RenderApp)
|
||||
.add_systems(XrSessionExiting, |mut cmds: Commands| {
|
||||
@@ -41,18 +36,15 @@ impl Plugin for OxrSessionPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_stopping_state(session: Res<OxrSession>, session_started: Res<OxrSessionStarted>) {
|
||||
fn handle_stopping_state(session: Res<OxrSession>, mut session_started: ResMut<OxrSessionStarted>) {
|
||||
session.end().expect("Failed to end session");
|
||||
session_started.set(false);
|
||||
session_started.0 = false;
|
||||
}
|
||||
|
||||
fn clean_session(world: &mut World) {
|
||||
world.insert_resource(OxrCleanupSession(true));
|
||||
// It should be impossible to call this if the session is Unavailable
|
||||
world
|
||||
.get_resource::<XrSharedStatus>()
|
||||
.unwrap()
|
||||
.set(XrStatus::Available);
|
||||
*world.get_resource_mut::<XrStatus>().unwrap() = XrStatus::Available;
|
||||
}
|
||||
|
||||
#[derive(Resource, Default)]
|
||||
@@ -82,6 +74,7 @@ fn run_session_status_schedules(world: &mut World) {
|
||||
world.run_schedule(XrSessionExiting);
|
||||
world.run_system_once(apply_deferred);
|
||||
world.remove_resource::<OxrSession>();
|
||||
info!("Main App destroy");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use bevy::{ecs::schedule::ScheduleLabel, prelude::*, render::RenderApp};
|
||||
use bevy::{
|
||||
ecs::schedule::ScheduleLabel, prelude::*, render::extract_resource::ExtractResource,
|
||||
render::RenderApp,
|
||||
};
|
||||
|
||||
pub struct XrSessionPlugin;
|
||||
|
||||
@@ -16,7 +17,7 @@ impl Plugin for XrSessionPlugin {
|
||||
.add_event::<XrStatusChanged>()
|
||||
.add_systems(
|
||||
PreUpdate,
|
||||
handle_session.run_if(resource_exists::<XrSharedStatus>),
|
||||
handle_session.run_if(resource_exists::<XrStatus>),
|
||||
);
|
||||
}
|
||||
fn finish(&self, app: &mut App) {
|
||||
@@ -47,24 +48,7 @@ pub struct XrSessionExiting;
|
||||
#[derive(Event, Clone, Copy, Deref)]
|
||||
pub struct XrStatusChanged(pub XrStatus);
|
||||
|
||||
#[derive(Resource, Clone)]
|
||||
pub struct XrSharedStatus(Arc<RwLock<XrStatus>>);
|
||||
|
||||
impl XrSharedStatus {
|
||||
pub fn new(status: XrStatus) -> Self {
|
||||
Self(Arc::new(RwLock::new(status)))
|
||||
}
|
||||
|
||||
pub fn get(&self) -> XrStatus {
|
||||
*self.0.read().unwrap()
|
||||
}
|
||||
|
||||
pub fn set(&self, status: XrStatus) {
|
||||
*self.0.write().unwrap() = status;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, ExtractResource, Resource, PartialEq, Eq)]
|
||||
#[repr(u8)]
|
||||
pub enum XrStatus {
|
||||
/// An XR session is not available here
|
||||
@@ -84,7 +68,7 @@ pub enum XrStatus {
|
||||
}
|
||||
|
||||
pub fn handle_session(
|
||||
status: Res<XrSharedStatus>,
|
||||
current_status: Res<XrStatus>,
|
||||
mut previous_status: Local<Option<XrStatus>>,
|
||||
mut create_session: EventWriter<CreateXrSession>,
|
||||
mut begin_session: EventWriter<BeginXrSession>,
|
||||
@@ -92,9 +76,8 @@ pub fn handle_session(
|
||||
mut destroy_session: EventWriter<DestroyXrSession>,
|
||||
mut should_start_session: ResMut<XrCreateSessionWhenAvailabe>,
|
||||
) {
|
||||
let current_status = status.get();
|
||||
if *previous_status != Some(current_status) {
|
||||
match current_status {
|
||||
if *previous_status != Some(*current_status) {
|
||||
match *current_status {
|
||||
XrStatus::Unavailable => {}
|
||||
XrStatus::Available => {
|
||||
if **should_start_session {
|
||||
@@ -115,7 +98,7 @@ pub fn handle_session(
|
||||
}
|
||||
}
|
||||
}
|
||||
*previous_status = Some(current_status);
|
||||
*previous_status = Some(*current_status);
|
||||
}
|
||||
|
||||
/// A [`Condition`](bevy::ecs::schedule::Condition) that allows the system to run when the xr status changed to a specific [`XrStatus`].
|
||||
@@ -128,29 +111,23 @@ pub fn status_changed_to(
|
||||
}
|
||||
|
||||
/// A [`Condition`](bevy::ecs::schedule::Condition) system that says if the XR session is available. Returns true as long as [`XrStatus`] exists and isn't [`Unavailable`](XrStatus::Unavailable).
|
||||
pub fn session_available(status: Option<Res<XrSharedStatus>>) -> bool {
|
||||
status.is_some_and(|s| s.get() != XrStatus::Unavailable)
|
||||
pub fn session_available(status: Option<Res<XrStatus>>) -> bool {
|
||||
status.is_some_and(|s| *s != XrStatus::Unavailable)
|
||||
}
|
||||
|
||||
/// A [`Condition`](bevy::ecs::schedule::Condition) system that says if the XR session is ready or running
|
||||
pub fn session_created(status: Option<Res<XrSharedStatus>>) -> bool {
|
||||
matches!(
|
||||
status.as_deref().map(XrSharedStatus::get),
|
||||
Some(XrStatus::Ready | XrStatus::Running)
|
||||
)
|
||||
pub fn session_ready_or_running(status: Option<Res<XrStatus>>) -> bool {
|
||||
matches!(status.as_deref(), Some(XrStatus::Ready | XrStatus::Running))
|
||||
}
|
||||
|
||||
/// A [`Condition`](bevy::ecs::schedule::Condition) system that says if the XR session is running
|
||||
pub fn session_running(status: Option<Res<XrSharedStatus>>) -> bool {
|
||||
matches!(
|
||||
status.as_deref().map(XrSharedStatus::get),
|
||||
Some(XrStatus::Running)
|
||||
)
|
||||
pub fn session_running(status: Option<Res<XrStatus>>) -> bool {
|
||||
matches!(status.as_deref(), Some(XrStatus::Running))
|
||||
}
|
||||
|
||||
/// A function that returns a [`Condition`](bevy::ecs::schedule::Condition) system that says if an the [`XrStatus`] is in a specific state
|
||||
pub fn status_equals(status: XrStatus) -> impl FnMut(Option<Res<XrSharedStatus>>) -> bool {
|
||||
move |state: Option<Res<XrSharedStatus>>| state.is_some_and(|s| s.get() == status)
|
||||
pub fn status_equals(status: XrStatus) -> impl FnMut(Option<Res<XrStatus>>) -> bool {
|
||||
move |state: Option<Res<XrStatus>>| state.is_some_and(|s| *s == status)
|
||||
}
|
||||
|
||||
/// Event sent to backends to create an XR session. Should only be called in the [`XrStatus::Available`] state.
|
||||
|
||||
Reference in New Issue
Block a user