Merge pull request #108 from Schmarni-Dev/oxr_sessions
Refactor: Openxr Session toggling
This commit is contained in:
62
crates/bevy_openxr/examples/sessions.rs
Normal file
62
crates/bevy_openxr/examples/sessions.rs
Normal file
@@ -0,0 +1,62 @@
|
||||
//! A simple 3D scene with light shining over a cube sitting on a plane.
|
||||
|
||||
use bevy::prelude::*;
|
||||
use bevy_openxr::add_xr_plugins;
|
||||
use bevy_xr::session::{ XrStatus};
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(add_xr_plugins(DefaultPlugins))
|
||||
.add_plugins(bevy_xr_utils::hand_gizmos::HandGizmosPlugin)
|
||||
.add_systems(Startup, setup)
|
||||
.add_systems(Update, handle_input)
|
||||
.insert_resource(AmbientLight::default())
|
||||
.run();
|
||||
}
|
||||
|
||||
fn handle_input(
|
||||
keys: Res<ButtonInput<KeyCode>>,
|
||||
mut end: EventWriter<bevy_xr::session::EndXrSession>,
|
||||
mut _destroy: EventWriter<bevy_xr::session::DestroyXrSession>,
|
||||
mut _begin: EventWriter<bevy_xr::session::BeginXrSession>,
|
||||
mut create: EventWriter<bevy_xr::session::CreateXrSession>,
|
||||
state: Res<XrStatus>,
|
||||
) {
|
||||
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<Assets<Mesh>>,
|
||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||
) {
|
||||
// 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, 0.5, 0.0),
|
||||
..default()
|
||||
});
|
||||
commands.spawn(Camera3dBundle {
|
||||
transform: Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
|
||||
..default()
|
||||
});
|
||||
}
|
||||
@@ -5,19 +5,23 @@ use bevy::ecs::schedule::ScheduleLabel;
|
||||
use bevy::ecs::system::RunSystemOnce;
|
||||
use bevy::prelude::*;
|
||||
use bevy::utils::HashMap;
|
||||
use bevy_xr::session::{status_changed_to, XrStatus};
|
||||
use openxr::sys::ActionSuggestedBinding;
|
||||
|
||||
use crate::resources::OxrInstance;
|
||||
use crate::{resources::OxrInstance, session::OxrSessionStatusEvent};
|
||||
|
||||
impl Plugin for OxrActionBindingPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_schedule(Schedule::new(OxrSendActionBindings));
|
||||
app.add_event::<OxrSuggestActionBinding>();
|
||||
app.add_systems(
|
||||
Update,
|
||||
run_action_binding_sugestion
|
||||
.run_if(status_changed_to(XrStatus::Ready).and_then(run_once())),
|
||||
PostUpdate,
|
||||
run_action_binding_sugestion.run_if(
|
||||
|mut session_state: EventReader<OxrSessionStatusEvent>| {
|
||||
session_state
|
||||
.read()
|
||||
.any(|s| *s == OxrSessionStatusEvent::Created)
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::resources::OxrSession;
|
||||
use crate::session::{OxrSession, OxrSessionStatusEvent};
|
||||
use bevy::prelude::*;
|
||||
use bevy_xr::session::status_changed_to;
|
||||
|
||||
@@ -7,7 +7,11 @@ impl Plugin for OxrActionAttachingPlugin {
|
||||
app.add_event::<OxrAttachActionSet>();
|
||||
app.add_systems(
|
||||
PostUpdate,
|
||||
attach_sets.run_if(status_changed_to(bevy_xr::session::XrStatus::Ready)),
|
||||
attach_sets.run_if(|mut session_status_event: EventReader<OxrSessionStatusEvent>| {
|
||||
session_status_event
|
||||
.read()
|
||||
.any(|s| *s == OxrSessionStatusEvent::Created)
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::resources::OxrSession;
|
||||
use crate::session::OxrSession;
|
||||
use bevy::prelude::*;
|
||||
use bevy_xr::session::session_running;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ use bevy::prelude::*;
|
||||
use bevy_xr::hands::{LeftHand, RightHand};
|
||||
use bevy_xr::{
|
||||
hands::{HandBone, HandBoneRadius},
|
||||
session::{session_running, status_changed_to, XrStatus},
|
||||
session::{session_running, XrSessionCreated, XrSessionExiting},
|
||||
};
|
||||
use openxr::SpaceLocationFlags;
|
||||
|
||||
@@ -10,7 +10,8 @@ use crate::resources::Pipelined;
|
||||
use crate::{
|
||||
init::OxrTrackingRoot,
|
||||
reference_space::{OxrPrimaryReferenceSpace, OxrReferenceSpace},
|
||||
resources::{OxrFrameState, OxrSession},
|
||||
resources::OxrFrameState,
|
||||
session::OxrSession,
|
||||
};
|
||||
|
||||
pub struct HandTrackingPlugin {
|
||||
@@ -28,14 +29,8 @@ impl Plugin for HandTrackingPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(PreUpdate, locate_hands.run_if(session_running));
|
||||
if self.default_hands {
|
||||
app.add_systems(
|
||||
PreUpdate,
|
||||
clean_up_default_hands.run_if(status_changed_to(XrStatus::Exiting)),
|
||||
);
|
||||
app.add_systems(
|
||||
PostUpdate,
|
||||
spawn_default_hands.run_if(status_changed_to(XrStatus::Ready)),
|
||||
);
|
||||
app.add_systems(XrSessionExiting, clean_up_default_hands);
|
||||
app.add_systems(XrSessionCreated, spawn_default_hands);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,7 +40,7 @@ fn spawn_default_hands(
|
||||
session: Res<OxrSession>,
|
||||
root: Query<Entity, With<OxrTrackingRoot>>,
|
||||
) {
|
||||
info!("spawning hands");
|
||||
debug!("spawning default hands");
|
||||
let Ok(root) = root.get_single() else {
|
||||
error!("unable to get tracking root, skipping hand creation");
|
||||
return;
|
||||
@@ -77,6 +72,7 @@ fn spawn_default_hands(
|
||||
for bone in HandBone::get_all_bones() {
|
||||
let bone_left = cmds
|
||||
.spawn((
|
||||
DefaultHandBone,
|
||||
SpatialBundle::default(),
|
||||
bone,
|
||||
HandBoneRadius(0.0),
|
||||
@@ -85,6 +81,7 @@ fn spawn_default_hands(
|
||||
.id();
|
||||
let bone_right = cmds
|
||||
.spawn((
|
||||
DefaultHandBone,
|
||||
SpatialBundle::default(),
|
||||
bone,
|
||||
HandBoneRadius(0.0),
|
||||
@@ -113,15 +110,16 @@ fn spawn_default_hands(
|
||||
#[derive(Component)]
|
||||
struct DefaultHandTracker;
|
||||
#[derive(Component)]
|
||||
struct DefaultHandBones;
|
||||
struct DefaultHandBone;
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn clean_up_default_hands(
|
||||
mut cmds: Commands,
|
||||
query: Query<Entity, Or<(With<DefaultHandTracker>, With<DefaultHandBones>)>>,
|
||||
query: Query<Entity, Or<(With<DefaultHandTracker>, With<DefaultHandBone>)>>,
|
||||
) {
|
||||
for e in &query {
|
||||
cmds.entity(e).despawn();
|
||||
debug!("removing default hand entity");
|
||||
cmds.entity(e).despawn_recursive();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ use openxr::PassthroughCapabilityFlagsFB;
|
||||
|
||||
use crate::layer_builder::PassthroughLayer;
|
||||
use crate::resources::*;
|
||||
use crate::session::OxrSession;
|
||||
use crate::types::*;
|
||||
|
||||
pub struct OxrPassthroughPlugin;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use bevy::app::MainScheduleOrder;
|
||||
use bevy::ecs::schedule::ScheduleLabel;
|
||||
use bevy::ecs::system::RunSystemOnce;
|
||||
use bevy::prelude::*;
|
||||
use bevy::render::extract_resource::ExtractResourcePlugin;
|
||||
use bevy::render::renderer::RenderAdapter;
|
||||
@@ -22,12 +23,15 @@ use bevy_xr::session::BeginXrSession;
|
||||
use bevy_xr::session::CreateXrSession;
|
||||
use bevy_xr::session::DestroyXrSession;
|
||||
use bevy_xr::session::EndXrSession;
|
||||
use bevy_xr::session::XrSessionExiting;
|
||||
use bevy_xr::session::XrStatus;
|
||||
use bevy_xr::session::XrStatusChanged;
|
||||
|
||||
use crate::error::OxrError;
|
||||
use crate::graphics::*;
|
||||
use crate::resources::*;
|
||||
use crate::session::OxrSession;
|
||||
use crate::session::OxrSessionStatusEvent;
|
||||
use crate::types::*;
|
||||
|
||||
pub fn session_started(started: Option<Res<OxrSessionStarted>>) -> bool {
|
||||
@@ -45,9 +49,6 @@ pub struct OxrLast;
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, SystemSet)]
|
||||
pub struct OxrHandleEvents;
|
||||
|
||||
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct OxrSessionCreated;
|
||||
|
||||
pub struct OxrInitPlugin {
|
||||
/// Information about the app this is being used to build.
|
||||
pub app_info: AppInfo,
|
||||
@@ -112,7 +113,6 @@ impl Plugin for OxrInitPlugin {
|
||||
ExtractResourcePlugin::<OxrSessionStarted>::default(),
|
||||
))
|
||||
.init_schedule(OxrLast)
|
||||
.init_schedule(OxrSessionCreated)
|
||||
.add_systems(
|
||||
OxrLast,
|
||||
(
|
||||
@@ -126,7 +126,7 @@ impl Plugin for OxrInitPlugin {
|
||||
.run_if(status_equals(XrStatus::Ready)),
|
||||
end_xr_session
|
||||
.run_if(on_event::<EndXrSession>())
|
||||
.run_if(status_equals(XrStatus::Stopping)),
|
||||
.run_if(status_equals(XrStatus::Running)),
|
||||
destroy_xr_session
|
||||
.run_if(on_event::<DestroyXrSession>())
|
||||
.run_if(status_equals(XrStatus::Exiting)),
|
||||
@@ -134,6 +134,7 @@ impl Plugin for OxrInitPlugin {
|
||||
.chain()
|
||||
.in_set(OxrHandleEvents),
|
||||
)
|
||||
.add_systems(XrSessionExiting, destroy_xr_session)
|
||||
.add_systems(
|
||||
PostUpdate,
|
||||
update_root_transform.after(TransformSystem::TransformPropagate),
|
||||
@@ -163,7 +164,7 @@ impl Plugin for OxrInitPlugin {
|
||||
Render,
|
||||
destroy_xr_session_render
|
||||
.run_if(resource_equals(OxrCleanupSession(true)))
|
||||
.after(RenderSet::ExtractCommands),
|
||||
.after(RenderSet::Cleanup),
|
||||
)
|
||||
.add_systems(
|
||||
ExtractSchedule,
|
||||
@@ -276,51 +277,6 @@ impl OxrInitPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset_per_frame_resources(mut cleanup: ResMut<OxrCleanupSession>) {
|
||||
**cleanup = false;
|
||||
}
|
||||
|
||||
/// Polls any OpenXR events and handles them accordingly
|
||||
pub fn poll_events(
|
||||
instance: Res<OxrInstance>,
|
||||
mut status: ResMut<XrStatus>,
|
||||
mut changed_event: EventWriter<XrStatusChanged>,
|
||||
) {
|
||||
let _span = info_span!("xr_poll_events");
|
||||
let mut buffer = Default::default();
|
||||
while let Some(event) = instance
|
||||
.poll_event(&mut buffer)
|
||||
.expect("Failed to poll event")
|
||||
{
|
||||
use openxr::Event::*;
|
||||
match event {
|
||||
SessionStateChanged(state) => {
|
||||
use openxr::SessionState;
|
||||
|
||||
let state = state.state();
|
||||
|
||||
info!("entered XR state {:?}", state);
|
||||
|
||||
let new_status = match state {
|
||||
SessionState::IDLE => XrStatus::Idle,
|
||||
SessionState::READY => XrStatus::Ready,
|
||||
SessionState::SYNCHRONIZED | SessionState::VISIBLE | SessionState::FOCUSED => {
|
||||
XrStatus::Running
|
||||
}
|
||||
SessionState::STOPPING => XrStatus::Stopping,
|
||||
SessionState::EXITING | SessionState::LOSS_PENDING => XrStatus::Exiting,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
changed_event.send(XrStatusChanged(new_status));
|
||||
*status = new_status;
|
||||
}
|
||||
InstanceLossPending(_) => {}
|
||||
EventsLost(e) => warn!("lost {} XR events", e.lost_event_count()),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn init_xr_session(
|
||||
device: &wgpu::Device,
|
||||
instance: &OxrInstance,
|
||||
@@ -455,35 +411,6 @@ fn init_xr_session(
|
||||
))
|
||||
}
|
||||
|
||||
pub fn create_xr_session(world: &mut World) {
|
||||
let device = world.resource::<RenderDevice>();
|
||||
let instance = world.resource::<OxrInstance>();
|
||||
let create_info = world.non_send_resource::<SessionConfigInfo>();
|
||||
let system_id = world.resource::<OxrSystemId>();
|
||||
match init_xr_session(
|
||||
device.wgpu_device(),
|
||||
&instance,
|
||||
**system_id,
|
||||
create_info.clone(),
|
||||
) {
|
||||
Ok((session, frame_waiter, frame_stream, swapchain, images, graphics_info)) => {
|
||||
world.insert_resource(session.clone());
|
||||
world.insert_resource(frame_waiter);
|
||||
world.insert_resource(images.clone());
|
||||
world.insert_resource(graphics_info.clone());
|
||||
world.insert_resource(OxrRenderResources {
|
||||
session,
|
||||
frame_stream,
|
||||
swapchain,
|
||||
images,
|
||||
graphics_info,
|
||||
});
|
||||
world.run_schedule(OxrSessionCreated);
|
||||
}
|
||||
Err(e) => error!("Failed to initialize XrSession: {e}"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn begin_xr_session(session: Res<OxrSession>, mut session_started: ResMut<OxrSessionStarted>) {
|
||||
let _span = info_span!("xr_begin_session");
|
||||
session
|
||||
@@ -494,7 +421,7 @@ pub fn begin_xr_session(session: Res<OxrSession>, mut session_started: ResMut<Ox
|
||||
|
||||
pub fn end_xr_session(session: Res<OxrSession>, mut session_started: ResMut<OxrSessionStarted>) {
|
||||
let _span = info_span!("xr_end_session");
|
||||
session.end().expect("Failed to end session");
|
||||
session.request_exit().expect("Failed to end session");
|
||||
session_started.0 = false;
|
||||
}
|
||||
|
||||
@@ -508,6 +435,36 @@ struct OxrRenderResources {
|
||||
graphics_info: OxrGraphicsInfo,
|
||||
}
|
||||
|
||||
pub fn create_xr_session(
|
||||
device: Res<RenderDevice>,
|
||||
instance: Res<OxrInstance>,
|
||||
create_info: NonSend<SessionConfigInfo>,
|
||||
system_id: Res<OxrSystemId>,
|
||||
mut commands: Commands,
|
||||
) {
|
||||
match init_xr_session(
|
||||
device.wgpu_device(),
|
||||
&instance,
|
||||
**system_id,
|
||||
create_info.clone(),
|
||||
) {
|
||||
Ok((session, frame_waiter, frame_stream, swapchain, images, graphics_info)) => {
|
||||
commands.insert_resource(session.clone());
|
||||
commands.insert_resource(frame_waiter);
|
||||
commands.insert_resource(images.clone());
|
||||
commands.insert_resource(graphics_info.clone());
|
||||
commands.insert_resource(OxrRenderResources {
|
||||
session,
|
||||
frame_stream,
|
||||
swapchain,
|
||||
images,
|
||||
graphics_info,
|
||||
});
|
||||
}
|
||||
Err(e) => error!("Failed to initialize XrSession: {e}"),
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
@@ -528,12 +485,64 @@ pub fn transfer_xr_resources(mut commands: Commands, mut world: ResMut<MainWorld
|
||||
commands.insert_resource(graphics_info);
|
||||
}
|
||||
|
||||
pub fn destroy_xr_session(world: &mut World) {
|
||||
world.remove_resource::<OxrSession>();
|
||||
world.remove_resource::<OxrFrameWaiter>();
|
||||
world.remove_resource::<OxrSwapchainImages>();
|
||||
world.remove_resource::<OxrGraphicsInfo>();
|
||||
world.insert_resource(OxrCleanupSession(true));
|
||||
/// Polls any OpenXR events and handles them accordingly
|
||||
pub fn poll_events(
|
||||
instance: Res<OxrInstance>,
|
||||
mut status: ResMut<XrStatus>,
|
||||
mut changed_event: EventWriter<XrStatusChanged>,
|
||||
mut session_status_events: EventWriter<OxrSessionStatusEvent>,
|
||||
) {
|
||||
let _span = info_span!("xr_poll_events");
|
||||
let mut buffer = Default::default();
|
||||
while let Some(event) = instance
|
||||
.poll_event(&mut buffer)
|
||||
.expect("Failed to poll event")
|
||||
{
|
||||
use openxr::Event::*;
|
||||
match event {
|
||||
SessionStateChanged(state) => {
|
||||
use openxr::SessionState;
|
||||
|
||||
let state = state.state();
|
||||
|
||||
info!("entered XR state {:?}", state);
|
||||
|
||||
let new_status = match state {
|
||||
SessionState::IDLE => {
|
||||
if *status == XrStatus::Available {
|
||||
session_status_events.send(OxrSessionStatusEvent::Created);
|
||||
}
|
||||
XrStatus::Idle
|
||||
}
|
||||
SessionState::READY => XrStatus::Ready,
|
||||
SessionState::SYNCHRONIZED | SessionState::VISIBLE | SessionState::FOCUSED => {
|
||||
XrStatus::Running
|
||||
}
|
||||
SessionState::STOPPING => XrStatus::Stopping,
|
||||
SessionState::EXITING | SessionState::LOSS_PENDING => {
|
||||
session_status_events.send(OxrSessionStatusEvent::AboutToBeDestroyed);
|
||||
XrStatus::Exiting
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
changed_event.send(XrStatusChanged(new_status));
|
||||
*status = new_status;
|
||||
}
|
||||
InstanceLossPending(_) => {}
|
||||
EventsLost(e) => warn!("lost {} XR events", e.lost_event_count()),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset_per_frame_resources(mut cleanup: ResMut<OxrCleanupSession>) {
|
||||
**cleanup = false;
|
||||
}
|
||||
|
||||
pub fn destroy_xr_session(mut commands: Commands) {
|
||||
commands.remove_resource::<OxrFrameWaiter>();
|
||||
commands.remove_resource::<OxrSwapchainImages>();
|
||||
commands.remove_resource::<OxrGraphicsInfo>();
|
||||
}
|
||||
|
||||
pub fn destroy_xr_session_render(world: &mut World) {
|
||||
@@ -541,5 +550,9 @@ 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));
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ use render::OxrRenderPlugin;
|
||||
use self::{
|
||||
features::{handtracking::HandTrackingPlugin, passthrough::OxrPassthroughPlugin},
|
||||
reference_space::OxrReferenceSpacePlugin,
|
||||
session::OxrSessionPlugin,
|
||||
};
|
||||
|
||||
pub mod action_binding;
|
||||
@@ -28,6 +29,7 @@ pub mod layer_builder;
|
||||
pub mod reference_space;
|
||||
pub mod render;
|
||||
pub mod resources;
|
||||
pub mod session;
|
||||
pub mod types;
|
||||
|
||||
pub fn add_xr_plugins<G: PluginGroup>(plugins: G) -> PluginGroupBuilder {
|
||||
@@ -37,6 +39,7 @@ pub fn add_xr_plugins<G: PluginGroup>(plugins: G) -> PluginGroupBuilder {
|
||||
.disable::<PipelinedRenderingPlugin>()
|
||||
.add_before::<RenderPlugin, _>(XrSessionPlugin)
|
||||
.add_before::<RenderPlugin, _>(OxrInitPlugin::default())
|
||||
.add(OxrSessionPlugin)
|
||||
.add(OxrReferenceSpacePlugin::default())
|
||||
.add(OxrRenderPlugin)
|
||||
.add(OxrPassthroughPlugin)
|
||||
@@ -46,6 +49,8 @@ pub fn add_xr_plugins<G: PluginGroup>(plugins: G) -> PluginGroupBuilder {
|
||||
.add(action_binding::OxrActionBindingPlugin)
|
||||
.add(action_set_syncing::OxrActionSyncingPlugin)
|
||||
// .add(XrActionPlugin)
|
||||
// we should probably handle the exiting ourselfs so that we can correctly end the
|
||||
// session and instance
|
||||
.set(WindowPlugin {
|
||||
primary_window: Some(Window {
|
||||
transparent: true,
|
||||
|
||||
@@ -2,10 +2,14 @@ use std::sync::Arc;
|
||||
|
||||
use bevy::{
|
||||
prelude::*,
|
||||
render::extract_resource::{ExtractResource, ExtractResourcePlugin},
|
||||
render::{
|
||||
extract_resource::{ExtractResource, ExtractResourcePlugin},
|
||||
RenderApp,
|
||||
},
|
||||
};
|
||||
use bevy_xr::session::{XrSessionCreated, XrSessionExiting};
|
||||
|
||||
use crate::{init::OxrSessionCreated, resources::OxrSession};
|
||||
use crate::session::OxrSession;
|
||||
|
||||
pub struct OxrReferenceSpacePlugin {
|
||||
pub default_primary_ref_space: openxr::ReferenceSpaceType,
|
||||
@@ -19,8 +23,7 @@ impl Default for OxrReferenceSpacePlugin {
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
struct OxrPrimaryReferenceSpaceType(openxr::ReferenceSpaceType);
|
||||
// TODO: this will keep the session alive so we need to remove this in the render world too
|
||||
struct OxrDefaultPrimaryReferenceSpaceType(openxr::ReferenceSpaceType);
|
||||
/// The Default Reference space used for locating things
|
||||
#[derive(Resource, Deref, ExtractResource, Clone)]
|
||||
pub struct OxrPrimaryReferenceSpace(pub Arc<openxr::Space>);
|
||||
@@ -31,18 +34,27 @@ pub struct OxrReferenceSpace(pub openxr::Space);
|
||||
|
||||
impl Plugin for OxrReferenceSpacePlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.insert_resource(OxrPrimaryReferenceSpaceType(self.default_primary_ref_space));
|
||||
app.insert_resource(OxrDefaultPrimaryReferenceSpaceType(
|
||||
self.default_primary_ref_space,
|
||||
));
|
||||
app.add_plugins(ExtractResourcePlugin::<OxrPrimaryReferenceSpace>::default());
|
||||
app.add_systems(
|
||||
OxrSessionCreated,
|
||||
set_primary_ref_space, // .in_set(OxrPreUpdateSet::UpdateCriticalComponents),
|
||||
);
|
||||
app.add_systems(XrSessionCreated, set_primary_ref_space);
|
||||
app.add_systems(XrSessionExiting, cleanup);
|
||||
app.sub_app_mut(RenderApp)
|
||||
.add_systems(XrSessionExiting, cleanup);
|
||||
}
|
||||
}
|
||||
|
||||
fn cleanup(mut cmds: Commands, query: Query<Entity, With<OxrReferenceSpace>>) {
|
||||
cmds.remove_resource::<OxrPrimaryReferenceSpace>();
|
||||
for e in &query {
|
||||
cmds.entity(e).remove::<OxrReferenceSpace>();
|
||||
}
|
||||
}
|
||||
|
||||
fn set_primary_ref_space(
|
||||
session: Res<OxrSession>,
|
||||
space_type: Res<OxrPrimaryReferenceSpaceType>,
|
||||
space_type: Res<OxrDefaultPrimaryReferenceSpaceType>,
|
||||
mut cmds: Commands,
|
||||
) {
|
||||
match session.create_reference_space(space_type.0, openxr::Posef::IDENTITY) {
|
||||
|
||||
@@ -10,13 +10,18 @@ use bevy::{
|
||||
Render, RenderApp, RenderSet,
|
||||
},
|
||||
tasks::ComputeTaskPool,
|
||||
transform::TransformSystem,
|
||||
};
|
||||
use bevy_xr::{
|
||||
camera::{XrCamera, XrCameraBundle, XrProjection},
|
||||
session::{session_running, XrSessionExiting},
|
||||
};
|
||||
use bevy_xr::camera::{XrCamera, XrCameraBundle, XrProjection};
|
||||
use openxr::ViewStateFlags;
|
||||
|
||||
use crate::{
|
||||
init::{session_started, OxrHandleEvents, OxrLast, OxrTrackingRoot},
|
||||
layer_builder::ProjectionLayer,
|
||||
session::OxrSession,
|
||||
};
|
||||
use crate::{reference_space::OxrPrimaryReferenceSpace, resources::*};
|
||||
|
||||
@@ -73,6 +78,7 @@ impl Plugin for OxrRenderPlugin {
|
||||
.chain()
|
||||
.after(OxrHandleEvents),
|
||||
)
|
||||
.add_systems(XrSessionExiting, clean_views)
|
||||
.init_resource::<OxrViews>();
|
||||
|
||||
let render_app = app.sub_app_mut(RenderApp);
|
||||
@@ -90,6 +96,8 @@ impl Plugin for OxrRenderPlugin {
|
||||
.after(RenderSet::Render)
|
||||
.before(RenderSet::Cleanup),
|
||||
)
|
||||
.add_systems(Last, wait_frame.run_if(session_started));
|
||||
app.sub_app_mut(RenderApp)
|
||||
.add_systems(
|
||||
Render,
|
||||
(
|
||||
@@ -111,6 +119,13 @@ impl Plugin for OxrRenderPlugin {
|
||||
.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,
|
||||
@@ -181,6 +196,19 @@ fn update_rendering(app_world: &mut World, _sub_app: &mut App) {
|
||||
|
||||
pub const XR_TEXTURE_INDEX: u32 = 3383858418;
|
||||
|
||||
pub fn clean_views(
|
||||
mut manual_texture_views: ResMut<ManualTextureViews>,
|
||||
mut commands: Commands,
|
||||
cam_query: Query<(Entity, &XrCamera)>,
|
||||
) {
|
||||
for (e, cam) in &cam_query {
|
||||
manual_texture_views.remove(&ManualTextureViewHandle(XR_TEXTURE_INDEX + cam.0));
|
||||
commands.entity(e).despawn_recursive();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: have cameras initialized externally and then recieved by this function.
|
||||
/// This is needed to properly initialize the texture views so that bevy will set them to the correct resolution despite them being updated in the render world.
|
||||
pub fn init_views(
|
||||
graphics_info: Res<OxrGraphicsInfo>,
|
||||
mut manual_texture_views: ResMut<ManualTextureViews>,
|
||||
|
||||
@@ -2,11 +2,11 @@ use std::sync::Arc;
|
||||
|
||||
use bevy::prelude::*;
|
||||
use bevy::render::extract_resource::ExtractResource;
|
||||
use openxr::AnyGraphics;
|
||||
|
||||
use crate::error::OxrError;
|
||||
use crate::graphics::*;
|
||||
use crate::layer_builder::{CompositionLayer, LayerProvider};
|
||||
use crate::session::OxrSession;
|
||||
use crate::types::*;
|
||||
|
||||
/// Wrapper around an [`Entry`](openxr::Entry) with some methods overridden to use bevy types.
|
||||
@@ -146,96 +146,6 @@ impl OxrInstance {
|
||||
}
|
||||
}
|
||||
|
||||
/// Graphics agnostic wrapper around [openxr::Session].
|
||||
///
|
||||
/// See [`openxr::Session`] for other available methods.
|
||||
#[derive(Resource, Deref, Clone)]
|
||||
pub struct OxrSession(
|
||||
/// A session handle with [`AnyGraphics`].
|
||||
/// Having this here allows the majority of [`Session`](openxr::Session)'s methods to work without having to rewrite them.
|
||||
#[deref]
|
||||
pub(crate) openxr::Session<AnyGraphics>,
|
||||
/// A [`GraphicsWrap`] with [`openxr::Session<G>`] as the inner type.
|
||||
/// This is so that we can still operate on functions that don't take [`AnyGraphics`] as the generic.
|
||||
pub(crate) GraphicsWrap<Self>,
|
||||
);
|
||||
|
||||
impl GraphicsType for OxrSession {
|
||||
type Inner<G: GraphicsExt> = openxr::Session<G>;
|
||||
}
|
||||
|
||||
impl<G: GraphicsExt> From<openxr::Session<G>> for OxrSession {
|
||||
fn from(session: openxr::Session<G>) -> Self {
|
||||
Self::from_inner(session)
|
||||
}
|
||||
}
|
||||
|
||||
impl OxrSession {
|
||||
/// Creates a new [`OxrSession`] from an [`openxr::Session`].
|
||||
/// In the majority of cases, you should use [`create_session`](OxrInstance::create_session) instead.
|
||||
pub fn from_inner<G: GraphicsExt>(session: openxr::Session<G>) -> Self {
|
||||
Self(session.clone().into_any_graphics(), G::wrap(session))
|
||||
}
|
||||
|
||||
/// Returns [`GraphicsWrap`] with [`openxr::Session<G>`] as the inner type.
|
||||
///
|
||||
/// This can be useful if you need access to the original [`openxr::Session`] with the graphics API still specified.
|
||||
pub fn typed_session(&self) -> &GraphicsWrap<Self> {
|
||||
&self.1
|
||||
}
|
||||
|
||||
/// Enumerates all available swapchain formats and converts them to wgpu's [`TextureFormat`](wgpu::TextureFormat).
|
||||
///
|
||||
/// Calls [`enumerate_swapchain_formats`](openxr::Session::enumerate_swapchain_formats) internally.
|
||||
pub fn enumerate_swapchain_formats(&self) -> Result<Vec<wgpu::TextureFormat>> {
|
||||
graphics_match!(
|
||||
&self.1;
|
||||
session => Ok(session.enumerate_swapchain_formats()?.into_iter().filter_map(Api::into_wgpu_format).collect())
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates an [OxrSwapchain].
|
||||
///
|
||||
/// Calls [`create_swapchain`](openxr::Session::create_swapchain) internally.
|
||||
pub fn create_swapchain(&self, info: SwapchainCreateInfo) -> Result<OxrSwapchain> {
|
||||
Ok(OxrSwapchain(graphics_match!(
|
||||
&self.1;
|
||||
session => session.create_swapchain(&info.try_into()?)? => OxrSwapchain
|
||||
)))
|
||||
}
|
||||
|
||||
/// Creates a passthrough.
|
||||
///
|
||||
/// Requires [`XR_FB_passthrough`](https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#XR_FB_passthrough).
|
||||
///
|
||||
/// Calls [`create_passthrough`](openxr::Session::create_passthrough) internally.
|
||||
pub fn create_passthrough(&self, flags: openxr::PassthroughFlagsFB) -> Result<OxrPassthrough> {
|
||||
Ok(OxrPassthrough(
|
||||
graphics_match! {
|
||||
&self.1;
|
||||
session => session.create_passthrough(flags)?
|
||||
},
|
||||
flags,
|
||||
))
|
||||
}
|
||||
|
||||
/// Creates a passthrough layer that can be used to make a [`CompositionLayerPassthrough`](crate::layer_builder::CompositionLayerPassthrough) for frame submission.
|
||||
///
|
||||
/// Requires [`XR_FB_passthrough`](https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#XR_FB_passthrough).
|
||||
///
|
||||
/// Calls [`create_passthrough_layer`](openxr::Session::create_passthrough_layer) internally.
|
||||
pub fn create_passthrough_layer(
|
||||
&self,
|
||||
passthrough: &OxrPassthrough,
|
||||
purpose: openxr::PassthroughLayerPurposeFB,
|
||||
) -> Result<OxrPassthroughLayer> {
|
||||
Ok(OxrPassthroughLayer(graphics_match! {
|
||||
&self.1;
|
||||
session => session.create_passthrough_layer(&passthrough.0, passthrough.1, purpose)?
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// Graphics agnostic wrapper around [openxr::FrameStream]
|
||||
#[derive(Resource)]
|
||||
pub struct OxrFrameStream(pub GraphicsWrap<Self>);
|
||||
@@ -365,15 +275,15 @@ impl OxrSwapchain {
|
||||
images.push(Api::to_wgpu_img(image, device, format, resolution)?);
|
||||
}
|
||||
}
|
||||
Ok(OxrSwapchainImages(images.into()))
|
||||
Ok(OxrSwapchainImages(images.leak()))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Stores the generated swapchain images.
|
||||
#[derive(Debug, Deref, Resource, ExtractResource, Clone)]
|
||||
pub struct OxrSwapchainImages(pub Arc<Vec<wgpu::Texture>>);
|
||||
#[derive(Debug, Deref, Resource, Clone, Copy, ExtractResource)]
|
||||
pub struct OxrSwapchainImages(pub &'static [wgpu::Texture]);
|
||||
|
||||
/// Thread safe wrapper around [openxr::Space] representing the stage.
|
||||
// #[derive(Deref, Clone, Resource)]
|
||||
@@ -396,7 +306,7 @@ pub struct OxrSystemId(pub openxr::SystemId);
|
||||
pub struct OxrPassthrough(
|
||||
#[deref] pub openxr::Passthrough,
|
||||
/// The flags are stored here so that they don't need to be passed in again when creating an [`OxrPassthroughLayer`].
|
||||
openxr::PassthroughFlagsFB,
|
||||
pub openxr::PassthroughFlagsFB,
|
||||
);
|
||||
|
||||
impl OxrPassthrough {
|
||||
|
||||
171
crates/bevy_openxr/src/openxr/session.rs
Normal file
171
crates/bevy_openxr/src/openxr/session.rs
Normal file
@@ -0,0 +1,171 @@
|
||||
use crate::init::{OxrHandleEvents, OxrLast};
|
||||
use crate::resources::{
|
||||
OxrCleanupSession, OxrPassthrough, OxrPassthroughLayer, OxrSessionStarted, OxrSwapchain,
|
||||
};
|
||||
use crate::types::{Result, SwapchainCreateInfo};
|
||||
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, XrStatus};
|
||||
use openxr::AnyGraphics;
|
||||
|
||||
use crate::graphics::{graphics_match, GraphicsExt, GraphicsType, GraphicsWrap};
|
||||
|
||||
#[derive(Event, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum OxrSessionStatusEvent {
|
||||
Created,
|
||||
AboutToBeDestroyed,
|
||||
}
|
||||
|
||||
pub struct OxrSessionPlugin;
|
||||
|
||||
impl Plugin for OxrSessionPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_event::<OxrSessionStatusEvent>();
|
||||
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| {
|
||||
cmds.remove_resource::<OxrCleanupSession>();
|
||||
});
|
||||
app.add_systems(
|
||||
PreUpdate,
|
||||
handle_stopping_state.run_if(status_changed_to(XrStatus::Stopping)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_stopping_state(session: Res<OxrSession>, mut session_started: ResMut<OxrSessionStarted>) {
|
||||
session.end().expect("Failed to end session");
|
||||
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_mut::<XrStatus>().unwrap() = XrStatus::Available;
|
||||
}
|
||||
|
||||
#[derive(Resource, Default)]
|
||||
struct SessionStatusReader(ManualEventReader<OxrSessionStatusEvent>);
|
||||
|
||||
fn run_session_status_schedules(world: &mut World) {
|
||||
let mut reader = world
|
||||
.remove_resource::<SessionStatusReader>()
|
||||
.unwrap_or_default();
|
||||
|
||||
let events = reader
|
||||
.0
|
||||
.read(
|
||||
world
|
||||
.get_resource::<Events<OxrSessionStatusEvent>>()
|
||||
.unwrap(),
|
||||
)
|
||||
.copied()
|
||||
.collect::<Vec<_>>();
|
||||
for e in events.iter() {
|
||||
match e {
|
||||
OxrSessionStatusEvent::Created => {
|
||||
world.run_schedule(XrSessionCreated);
|
||||
world.run_system_once(apply_deferred);
|
||||
}
|
||||
OxrSessionStatusEvent::AboutToBeDestroyed => {
|
||||
world.run_schedule(XrSessionExiting);
|
||||
world.run_system_once(apply_deferred);
|
||||
world.remove_resource::<OxrSession>();
|
||||
}
|
||||
}
|
||||
}
|
||||
world.insert_resource(reader);
|
||||
}
|
||||
|
||||
/// Graphics agnostic wrapper around [openxr::Session].
|
||||
///
|
||||
/// See [`openxr::Session`] for other available methods.
|
||||
#[derive(Resource, Deref, Clone)]
|
||||
pub struct OxrSession(
|
||||
/// A session handle with [`AnyGraphics`].
|
||||
/// Having this here allows the majority of [`Session`](openxr::Session)'s methods to work without having to rewrite them.
|
||||
#[deref]
|
||||
pub(crate) openxr::Session<AnyGraphics>,
|
||||
/// A [`GraphicsWrap`] with [`openxr::Session<G>`] as the inner type.
|
||||
/// This is so that we can still operate on functions that don't take [`AnyGraphics`] as the generic.
|
||||
pub(crate) GraphicsWrap<Self>,
|
||||
);
|
||||
|
||||
impl GraphicsType for OxrSession {
|
||||
type Inner<G: GraphicsExt> = openxr::Session<G>;
|
||||
}
|
||||
|
||||
impl<G: GraphicsExt> From<openxr::Session<G>> for OxrSession {
|
||||
fn from(session: openxr::Session<G>) -> Self {
|
||||
Self::from_inner(session)
|
||||
}
|
||||
}
|
||||
|
||||
impl OxrSession {
|
||||
/// Creates a new [`OxrSession`] from an [`openxr::Session`].
|
||||
/// In the majority of cases, you should use [`create_session`](OxrInstance::create_session) instead.
|
||||
pub fn from_inner<G: GraphicsExt>(session: openxr::Session<G>) -> Self {
|
||||
Self(session.clone().into_any_graphics(), G::wrap(session))
|
||||
}
|
||||
|
||||
/// Returns [`GraphicsWrap`] with [`openxr::Session<G>`] as the inner type.
|
||||
///
|
||||
/// This can be useful if you need access to the original [`openxr::Session`] with the graphics API still specified.
|
||||
pub fn typed_session(&self) -> &GraphicsWrap<Self> {
|
||||
&self.1
|
||||
}
|
||||
|
||||
/// Enumerates all available swapchain formats and converts them to wgpu's [`TextureFormat`](wgpu::TextureFormat).
|
||||
///
|
||||
/// Calls [`enumerate_swapchain_formats`](openxr::Session::enumerate_swapchain_formats) internally.
|
||||
pub fn enumerate_swapchain_formats(&self) -> Result<Vec<wgpu::TextureFormat>> {
|
||||
graphics_match!(
|
||||
&self.1;
|
||||
session => Ok(session.enumerate_swapchain_formats()?.into_iter().filter_map(Api::into_wgpu_format).collect())
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates an [OxrSwapchain].
|
||||
///
|
||||
/// Calls [`create_swapchain`](openxr::Session::create_swapchain) internally.
|
||||
pub fn create_swapchain(&self, info: SwapchainCreateInfo) -> Result<OxrSwapchain> {
|
||||
Ok(OxrSwapchain(graphics_match!(
|
||||
&self.1;
|
||||
session => session.create_swapchain(&info.try_into()?)? => OxrSwapchain
|
||||
)))
|
||||
}
|
||||
|
||||
/// Creates a passthrough.
|
||||
///
|
||||
/// Requires [`XR_FB_passthrough`](https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#XR_FB_passthrough).
|
||||
///
|
||||
/// Calls [`create_passthrough`](openxr::Session::create_passthrough) internally.
|
||||
pub fn create_passthrough(&self, flags: openxr::PassthroughFlagsFB) -> Result<OxrPassthrough> {
|
||||
Ok(OxrPassthrough(
|
||||
graphics_match! {
|
||||
&self.1;
|
||||
session => session.create_passthrough(flags)?
|
||||
},
|
||||
flags,
|
||||
))
|
||||
}
|
||||
|
||||
/// Creates a passthrough layer that can be used to make a [`CompositionLayerPassthrough`](crate::layer_builder::CompositionLayerPassthrough) for frame submission.
|
||||
///
|
||||
/// Requires [`XR_FB_passthrough`](https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#XR_FB_passthrough).
|
||||
///
|
||||
/// Calls [`create_passthrough_layer`](openxr::Session::create_passthrough_layer) internally.
|
||||
pub fn create_passthrough_layer(
|
||||
&self,
|
||||
passthrough: &OxrPassthrough,
|
||||
purpose: openxr::PassthroughLayerPurposeFB,
|
||||
) -> Result<OxrPassthroughLayer> {
|
||||
Ok(OxrPassthroughLayer(graphics_match! {
|
||||
&self.1;
|
||||
session => session.create_passthrough_layer(&passthrough.0, passthrough.1, purpose)?
|
||||
}))
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,15 @@
|
||||
use bevy::{prelude::*, render::extract_resource::ExtractResource};
|
||||
use bevy::{
|
||||
ecs::schedule::ScheduleLabel, prelude::*, render::extract_resource::ExtractResource,
|
||||
render::RenderApp,
|
||||
};
|
||||
|
||||
pub struct XrSessionPlugin;
|
||||
|
||||
impl Plugin for XrSessionPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.init_resource::<XrCreateSessionWhenAvailabe>();
|
||||
app.init_schedule(XrSessionCreated);
|
||||
app.init_schedule(XrSessionExiting);
|
||||
app.add_event::<CreateXrSession>()
|
||||
.add_event::<DestroyXrSession>()
|
||||
.add_event::<BeginXrSession>()
|
||||
@@ -14,7 +20,30 @@ impl Plugin for XrSessionPlugin {
|
||||
handle_session.run_if(resource_exists::<XrStatus>),
|
||||
);
|
||||
}
|
||||
fn finish(&self, app: &mut App) {
|
||||
// This is in finnish because we need the RenderPlugin to already be added.
|
||||
app.get_sub_app_mut(RenderApp)
|
||||
.unwrap()
|
||||
.init_schedule(XrSessionExiting);
|
||||
}
|
||||
}
|
||||
|
||||
/// Automatically starts session when it's available, gets set to false after the start event was
|
||||
/// sent
|
||||
#[derive(Clone, Copy, Resource, Deref, DerefMut)]
|
||||
pub struct XrCreateSessionWhenAvailabe(pub bool);
|
||||
|
||||
impl Default for XrCreateSessionWhenAvailabe {
|
||||
fn default() -> Self {
|
||||
XrCreateSessionWhenAvailabe(true)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(ScheduleLabel, Clone, Copy, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct XrSessionCreated;
|
||||
|
||||
#[derive(ScheduleLabel, Clone, Copy, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct XrSessionExiting;
|
||||
|
||||
#[derive(Event, Clone, Copy, Deref)]
|
||||
pub struct XrStatusChanged(pub XrStatus);
|
||||
@@ -43,14 +72,18 @@ pub fn handle_session(
|
||||
mut previous_status: Local<Option<XrStatus>>,
|
||||
mut create_session: EventWriter<CreateXrSession>,
|
||||
mut begin_session: EventWriter<BeginXrSession>,
|
||||
mut end_session: EventWriter<EndXrSession>,
|
||||
// mut end_session: EventWriter<EndXrSession>,
|
||||
mut destroy_session: EventWriter<DestroyXrSession>,
|
||||
mut should_start_session: ResMut<XrCreateSessionWhenAvailabe>,
|
||||
) {
|
||||
if *previous_status != Some(*current_status) {
|
||||
match *current_status {
|
||||
XrStatus::Unavailable => {}
|
||||
XrStatus::Available => {
|
||||
if **should_start_session {
|
||||
create_session.send_default();
|
||||
**should_start_session = false;
|
||||
}
|
||||
}
|
||||
XrStatus::Idle => {}
|
||||
XrStatus::Ready => {
|
||||
@@ -58,7 +91,7 @@ pub fn handle_session(
|
||||
}
|
||||
XrStatus::Running => {}
|
||||
XrStatus::Stopping => {
|
||||
end_session.send_default();
|
||||
// end_session.send_default();
|
||||
}
|
||||
XrStatus::Exiting => {
|
||||
destroy_session.send_default();
|
||||
|
||||
@@ -55,9 +55,8 @@
|
||||
//!
|
||||
use bevy::prelude::*;
|
||||
use bevy_openxr::{
|
||||
action_binding::OxrSuggestActionBinding,
|
||||
action_set_attaching::OxrAttachActionSet,
|
||||
resources::{OxrInstance, OxrSession},
|
||||
action_binding::OxrSuggestActionBinding, action_set_attaching::OxrAttachActionSet,
|
||||
resources::OxrInstance, session::OxrSession,
|
||||
};
|
||||
use openxr::{ActiveActionSet, Path, Vector2f};
|
||||
use std::borrow::Cow;
|
||||
@@ -65,7 +64,10 @@ use std::borrow::Cow;
|
||||
pub struct XRUtilsActionsPlugin;
|
||||
impl Plugin for XRUtilsActionsPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(Startup, create_openxr_events.in_set(XRUtilsActionSystemSet::CreateEvents));
|
||||
app.add_systems(
|
||||
Startup,
|
||||
create_openxr_events.in_set(XRUtilsActionSystemSet::CreateEvents),
|
||||
);
|
||||
app.add_systems(Update, sync_active_action_sets);
|
||||
app.add_systems(
|
||||
Update,
|
||||
|
||||
Reference in New Issue
Block a user