Merge pull request #146 from Schmarni-Dev/hookup_more_input_info

Expose Velocities and SpaceFlags and add InteractionProfile changed event
This commit is contained in:
Schmarni
2024-07-28 00:13:38 +02:00
committed by GitHub
4 changed files with 215 additions and 77 deletions

View File

@@ -1,17 +1,18 @@
use bevy::prelude::*; use bevy::prelude::*;
use bevy_mod_xr::hands::{LeftHand, RightHand, XrHandBoneEntities, HAND_JOINT_COUNT}; use bevy_mod_xr::hands::{LeftHand, RightHand, XrHandBoneEntities, HAND_JOINT_COUNT};
use bevy_mod_xr::session::{XrPreDestroySession, XrSessionCreated, XrTrackingRoot}; use bevy_mod_xr::session::{XrPreDestroySession, XrSessionCreated, XrTrackingRoot};
use bevy_mod_xr::spaces::{XrPrimaryReferenceSpace, XrReferenceSpace}; use bevy_mod_xr::spaces::{XrPrimaryReferenceSpace, XrReferenceSpace, XrVelocity};
use bevy_mod_xr::{ use bevy_mod_xr::{
hands::{HandBone, HandBoneRadius}, hands::{HandBone, HandBoneRadius},
session::session_running, session::session_running,
}; };
use openxr::SpaceLocationFlags; use openxr::SpaceLocationFlags;
use crate::init::create_xr_session; use crate::helper_traits::ToVec3;
use crate::resources::OxrFrameState; use crate::resources::OxrFrameState;
use crate::resources::Pipelined; use crate::resources::Pipelined;
use crate::session::OxrSession; use crate::session::OxrSession;
use crate::spaces::{OxrSpaceLocationFlags, OxrSpaceVelocityFlags};
pub struct HandTrackingPlugin { pub struct HandTrackingPlugin {
default_hands: bool, default_hands: bool,
@@ -43,7 +44,12 @@ pub fn spawn_hand_bones<T: Bundle + Clone>(
#[allow(clippy::needless_range_loop)] #[allow(clippy::needless_range_loop)]
for bone in HandBone::get_all_bones().into_iter() { for bone in HandBone::get_all_bones().into_iter() {
bones[bone as usize] = cmds bones[bone as usize] = cmds
.spawn((SpatialBundle::default(), bone, HandBoneRadius(0.0))) .spawn((
SpatialBundle::default(),
bone,
HandBoneRadius(0.0),
OxrSpaceLocationFlags(openxr::SpaceLocationFlags::default()),
))
.insert(bundle.clone()) .insert(bundle.clone())
.id(); .id();
} }
@@ -101,9 +107,9 @@ fn spawn_default_hands(
} }
#[derive(Component, Clone, Copy)] #[derive(Component, Clone, Copy)]
struct DefaultHandTracker; pub struct DefaultHandTracker;
#[derive(Component, Clone, Copy)] #[derive(Component, Clone, Copy)]
struct DefaultHandBone; pub struct DefaultHandBone;
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
fn clean_up_default_hands( fn clean_up_default_hands(
@@ -128,34 +134,60 @@ fn locate_hands(
&XrHandBoneEntities, &XrHandBoneEntities,
)>, )>,
session: Res<OxrSession>, session: Res<OxrSession>,
mut bone_query: Query<(&HandBone, &mut HandBoneRadius, &mut Transform)>, mut bone_query: Query<(
&HandBone,
&mut HandBoneRadius,
&mut Transform,
Option<&mut XrVelocity>,
&mut OxrSpaceLocationFlags,
Option<&mut OxrSpaceVelocityFlags>,
)>,
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 wants_velocities = hand_entities
.0
.iter()
.filter_map(|e| bone_query.get(*e).ok())
.any(|v| v.3.is_some());
let time = if pipelined.is_some() {
openxr::Time::from_nanos(
frame_state.predicted_display_time.as_nanos()
+ frame_state.predicted_display_period.as_nanos(),
)
} else {
frame_state.predicted_display_time
};
let 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 let (joints, vels) = if wants_velocities {
let joints = match session.locate_hand_joints( let (loc, vel) =
tracker, match session.locate_hand_joints_with_velocities(tracker, ref_space, time) {
ref_space, Ok(Some(v)) => v,
if pipelined.is_some() { Ok(None) => continue,
openxr::Time::from_nanos( Err(openxr::sys::Result::ERROR_EXTENSION_NOT_PRESENT) => {
frame_state.predicted_display_time.as_nanos() error!("HandTracking Extension not loaded");
+ frame_state.predicted_display_period.as_nanos(), continue;
) }
} else { Err(err) => {
frame_state.predicted_display_time warn!("Error while locating hand joints: {}", err.to_string());
}, continue;
) { }
Ok(Some(v)) => v, };
Ok(None) => continue, (loc, Some(vel))
Err(openxr::sys::Result::ERROR_EXTENSION_NOT_PRESENT) => { } else {
error!("HandTracking Extension not loaded"); let space = match session.locate_hand_joints(tracker, ref_space, time) {
continue; Ok(Some(v)) => v,
} Ok(None) => continue,
Err(err) => { Err(openxr::sys::Result::ERROR_EXTENSION_NOT_PRESENT) => {
warn!("Error while locating hand joints: {}", err.to_string()); error!("HandTracking Extension not loaded");
continue; continue;
} }
Err(err) => {
warn!("Error while locating hand joints: {}", err.to_string());
continue;
}
};
(space, None)
}; };
let bone_entities = match bone_query.get_many_mut(hand_entities.0) { let bone_entities = match bone_query.get_many_mut(hand_entities.0) {
Ok(v) => v, Ok(v) => v,
@@ -164,28 +196,45 @@ fn locate_hands(
continue; continue;
} }
}; };
for (bone, mut bone_radius, mut transform) in bone_entities { for (bone, mut bone_radius, mut transform, velocity, mut location_flags, velocity_flags) in
bone_entities
{
let joint = joints[*bone as usize]; let joint = joints[*bone as usize];
**bone_radius = joint.radius; if let Some(mut velocity) = velocity {
let Some(vels) = vels.as_ref() else {
error!("somehow got a hand bone with an XrVelocity component, but there are no velocities");
continue;
};
let Some(mut vel_flags) = velocity_flags else {
error!("somehow got a hand bone with an XrVelocity component, but without velocity flags");
continue;
};
let vel = vels[*bone as usize];
let flags = OxrSpaceVelocityFlags(vel.velocity_flags);
if flags.linear_valid() {
velocity.linear = vel.linear_velocity.to_vec3();
}
if flags.angular_valid() {
velocity.angular = vel.angular_velocity.to_vec3();
}
*vel_flags = flags;
}
if joint **bone_radius = joint.radius;
.location_flags let flags = OxrSpaceLocationFlags(joint.location_flags);
.contains(SpaceLocationFlags::POSITION_VALID) if flags.pos_valid() {
{
transform.translation.x = joint.pose.position.x; transform.translation.x = joint.pose.position.x;
transform.translation.y = joint.pose.position.y; transform.translation.y = joint.pose.position.y;
transform.translation.z = joint.pose.position.z; transform.translation.z = joint.pose.position.z;
} }
if joint if flags.rot_valid() {
.location_flags
.contains(SpaceLocationFlags::ORIENTATION_VALID)
{
transform.rotation.x = joint.pose.orientation.x; transform.rotation.x = joint.pose.orientation.x;
transform.rotation.y = joint.pose.orientation.y; transform.rotation.y = joint.pose.orientation.y;
transform.rotation.z = joint.pose.orientation.z; transform.rotation.z = joint.pose.orientation.z;
transform.rotation.w = joint.pose.orientation.w; transform.rotation.w = joint.pose.orientation.w;
} }
*location_flags = flags;
} }
} }
} }

View File

@@ -15,6 +15,7 @@ use bevy::render::RenderPlugin;
use bevy::winit::UpdateMode; use bevy::winit::UpdateMode;
use bevy::winit::WinitSettings; use bevy::winit::WinitSettings;
use bevy_mod_xr::session::*; use bevy_mod_xr::session::*;
use openxr::Event;
use crate::error::OxrError; use crate::error::OxrError;
use crate::features::overlay::OxrOverlaySessionEvent; use crate::features::overlay::OxrOverlaySessionEvent;
@@ -80,6 +81,7 @@ impl Default for OxrInitPlugin {
impl Plugin for OxrInitPlugin { impl Plugin for OxrInitPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_event::<OxrInteractionProfileChanged>();
match self.init_xr() { match self.init_xr() {
Ok(( Ok((
instance, instance,
@@ -260,12 +262,15 @@ impl OxrInitPlugin {
)) ))
} }
} }
#[derive(Event, Clone, Copy, Debug, Default)]
pub struct OxrInteractionProfileChanged;
/// Polls any OpenXR events and handles them accordingly /// Polls any OpenXR events and handles them accordingly
pub fn poll_events( pub fn poll_events(
instance: Res<OxrInstance>, instance: Res<OxrInstance>,
mut status: ResMut<XrState>, mut status: ResMut<XrState>,
mut changed_event: EventWriter<XrStateChanged>, mut changed_event: EventWriter<XrStateChanged>,
mut interaction_profile_changed_event: EventWriter<OxrInteractionProfileChanged>,
mut overlay_writer: Option<ResMut<Events<OxrOverlaySessionEvent>>>, mut overlay_writer: Option<ResMut<Events<OxrOverlaySessionEvent>>>,
) { ) {
let _span = info_span!("xr_poll_events"); let _span = info_span!("xr_poll_events");
@@ -313,6 +318,10 @@ pub fn poll_events(
warn!("Overlay Event Recieved without the OverlayPlugin being added!"); warn!("Overlay Event Recieved without the OverlayPlugin being added!");
} }
} }
// we might want to check if this is the correct session?
Event::InteractionProfileChanged(_) => {
interaction_profile_changed_event.send_default();
}
_ => {} _ => {}
} }
} }

View File

@@ -3,12 +3,12 @@ use std::{mem::MaybeUninit, ptr, sync::Mutex};
use bevy::{prelude::*, utils::hashbrown::HashSet}; use bevy::{prelude::*, utils::hashbrown::HashSet};
use bevy_mod_xr::{ use bevy_mod_xr::{
session::{session_available, session_running, XrFirst, XrHandleEvents}, session::{session_available, session_running, XrFirst, XrHandleEvents},
spaces::{XrDestroySpace, XrPrimaryReferenceSpace, XrReferenceSpace, XrSpace, XrSpatialOffset}, spaces::{XrDestroySpace, XrPrimaryReferenceSpace, XrReferenceSpace, XrSpace, XrVelocity},
types::XrPose, types::XrPose,
}; };
use openxr::{ use openxr::{
sys, HandJointLocation, HandJointLocations, HandJointVelocities, HandJointVelocity, sys, HandJointLocation, HandJointLocations, HandJointVelocities, HandJointVelocity,
ReferenceSpaceType, SpaceLocationFlags, HAND_JOINT_COUNT, ReferenceSpaceType, SpaceLocationFlags, SpaceVelocityFlags, HAND_JOINT_COUNT,
}; };
use crate::{ use crate::{
@@ -27,6 +27,7 @@ impl Plugin for OxrSpacePatchingPlugin {
app.add_systems(Startup, patch_destroy_space.run_if(session_available)); app.add_systems(Startup, patch_destroy_space.run_if(session_available));
} }
} }
pub struct OxrSpatialPlugin; pub struct OxrSpatialPlugin;
impl Plugin for OxrSpatialPlugin { impl Plugin for OxrSpatialPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
@@ -42,10 +43,29 @@ impl Plugin for OxrSpatialPlugin {
update_space_transforms update_space_transforms
.in_set(OxrSpaceSyncSet) .in_set(OxrSpaceSyncSet)
.run_if(session_running), .run_if(session_running),
); )
.observe(add_location_flags)
.observe(add_velocity_flags);
} }
} }
fn add_velocity_flags(event: Trigger<OnAdd, XrVelocity>, mut cmds: Commands) {
if event.entity() == Entity::PLACEHOLDER {
error!("called add_location_flags observer without entity");
return;
}
cmds.entity(event.entity())
.insert(OxrSpaceLocationFlags(openxr::SpaceLocationFlags::default()));
}
fn add_location_flags(event: Trigger<OnAdd, XrSpace>, mut cmds: Commands) {
if event.entity() == Entity::PLACEHOLDER {
error!("called add_location_flags observer without entity");
return;
}
cmds.entity(event.entity())
.insert(OxrSpaceLocationFlags(openxr::SpaceLocationFlags::default()));
}
fn destroy_space_event(instance: Res<OxrInstance>, mut events: EventReader<XrDestroySpace>) { fn destroy_space_event(instance: Res<OxrInstance>, mut events: EventReader<XrDestroySpace>) {
for space in events.read() { for space in events.read() {
match instance.destroy_space(space.0) { match instance.destroy_space(space.0) {
@@ -92,6 +112,34 @@ unsafe extern "system" fn patched_destroy_space(space: openxr::sys::Space) -> op
} }
} }
#[derive(Clone, Copy, Component)]
pub struct OxrSpaceLocationFlags(pub openxr::SpaceLocationFlags);
impl OxrSpaceLocationFlags {
pub fn pos_valid(&self) -> bool {
self.0.contains(SpaceLocationFlags::POSITION_VALID)
}
pub fn pos_tracked(&self) -> bool {
self.0.contains(SpaceLocationFlags::POSITION_TRACKED)
}
pub fn rot_valid(&self) -> bool {
self.0.contains(SpaceLocationFlags::ORIENTATION_VALID)
}
pub fn rot_tracked(&self) -> bool {
self.0.contains(SpaceLocationFlags::ORIENTATION_TRACKED)
}
}
#[derive(Clone, Copy, Component)]
pub struct OxrSpaceVelocityFlags(pub openxr::SpaceVelocityFlags);
impl OxrSpaceVelocityFlags {
pub fn linear_valid(&self) -> bool {
self.0.contains(SpaceVelocityFlags::LINEAR_VALID)
}
pub fn angular_valid(&self) -> bool {
self.0.contains(SpaceVelocityFlags::ANGULAR_VALID)
}
}
#[allow(clippy::type_complexity)]
fn update_space_transforms( fn update_space_transforms(
session: Res<OxrSession>, session: Res<OxrSession>,
default_ref_space: Res<XrPrimaryReferenceSpace>, default_ref_space: Res<XrPrimaryReferenceSpace>,
@@ -100,39 +148,61 @@ fn update_space_transforms(
mut query: Query<( mut query: Query<(
&mut Transform, &mut Transform,
&XrSpace, &XrSpace,
Option<&XrSpatialOffset>, Option<&mut XrVelocity>,
Option<&XrReferenceSpace>, Option<&XrReferenceSpace>,
&mut OxrSpaceLocationFlags,
Option<&mut OxrSpaceVelocityFlags>,
)>, )>,
) { ) {
for (mut transform, space, offset, ref_space) in &mut query { for (
let offset = offset.copied().unwrap_or_default(); mut transform,
space,
velocity,
ref_space,
mut space_location_flags,
space_velocity_flags,
) in &mut query
{
let ref_space = ref_space.unwrap_or(&default_ref_space); let ref_space = ref_space.unwrap_or(&default_ref_space);
if let Ok(space_location) = session.locate_space( let time = if pipelined.is_some() {
space, openxr::Time::from_nanos(
ref_space, frame_state.predicted_display_time.as_nanos()
if pipelined.is_some() { + frame_state.predicted_display_period.as_nanos(),
openxr::Time::from_nanos( )
frame_state.predicted_display_time.as_nanos() } else {
+ frame_state.predicted_display_period.as_nanos(), frame_state.predicted_display_time
) };
} else { let space_location = if let Some(mut velocity) = velocity {
frame_state.predicted_display_time match session.locate_space_with_velocity(space, ref_space, time) {
}, Ok((location, space_velocity)) => {
) { let flags = OxrSpaceVelocityFlags(space_velocity.velocity_flags);
if space_location if flags.linear_valid() {
.location_flags velocity.linear = space_velocity.linear_velocity.to_vec3();
.contains(SpaceLocationFlags::POSITION_VALID) }
{ if flags.linear_valid() {
transform.translation = offset velocity.linear = space_velocity.linear_velocity.to_vec3();
.to_transform() }
.transform_point(space_location.pose.position.to_vec3()) let Some(mut vel_flags) = space_velocity_flags else {
error!("XrVelocity without OxrSpaceVelocityFlags");
return;
};
*vel_flags = flags;
Ok(location)
}
Err(err) => Err(err),
} }
if space_location } else {
.location_flags session.locate_space(space, ref_space, time)
.contains(SpaceLocationFlags::ORIENTATION_VALID) };
{ if let Ok(space_location) = space_location {
transform.rotation = offset.rotation * space_location.pose.orientation.to_quat(); let flags = OxrSpaceLocationFlags(space_location.location_flags);
if flags.pos_valid() {
transform.translation = space_location.pose.position.to_vec3();
} }
if flags.rot_valid() {
transform.rotation = space_location.pose.orientation.to_quat();
}
*space_location_flags = flags;
} }
} }
} }

View File

@@ -3,19 +3,29 @@ use bevy::{
render::{extract_component::ExtractComponent, extract_resource::ExtractResource}, render::{extract_component::ExtractComponent, extract_resource::ExtractResource},
}; };
use crate::types::XrPose;
/// Any Spaces will be invalid after the owning session exits /// Any Spaces will be invalid after the owning session exits
#[repr(transparent)] #[repr(transparent)]
#[derive(Clone, Copy, Hash, PartialEq, Eq, Reflect, Debug, Component, ExtractComponent)] #[derive(Clone, Copy, Hash, PartialEq, Eq, Reflect, Debug, Component, ExtractComponent)]
pub struct XrSpace(u64); pub struct XrSpace(u64);
// Does repr(transparent) even make sense here? #[derive(Clone, Copy, Reflect, Debug, Component, ExtractComponent, Default)]
#[repr(transparent)] pub struct XrVelocity {
#[derive( /// Velocity of a space relative to it's reference space
Clone, Copy, PartialEq, Reflect, Debug, Component, ExtractComponent, Default, Deref, DerefMut, pub linear: Vec3,
)] /// Angular Velocity of a space relative to it's reference space
pub struct XrSpatialOffset(pub XrPose); /// the direction of the vector is parrelel to the axis of rotation,
/// the magnitude is the relative angular speed in radians per second
/// the vector follows the right-hand rule for torque/rotation
pub angular: Vec3,
}
impl XrVelocity {
pub const fn new() -> XrVelocity {
XrVelocity {
linear: Vec3::ZERO,
angular: Vec3::ZERO,
}
}
}
#[derive(Event, Clone, Copy, Deref, DerefMut)] #[derive(Event, Clone, Copy, Deref, DerefMut)]
pub struct XrDestroySpace(pub XrSpace); pub struct XrDestroySpace(pub XrSpace);