use std::{f32::consts::PI, ops::Mul, time::Duration}; use bevy::{ diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}, ecs::schedule::ScheduleLabel, input::{keyboard::KeyCode, Input}, log::info, prelude::{ bevy_main, default, shape, App, Assets, Color, Commands, Component, Entity, Event, EventReader, EventWriter, FixedUpdate, Gizmos, GlobalTransform, IntoSystemConfigs, IntoSystemSetConfigs, Mesh, PbrBundle, PostUpdate, Quat, Query, Res, ResMut, Resource, Schedule, SpatialBundle, StandardMaterial, Startup, Transform, Update, Vec3, Vec3Swizzles, With, Without, World, }, time::{Fixed, Time, Timer, TimerMode}, transform::TransformSystem, }; use bevy_oxr::{ graphics::{extensions::XrExtensions, XrAppInfo, XrPreferdBlendMode}, input::XrInput, resources::{XrFrameState, XrInstance, XrSession}, xr_init::{xr_only, XrEnableRequest, XrEnableStatus}, xr_input::{ actions::XrActionSets, debug_gizmos::OpenXrDebugRenderer, hands::common::{HandInputDebugRenderer, HandResource, HandsResource, OpenXrHandInput}, hands::HandBone, 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, OpenXRTrackingRoot, }, Hand, }, DefaultXrPlugins, }; fn input_stuff( keys: Res>, status: Res, mut request: EventWriter, ) { if keys.just_pressed(KeyCode::Space) { match status.into_inner() { XrEnableStatus::Enabled => request.send(XrEnableRequest::TryDisable), XrEnableStatus::Disabled => request.send(XrEnableRequest::TryEnable), } } } 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(); let mut xr_extensions = XrExtensions::default(); app.add_systems(Update, input_stuff) //lets get the usual diagnostic stuff added .add_plugins(LogDiagnosticsPlugin::default()) .add_plugins(FrameTimeDiagnosticsPlugin) //lets get the xr defaults added .add_plugins(DefaultXrPlugins { reqeusted_extensions: xr_extensions, prefered_blend_mode: XrPreferdBlendMode::Opaque, app_info: XrAppInfo { name: "Bevy OXR Demo".into(), }, }) //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.run_if(xr_only())) .insert_resource(PrototypeLocomotionConfig::default()) //lets add the interaction systems .add_event::() .add_systems(Update, prototype_interaction_input.run_if(xr_only())) .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 .run_if(xr_only()) .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.run_if(xr_only())) .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( FixedUpdate, update_physics_hands.before(PhysicsSet::SyncBackend), ) .add_event::() .add_systems(Update, handle_ghost_hand_events.after(update_grabbables)) .insert_resource(GhostTimers { left: Timer::from_seconds(0.25, TimerMode::Once), right: Timer::from_seconds(0.25, TimerMode::Once), }) .add_systems(Update, watch_ghost_timers.before(handle_ghost_hand_events)); //configure rapier sets let mut physics_schedule = Schedule::new(PhysicsSchedule); physics_schedule.configure_sets( ( PhysicsSet::SyncBackend, PhysicsSet::StepSimulation, PhysicsSet::Writeback, ) .chain() .before(TransformSystem::TransformPropagate), ); app.configure_sets( PostUpdate, ( PhysicsSet::SyncBackend, PhysicsSet::StepSimulation, PhysicsSet::Writeback, ) .chain() .before(TransformSystem::TransformPropagate), ); //add rapier systems physics_schedule.add_systems(( RapierPhysicsPlugin::::get_systems(PhysicsSet::SyncBackend) .in_set(PhysicsSet::SyncBackend), RapierPhysicsPlugin::::get_systems(PhysicsSet::StepSimulation) .in_set(PhysicsSet::StepSimulation), RapierPhysicsPlugin::::get_systems(PhysicsSet::Writeback) .in_set(PhysicsSet::Writeback), )); app.add_schedule(physics_schedule) // configure our fixed timestep schedule to run at the rate we want .insert_resource(Time::::from_duration(Duration::from_secs_f32( FIXED_TIMESTEP, ))) .add_systems(FixedUpdate, run_physics_schedule) .add_systems(Startup, configure_physics); app.run(); } //fixed timesteps? const FIXED_TIMESTEP: f32 = 1. / 90.; // A label for our new Schedule! #[derive(ScheduleLabel, Debug, Hash, PartialEq, Eq, Clone)] struct PhysicsSchedule; fn run_physics_schedule(world: &mut World) { world.run_schedule(PhysicsSchedule); } fn configure_physics(mut rapier_config: ResMut) { rapier_config.timestep_mode = TimestepMode::Fixed { dt: FIXED_TIMESTEP, substeps: 1, } } fn spawn_controllers_example(mut commands: Commands) { //left hand commands.spawn(( OpenXRLeftController, OpenXRController, OpenXRTracker, SpatialBundle::default(), XRDirectInteractor, XRInteractorState::default(), XRSelection::default(), Hand::Left, )); //right hand commands.spawn(( OpenXRRightController, OpenXRController, OpenXRTracker, SpatialBundle::default(), XRDirectInteractor, XRInteractorState::default(), XRSelection::default(), Hand::Right, )); } 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, ]; let radius = 0.010; let left_hand_membership_group = Group::GROUP_1; let right_hand_membership_group = Group::GROUP_2; let floor_membership = Group::GROUP_3; for hand in hands.iter() { let hand_membership = match hand { Hand::Left => left_hand_membership_group, Hand::Right => right_hand_membership_group, }; let mut hand_filter: Group = Group::ALL; hand_filter.remove(hand_membership); hand_filter.remove(floor_membership); 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::Dynamic, Velocity::default(), CollisionGroups::new(hand_membership, Group::from_bits(0b0001).unwrap()), // SolverGroups::new(self_group, interaction_group), bone.clone(), BoneInitState::False, hand.clone(), )); } } } pub enum MatchingType { PositionMatching, VelocityMatching, } fn update_physics_hands( hands_res: Option>, mut bone_query: Query<( &mut Transform, &mut Collider, &PhysicsHandBone, &mut BoneInitState, &Hand, &mut Velocity, )>, hand_query: Query<(&Transform, &HandBone, &Hand, Without)>, time: Res