69
crates/bevy_openxr/examples/sessions.rs
Normal file
69
crates/bevy_openxr/examples/sessions.rs
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
//! A simple 3D scene with light shining over a cube sitting on a plane.
|
||||||
|
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use bevy_openxr::add_xr_plugins;
|
||||||
|
|
||||||
|
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)
|
||||||
|
.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>,
|
||||||
|
) {
|
||||||
|
if keys.just_pressed(KeyCode::KeyE) {
|
||||||
|
end.send_default();
|
||||||
|
}
|
||||||
|
if keys.just_pressed(KeyCode::KeyD) {
|
||||||
|
destroy.send_default();
|
||||||
|
}
|
||||||
|
if keys.just_pressed(KeyCode::KeyB) {
|
||||||
|
begin.send_default();
|
||||||
|
}
|
||||||
|
if keys.just_pressed(KeyCode::KeyC) {
|
||||||
|
create.send_default();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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()
|
||||||
|
});
|
||||||
|
// light
|
||||||
|
commands.spawn(PointLightBundle {
|
||||||
|
point_light: PointLight {
|
||||||
|
shadows_enabled: true,
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
transform: Transform::from_xyz(4.0, 8.0, 4.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::ecs::system::RunSystemOnce;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy::utils::HashMap;
|
use bevy::utils::HashMap;
|
||||||
use bevy_xr::session::{status_changed_to, XrStatus};
|
|
||||||
use openxr::sys::ActionSuggestedBinding;
|
use openxr::sys::ActionSuggestedBinding;
|
||||||
|
|
||||||
use crate::resources::OxrInstance;
|
use crate::{init::OxrPreUpdateSet, resources::OxrInstance, session::OxrSessionStatusEvent};
|
||||||
|
|
||||||
impl Plugin for OxrActionBindingPlugin {
|
impl Plugin for OxrActionBindingPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_schedule(Schedule::new(OxrSendActionBindings));
|
app.add_schedule(Schedule::new(OxrSendActionBindings));
|
||||||
app.add_event::<OxrSuggestActionBinding>();
|
app.add_event::<OxrSuggestActionBinding>();
|
||||||
app.add_systems(
|
app.add_systems(
|
||||||
Update,
|
PostUpdate,
|
||||||
run_action_binding_sugestion
|
run_action_binding_sugestion
|
||||||
.run_if(status_changed_to(XrStatus::Ready).and_then(run_once())),
|
.run_if(|mut session_state: EventReader<OxrSessionStatusEvent>| {
|
||||||
|
session_state
|
||||||
|
.read()
|
||||||
|
.any(|s| *s == OxrSessionStatusEvent::Created)
|
||||||
|
})
|
||||||
|
.after(OxrPreUpdateSet::HandleEvents),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::resources::OxrSession;
|
use crate::session::{OxrSession, OxrSessionStatusEvent};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy_xr::session::status_changed_to;
|
use bevy_xr::session::status_changed_to;
|
||||||
|
|
||||||
@@ -7,7 +7,11 @@ impl Plugin for OxrActionAttachingPlugin {
|
|||||||
app.add_event::<OxrAttachActionSet>();
|
app.add_event::<OxrAttachActionSet>();
|
||||||
app.add_systems(
|
app.add_systems(
|
||||||
PostUpdate,
|
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)
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,15 @@ use bevy::prelude::*;
|
|||||||
use bevy_xr::hands::{LeftHand, RightHand};
|
use bevy_xr::hands::{LeftHand, RightHand};
|
||||||
use bevy_xr::{
|
use bevy_xr::{
|
||||||
hands::{HandBone, HandBoneRadius},
|
hands::{HandBone, HandBoneRadius},
|
||||||
session::{session_running, status_changed_to, XrStatus},
|
session::{session_running, XrSessionCreated, XrSessionEnding},
|
||||||
};
|
};
|
||||||
use openxr::SpaceLocationFlags;
|
use openxr::SpaceLocationFlags;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
init::OxrTrackingRoot,
|
init::OxrTrackingRoot,
|
||||||
reference_space::{OxrPrimaryReferenceSpace, OxrReferenceSpace},
|
reference_space::{OxrPrimaryReferenceSpace, OxrReferenceSpace},
|
||||||
resources::{OxrSession, OxrTime},
|
resources::OxrTime,
|
||||||
|
session::OxrSession,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct HandTrackingPlugin {
|
pub struct HandTrackingPlugin {
|
||||||
@@ -27,14 +28,8 @@ impl Plugin for HandTrackingPlugin {
|
|||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_systems(PreUpdate, locate_hands.run_if(session_running));
|
app.add_systems(PreUpdate, locate_hands.run_if(session_running));
|
||||||
if self.default_hands {
|
if self.default_hands {
|
||||||
app.add_systems(
|
app.add_systems(XrSessionEnding, clean_up_default_hands);
|
||||||
PreUpdate,
|
app.add_systems(XrSessionCreated, spawn_default_hands);
|
||||||
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)),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use openxr::PassthroughCapabilityFlagsFB;
|
|||||||
|
|
||||||
use crate::layer_builder::PassthroughLayer;
|
use crate::layer_builder::PassthroughLayer;
|
||||||
use crate::resources::*;
|
use crate::resources::*;
|
||||||
|
use crate::session::OxrSession;
|
||||||
use crate::types::*;
|
use crate::types::*;
|
||||||
|
|
||||||
pub struct OxrPassthroughPlugin;
|
pub struct OxrPassthroughPlugin;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use bevy::ecs::system::RunSystemOnce;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy::render::extract_resource::ExtractResourcePlugin;
|
use bevy::render::extract_resource::ExtractResourcePlugin;
|
||||||
use bevy::render::renderer::RenderAdapter;
|
use bevy::render::renderer::RenderAdapter;
|
||||||
@@ -22,6 +23,7 @@ use bevy_xr::session::BeginXrSession;
|
|||||||
use bevy_xr::session::CreateXrSession;
|
use bevy_xr::session::CreateXrSession;
|
||||||
use bevy_xr::session::DestroyXrSession;
|
use bevy_xr::session::DestroyXrSession;
|
||||||
use bevy_xr::session::EndXrSession;
|
use bevy_xr::session::EndXrSession;
|
||||||
|
use bevy_xr::session::XrRenderSessionEnding;
|
||||||
use bevy_xr::session::XrSharedStatus;
|
use bevy_xr::session::XrSharedStatus;
|
||||||
use bevy_xr::session::XrStatus;
|
use bevy_xr::session::XrStatus;
|
||||||
use bevy_xr::session::XrStatusChanged;
|
use bevy_xr::session::XrStatusChanged;
|
||||||
@@ -30,6 +32,8 @@ use crate::error::OxrError;
|
|||||||
use crate::graphics::*;
|
use crate::graphics::*;
|
||||||
use crate::reference_space::OxrPrimaryReferenceSpace;
|
use crate::reference_space::OxrPrimaryReferenceSpace;
|
||||||
use crate::resources::*;
|
use crate::resources::*;
|
||||||
|
use crate::session::OxrSession;
|
||||||
|
use crate::session::OxrSessionStatusEvent;
|
||||||
use crate::types::*;
|
use crate::types::*;
|
||||||
|
|
||||||
pub fn session_started(started: Option<Res<OxrSessionStarted>>) -> bool {
|
pub fn session_started(started: Option<Res<OxrSessionStarted>>) -> bool {
|
||||||
@@ -514,6 +518,7 @@ pub fn poll_events(
|
|||||||
instance: Res<OxrInstance>,
|
instance: Res<OxrInstance>,
|
||||||
status: Res<XrSharedStatus>,
|
status: Res<XrSharedStatus>,
|
||||||
mut changed_event: EventWriter<XrStatusChanged>,
|
mut changed_event: EventWriter<XrStatusChanged>,
|
||||||
|
mut session_status_events: EventWriter<OxrSessionStatusEvent>,
|
||||||
) {
|
) {
|
||||||
let _span = info_span!("xr_poll_events");
|
let _span = info_span!("xr_poll_events");
|
||||||
let mut buffer = Default::default();
|
let mut buffer = Default::default();
|
||||||
@@ -531,13 +536,21 @@ pub fn poll_events(
|
|||||||
info!("entered XR state {:?}", state);
|
info!("entered XR state {:?}", state);
|
||||||
|
|
||||||
let new_status = match state {
|
let new_status = match state {
|
||||||
SessionState::IDLE => XrStatus::Idle,
|
SessionState::IDLE => {
|
||||||
|
if status.get() == XrStatus::Available {
|
||||||
|
session_status_events.send(OxrSessionStatusEvent::Created);
|
||||||
|
}
|
||||||
|
XrStatus::Idle
|
||||||
|
}
|
||||||
SessionState::READY => XrStatus::Ready,
|
SessionState::READY => XrStatus::Ready,
|
||||||
SessionState::SYNCHRONIZED | SessionState::VISIBLE | SessionState::FOCUSED => {
|
SessionState::SYNCHRONIZED | SessionState::VISIBLE | SessionState::FOCUSED => {
|
||||||
XrStatus::Running
|
XrStatus::Running
|
||||||
}
|
}
|
||||||
SessionState::STOPPING => XrStatus::Stopping,
|
SessionState::STOPPING => XrStatus::Stopping,
|
||||||
SessionState::EXITING | SessionState::LOSS_PENDING => XrStatus::Exiting,
|
SessionState::EXITING | SessionState::LOSS_PENDING => {
|
||||||
|
session_status_events.send(OxrSessionStatusEvent::AboutToBeDestroyed);
|
||||||
|
XrStatus::Exiting
|
||||||
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
changed_event.send(XrStatusChanged(new_status));
|
changed_event.send(XrStatusChanged(new_status));
|
||||||
@@ -555,19 +568,16 @@ pub fn reset_per_frame_resources(mut cleanup: ResMut<OxrCleanupSession>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn destroy_xr_session(mut commands: Commands) {
|
pub fn destroy_xr_session(mut commands: Commands) {
|
||||||
commands.remove_resource::<OxrSession>();
|
|
||||||
commands.remove_resource::<OxrFrameWaiter>();
|
commands.remove_resource::<OxrFrameWaiter>();
|
||||||
commands.remove_resource::<OxrSwapchainImages>();
|
commands.remove_resource::<OxrSwapchainImages>();
|
||||||
commands.remove_resource::<OxrGraphicsInfo>();
|
commands.remove_resource::<OxrGraphicsInfo>();
|
||||||
commands.remove_resource::<OxrPrimaryReferenceSpace>();
|
|
||||||
commands.insert_resource(OxrCleanupSession(true));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn destroy_xr_session_render(world: &mut World) {
|
pub fn destroy_xr_session_render(world: &mut World) {
|
||||||
world.remove_resource::<OxrSwapchain>();
|
world.remove_resource::<OxrSwapchain>();
|
||||||
world.remove_resource::<OxrFrameStream>();
|
world.remove_resource::<OxrFrameStream>();
|
||||||
world.remove_resource::<OxrPrimaryReferenceSpace>();
|
|
||||||
world.remove_resource::<OxrSwapchainImages>();
|
world.remove_resource::<OxrSwapchainImages>();
|
||||||
world.remove_resource::<OxrGraphicsInfo>();
|
world.remove_resource::<OxrGraphicsInfo>();
|
||||||
world.remove_resource::<OxrSession>();
|
world.run_schedule(XrRenderSessionEnding);
|
||||||
|
world.run_system_once(apply_deferred);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ use render::OxrRenderPlugin;
|
|||||||
use self::{
|
use self::{
|
||||||
features::{handtracking::HandTrackingPlugin, passthrough::OxrPassthroughPlugin},
|
features::{handtracking::HandTrackingPlugin, passthrough::OxrPassthroughPlugin},
|
||||||
reference_space::OxrReferenceSpacePlugin,
|
reference_space::OxrReferenceSpacePlugin,
|
||||||
|
session::OxrSessionPlugin,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod action_binding;
|
pub mod action_binding;
|
||||||
@@ -28,6 +29,7 @@ pub mod layer_builder;
|
|||||||
pub mod reference_space;
|
pub mod reference_space;
|
||||||
pub mod render;
|
pub mod render;
|
||||||
pub mod resources;
|
pub mod resources;
|
||||||
|
pub mod session;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
pub fn add_xr_plugins<G: PluginGroup>(plugins: G) -> PluginGroupBuilder {
|
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>()
|
.disable::<PipelinedRenderingPlugin>()
|
||||||
.add_before::<RenderPlugin, _>(XrSessionPlugin)
|
.add_before::<RenderPlugin, _>(XrSessionPlugin)
|
||||||
.add_before::<RenderPlugin, _>(OxrInitPlugin::default())
|
.add_before::<RenderPlugin, _>(OxrInitPlugin::default())
|
||||||
|
.add(OxrSessionPlugin)
|
||||||
.add(OxrReferenceSpacePlugin::default())
|
.add(OxrReferenceSpacePlugin::default())
|
||||||
.add(OxrRenderPlugin)
|
.add(OxrRenderPlugin)
|
||||||
.add(OxrPassthroughPlugin)
|
.add(OxrPassthroughPlugin)
|
||||||
@@ -46,6 +49,8 @@ pub fn add_xr_plugins<G: PluginGroup>(plugins: G) -> PluginGroupBuilder {
|
|||||||
.add(action_binding::OxrActionBindingPlugin)
|
.add(action_binding::OxrActionBindingPlugin)
|
||||||
.add(action_set_syncing::OxrActionSyncingPlugin)
|
.add(action_set_syncing::OxrActionSyncingPlugin)
|
||||||
// .add(XrActionPlugin)
|
// .add(XrActionPlugin)
|
||||||
|
// we should probably handle the exiting ourselfs so that we can correctly end the
|
||||||
|
// session and instance
|
||||||
.set(WindowPlugin {
|
.set(WindowPlugin {
|
||||||
primary_window: Some(Window {
|
primary_window: Some(Window {
|
||||||
transparent: true,
|
transparent: true,
|
||||||
|
|||||||
@@ -4,9 +4,11 @@ use bevy::{
|
|||||||
prelude::*,
|
prelude::*,
|
||||||
render::extract_resource::{ExtractResource, ExtractResourcePlugin},
|
render::extract_resource::{ExtractResource, ExtractResourcePlugin},
|
||||||
};
|
};
|
||||||
use bevy_xr::session::{status_changed_to, XrStatus};
|
use bevy_xr::session::{
|
||||||
|
status_changed_to, XrRenderSessionEnding, XrSessionCreated, XrSessionEnding, XrStatus,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{init::OxrPreUpdateSet, resources::OxrSession};
|
use crate::{init::OxrPreUpdateSet, session::OxrSession};
|
||||||
|
|
||||||
pub struct OxrReferenceSpacePlugin {
|
pub struct OxrReferenceSpacePlugin {
|
||||||
pub default_primary_ref_space: openxr::ReferenceSpaceType,
|
pub default_primary_ref_space: openxr::ReferenceSpaceType,
|
||||||
@@ -20,8 +22,7 @@ impl Default for OxrReferenceSpacePlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Resource)]
|
#[derive(Resource)]
|
||||||
struct OxrPrimaryReferenceSpaceType(openxr::ReferenceSpaceType);
|
struct OxrDefaultPrimaryReferenceSpaceType(openxr::ReferenceSpaceType);
|
||||||
// TODO: this will keep the session alive so we need to remove this in the render world too
|
|
||||||
/// The Default Reference space used for locating things
|
/// The Default Reference space used for locating things
|
||||||
#[derive(Resource, Deref, ExtractResource, Clone)]
|
#[derive(Resource, Deref, ExtractResource, Clone)]
|
||||||
pub struct OxrPrimaryReferenceSpace(pub Arc<openxr::Space>);
|
pub struct OxrPrimaryReferenceSpace(pub Arc<openxr::Space>);
|
||||||
@@ -32,20 +33,24 @@ pub struct OxrReferenceSpace(pub openxr::Space);
|
|||||||
|
|
||||||
impl Plugin for OxrReferenceSpacePlugin {
|
impl Plugin for OxrReferenceSpacePlugin {
|
||||||
fn build(&self, app: &mut App) {
|
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_plugins(ExtractResourcePlugin::<OxrPrimaryReferenceSpace>::default());
|
||||||
app.add_systems(
|
app.add_systems(XrSessionCreated, set_primary_ref_space);
|
||||||
PreUpdate,
|
app.add_systems(XrSessionEnding, cleanup);
|
||||||
set_primary_ref_space
|
app.add_systems(XrRenderSessionEnding, cleanup);
|
||||||
.run_if(status_changed_to(XrStatus::Ready))
|
}
|
||||||
.in_set(OxrPreUpdateSet::UpdateCriticalComponents),
|
}
|
||||||
);
|
|
||||||
|
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(
|
fn set_primary_ref_space(
|
||||||
session: Res<OxrSession>,
|
session: Res<OxrSession>,
|
||||||
space_type: Res<OxrPrimaryReferenceSpaceType>,
|
space_type: Res<OxrDefaultPrimaryReferenceSpaceType>,
|
||||||
mut cmds: Commands,
|
mut cmds: Commands,
|
||||||
) {
|
) {
|
||||||
match session.create_reference_space(space_type.0, openxr::Posef::IDENTITY) {
|
match session.create_reference_space(space_type.0, openxr::Posef::IDENTITY) {
|
||||||
|
|||||||
@@ -16,9 +16,10 @@ use bevy_xr::{
|
|||||||
};
|
};
|
||||||
use openxr::ViewStateFlags;
|
use openxr::ViewStateFlags;
|
||||||
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
init::{session_started, OxrPreUpdateSet, OxrTrackingRoot},
|
init::{session_started, OxrPreUpdateSet, OxrTrackingRoot},
|
||||||
layer_builder::ProjectionLayer,
|
layer_builder::ProjectionLayer, session::OxrSession,
|
||||||
};
|
};
|
||||||
use crate::{reference_space::OxrPrimaryReferenceSpace, resources::*};
|
use crate::{reference_space::OxrPrimaryReferenceSpace, resources::*};
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use openxr::AnyGraphics;
|
|||||||
use crate::error::OxrError;
|
use crate::error::OxrError;
|
||||||
use crate::graphics::*;
|
use crate::graphics::*;
|
||||||
use crate::layer_builder::{CompositionLayer, LayerProvider};
|
use crate::layer_builder::{CompositionLayer, LayerProvider};
|
||||||
|
use crate::session::OxrSession;
|
||||||
use crate::types::*;
|
use crate::types::*;
|
||||||
|
|
||||||
/// Wrapper around an [`Entry`](openxr::Entry) with some methods overridden to use bevy types.
|
/// Wrapper around an [`Entry`](openxr::Entry) with some methods overridden to use bevy types.
|
||||||
@@ -147,95 +148,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]
|
/// Graphics agnostic wrapper around [openxr::FrameStream]
|
||||||
#[derive(Resource)]
|
#[derive(Resource)]
|
||||||
@@ -397,7 +309,7 @@ pub struct OxrSystemId(pub openxr::SystemId);
|
|||||||
pub struct OxrPassthrough(
|
pub struct OxrPassthrough(
|
||||||
#[deref] pub openxr::Passthrough,
|
#[deref] pub openxr::Passthrough,
|
||||||
/// The flags are stored here so that they don't need to be passed in again when creating an [`OxrPassthroughLayer`].
|
/// 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 {
|
impl OxrPassthrough {
|
||||||
|
|||||||
144
crates/bevy_openxr/src/openxr/session.rs
Normal file
144
crates/bevy_openxr/src/openxr/session.rs
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
use crate::init::OxrPreUpdateSet;
|
||||||
|
use crate::resources::{OxrCleanupSession, OxrPassthrough, OxrPassthroughLayer, OxrSwapchain};
|
||||||
|
use crate::types::{Result, SwapchainCreateInfo};
|
||||||
|
use bevy::ecs::system::{RunSystemOnce, SystemState};
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use bevy_xr::session::{XrRenderSessionEnding, XrSessionCreated, XrSessionEnding};
|
||||||
|
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(
|
||||||
|
PreUpdate,
|
||||||
|
run_session_status_schedules.in_set(OxrPreUpdateSet::HandleEvents),
|
||||||
|
);
|
||||||
|
app.add_systems(XrSessionEnding, clean_session);
|
||||||
|
app.add_systems(XrRenderSessionEnding, |mut cmds: Commands| {
|
||||||
|
cmds.remove_resource::<OxrSession>()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clean_session(mut cmds: Commands) {
|
||||||
|
cmds.remove_resource::<OxrSession>();
|
||||||
|
cmds.insert_resource(OxrCleanupSession(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_session_status_schedules(world: &mut World) {
|
||||||
|
let mut state = SystemState::<EventReader<OxrSessionStatusEvent>>::new(world);
|
||||||
|
let mut e = state.get_mut(world);
|
||||||
|
let events = e.read().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(XrSessionEnding);
|
||||||
|
world.run_system_once(apply_deferred);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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,11 +1,13 @@
|
|||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
use bevy::prelude::*;
|
use bevy::{ecs::schedule::ScheduleLabel, prelude::*};
|
||||||
|
|
||||||
pub struct XrSessionPlugin;
|
pub struct XrSessionPlugin;
|
||||||
|
|
||||||
impl Plugin for XrSessionPlugin {
|
impl Plugin for XrSessionPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
|
app.init_schedule(XrSessionCreated);
|
||||||
|
app.init_schedule(XrSessionEnding);
|
||||||
app.add_event::<CreateXrSession>()
|
app.add_event::<CreateXrSession>()
|
||||||
.add_event::<DestroyXrSession>()
|
.add_event::<DestroyXrSession>()
|
||||||
.add_event::<BeginXrSession>()
|
.add_event::<BeginXrSession>()
|
||||||
@@ -16,8 +18,21 @@ impl Plugin for XrSessionPlugin {
|
|||||||
handle_session.run_if(resource_exists::<XrSharedStatus>),
|
handle_session.run_if(resource_exists::<XrSharedStatus>),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
fn finish(&self, app: &mut App) {
|
||||||
|
// This is in finnish because we need the RenderPlugin to already be added.
|
||||||
|
app.init_schedule(XrRenderSessionEnding);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(ScheduleLabel, Clone, Copy, PartialEq, Eq, Debug, Hash)]
|
||||||
|
pub struct XrSessionCreated;
|
||||||
|
|
||||||
|
#[derive(ScheduleLabel, Clone, Copy, PartialEq, Eq, Debug, Hash)]
|
||||||
|
pub struct XrSessionEnding;
|
||||||
|
|
||||||
|
#[derive(ScheduleLabel, Clone, Copy, PartialEq, Eq, Debug, Hash)]
|
||||||
|
pub struct XrRenderSessionEnding;
|
||||||
|
|
||||||
#[derive(Event, Clone, Copy, Deref)]
|
#[derive(Event, Clone, Copy, Deref)]
|
||||||
pub struct XrStatusChanged(pub XrStatus);
|
pub struct XrStatusChanged(pub XrStatus);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user