From 6e83b887be28d6f38962d5f13293b580b27ee28b Mon Sep 17 00:00:00 2001 From: Jay Christy Date: Wed, 27 Sep 2023 23:43:03 -0400 Subject: [PATCH] interactions, and grabbables --- examples/xr.rs | 59 +++++++++-- src/xr_input/interactions.rs | 188 ++++++++++++++++++++--------------- 2 files changed, 159 insertions(+), 88 deletions(-) diff --git a/examples/xr.rs b/examples/xr.rs index e8252f4..68b7b5d 100644 --- a/examples/xr.rs +++ b/examples/xr.rs @@ -5,13 +5,13 @@ use bevy_openxr::input::XrInput; use bevy_openxr::resources::{XrFrameState, XrInstance, XrSession}; use bevy_openxr::xr_input::debug_gizmos::OpenXrDebugRenderer; use bevy_openxr::xr_input::interactions::{ - draw_interaction_gizmos, direct_interaction, XRDirectInteractor, XRInteractable, - XRInteractableState, XRInteractorState, XRRayInteractor, ray_interaction, + interactions, draw_interaction_gizmos, update_interactable_states, InteractionEvent, + XRDirectInteractor, XRInteractable, XRInteractableState, XRInteractorState, XRRayInteractor, }; use bevy_openxr::xr_input::oculus_touch::OculusController; use bevy_openxr::xr_input::prototype_locomotion::{proto_locomotion, PrototypeLocomotionConfig}; use bevy_openxr::xr_input::trackers::{ - OpenXRController, OpenXRLeftController, OpenXRRightController, OpenXRTracker, AimPose, + AimPose, OpenXRController, OpenXRLeftController, OpenXRRightController, OpenXRTracker, }; use bevy_openxr::xr_input::Hand; use bevy_openxr::DefaultXrPlugins; @@ -29,10 +29,15 @@ fn main() { .add_systems(Update, proto_locomotion) .add_systems(Startup, spawn_controllers_example) .insert_resource(PrototypeLocomotionConfig::default()) - .add_systems(Update, draw_interaction_gizmos) - .add_systems(Update, direct_interaction) - .add_systems(Update, ray_interaction) + .add_systems( + Update, + draw_interaction_gizmos.after(update_interactable_states), + ) + .add_systems(Update, interactions) .add_systems(Update, prototype_interaction_input) + .add_systems(Update, update_interactable_states.after(interactions)) + .add_systems(Update, update_grabbables.after(update_interactable_states)) + .add_event::() .run(); } @@ -85,6 +90,7 @@ fn setup( }, XRInteractable, XRInteractableState::default(), + Grabbable, )); } @@ -95,7 +101,6 @@ fn spawn_controllers_example(mut commands: Commands) { OpenXRController, OpenXRTracker, SpatialBundle::default(), - XRDirectInteractor, XRRayInteractor, AimPose(Transform::default()), XRInteractorState::default(), @@ -128,7 +133,7 @@ fn prototype_interaction_input( mut left_interactor_query: Query< (&mut XRInteractorState), ( - With, + With, With, Without, ), @@ -155,3 +160,41 @@ fn prototype_interaction_input( *right_state = XRInteractorState::Idle; } } + +#[derive(Component)] +pub struct Grabbable; + +pub fn update_grabbables( + mut events: EventReader, + mut grabbable_query: Query<(&mut Transform, With, Without)>, + interactor_query: Query<(&GlobalTransform, With, Without)>, +) { + //so basically the idea is to try all the events? + for event in events.read() { + info!("some event"); + match grabbable_query.get_mut(event.interactable) { + 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) => { + info!("its a direct interactor?"); + info!( + "before gT: {:?}, iT: {:?}", + grabbable_transform, interactor_transform + ); + *grabbable_transform.0 = interactor_transform.0.compute_transform(); + info!( + "after gT: {:?}, iT: {:?}", + grabbable_transform, interactor_transform + ); + } + Err(_) => info!("not a direct interactor"), + } + } + Err(_) => { + info!("not a grabbable?") + } + } + } +} diff --git a/src/xr_input/interactions.rs b/src/xr_input/interactions.rs index a36d832..86a35e5 100644 --- a/src/xr_input/interactions.rs +++ b/src/xr_input/interactions.rs @@ -1,7 +1,8 @@ use std::f32::consts::PI; use bevy::prelude::{ - info, Color, Component, Gizmos, GlobalTransform, Quat, Query, Transform, Vec3, With, Without, + info, Color, Component, Entity, Event, EventReader, EventWriter, Gizmos, GlobalTransform, Quat, + Query, Transform, Vec3, With, Without, }; use super::trackers::{AimPose, OpenXRTrackingRoot}; @@ -12,7 +13,7 @@ pub struct XRDirectInteractor; #[derive(Component)] pub struct XRRayInteractor; -#[derive(Component)] +#[derive(Component, Clone, Copy)] pub enum XRInteractableState { Idle, Hover, @@ -110,46 +111,113 @@ pub fn draw_interaction_gizmos( } } -pub fn direct_interaction( +#[derive(Event)] +pub struct InteractionEvent { + pub interactor: Entity, + pub interactable: Entity, + pub interactable_state: XRInteractableState, +} + +pub fn interactions( mut interactable_query: Query< - (&GlobalTransform, &mut XRInteractableState), + (&GlobalTransform, &mut XRInteractableState, Entity), (With, Without), >, interactor_query: Query< - (&GlobalTransform, &XRInteractorState), - (With, Without), + ( + &GlobalTransform, + &XRInteractorState, + Entity, + Option<&XRDirectInteractor>, + Option<&XRRayInteractor>, + Option<&AimPose>, + ), + (Without), >, + tracking_root_query: Query<(&mut Transform, With)>, + mut writer: EventWriter, ) { - for (xr_interactable_global_transform, mut state) in interactable_query.iter_mut() { + for (xr_interactable_global_transform, mut state, interactable_entity) in + interactable_query.iter_mut() + { let mut hovered = false; - let mut selected = false; - for (interactor_global_transform, interactor_state) in interactor_query.iter() { - //check for sphere overlaps - let size = 0.1; - if interactor_global_transform - .compute_transform() - .translation - .distance_squared( - xr_interactable_global_transform + for (interactor_global_transform, interactor_state, interactor_entity, direct, ray, aim) in + interactor_query.iter() + { + match direct { + Some(_) => { + //check for sphere overlaps + let size = 0.1; + if interactor_global_transform .compute_transform() - .translation, - ) - < (size * size) * 2.0 - { - //check for selections first - match interactor_state { - XRInteractorState::Idle => hovered = true, - XRInteractorState::Selecting => { - selected = true; + .translation + .distance_squared( + xr_interactable_global_transform + .compute_transform() + .translation, + ) + < (size * size) * 2.0 + { + //check for selections first + match interactor_state { + XRInteractorState::Idle => hovered = true, + XRInteractorState::Selecting => { + //welp now I gota actually make things do stuff lol + let event = InteractionEvent { + interactor: interactor_entity, + interactable: interactable_entity, + interactable_state: XRInteractableState::Select, + }; + writer.send(event); + } + } } } + None => (), + } + match ray { + Some(_) => { + //check for ray-sphere intersection + let sphere_transform = xr_interactable_global_transform.compute_transform(); + let center = sphere_transform.translation; + let radius: f32 = 0.1; + //I hate this but the aim pose needs the root for now + let root = tracking_root_query.get_single().unwrap().0; + match aim { + Some(aim) => { + let ray_origin = + root.translation + root.rotation.mul_vec3(aim.0.translation); + let ray_dir = root.rotation.mul_vec3(aim.0.forward()); + + if ray_sphere_intersection( + center, + radius, + ray_origin, + ray_dir.normalize_or_zero(), + ) { + //check for selections first + match interactor_state { + XRInteractorState::Idle => hovered = true, + XRInteractorState::Selecting => { + //welp now I gota actually make things do stuff lol + let event = InteractionEvent { + interactor: interactor_entity, + interactable: interactable_entity, + interactable_state: XRInteractableState::Select, + }; + writer.send(event); + } + } + } + } + None => info!("no aim pose"), + } + } + None => (), } } - //check what we found - //also i dont like this - if selected { - *state = XRInteractableState::Select; - } else if hovered { + //still hate this + if hovered { *state = XRInteractableState::Hover; } else { *state = XRInteractableState::Idle; @@ -157,58 +225,18 @@ pub fn direct_interaction( } } -pub fn ray_interaction( - mut interactable_query: Query< - (&GlobalTransform, &mut XRInteractableState), - (With, Without), - >, - interactor_query: Query< - (&GlobalTransform, &XRInteractorState, Option<&AimPose>), - (With, Without), - >, - tracking_root_query: Query<(&mut Transform, With)>, +pub fn update_interactable_states( + mut events: EventReader, + mut interactable_query: Query<(Entity, &mut XRInteractableState), (With)>, ) { - for (xr_interactable_global_transform, mut state) in interactable_query.iter_mut() { - let mut hovered = false; - let mut selected = false; - for (interactor_global_transform, interactor_state, aim) in interactor_query.iter() { - //check for ray-sphere intersection - let sphere_transform = xr_interactable_global_transform.compute_transform(); - let center = sphere_transform.translation; - let radius: f32 = 0.1; - //I hate this but the aim pose needs the root for now - let root = tracking_root_query.get_single().unwrap().0; - match aim { - Some(aim) => { - let ray_origin = root.translation + root.rotation.mul_vec3(aim.0.translation); - let ray_dir = root.rotation.mul_vec3(aim.0.forward()); - - if ray_sphere_intersection( - center, - radius, - ray_origin, - ray_dir.normalize_or_zero(), - ) { - //check for selections first - match interactor_state { - XRInteractorState::Idle => hovered = true, - XRInteractorState::Selecting => { - selected = true; - } - } - } - } - None => info!("no aim pose"), + for event in events.read() { + //lets change the state? + match interactable_query.get_mut(event.interactable) { + Ok((_entity, mut entity_state)) => { + *entity_state = event.interactable_state; + } + Err(_) => { } - } - //check what we found - //also i dont like this - if selected { - *state = XRInteractableState::Select; - } else if hovered { - *state = XRInteractableState::Hover; - } else { - *state = XRInteractableState::Idle; } } }