diff --git a/crates/bevy_openxr/src/openxr/features/handtracking.rs b/crates/bevy_openxr/src/openxr/features/handtracking.rs index 57726e1..6c0cff6 100644 --- a/crates/bevy_openxr/src/openxr/features/handtracking.rs +++ b/crates/bevy_openxr/src/openxr/features/handtracking.rs @@ -1,6 +1,9 @@ use bevy::prelude::*; -use bevy_mod_xr::hands::{HandBone, HandBoneRadius}; -use bevy_mod_xr::hands::{LeftHand, RightHand, XrHandBoneEntities, HAND_JOINT_COUNT}; +use bevy_mod_xr::hands::{ + spawn_hand_bones, HandBone, HandBoneRadius, HandSide, SpawnHandTracker, + SpawnHandTrackerCommandExecutor, +}; +use bevy_mod_xr::hands::{LeftHand, RightHand, XrHandBoneEntities}; use bevy_mod_xr::session::{XrPreDestroySession, XrSessionCreated, XrTrackingRoot}; use bevy_mod_xr::spaces::{ XrPrimaryReferenceSpace, XrReferenceSpace, XrSpaceLocationFlags, XrSpaceVelocityFlags, @@ -9,11 +12,11 @@ use bevy_mod_xr::spaces::{ use openxr::{SpaceLocationFlags, SpaceVelocityFlags}; use crate::helper_traits::ToVec3; -use crate::openxr_session_running; use crate::resources::OxrFrameState; use crate::resources::Pipelined; use crate::session::OxrSession; use crate::spaces::{OxrSpaceLocationFlags, OxrSpaceVelocityFlags}; +use crate::{openxr_session_available, openxr_session_running}; pub struct HandTrackingPlugin { default_hands: bool, @@ -33,42 +36,24 @@ impl Plugin for HandTrackingPlugin { app.add_systems(XrPreDestroySession, clean_up_default_hands) .add_systems(XrSessionCreated, spawn_default_hands); } + app.add_systems(Startup, set_spawn_executor.run_if(openxr_session_available)); } } -pub fn spawn_hand_bones( - cmds: &mut Commands, - bundle: T, -) -> [Entity; HAND_JOINT_COUNT] { - let mut bones: [Entity; HAND_JOINT_COUNT] = [Entity::PLACEHOLDER; HAND_JOINT_COUNT]; - // screw you clippy, i don't see a better way to init this array - #[allow(clippy::needless_range_loop)] - for bone in HandBone::get_all_bones().into_iter() { - bones[bone as usize] = cmds - .spawn(( - SpatialBundle::default(), - bone, - HandBoneRadius(0.0), - OxrSpaceLocationFlags(openxr::SpaceLocationFlags::default()), - XrSpaceLocationFlags::default(), - )) - .insert(bundle.clone()) - .id(); - } - bones +fn set_spawn_executor(mut cmds: Commands) { + cmds.insert_resource(SpawnHandTrackerCommandExecutor(handle_tracker_spawn)) } -fn spawn_default_hands( - mut cmds: Commands, - session: Res, - root: Query>, -) { - debug!("spawning default hands"); - let Ok(root) = root.get_single() else { - error!("unable to get tracking root, skipping hand creation"); +fn handle_tracker_spawn(world: &mut World, tracker: Entity, side: HandSide) { + let Some(session) = world.get_resource_mut::() else { + error!("unable to get session while creating hand tracker"); return; }; - let tracker_left = match session.create_hand_tracker(openxr::HandEXT::LEFT) { + debug!("spawning hand"); + let oxr_tracker = match session.create_hand_tracker(match side { + HandSide::Left => openxr::HandEXT::LEFT, + HandSide::Right => openxr::HandEXT::RIGHT, + }) { Ok(t) => t, Err(openxr::sys::Result::ERROR_EXTENSION_NOT_PRESENT) => { warn!("Handtracking Extension not loaded, Unable to create Handtracker!"); @@ -79,33 +64,44 @@ fn spawn_default_hands( return; } }; - let tracker_right = match session.create_hand_tracker(openxr::HandEXT::RIGHT) { - Ok(t) => t, - Err(openxr::sys::Result::ERROR_EXTENSION_NOT_PRESENT) => { - warn!("Handtracking Extension not loaded, Unable to create Handtracker!"); - return; - } - Err(err) => { - warn!("Error while creating Handtracker: {}", err.to_string()); - return; - } + + world + .entity_mut(tracker) + .insert(OxrHandTracker(oxr_tracker)); +} + +fn spawn_default_hands(mut cmds: Commands, root: Query>) { + let Ok(root) = root.get_single() else { + error!("unable to get tracking root, skipping handtracker creation"); + return; }; - let left_bones = spawn_hand_bones(&mut cmds, (DefaultHandBone, LeftHand)); - let right_bones = spawn_hand_bones(&mut cmds, (DefaultHandBone, RightHand)); + debug!("spawning default hands"); + let left_bones = spawn_hand_bones(&mut cmds, |_| { + ( + DefaultHandBone, + LeftHand, + OxrSpaceLocationFlags(openxr::SpaceLocationFlags::default()), + ) + }); + let right_bones = spawn_hand_bones(&mut cmds, |_| { + ( + DefaultHandBone, + RightHand, + OxrSpaceLocationFlags(openxr::SpaceLocationFlags::default()), + ) + }); cmds.entity(root).push_children(&left_bones); cmds.entity(root).push_children(&right_bones); - cmds.spawn(( - DefaultHandTracker, - OxrHandTracker(tracker_left), - XrHandBoneEntities(left_bones), - LeftHand, - )); - cmds.spawn(( - DefaultHandTracker, - OxrHandTracker(tracker_right), - XrHandBoneEntities(right_bones), - RightHand, - )); + cmds.push(SpawnHandTracker { + joints: XrHandBoneEntities(left_bones), + tracker_bundle: DefaultHandTracker, + side: HandSide::Left, + }); + cmds.push(SpawnHandTracker { + joints: XrHandBoneEntities(right_bones), + tracker_bundle: DefaultHandTracker, + side: HandSide::Right, + }); } #[derive(Component, Clone, Copy)] diff --git a/crates/bevy_xr/src/hands.rs b/crates/bevy_xr/src/hands.rs index 82e400a..17a6fd5 100644 --- a/crates/bevy_xr/src/hands.rs +++ b/crates/bevy_xr/src/hands.rs @@ -1,16 +1,50 @@ use bevy::{ - ecs::{component::Component, entity::Entity}, + app::Plugin, + ecs::{component::Component, entity::Entity, world::Command}, + hierarchy::BuildWorldChildren, + log::{error, warn}, math::bool, - prelude::{Deref, DerefMut}, + prelude::{Bundle, Commands, Deref, DerefMut, Resource, SpatialBundle, With, World}, +}; + +use crate::{ + session::{XrSessionCreated, XrTrackingRoot}, + spaces::XrSpaceLocationFlags, }; pub const HAND_JOINT_COUNT: usize = 26; +pub fn spawn_hand_bones( + cmds: &mut Commands, + mut get_bundle: impl FnMut(HandBone) -> T, +) -> [Entity; HAND_JOINT_COUNT] { + let mut bones: [Entity; HAND_JOINT_COUNT] = [Entity::PLACEHOLDER; HAND_JOINT_COUNT]; + for bone in HandBone::get_all_bones().into_iter() { + bones[bone as usize] = cmds + .spawn(( + SpatialBundle::default(), + bone, + HandBoneRadius(0.0), + XrSpaceLocationFlags::default(), + )) + .insert((get_bundle)(bone)) + .id(); + } + bones +} + +#[derive(Clone, Copy, Component, Debug)] +pub enum HandSide { + Left, + Right, +} + #[derive(Clone, Copy, Component, Debug)] pub struct LeftHand; #[derive(Clone, Copy, Component, Debug)] pub struct RightHand; +/// Hand Joint Entities orderd #[derive(Deref, DerefMut, Component, Clone, Copy)] pub struct XrHandBoneEntities(pub [Entity; HAND_JOINT_COUNT]); @@ -141,3 +175,43 @@ impl HandBone { ] } } + +/// Use by a backend to run custom logic when spawning a hand tracker +#[derive(Resource)] +pub struct SpawnHandTrackerCommandExecutor(pub fn(&mut World, Entity, HandSide)); + +/// `tracker_bundle` is inserted after the backend specific code is run +pub struct SpawnHandTracker { + pub joints: XrHandBoneEntities, + pub tracker_bundle: B, + pub side: HandSide, +} + +impl Command for SpawnHandTracker { + fn apply(self, world: &mut bevy::prelude::World) { + let Some(executor) = world.remove_resource::() else { + warn!("no SpawnHandTracker executor defined, skipping handtracker creation"); + return; + }; + let Ok(root) = world + .query_filtered::>() + .get_single(world) + else { + error!("unable to get tracking root, skipping handtracker creation"); + return; + }; + let mut tracker = world.spawn(self.joints); + match &self.side { + HandSide::Left => tracker.insert(LeftHand), + HandSide::Right => tracker.insert(LeftHand), + }; + let tracker = tracker.id(); + world.entity_mut(root).push_children(&[tracker]); + executor.0(world, tracker, self.side); + if let Some(mut tracker) = world.get_entity_mut(tracker) { + tracker.insert(self.side); + tracker.insert(self.tracker_bundle); + } + world.insert_resource(executor); + } +}