diff --git a/examples/demo/src/main.rs b/examples/demo/src/main.rs index 2f7fbb2..3aa7447 100644 --- a/examples/demo/src/main.rs +++ b/examples/demo/src/main.rs @@ -15,10 +15,11 @@ use bevy_openxr::{ resources::{XrFrameState, XrInstance, XrSession}, xr_input::{ debug_gizmos::OpenXrDebugRenderer, + hand::{HandBone, HandInputDebugRenderer, HandResource, HandsResource, OpenXrHandInput}, interactions::{ draw_interaction_gizmos, draw_socket_gizmos, interactions, socket_interactions, update_interactable_states, InteractionEvent, Touched, XRDirectInteractor, - XRInteractable, XRInteractableState, XRInteractorState, XRRayInteractor, + XRInteractable, XRInteractableState, XRInteractorState, XRSelection, }, oculus_touch::OculusController, prototype_locomotion::{proto_locomotion, PrototypeLocomotionConfig}, @@ -49,7 +50,7 @@ fn main() { .add_plugins(OpenXrDebugRenderer) //rapier goes here .add_plugins(RapierPhysicsPlugin::::default().with_default_system_setup(false)) - // .add_plugins(RapierDebugRenderPlugin::default()) + .add_plugins(RapierDebugRenderPlugin::default()) //lets setup the starting scene .add_systems(Startup, setup_scene) .add_systems(Startup, spawn_controllers_example) //you need to spawn controllers or it crashes TODO:: Fix this @@ -80,7 +81,14 @@ fn main() { bevy::time::TimerMode::Once, ))) .add_systems(Update, request_cube_spawn) - .add_systems(Update, cube_spawner.after(request_cube_spawn)); + .add_systems(Update, cube_spawner.after(request_cube_spawn)) + //test capsule + .add_systems(Startup, spawn_capsule) + //physics hands + .add_plugins(OpenXrHandInput) + .add_plugins(HandInputDebugRenderer) + .add_systems(Startup, spawn_physics_hands) + .add_systems(Update, update_physics_hands); //configure rapier sets app.configure_sets( @@ -121,6 +129,7 @@ fn spawn_controllers_example(mut commands: Commands) { SpatialBundle::default(), XRDirectInteractor, XRInteractorState::default(), + XRSelection::default(), )); //right hand commands.spawn(( @@ -130,9 +139,283 @@ fn spawn_controllers_example(mut commands: Commands) { SpatialBundle::default(), XRDirectInteractor, XRInteractorState::default(), + XRSelection::default(), )); } +fn spawn_capsule( + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, +) { + commands.spawn(( + PbrBundle { + mesh: meshes.add(Mesh::from(shape::Capsule { + radius: 0.033, + depth: 0.115, + ..default() + })), + material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()), + transform: Transform::from_xyz(0.0, 2.0, 0.0), + ..default() + }, + // Collider::capsule_y(0.0575, 0.034), + Collider::capsule( + Vec3 { + x: 0.0, + y: -0.0575, + z: 0.0, + }, + Vec3 { + x: 0.0, + y: 0.0575, + z: 0.0, + }, + 0.034, + ), + RigidBody::Dynamic, + )); +} + +#[derive(Component, PartialEq, Debug, Clone, Copy)] +pub enum PhysicsHandBone { + Palm, + Wrist, + ThumbMetacarpal, + ThumbProximal, + ThumbDistal, + ThumbTip, + IndexMetacarpal, + IndexProximal, + IndexIntermediate, + IndexDistal, + IndexTip, + MiddleMetacarpal, + MiddleProximal, + MiddleIntermediate, + MiddleDistal, + MiddleTip, + RingMetacarpal, + RingProximal, + RingIntermediate, + RingDistal, + RingTip, + LittleMetacarpal, + LittleProximal, + LittleIntermediate, + LittleDistal, + LittleTip, +} +#[derive(Component, PartialEq)] +pub enum BoneInitState { + True, + False, +} + +fn spawn_physics_hands(mut commands: Commands) { + //here we go + let hands = [Hand::Left, Hand::Right]; + let bones = [ + PhysicsHandBone::Palm, + PhysicsHandBone::Wrist, + PhysicsHandBone::ThumbMetacarpal, + PhysicsHandBone::ThumbProximal, + PhysicsHandBone::ThumbDistal, + PhysicsHandBone::ThumbTip, + PhysicsHandBone::IndexMetacarpal, + PhysicsHandBone::IndexProximal, + PhysicsHandBone::IndexIntermediate, + PhysicsHandBone::IndexDistal, + PhysicsHandBone::IndexTip, + PhysicsHandBone::MiddleMetacarpal, + PhysicsHandBone::MiddleProximal, + PhysicsHandBone::MiddleIntermediate, + PhysicsHandBone::MiddleDistal, + PhysicsHandBone::MiddleTip, + PhysicsHandBone::RingMetacarpal, + PhysicsHandBone::RingProximal, + PhysicsHandBone::RingIntermediate, + PhysicsHandBone::RingDistal, + PhysicsHandBone::RingTip, + PhysicsHandBone::LittleMetacarpal, + PhysicsHandBone::LittleProximal, + PhysicsHandBone::LittleIntermediate, + PhysicsHandBone::LittleDistal, + PhysicsHandBone::LittleTip, + ]; + //lets just do the Right ThumbMetacarpal for now + //i dont understand the groups yet + let self_group = Group::GROUP_1; + let interaction_group = Group::ALL; + let radius = 0.010; + + for hand in hands.iter() { + for bone in bones.iter() { + //spawn the thing + commands.spawn(( + SpatialBundle::default(), + Collider::capsule( + Vec3 { + x: 0.0, + y: -0.0575, + z: 0.0, + }, + Vec3 { + x: 0.0, + y: 0.0575, + z: 0.0, + }, + radius, + ), + RigidBody::KinematicPositionBased, + // CollisionGroups::new(self_group, interaction_group), + // SolverGroups::new(self_group, interaction_group), + bone.clone(), + BoneInitState::False, + hand.clone(), + )); + } + } +} + +fn update_physics_hands( + hands_res: Option>, + mut bone_query: Query<( + &mut Transform, + &mut Collider, + &PhysicsHandBone, + &mut BoneInitState, + &Hand, + )>, + hand_query: Query<(&Transform, &HandBone, &Hand, Without)>, +) { + //sanity check do we even have hands? + match hands_res { + Some(res) => { + //config stuff + let radius = 0.010; + for mut bone in bone_query.iter_mut() { + let hand_res = match bone.4 { + Hand::Left => res.left, + Hand::Right => res.right, + }; + + //lets just do the Right ThumbMetacarpal for now + let result = get_start_and_end_entities(hand_res, bone.2); + if let Some((start_entity, end_entity)) = result { + //now we need their transforms + let start_components = hand_query.get(start_entity); + let end_components = hand_query.get(end_entity); + let direction = end_components.unwrap().0.translation + - start_components.unwrap().0.translation; + if direction.length() < 0.001 { + //i hate this but we need to skip init if the length is zero + return; + } + + match *bone.3 { + BoneInitState::True => { + //if we are init then we just move em? + *bone.0 = start_components + .unwrap() + .0 + .clone() + .looking_at(end_components.unwrap().0.translation, Vec3::Y); + } + BoneInitState::False => { + //build a new collider? + *bone.1 = Collider::capsule( + Vec3::splat(0.0), + Vec3 { + x: 0.0, + y: 0.0, + z: -direction.length(), + }, + radius, + ); + *bone.3 = BoneInitState::True; + } + } + } + } + } + None => info!("hand states resource not initialized yet"), + } +} + +fn get_start_and_end_entities( + hand_res: HandResource, + bone: &PhysicsHandBone, +) -> Option<(Entity, Entity)> { + match bone { + PhysicsHandBone::Palm => return None, + PhysicsHandBone::Wrist => return None, + PhysicsHandBone::ThumbMetacarpal => { + return Some((hand_res.thumb.metacarpal, hand_res.thumb.proximal)) + } + PhysicsHandBone::ThumbProximal => { + return Some((hand_res.thumb.proximal, hand_res.thumb.distal)) + } + PhysicsHandBone::ThumbDistal => return Some((hand_res.thumb.distal, hand_res.thumb.tip)), + PhysicsHandBone::ThumbTip => return None, + PhysicsHandBone::IndexMetacarpal => { + return Some((hand_res.index.metacarpal, hand_res.index.proximal)) + } + PhysicsHandBone::IndexProximal => { + return Some((hand_res.index.proximal, hand_res.index.intermediate)) + } + PhysicsHandBone::IndexIntermediate => { + return Some((hand_res.index.intermediate, hand_res.index.distal)) + } + PhysicsHandBone::IndexDistal => return Some((hand_res.index.distal, hand_res.index.tip)), + PhysicsHandBone::IndexTip => return None, + PhysicsHandBone::MiddleMetacarpal => { + return Some((hand_res.middle.metacarpal, hand_res.middle.proximal)) + } + PhysicsHandBone::MiddleProximal => { + return Some((hand_res.middle.proximal, hand_res.middle.intermediate)) + } + PhysicsHandBone::MiddleIntermediate => { + return Some((hand_res.middle.intermediate, hand_res.middle.distal)) + } + PhysicsHandBone::MiddleDistal => { + return Some((hand_res.middle.distal, hand_res.middle.tip)) + } + PhysicsHandBone::MiddleTip => return None, + PhysicsHandBone::RingMetacarpal => { + return Some((hand_res.ring.metacarpal, hand_res.ring.proximal)) + } + PhysicsHandBone::RingProximal => { + return Some((hand_res.ring.proximal, hand_res.ring.intermediate)) + } + PhysicsHandBone::RingIntermediate => { + return Some((hand_res.ring.intermediate, hand_res.ring.distal)) + } + PhysicsHandBone::RingDistal => return Some((hand_res.ring.distal, hand_res.ring.tip)), + PhysicsHandBone::RingTip => return None, + PhysicsHandBone::LittleMetacarpal => { + return Some((hand_res.little.metacarpal, hand_res.little.proximal)) + } + PhysicsHandBone::LittleProximal => { + return Some((hand_res.little.proximal, hand_res.little.intermediate)) + } + PhysicsHandBone::LittleIntermediate => { + return Some((hand_res.little.intermediate, hand_res.little.distal)) + } + PhysicsHandBone::LittleDistal => { + return Some((hand_res.little.distal, hand_res.little.tip)) + } + PhysicsHandBone::LittleTip => return None, + }; +} + +fn get_hand_res(res: &Res<'_, HandsResource>, hand: Hand) -> HandResource { + match hand { + Hand::Left => res.left.clone(), + Hand::Right => res.right.clone(), + } +} + #[derive(Event, Default)] pub struct SpawnCubeRequest; @@ -247,12 +530,18 @@ pub struct Grabbable; pub fn update_grabbables( mut events: EventReader, mut grabbable_query: Query<( + Entity, &mut Transform, With, Without, Option<&mut RigidBody>, )>, - interactor_query: Query<(&GlobalTransform, &XRInteractorState, Without)>, + mut interactor_query: Query<( + &GlobalTransform, + &XRInteractorState, + &mut XRSelection, + Without, + )>, ) { //so basically the idea is to try all the events? for event in events.read() { @@ -261,24 +550,48 @@ pub fn update_grabbables( Ok(mut grabbable_transform) => { // info!("we got a grabbable"); //now we need the location of our interactor - match interactor_query.get(event.interactor) { - Ok(interactor_transform) => { - match interactor_transform.1 { - XRInteractorState::Idle => match grabbable_transform.3 { - Some(mut thing) => { - *thing = RigidBody::Dynamic; - } - None => (), - }, - XRInteractorState::Selecting => { - // info!("its a direct interactor?"); - match grabbable_transform.3 { - Some(mut thing) => { - *thing = RigidBody::KinematicPositionBased; + match interactor_query.get_mut(event.interactor) { + Ok(mut interactor_transform) => { + match *interactor_transform.2 { + XRSelection::Empty => { + match interactor_transform.1 { + XRInteractorState::Idle => match grabbable_transform.4 { + Some(mut thing) => { + *thing = RigidBody::Dynamic; + *interactor_transform.2 = XRSelection::Empty; + } + None => (), + }, + XRInteractorState::Selecting => { + // info!("its a direct interactor?"); + match grabbable_transform.4 { + Some(mut thing) => { + *thing = RigidBody::KinematicPositionBased; + *interactor_transform.2 = + XRSelection::Full(grabbable_transform.0); + } + None => (), + } + *grabbable_transform.1 = + interactor_transform.0.compute_transform(); } - None => (), } - *grabbable_transform.0 = interactor_transform.0.compute_transform(); + } + XRSelection::Full(ent) => { + info!("nah bro we holding something"); + match grabbable_transform.0 == ent { + true => { + *grabbable_transform.1 = + interactor_transform.0.compute_transform(); + } + false => {} + } + match interactor_transform.1 { + XRInteractorState::Idle => { + *interactor_transform.2 = XRSelection::Empty + } + XRInteractorState::Selecting => {} + } } } } diff --git a/src/xr_input/hand.rs b/src/xr_input/hand.rs index 81f5936..f23b18f 100644 --- a/src/xr_input/hand.rs +++ b/src/xr_input/hand.rs @@ -38,7 +38,7 @@ impl Plugin for OpenXrHandInput { #[derive(Default)] pub struct HandInputDebugRenderer; -impl Plugin for HandInputDebugRenderer{ +impl Plugin for HandInputDebugRenderer { fn build(&self, app: &mut bevy::prelude::App) { app.add_systems(PostUpdate, draw_hand_entities); } @@ -56,12 +56,12 @@ impl Default for HandInputSource { } } -#[derive(Resource, Default)] +#[derive(Resource, Default, Clone, Copy)] pub struct HandsResource { pub left: HandResource, pub right: HandResource, } - +#[derive(Clone, Copy)] pub struct HandResource { pub palm: Entity, pub wrist: Entity, @@ -85,7 +85,7 @@ impl Default for HandResource { } } } - +#[derive(Clone, Copy)] pub struct ThumbResource { pub metacarpal: Entity, pub proximal: Entity, @@ -103,6 +103,7 @@ impl Default for ThumbResource { } } } +#[derive(Clone, Copy)] pub struct IndexResource { pub metacarpal: Entity, pub proximal: Entity, @@ -122,6 +123,7 @@ impl Default for IndexResource { } } } +#[derive(Clone, Copy)] pub struct MiddleResource { pub metacarpal: Entity, pub proximal: Entity, @@ -140,6 +142,7 @@ impl Default for MiddleResource { } } } +#[derive(Clone, Copy)] pub struct RingResource { pub metacarpal: Entity, pub proximal: Entity, @@ -158,6 +161,7 @@ impl Default for RingResource { } } } +#[derive(Clone, Copy)] pub struct LittleResource { pub metacarpal: Entity, pub proximal: Entity, diff --git a/src/xr_input/interactions.rs b/src/xr_input/interactions.rs index 9660839..5d7463b 100644 --- a/src/xr_input/interactions.rs +++ b/src/xr_input/interactions.rs @@ -42,6 +42,16 @@ impl Default for XRInteractorState { XRInteractorState::Idle } } +#[derive(Component)] +pub enum XRSelection { + Empty, + Full(Entity) +} +impl Default for XRSelection { + fn default() -> Self { + XRSelection::Empty + } +} #[derive(Component)] pub struct XRInteractable;