Merge pull request #122 from Schmarni-Dev/spatial_existence

RFC: New Space Api
This commit is contained in:
ForTehLose
2024-06-28 13:35:33 -04:00
committed by GitHub
15 changed files with 961 additions and 64 deletions

View File

@@ -18,14 +18,8 @@ fn main() {
create_action_entities.before(XRUtilsActionSystemSet::CreateEvents), create_action_entities.before(XRUtilsActionSystemSet::CreateEvents),
) )
.add_plugins(XRUtilsActionsPlugin) .add_plugins(XRUtilsActionsPlugin)
.add_systems( .add_systems(Update, read_action_with_marker_component)
Update, .add_systems(Update, handle_flight_input)
read_action_with_marker_component.after(XRUtilsActionSystemSet::SyncActionStates),
)
.add_systems(
Update,
handle_flight_input.after(XRUtilsActionSystemSet::SyncActionStates),
)
// Realtime lighting is expensive, use ambient light instead // Realtime lighting is expensive, use ambient light instead
.insert_resource(AmbientLight { .insert_resource(AmbientLight {
color: Default::default(), color: Default::default(),
@@ -89,16 +83,22 @@ fn create_action_entities(mut commands: Commands) {
.id(); .id();
//create a binding //create a binding
let binding = commands let binding_index = commands
.spawn(XRUtilsBinding { .spawn(XRUtilsBinding {
profile: "/interaction_profiles/valve/index_controller".into(), profile: "/interaction_profiles/valve/index_controller".into(),
binding: "/user/hand/right/input/thumbstick".into(), binding: "/user/hand/right/input/thumbstick".into(),
}) })
.id(); .id();
let binding_touch = commands
.spawn(XRUtilsBinding {
profile: "/interaction_profiles/oculus/touch_controller".into(),
binding: "/user/hand/right/input/thumbstick".into(),
})
.id();
//add action to set, this isnt the best //add action to set, this isnt the best
//TODO look into a better system //TODO look into a better system
commands.entity(action).add_child(binding); commands.entity(action).add_child(binding_index);
commands.entity(action).add_child(binding_touch);
commands.entity(set).add_child(action); commands.entity(set).add_child(action);
} }

View File

@@ -0,0 +1,160 @@
use std::ops::Deref;
use bevy::prelude::*;
use bevy_openxr::{
action_binding::{OxrSendActionBindings, OxrSuggestActionBinding},
action_set_attaching::OxrAttachActionSet,
action_set_syncing::{OxrActionSetSyncSet, OxrSyncActionSet},
add_xr_plugins,
init::OxrTrackingRoot,
resources::OxrInstance,
session::OxrSession,
spaces::OxrSpaceExt,
};
use bevy_xr::{
session::{session_available, XrSessionCreated},
spaces::XrSpace,
types::XrPose,
};
use openxr::Posef;
fn main() {
let mut app = App::new();
app.add_plugins(add_xr_plugins(DefaultPlugins));
app.add_systems(XrSessionCreated, spawn_hands);
app.add_systems(XrSessionCreated, attach_set);
app.add_systems(PreUpdate, sync_actions.before(OxrActionSetSyncSet));
app.add_systems(OxrSendActionBindings, suggest_action_bindings);
app.add_systems(Startup, create_actions.run_if(session_available));
app.add_systems(Startup, setup);
app.run();
}
fn attach_set(actions: Res<ControllerActions>, mut attach: EventWriter<OxrAttachActionSet>) {
attach.send(OxrAttachActionSet(actions.set.clone()));
}
#[derive(Resource)]
struct ControllerActions {
set: openxr::ActionSet,
left: openxr::Action<Posef>,
right: openxr::Action<Posef>,
}
fn sync_actions(actions: Res<ControllerActions>, mut sync: EventWriter<OxrSyncActionSet>) {
sync.send(OxrSyncActionSet(actions.set.clone()));
}
/// 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()
});
}
fn suggest_action_bindings(
actions: Res<ControllerActions>,
mut bindings: EventWriter<OxrSuggestActionBinding>,
) {
bindings.send(OxrSuggestActionBinding {
action: actions.left.as_raw(),
interaction_profile: "/interaction_profiles/oculus/touch_controller".into(),
bindings: vec!["/user/hand/left/input/grip/pose".into()],
});
bindings.send(OxrSuggestActionBinding {
action: actions.right.as_raw(),
interaction_profile: "/interaction_profiles/oculus/touch_controller".into(),
bindings: vec!["/user/hand/right/input/grip/pose".into()],
});
}
fn create_actions(instance: Res<OxrInstance>, mut cmds: Commands) {
let set = instance.create_action_set("hands", "Hands", 0).unwrap();
let left = set
.create_action("left_pose", "Left Hand Grip Pose", &[])
.unwrap();
let right = set
.create_action("right_pose", "Right Hand Grip Pose", &[])
.unwrap();
cmds.insert_resource(ControllerActions { set, left, right })
}
fn spawn_hands(
actions: Res<ControllerActions>,
mut cmds: Commands,
root: Query<Entity, With<OxrTrackingRoot>>,
session: Res<OxrSession>,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
// This is a demonstation of how to integrate with the openxr crate, the right space is the
// recommended way
let left_space = XrSpace::from_openxr_space(
actions
.left
.create_space(
session.deref().deref().clone(),
openxr::Path::NULL,
Posef::IDENTITY,
)
.unwrap(),
);
let right_space = session
.create_action_space(&actions.right, openxr::Path::NULL, XrPose::IDENTITY)
.unwrap();
let left = cmds
.spawn((
PbrBundle {
mesh: meshes.add(Cuboid::new(0.1, 0.1, 0.05)),
material: materials.add(Color::rgb_u8(124, 144, 255)),
transform: Transform::from_xyz(0.0, 0.5, 0.0),
..default()
},
left_space,
Controller,
))
.id();
let right = cmds
.spawn((
PbrBundle {
mesh: meshes.add(Cuboid::new(0.1, 0.1, 0.05)),
material: materials.add(Color::rgb_u8(124, 144, 255)),
transform: Transform::from_xyz(0.0, 0.5, 0.0),
..default()
},
right_space,
Controller,
))
.id();
cmds.entity(root.single()).push_children(&[left, right]);
}
#[derive(Component)]
struct Controller;

View File

@@ -1,4 +1,7 @@
use crate::{action_binding::run_action_binding_sugestion, session::{OxrSession, OxrSessionStatusEvent}}; use crate::{
action_binding::run_action_binding_sugestion,
session::{OxrSession, OxrSessionStatusEvent},
};
use bevy::prelude::*; use bevy::prelude::*;
impl Plugin for OxrActionAttachingPlugin { impl Plugin for OxrActionAttachingPlugin {

View File

@@ -1,5 +1,6 @@
use bevy::prelude::*; use bevy::prelude::*;
use bevy_xr::hands::{LeftHand, RightHand}; use bevy_xr::hands::{LeftHand, RightHand};
use bevy_xr::spaces::{XrPrimaryReferenceSpace, XrReferenceSpace};
use bevy_xr::{ use bevy_xr::{
hands::{HandBone, HandBoneRadius}, hands::{HandBone, HandBoneRadius},
session::{session_running, XrSessionCreated, XrSessionExiting}, session::{session_running, XrSessionCreated, XrSessionExiting},
@@ -9,7 +10,6 @@ use openxr::SpaceLocationFlags;
use crate::resources::Pipelined; use crate::resources::Pipelined;
use crate::{ use crate::{
init::OxrTrackingRoot, init::OxrTrackingRoot,
reference_space::{OxrPrimaryReferenceSpace, OxrReferenceSpace},
resources::OxrFrameState, resources::OxrFrameState,
session::OxrSession, session::OxrSession,
}; };
@@ -130,21 +130,23 @@ pub struct OxrHandBoneEntities(pub [Entity; 26]);
pub struct OxrHandTracker(pub openxr::HandTracker); pub struct OxrHandTracker(pub openxr::HandTracker);
fn locate_hands( fn locate_hands(
default_ref_space: Res<OxrPrimaryReferenceSpace>, default_ref_space: Res<XrPrimaryReferenceSpace>,
frame_state: Res<OxrFrameState>, frame_state: Res<OxrFrameState>,
tracker_query: Query<( tracker_query: Query<(
&OxrHandTracker, &OxrHandTracker,
Option<&OxrReferenceSpace>, Option<&XrReferenceSpace>,
&OxrHandBoneEntities, &OxrHandBoneEntities,
)>, )>,
session: Res<OxrSession>,
mut bone_query: Query<(&HandBone, &mut HandBoneRadius, &mut Transform)>, mut bone_query: Query<(&HandBone, &mut HandBoneRadius, &mut Transform)>,
pipelined: Option<Res<Pipelined>>, pipelined: Option<Res<Pipelined>>,
) { ) {
for (tracker, ref_space, hand_entities) in &tracker_query { for (tracker, ref_space, hand_entities) in &tracker_query {
let ref_space = ref_space.map(|v| &v.0).unwrap_or(&default_ref_space.0); let ref_space = ref_space.map(|v| &v.0).unwrap_or(&default_ref_space.0);
// relate_hand_joints also provides velocities // relate_hand_joints also provides velocities
let joints = match ref_space.locate_hand_joints( let joints = match session.locate_hand_joints(
tracker, tracker,
ref_space,
if pipelined.is_some() { if pipelined.is_some() {
openxr::Time::from_nanos( openxr::Time::from_nanos(
frame_state.predicted_display_time.as_nanos() frame_state.predicted_display_time.as_nanos()

View File

@@ -1,4 +1,5 @@
use bevy::prelude::*; use bevy::prelude::*;
use bevy_xr::types::XrPose;
pub trait ToPosef { pub trait ToPosef {
fn to_posef(&self) -> openxr::Posef; fn to_posef(&self) -> openxr::Posef;
@@ -6,6 +7,9 @@ pub trait ToPosef {
pub trait ToTransform { pub trait ToTransform {
fn to_transform(&self) -> Transform; fn to_transform(&self) -> Transform;
} }
pub trait ToXrPose {
fn to_xr_pose(&self) -> XrPose;
}
pub trait ToQuaternionf { pub trait ToQuaternionf {
fn to_quaternionf(&self) -> openxr::Quaternionf; fn to_quaternionf(&self) -> openxr::Quaternionf;
} }
@@ -38,6 +42,22 @@ impl ToTransform for openxr::Posef {
.with_rotation(self.orientation.to_quat()) .with_rotation(self.orientation.to_quat())
} }
} }
impl ToXrPose for openxr::Posef {
fn to_xr_pose(&self) -> XrPose {
XrPose {
translation: self.position.to_vec3(),
rotation: self.orientation.to_quat(),
}
}
}
impl ToPosef for XrPose {
fn to_posef(&self) -> openxr::Posef {
openxr::Posef {
orientation: self.rotation.to_quaternionf(),
position: self.translation.to_vector3f(),
}
}
}
impl ToQuaternionf for Quat { impl ToQuaternionf for Quat {
fn to_quaternionf(&self) -> openxr::Quaternionf { fn to_quaternionf(&self) -> openxr::Quaternionf {
@@ -51,7 +71,14 @@ impl ToQuaternionf for Quat {
} }
impl ToQuat for openxr::Quaternionf { impl ToQuat for openxr::Quaternionf {
fn to_quat(&self) -> Quat { fn to_quat(&self) -> Quat {
Quat::from_xyzw(self.x, self.y, self.z, self.w) let mut quat = Quat::from_xyzw(self.x, self.y, self.z, self.w);
if quat.length() == 0.0 {
quat = Quat::IDENTITY;
}
if !quat.is_normalized() {
quat = quat.normalize();
}
quat
} }
} }
impl ToVector3f for Vec3 { impl ToVector3f for Vec3 {

View File

@@ -1,11 +1,12 @@
use std::mem; use std::mem;
use bevy::ecs::world::World; use bevy::ecs::world::World;
use openxr::{sys, CompositionLayerFlags, Fovf, Posef, Rect2Di, Space}; use bevy_xr::spaces::{XrPrimaryReferenceSpace, XrSpace};
use openxr::{sys, CompositionLayerFlags, Fovf, Posef, Rect2Di};
use crate::graphics::graphics_match; use crate::graphics::graphics_match;
use crate::reference_space::OxrPrimaryReferenceSpace;
use crate::resources::*; use crate::resources::*;
use crate::spaces::OxrSpaceExt as _;
pub trait LayerProvider { pub trait LayerProvider {
fn get<'a>(&'a self, world: &'a World) -> Option<Box<dyn CompositionLayer + '_>>; fn get<'a>(&'a self, world: &'a World) -> Option<Box<dyn CompositionLayer + '_>>;
@@ -17,7 +18,7 @@ pub struct PassthroughLayer;
impl LayerProvider for ProjectionLayer { impl LayerProvider for ProjectionLayer {
fn get<'a>(&self, world: &'a World) -> Option<Box<dyn CompositionLayer<'a> + 'a>> { fn get<'a>(&self, world: &'a World) -> Option<Box<dyn CompositionLayer<'a> + 'a>> {
let stage = world.get_resource::<OxrPrimaryReferenceSpace>()?; let stage = world.get_resource::<XrPrimaryReferenceSpace>()?;
let openxr_views = world.get_resource::<OxrViews>()?; let openxr_views = world.get_resource::<OxrViews>()?;
let swapchain = world.get_resource::<OxrSwapchain>()?; let swapchain = world.get_resource::<OxrSwapchain>()?;
let graphics_info = world.get_resource::<OxrGraphicsInfo>()?; let graphics_info = world.get_resource::<OxrGraphicsInfo>()?;
@@ -205,8 +206,8 @@ impl<'a> CompositionLayerProjection<'a> {
self self
} }
#[inline] #[inline]
pub fn space(mut self, value: &'a Space) -> Self { pub fn space(mut self, value: &XrSpace) -> Self {
self.inner.space = value.as_raw(); self.inner.space = value.as_raw_openxr_space();
self self
} }
#[inline] #[inline]

View File

@@ -32,6 +32,7 @@ pub mod render;
pub mod resources; pub mod resources;
pub mod session; pub mod session;
pub mod types; pub mod types;
pub mod spaces;
pub fn add_xr_plugins<G: PluginGroup>(plugins: G) -> PluginGroupBuilder { pub fn add_xr_plugins<G: PluginGroup>(plugins: G) -> PluginGroupBuilder {
plugins plugins
@@ -50,6 +51,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(features::overlay::OxrOverlayPlugin) .add(features::overlay::OxrOverlayPlugin)
.add(spaces::OxrSpatialPlugin)
.add(spaces::OxrSpacePatchingPlugin)
// .add(XrActionPlugin) // .add(XrActionPlugin)
// we should probably handle the exiting ourselfs so that we can correctly end the // we should probably handle the exiting ourselfs so that we can correctly end the
// session and instance // session and instance

View File

@@ -5,11 +5,14 @@ use bevy::{
render::{ render::{
extract_resource::{ExtractResource, ExtractResourcePlugin}, extract_resource::{ExtractResource, ExtractResourcePlugin},
RenderApp, RenderApp,
}, }, utils::HashSet,
};
use bevy_xr::{
session::{XrSessionCreated, XrSessionExiting},
spaces::{XrDestroySpace, XrPrimaryReferenceSpace, XrReferenceSpace, XrSpace},
}; };
use bevy_xr::session::{XrSessionCreated, XrSessionExiting};
use crate::session::OxrSession; use crate::{resources::OxrInstance, session::OxrSession};
pub struct OxrReferenceSpacePlugin { pub struct OxrReferenceSpacePlugin {
pub default_primary_ref_space: openxr::ReferenceSpaceType, pub default_primary_ref_space: openxr::ReferenceSpaceType,
@@ -25,19 +28,19 @@ impl Default for OxrReferenceSpacePlugin {
#[derive(Resource)] #[derive(Resource)]
struct OxrDefaultPrimaryReferenceSpaceType(openxr::ReferenceSpaceType); struct OxrDefaultPrimaryReferenceSpaceType(openxr::ReferenceSpaceType);
/// The Default Reference space used for locating things /// The Default Reference space used for locating things
#[derive(Resource, Deref, ExtractResource, Clone)] // #[derive(Resource, Deref, ExtrctResource, Clone)]
pub struct OxrPrimaryReferenceSpace(pub Arc<openxr::Space>); // pub struct OxrPrimaryReferenceSpace(pub Arc<openxr::Space>);
/// The Reference space used for locating spaces on this entity /// The Reference space used for locating spaces on this entity
#[derive(Component)] // #[derive(Component)]
pub struct OxrReferenceSpace(pub openxr::Space); // 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(OxrDefaultPrimaryReferenceSpaceType( app.insert_resource(OxrDefaultPrimaryReferenceSpaceType(
self.default_primary_ref_space, self.default_primary_ref_space,
)); ));
app.add_plugins(ExtractResourcePlugin::<OxrPrimaryReferenceSpace>::default()); app.add_plugins(ExtractResourcePlugin::<XrPrimaryReferenceSpace>::default());
app.add_systems(XrSessionCreated, set_primary_ref_space); app.add_systems(XrSessionCreated, set_primary_ref_space);
app.add_systems(XrSessionExiting, cleanup); app.add_systems(XrSessionExiting, cleanup);
app.sub_app_mut(RenderApp) app.sub_app_mut(RenderApp)
@@ -45,10 +48,23 @@ impl Plugin for OxrReferenceSpacePlugin {
} }
} }
fn cleanup(mut cmds: Commands, query: Query<Entity, With<OxrReferenceSpace>>) { fn cleanup(
cmds.remove_resource::<OxrPrimaryReferenceSpace>(); query: Query<(Entity, &XrReferenceSpace)>,
for e in &query { mut cmds: Commands,
cmds.entity(e).remove::<OxrReferenceSpace>(); instance: Res<OxrInstance>,
ref_space: Option<Res<XrPrimaryReferenceSpace>>,
) {
let mut to_destroy = HashSet::<XrSpace>::new();
if let Some(space) = ref_space {
to_destroy.insert(***space);
}
cmds.remove_resource::<XrPrimaryReferenceSpace>();
for (e, space) in &query {
cmds.entity(e).remove::<XrReferenceSpace>();
to_destroy.insert(**space);
}
for space in to_destroy.into_iter() {
let _ = instance.destroy_space(space);
} }
} }
@@ -57,9 +73,9 @@ fn set_primary_ref_space(
space_type: Res<OxrDefaultPrimaryReferenceSpaceType>, 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, Transform::IDENTITY) {
Ok(space) => { Ok(space) => {
cmds.insert_resource(OxrPrimaryReferenceSpace(Arc::new(space))); cmds.insert_resource(XrPrimaryReferenceSpace(space));
} }
Err(openxr::sys::Result::ERROR_EXTENSION_NOT_PRESENT) => { Err(openxr::sys::Result::ERROR_EXTENSION_NOT_PRESENT) => {
error!("Required Extension for Reference Space not loaded"); error!("Required Extension for Reference Space not loaded");

View File

@@ -15,15 +15,17 @@ use bevy::{
use bevy_xr::{ use bevy_xr::{
camera::{XrCamera, XrCameraBundle, XrProjection}, camera::{XrCamera, XrCameraBundle, XrProjection},
session::{session_running, XrSessionExiting}, session::{session_running, XrSessionExiting},
spaces::XrPrimaryReferenceSpace,
}; };
use openxr::ViewStateFlags; use openxr::ViewStateFlags;
use crate::resources::*;
use crate::spaces::OxrSpaceExt as _;
use crate::{ use crate::{
init::{session_started, OxrHandleEvents, OxrLast, OxrTrackingRoot}, init::{session_started, OxrHandleEvents, OxrLast, OxrTrackingRoot},
layer_builder::ProjectionLayer, layer_builder::ProjectionLayer,
session::OxrSession, session::OxrSession,
}; };
use crate::{reference_space::OxrPrimaryReferenceSpace, resources::*};
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, SystemSet)] #[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, SystemSet)]
pub struct OxrRenderBegin; pub struct OxrRenderBegin;
@@ -273,7 +275,7 @@ pub fn update_cameras(
pub fn locate_views( pub fn locate_views(
session: Res<OxrSession>, session: Res<OxrSession>,
ref_space: Res<OxrPrimaryReferenceSpace>, ref_space: Res<XrPrimaryReferenceSpace>,
frame_state: Res<OxrFrameState>, frame_state: Res<OxrFrameState>,
mut openxr_views: ResMut<OxrViews>, mut openxr_views: ResMut<OxrViews>,
pipelined: Option<Res<Pipelined>>, pipelined: Option<Res<Pipelined>>,

View File

@@ -6,11 +6,14 @@ use crate::resources::{
OxrCleanupSession, OxrPassthrough, OxrPassthroughLayer, OxrSessionStarted, OxrSwapchain, OxrCleanupSession, OxrPassthrough, OxrPassthroughLayer, OxrSessionStarted, OxrSwapchain,
}; };
use crate::types::{Result, SwapchainCreateInfo}; use crate::types::{Result, SwapchainCreateInfo};
use bevy::app::AppExit;
use bevy::ecs::event::ManualEventReader; use bevy::ecs::event::ManualEventReader;
use bevy::ecs::system::RunSystemOnce; use bevy::ecs::system::RunSystemOnce;
use bevy::prelude::*; use bevy::prelude::*;
use bevy::render::RenderApp; use bevy::render::RenderApp;
use bevy_xr::session::{status_changed_to, XrSessionCreated, XrSessionExiting, XrStatus}; use bevy_xr::session::{
session_running, status_changed_to, XrSessionCreated, XrSessionExiting, XrStatus,
};
use openxr::AnyGraphics; use openxr::AnyGraphics;
use crate::graphics::{graphics_match, GraphicsExt, GraphicsType, GraphicsWrap}; use crate::graphics::{graphics_match, GraphicsExt, GraphicsType, GraphicsWrap};
@@ -28,6 +31,12 @@ impl Plugin for OxrSessionPlugin {
app.init_non_send_resource::<OxrSessionCreateNextChain>(); app.init_non_send_resource::<OxrSessionCreateNextChain>();
app.add_event::<OxrSessionStatusEvent>(); app.add_event::<OxrSessionStatusEvent>();
app.add_systems(OxrLast, run_session_status_schedules.after(OxrHandleEvents)); app.add_systems(OxrLast, run_session_status_schedules.after(OxrHandleEvents));
app.add_systems(
OxrLast,
exits_session_on_app_exit
.before(OxrHandleEvents)
.run_if(on_event::<AppExit>().and_then(session_running)),
);
app.add_systems(XrSessionExiting, clean_session); app.add_systems(XrSessionExiting, clean_session);
app.sub_app_mut(RenderApp) app.sub_app_mut(RenderApp)
.add_systems(XrSessionExiting, |mut cmds: Commands| { .add_systems(XrSessionExiting, |mut cmds: Commands| {
@@ -39,6 +48,9 @@ impl Plugin for OxrSessionPlugin {
); );
} }
} }
fn exits_session_on_app_exit(session: Res<OxrSession>) {
session.request_exit().unwrap()
}
fn handle_stopping_state(session: Res<OxrSession>, mut session_started: ResMut<OxrSessionStarted>) { fn handle_stopping_state(session: Res<OxrSession>, mut session_started: ResMut<OxrSessionStarted>) {
session.end().expect("Failed to end session"); session.end().expect("Failed to end session");

View File

@@ -0,0 +1,599 @@
use std::{mem::MaybeUninit, ptr, sync::Mutex};
use bevy::{prelude::*, utils::hashbrown::HashSet};
use bevy_xr::{
session::{session_available, session_running, XrSessionExiting},
spaces::{XrDestroySpace, XrPrimaryReferenceSpace, XrReferenceSpace, XrSpace, XrSpatialOffset},
types::XrPose,
};
use openxr::{
sys, HandJointLocation, HandJointLocations, HandJointVelocities, HandJointVelocity,
ReferenceSpaceType, SpaceLocationFlags, HAND_JOINT_COUNT,
};
use crate::{
helper_traits::{ToPosef, ToQuat, ToVec3},
init::{OxrHandleEvents, OxrLast},
resources::{OxrFrameState, OxrInstance, Pipelined},
session::OxrSession,
};
#[derive(SystemSet, Hash, Debug, Clone, Copy, PartialEq, Eq)]
pub struct OxrSpaceSyncSet;
/// VERY IMPORTENT!! only disable when you know what you are doing
pub struct OxrSpacePatchingPlugin;
impl Plugin for OxrSpacePatchingPlugin {
fn build(&self, app: &mut App) {
app.add_systems(Startup, patch_destroy_space.run_if(session_available));
}
}
pub struct OxrSpatialPlugin;
impl Plugin for OxrSpatialPlugin {
fn build(&self, app: &mut App) {
app.add_event::<XrDestroySpace>();
app.add_systems(OxrLast, destroy_space_event.before(OxrHandleEvents));
app.add_systems(XrSessionExiting, destroy_space_components);
app.add_systems(
PreUpdate,
update_space_transforms
.in_set(OxrSpaceSyncSet)
.run_if(session_running),
);
}
}
fn destroy_space_components(
query: Query<(Entity, &XrSpace)>,
mut cmds: Commands,
mut sender: EventWriter<XrDestroySpace>,
) {
let mut to_destroy = HashSet::<XrSpace>::new();
for (e, space) in &query {
to_destroy.insert(*space);
cmds.entity(e).remove::<XrSpace>();
}
for space in to_destroy.into_iter() {
sender.send(XrDestroySpace(space));
}
}
fn destroy_space_event(instance: Res<OxrInstance>, mut events: EventReader<XrDestroySpace>) {
for space in events.read() {
match instance.destroy_space(space.0) {
Ok(_) => (),
Err(err) => warn!("error while destroying space: {}", err),
}
}
}
pub static OXR_DO_NOT_CALL_DESTOY_SPACE_FOR_SPACES: Mutex<Option<HashSet<u64>>> = Mutex::new(None);
pub static OXR_ORIGINAL_DESTOY_SPACE: Mutex<Option<openxr::sys::pfn::DestroySpace>> =
Mutex::new(None);
fn patch_destroy_space(instance: ResMut<OxrInstance>) {
OXR_DO_NOT_CALL_DESTOY_SPACE_FOR_SPACES
.lock()
.unwrap()
.replace(HashSet::new());
let raw_instance_ptr = instance.fp() as *const _ as *mut openxr::raw::Instance;
unsafe {
OXR_ORIGINAL_DESTOY_SPACE
.lock()
.unwrap()
.replace((*raw_instance_ptr).destroy_space);
(*raw_instance_ptr).destroy_space = patched_destroy_space;
}
}
unsafe extern "system" fn patched_destroy_space(space: openxr::sys::Space) -> openxr::sys::Result {
if !OXR_DO_NOT_CALL_DESTOY_SPACE_FOR_SPACES
.lock()
.unwrap()
.as_ref()
.unwrap()
.contains(&space.into_raw())
{
OXR_ORIGINAL_DESTOY_SPACE
.lock()
.unwrap()
.expect("has to be initialized")(space)
} else {
info!("Inject Worked, not destroying space");
openxr::sys::Result::SUCCESS
}
}
fn update_space_transforms(
session: Res<OxrSession>,
default_ref_space: Res<XrPrimaryReferenceSpace>,
pipelined: Option<Res<Pipelined>>,
frame_state: Res<OxrFrameState>,
mut query: Query<(
&mut Transform,
&XrSpace,
Option<&XrSpatialOffset>,
Option<&XrReferenceSpace>,
)>,
) {
for (mut transform, space, offset, ref_space) in &mut query {
let offset = offset.copied().unwrap_or_default();
let ref_space = ref_space.unwrap_or(&default_ref_space);
if let Ok(space_location) = session.locate_space(
space,
ref_space,
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
},
) {
if space_location
.location_flags
.contains(SpaceLocationFlags::POSITION_VALID)
{
transform.translation = offset
.to_transform()
.transform_point(space_location.pose.position.to_vec3())
}
if space_location
.location_flags
.contains(SpaceLocationFlags::ORIENTATION_VALID)
{
transform.rotation = offset.rotation * space_location.pose.orientation.to_quat();
}
}
}
}
impl OxrSession {
pub fn create_action_space<T: openxr::ActionTy>(
&self,
action: &openxr::Action<T>,
subaction_path: openxr::Path,
pose_in_space: XrPose,
) -> openxr::Result<XrSpace> {
let info = sys::ActionSpaceCreateInfo {
ty: sys::ActionSpaceCreateInfo::TYPE,
next: ptr::null(),
action: action.as_raw(),
subaction_path,
pose_in_action_space: pose_in_space.to_posef(),
};
let mut out = sys::Space::NULL;
unsafe {
cvt((self.instance().fp().create_action_space)(
self.as_raw(),
&info,
&mut out,
))?;
Ok(XrSpace::from_raw(out.into_raw()))
}
}
pub fn create_reference_space(
&self,
ref_space_type: ReferenceSpaceType,
pose_in_ref_space: Transform,
) -> openxr::Result<XrReferenceSpace> {
let info = sys::ReferenceSpaceCreateInfo {
ty: sys::ReferenceSpaceCreateInfo::TYPE,
next: ptr::null(),
reference_space_type: ref_space_type,
pose_in_reference_space: pose_in_ref_space.to_posef(),
};
let mut out = sys::Space::NULL;
unsafe {
cvt((self.instance().fp().create_reference_space)(
self.as_raw(),
&info,
&mut out,
))?;
Ok(XrReferenceSpace(XrSpace::from_raw(out.into_raw())))
}
}
}
fn locate_space(
instance: &openxr::Instance,
space: &XrSpace,
base: &XrSpace,
time: openxr::Time,
) -> openxr::Result<openxr::SpaceLocation> {
unsafe {
let mut x = sys::SpaceLocation::out(ptr::null_mut());
cvt((instance.fp().locate_space)(
space.as_raw_openxr_space(),
base.as_raw_openxr_space(),
time,
x.as_mut_ptr(),
))?;
Ok(create_space_location(&x))
}
}
fn locate_space_with_velocity(
instance: &openxr::Instance,
space: &XrSpace,
base: &XrSpace,
time: openxr::Time,
) -> openxr::Result<(openxr::SpaceLocation, openxr::SpaceVelocity)> {
unsafe {
let mut velocity = sys::SpaceVelocity::out(ptr::null_mut());
let mut location = sys::SpaceLocation::out(&mut velocity as *mut _ as _);
cvt((instance.fp().locate_space)(
space.as_raw_openxr_space(),
base.as_raw_openxr_space(),
time,
location.as_mut_ptr(),
))?;
Ok((
create_space_location(&location),
create_space_velocity(&velocity),
))
}
}
pub fn locate_hand_joints(
instance: &openxr::Instance,
tracker: &openxr::HandTracker,
base: &XrSpace,
time: openxr::Time,
) -> openxr::Result<Option<HandJointLocations>> {
unsafe {
let locate_info = sys::HandJointsLocateInfoEXT {
ty: sys::HandJointsLocateInfoEXT::TYPE,
next: ptr::null(),
base_space: base.as_raw_openxr_space(),
time,
};
let mut locations =
MaybeUninit::<[openxr::HandJointLocation; openxr::HAND_JOINT_COUNT]>::uninit();
let mut location_info = sys::HandJointLocationsEXT {
ty: sys::HandJointLocationsEXT::TYPE,
next: ptr::null_mut(),
is_active: false.into(),
joint_count: openxr::HAND_JOINT_COUNT as u32,
joint_locations: locations.as_mut_ptr() as _,
};
cvt((instance
.exts()
.ext_hand_tracking
.as_ref()
.expect("Somehow created HandTracker without XR_EXT_hand_tracking being enabled")
.locate_hand_joints)(
tracker.as_raw(),
&locate_info,
&mut location_info,
))?;
Ok(if location_info.is_active.into() {
Some(locations.assume_init())
} else {
None
})
}
}
pub fn locate_hand_joints_with_velocities(
instance: &openxr::Instance,
tracker: &openxr::HandTracker,
base: &XrSpace,
time: openxr::Time,
) -> openxr::Result<Option<(HandJointLocations, HandJointVelocities)>> {
unsafe {
let locate_info = sys::HandJointsLocateInfoEXT {
ty: sys::HandJointsLocateInfoEXT::TYPE,
next: ptr::null(),
base_space: base.as_raw_openxr_space(),
time,
};
let mut velocities = MaybeUninit::<[HandJointVelocity; HAND_JOINT_COUNT]>::uninit();
let mut velocity_info = sys::HandJointVelocitiesEXT {
ty: sys::HandJointVelocitiesEXT::TYPE,
next: ptr::null_mut(),
joint_count: HAND_JOINT_COUNT as u32,
joint_velocities: velocities.as_mut_ptr() as _,
};
let mut locations = MaybeUninit::<[HandJointLocation; HAND_JOINT_COUNT]>::uninit();
let mut location_info = sys::HandJointLocationsEXT {
ty: sys::HandJointLocationsEXT::TYPE,
next: &mut velocity_info as *mut _ as _,
is_active: false.into(),
joint_count: HAND_JOINT_COUNT as u32,
joint_locations: locations.as_mut_ptr() as _,
};
cvt((instance
.exts()
.ext_hand_tracking
.as_ref()
.expect("Somehow created HandTracker without XR_EXT_hand_tracking being enabled")
.locate_hand_joints)(
tracker.as_raw(),
&locate_info,
&mut location_info,
))?;
Ok(if location_info.is_active.into() {
Some((locations.assume_init(), velocities.assume_init()))
} else {
None
})
}
}
pub fn destroy_space(
instance: &openxr::Instance,
space: sys::Space,
) -> openxr::Result<sys::Result> {
OXR_DO_NOT_CALL_DESTOY_SPACE_FOR_SPACES
.lock()
.unwrap()
.as_mut()
.unwrap()
.remove(&space.into_raw());
let result = unsafe { (instance.fp().destroy_space)(space) };
cvt(result)
}
impl OxrSession {
pub fn allow_auto_destruct_of_openxr_space(&self, space: &openxr::Space) {
OXR_DO_NOT_CALL_DESTOY_SPACE_FOR_SPACES
.lock()
.unwrap()
.as_mut()
.unwrap()
.remove(&space.as_raw().into_raw());
}
pub fn destroy_space(&self, space: XrSpace) -> openxr::Result<sys::Result> {
destroy_space(self.instance(), space.as_raw_openxr_space())
}
pub fn destroy_openxr_space(&self, space: openxr::Space) -> openxr::Result<sys::Result> {
destroy_space(self.instance(), space.as_raw())
}
pub fn locate_views(
&self,
view_configuration_type: openxr::ViewConfigurationType,
display_time: openxr::Time,
ref_space: &XrReferenceSpace,
) -> openxr::Result<(openxr::ViewStateFlags, Vec<openxr::View>)> {
let info = sys::ViewLocateInfo {
ty: sys::ViewLocateInfo::TYPE,
next: ptr::null(),
view_configuration_type,
display_time,
space: ref_space.as_raw_openxr_space(),
};
let (flags, raw) = unsafe {
let mut out = sys::ViewState::out(ptr::null_mut());
let raw = get_arr_init(sys::View::out(ptr::null_mut()), |cap, count, buf| {
(self.instance().fp().locate_views)(
self.as_raw(),
&info,
out.as_mut_ptr(),
cap,
count,
buf as _,
)
})?;
(out.assume_init().view_state_flags, raw)
};
Ok((
flags,
raw.into_iter()
.map(|x| unsafe { create_view(flags, &x) })
.collect(),
))
}
pub fn locate_space(
&self,
space: &XrSpace,
base: &XrSpace,
time: openxr::Time,
) -> openxr::Result<openxr::SpaceLocation> {
locate_space(self.instance(), space, base, time)
}
pub fn locate_space_with_velocity(
&self,
space: &XrSpace,
base: &XrSpace,
time: openxr::Time,
) -> openxr::Result<(openxr::SpaceLocation, openxr::SpaceVelocity)> {
locate_space_with_velocity(self.instance(), space, base, time)
}
pub fn locate_hand_joints(
&self,
tracker: &openxr::HandTracker,
base: &XrSpace,
time: openxr::Time,
) -> openxr::Result<Option<openxr::HandJointLocations>> {
locate_hand_joints(self.instance(), tracker, base, time)
}
pub fn locate_hand_joints_with_velocities(
&self,
tracker: &openxr::HandTracker,
base: &XrSpace,
time: openxr::Time,
) -> openxr::Result<Option<(HandJointLocations, HandJointVelocities)>> {
locate_hand_joints_with_velocities(self.instance(), tracker, base, time)
}
}
impl OxrInstance {
pub fn allow_auto_destruct_of_openxr_space(&self, space: &openxr::Space) {
OXR_DO_NOT_CALL_DESTOY_SPACE_FOR_SPACES
.lock()
.unwrap()
.as_mut()
.unwrap()
.remove(&space.as_raw().into_raw());
}
pub fn destroy_space(&self, space: XrSpace) -> openxr::Result<sys::Result> {
destroy_space(self, space.as_raw_openxr_space())
}
pub fn destroy_openxr_space(&self, space: openxr::Space) -> openxr::Result<sys::Result> {
destroy_space(self, space.as_raw())
}
pub fn locate_space(
&self,
space: &XrSpace,
base: &XrSpace,
time: openxr::Time,
) -> openxr::Result<openxr::SpaceLocation> {
locate_space(self, space, base, time)
}
pub fn locate_space_with_velocity(
&self,
space: &XrSpace,
base: &XrSpace,
time: openxr::Time,
) -> openxr::Result<(openxr::SpaceLocation, openxr::SpaceVelocity)> {
locate_space_with_velocity(self, space, base, time)
}
pub fn locate_hand_joints(
&self,
tracker: &openxr::HandTracker,
base: &XrSpace,
time: openxr::Time,
) -> openxr::Result<Option<openxr::HandJointLocations>> {
locate_hand_joints(self, tracker, base, time)
}
pub fn locate_hand_joints_with_velocities(
&self,
tracker: &openxr::HandTracker,
base: &XrSpace,
time: openxr::Time,
) -> openxr::Result<Option<(HandJointLocations, HandJointVelocities)>> {
locate_hand_joints_with_velocities(self, tracker, base, time)
}
}
/// # Safety
/// This is an Extension trait. DO NOT IMPLEMENT IT!
pub unsafe trait OxrSpaceExt {
/// get an openxr::sys::Space as a reference to the XrSpace
/// does not remove the space from the space managment system!
fn as_raw_openxr_space(&self) -> sys::Space;
/// Adds the openxr::sys::Space into the the space managment system
fn from_raw_openxr_space(space: sys::Space) -> Self;
/// Adds the openxr::Space into the the space manegment system
fn from_openxr_space(space: openxr::Space) -> Self;
/// get an openxr::Space as a reference to the XrSpace
/// does not remove the space from the space managment system!
/// # Safety
/// Session has to be the session from which the space is from
unsafe fn as_openxr_space<T>(&self, session: &openxr::Session<T>) -> openxr::Space;
/// get an openxr::Space as an onwned version of the XrSpace
/// removes the space from the space managment system!
/// # Safety
/// Session has to be the session from which the space is from
unsafe fn into_openxr_space<T>(self, session: &openxr::Session<T>) -> openxr::Space;
}
unsafe impl OxrSpaceExt for XrSpace {
fn as_raw_openxr_space(&self) -> sys::Space {
sys::Space::from_raw(self.as_raw())
}
fn from_raw_openxr_space(space: sys::Space) -> Self {
let raw = space.into_raw();
OXR_DO_NOT_CALL_DESTOY_SPACE_FOR_SPACES
.lock()
.unwrap()
.as_mut()
.unwrap()
.insert(raw);
unsafe { XrSpace::from_raw(raw) }
}
fn from_openxr_space(space: openxr::Space) -> Self {
Self::from_raw_openxr_space(space.as_raw())
}
unsafe fn as_openxr_space<T>(&self, session: &openxr::Session<T>) -> openxr::Space {
unsafe { openxr::Space::reference_from_raw(session.clone(), self.as_raw_openxr_space()) }
}
unsafe fn into_openxr_space<T>(self, session: &openxr::Session<T>) -> openxr::Space {
OXR_DO_NOT_CALL_DESTOY_SPACE_FOR_SPACES
.lock()
.unwrap()
.as_mut()
.unwrap()
.remove(&self.as_raw());
unsafe { openxr::Space::reference_from_raw(session.clone(), self.as_raw_openxr_space()) }
}
}
fn cvt(x: sys::Result) -> openxr::Result<sys::Result> {
if x.into_raw() >= 0 {
Ok(x)
} else {
Err(x)
}
}
unsafe fn create_view(flags: openxr::ViewStateFlags, raw: &MaybeUninit<sys::View>) -> openxr::View {
// Applications *must* not read invalid parts of a poses, i.e. they may be uninitialized
let ptr = raw.as_ptr();
openxr::View {
pose: openxr::Posef {
orientation: flags
.contains(sys::ViewStateFlags::ORIENTATION_VALID)
.then(|| *ptr::addr_of!((*ptr).pose.orientation))
.unwrap_or_default(),
position: flags
.contains(sys::ViewStateFlags::POSITION_VALID)
.then(|| *ptr::addr_of!((*ptr).pose.position))
.unwrap_or_default(),
},
fov: *ptr::addr_of!((*ptr).fov),
}
}
unsafe fn create_space_location(raw: &MaybeUninit<sys::SpaceLocation>) -> openxr::SpaceLocation {
// Applications *must* not read invalid parts of a pose, i.e. they may be uninitialized
let ptr = raw.as_ptr();
let flags = *ptr::addr_of!((*ptr).location_flags);
openxr::SpaceLocation {
location_flags: flags,
pose: openxr::Posef {
orientation: flags
.contains(sys::SpaceLocationFlags::ORIENTATION_VALID)
.then(|| *ptr::addr_of!((*ptr).pose.orientation))
.unwrap_or_default(),
position: flags
.contains(sys::SpaceLocationFlags::POSITION_VALID)
.then(|| *ptr::addr_of!((*ptr).pose.position))
.unwrap_or_default(),
},
}
}
unsafe fn create_space_velocity(raw: &MaybeUninit<sys::SpaceVelocity>) -> openxr::SpaceVelocity {
// Applications *must* not read invalid velocities, i.e. they may be uninitialized
let ptr = raw.as_ptr();
let flags = *ptr::addr_of!((*ptr).velocity_flags);
openxr::SpaceVelocity {
velocity_flags: flags,
linear_velocity: flags
.contains(sys::SpaceVelocityFlags::LINEAR_VALID)
.then(|| *ptr::addr_of!((*ptr).linear_velocity))
.unwrap_or_default(),
angular_velocity: flags
.contains(sys::SpaceVelocityFlags::ANGULAR_VALID)
.then(|| *ptr::addr_of!((*ptr).angular_velocity))
.unwrap_or_default(),
}
}
fn get_arr_init<T: Copy>(
init: T,
mut getter: impl FnMut(u32, &mut u32, *mut T) -> sys::Result,
) -> openxr::Result<Vec<T>> {
let mut output = 0;
cvt(getter(0, &mut output, std::ptr::null_mut()))?;
let mut buffer = vec![init; output as usize];
loop {
match cvt(getter(output, &mut output, buffer.as_mut_ptr() as _)) {
Ok(_) => {
buffer.truncate(output as usize);
return Ok(buffer);
}
Err(sys::Result::ERROR_SIZE_INSUFFICIENT) => {
buffer.resize(output as usize, init);
}
Err(e) => {
return Err(e);
}
}
}
}

View File

@@ -3,3 +3,4 @@ pub mod camera;
pub mod hands; pub mod hands;
pub mod session; pub mod session;
pub mod types; pub mod types;
pub mod spaces;

View File

@@ -0,0 +1,44 @@
use bevy::{
prelude::*,
render::{extract_component::ExtractComponent, extract_resource::ExtractResource},
};
use crate::types::XrPose;
/// Any Spaces will be invalid after the owning session exits
#[repr(transparent)]
#[derive(Clone, Copy, Hash, PartialEq, Eq, Reflect, Debug, Component, ExtractComponent)]
pub struct XrSpace(u64);
// Does repr(transparent) even make sense here?
#[repr(transparent)]
#[derive(
Clone, Copy, PartialEq, Reflect, Debug, Component, ExtractComponent, Default, Deref, DerefMut,
)]
pub struct XrSpatialOffset(pub XrPose);
#[derive(Event, Clone, Copy, Deref, DerefMut)]
pub struct XrDestroySpace(pub XrSpace);
#[repr(transparent)]
#[derive(
Clone, Copy, Hash, PartialEq, Eq, Reflect, Debug, Component, Deref, DerefMut, ExtractComponent,
)]
pub struct XrReferenceSpace(pub XrSpace);
#[repr(transparent)]
#[derive(
Clone, Copy, Hash, PartialEq, Eq, Reflect, Debug, Resource, Deref, DerefMut, ExtractResource,
)]
pub struct XrPrimaryReferenceSpace(pub XrReferenceSpace);
impl XrSpace {
/// # Safety
/// only call with known valid handles
pub unsafe fn from_raw(handle: u64) -> Self {
Self(handle)
}
pub fn as_raw(&self) -> u64 {
self.0
}
}

View File

@@ -1,6 +1,26 @@
use bevy::math::{Quat, Vec3}; use bevy::{
math::{Quat, Vec3},
reflect::Reflect,
transform::components::Transform,
};
pub struct Pose { #[derive(Clone, Copy, PartialEq, Reflect, Debug)]
pub position: Vec3, pub struct XrPose {
pub orientation: Quat, pub translation: Vec3,
pub rotation: Quat,
}
impl Default for XrPose {
fn default() -> Self {
Self::IDENTITY
}
}
impl XrPose {
pub const IDENTITY: XrPose = XrPose {
translation: Vec3::ZERO,
rotation: Quat::IDENTITY,
};
pub const fn to_transform(self) -> Transform {
Transform::from_translation(self.translation).with_rotation(self.rotation)
}
} }

View File

@@ -56,42 +56,53 @@
use bevy::prelude::*; use bevy::prelude::*;
use bevy_openxr::{ use bevy_openxr::{
action_binding::OxrSuggestActionBinding, action_set_attaching::OxrAttachActionSet, action_binding::OxrSuggestActionBinding, action_set_attaching::OxrAttachActionSet,
action_set_syncing::OxrActionSetSyncSet, action_set_syncing::OxrSyncActionSet,
resources::OxrInstance, session::OxrSession, resources::OxrInstance, session::OxrSession,
}; };
use bevy_xr::session::{session_available, session_running}; use bevy_xr::session::{session_available, session_running};
use openxr::{ActiveActionSet, Path, Vector2f}; use openxr::{ActiveActionSet, Path, Vector2f};
use std::borrow::Cow; use std::borrow::Cow;
pub struct XRUtilsActionsPlugin; pub struct XRUtilsActionsPlugin;
impl Plugin for XRUtilsActionsPlugin { impl Plugin for XRUtilsActionsPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.configure_sets(
Startup,
XRUtilsActionSystemSet::CreateEvents.run_if(session_available),
);
app.configure_sets(
PreUpdate,
XRUtilsActionSystemSet::SyncActionStates.run_if(session_running),
);
app.add_systems( app.add_systems(
Startup, Startup,
create_openxr_events create_openxr_events
.in_set(XRUtilsActionSystemSet::CreateEvents) .in_set(XRUtilsActionSystemSet::CreateEvents)
.run_if(session_available), .run_if(session_available),
); );
app.add_systems(Update, sync_active_action_sets.run_if(session_running)); app.add_systems(Update, sync_active_action_sets.run_if(session_running));
app.add_systems( app.add_systems(
Update, PreUpdate,
sync_and_update_action_states_f32 sync_and_update_action_states_f32
.run_if(session_running) .run_if(session_running)
.in_set(XRUtilsActionSystemSet::SyncActionStates) .in_set(XRUtilsActionSystemSet::SyncActionStates)
.after(sync_active_action_sets), .after(OxrActionSetSyncSet),
); );
app.add_systems( app.add_systems(
Update, PreUpdate,
sync_and_update_action_states_bool sync_and_update_action_states_bool
.run_if(session_running) .run_if(session_running)
.in_set(XRUtilsActionSystemSet::SyncActionStates) .in_set(XRUtilsActionSystemSet::SyncActionStates)
.after(sync_active_action_sets), .after(OxrActionSetSyncSet),
); );
app.add_systems( app.add_systems(
Update, PreUpdate,
sync_and_update_action_states_vector sync_and_update_action_states_vector
.run_if(session_running) .run_if(session_running)
.in_set(XRUtilsActionSystemSet::SyncActionStates) .in_set(XRUtilsActionSystemSet::SyncActionStates)
.after(sync_active_action_sets), .after(OxrActionSetSyncSet),
); );
} }
} }
@@ -147,9 +158,9 @@ fn create_openxr_events(
//interaction profile //interaction profile
//get the binding entity and stuff //get the binding entity and stuff
let create_binding = bindings_query.get(bind).unwrap(); let create_binding = bindings_query.get(bind).unwrap();
let profile = Cow::from(create_binding.profile.clone()); let profile = create_binding.profile.clone();
//bindings //bindings
let binding = vec![Cow::from(create_binding.binding.clone())]; let binding = vec![create_binding.binding.clone()];
let sugestion = OxrSuggestActionBinding { let sugestion = OxrSuggestActionBinding {
action: action.as_raw(), action: action.as_raw(),
interaction_profile: profile, interaction_profile: profile,
@@ -186,9 +197,9 @@ fn create_openxr_events(
//interaction profile //interaction profile
//get the binding entity and stuff //get the binding entity and stuff
let create_binding = bindings_query.get(bind).unwrap(); let create_binding = bindings_query.get(bind).unwrap();
let profile = Cow::from(create_binding.profile.clone()); let profile = create_binding.profile.clone();
//bindings //bindings
let binding = vec![Cow::from(create_binding.binding.clone())]; let binding = vec![create_binding.binding.clone()];
let sugestion = OxrSuggestActionBinding { let sugestion = OxrSuggestActionBinding {
action: action.as_raw(), action: action.as_raw(),
interaction_profile: profile, interaction_profile: profile,
@@ -225,9 +236,9 @@ fn create_openxr_events(
//interaction profile //interaction profile
//get the binding entity and stuff //get the binding entity and stuff
let create_binding = bindings_query.get(bind).unwrap(); let create_binding = bindings_query.get(bind).unwrap();
let profile = Cow::from(create_binding.profile.clone()); let profile = create_binding.profile.clone();
//bindings //bindings
let binding = vec![Cow::from(create_binding.binding.clone())]; let binding = vec![create_binding.binding.clone()];
let sugestion = OxrSuggestActionBinding { let sugestion = OxrSuggestActionBinding {
action: action.as_raw(), action: action.as_raw(),
interaction_profile: profile, interaction_profile: profile,
@@ -245,17 +256,11 @@ fn create_openxr_events(
} }
fn sync_active_action_sets( fn sync_active_action_sets(
session: Res<OxrSession>, mut sync_set: EventWriter<OxrSyncActionSet>,
active_action_set_query: Query<&XRUtilsActionSetReference, With<ActiveSet>>, active_action_set_query: Query<&XRUtilsActionSetReference, With<ActiveSet>>,
) { ) {
let active_sets = active_action_set_query for set in &active_action_set_query {
.iter() sync_set.send(OxrSyncActionSet(set.0.clone()));
.map(|v| ActiveActionSet::from(&v.0))
.collect::<Vec<_>>();
let sync = session.sync_actions(&active_sets[..]);
match sync {
Ok(_) => (),
Err(_) => error!("sync error"),
} }
} }
@@ -336,7 +341,9 @@ fn sync_and_update_action_states_vector(
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, SystemSet)] #[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, SystemSet)]
pub enum XRUtilsActionSystemSet { pub enum XRUtilsActionSystemSet {
/// Runs in Startup
CreateEvents, CreateEvents,
/// Runs in PreUpdate
SyncActionStates, SyncActionStates,
} }