use bevy::{ diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}, log::info, prelude::{ *, bevy_main, default, shape, App, Assets, Color, Commands, Component, Event, EventReader, EventWriter, GlobalTransform, IntoSystemConfigs, IntoSystemSetConfigs, Mesh, PbrBundle, PostUpdate, Query, Res, ResMut, Resource, SpatialBundle, StandardMaterial, Startup, Transform, Update, With, Without, }, time::{Time, Timer}, transform::TransformSystem, }; use bevy_openxr::{ input::XrInput, 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, XRSelection, }, oculus_touch::OculusController, prototype_locomotion::{proto_locomotion, PrototypeLocomotionConfig}, trackers::{OpenXRController, OpenXRLeftController, OpenXRRightController, OpenXRTracker}, Hand, }, DefaultXrPlugins, }; mod setup; use crate::setup::setup_scene; use bevy_rapier3d::prelude::*; #[bevy_main] pub fn main() { color_eyre::install().unwrap(); info!("Running bevy_openxr demo"); let mut app = App::new(); app //lets get the usual diagnostic stuff added .add_plugins(LogDiagnosticsPlugin::default()) .add_plugins(FrameTimeDiagnosticsPlugin) //lets get the xr defaults added .add_plugins(DefaultXrPlugins) //lets add the debug renderer for the controllers .add_plugins(OpenXrDebugRenderer) //rapier goes here .add_plugins(RapierPhysicsPlugin::::default().with_default_system_setup(false)) .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 //add locomotion .add_systems(Update, proto_locomotion) .insert_resource(PrototypeLocomotionConfig::default()) //lets add the interaction systems .add_event::() .add_systems(Update, prototype_interaction_input) .add_systems(Update, interactions.before(update_interactable_states)) .add_systems(Update, update_interactable_states) .add_systems( Update, socket_interactions.before(update_interactable_states), ) //add the grabbable system .add_systems(Update, update_grabbables.after(update_interactable_states)) //draw the interaction gizmos .add_systems( Update, draw_interaction_gizmos.after(update_interactable_states), ) .add_systems(Update, draw_socket_gizmos.after(update_interactable_states)) //add our cube spawning system .add_event::() .insert_resource(SpawnCubeTimer(Timer::from_seconds( 0.25, bevy::time::TimerMode::Once, ))) .add_systems(Update, 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( PostUpdate, ( PhysicsSet::SyncBackend, PhysicsSet::StepSimulation, PhysicsSet::Writeback, ) .chain() .before(TransformSystem::TransformPropagate), ); //add rapier systems app.add_systems( PostUpdate, ( RapierPhysicsPlugin::::get_systems(PhysicsSet::SyncBackend) .in_set(PhysicsSet::SyncBackend), ( RapierPhysicsPlugin::::get_systems(PhysicsSet::StepSimulation), // despawn_one_box, ) .in_set(PhysicsSet::StepSimulation), RapierPhysicsPlugin::::get_systems(PhysicsSet::Writeback) .in_set(PhysicsSet::Writeback), ), ); app.run(); } fn spawn_controllers_example(mut commands: Commands) { //left hand commands.spawn(( OpenXRLeftController, OpenXRController, OpenXRTracker, SpatialBundle::default(), XRDirectInteractor, XRInteractorState::default(), XRSelection::default(), )); //right hand commands.spawn(( OpenXRRightController, OpenXRController, OpenXRTracker, 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; #[derive(Resource)] pub struct SpawnCubeTimer(Timer); fn request_cube_spawn( oculus_controller: Res, frame_state: Res, xr_input: Res, instance: Res, session: Res, mut writer: EventWriter, time: Res