diff --git a/crates/bevy_openxr/examples/actions.rs b/crates/bevy_openxr/examples/actions.rs index 82f1639..936e1fa 100644 --- a/crates/bevy_openxr/examples/actions.rs +++ b/crates/bevy_openxr/examples/actions.rs @@ -1,45 +1,30 @@ -// a simple example showing basic actions - -use std::borrow::Cow; - +// a simple example showing basic actions using the xr utils actions use bevy::{math::vec3, prelude::*}; use bevy_openxr::{ - action_binding::OxrSuggestActionBinding, - action_set_attaching::OxrAttachActionSet, add_xr_plugins, helper_traits::ToQuat, init::OxrTrackingRoot, - resources::{OxrInstance, OxrSession, OxrViews}, + resources::OxrViews, +}; +use bevy_xr_utils::xr_utils_actions::{ + ActiveSet, XRUtilsAction, XRUtilsActionSet, + XRUtilsActionState, XRUtilsActionSystemSet, XRUtilsActionsPlugin, XRUtilsBinding, }; -use openxr::{ActiveActionSet, Path, Vector2f}; fn main() { App::new() .add_plugins(add_xr_plugins(DefaultPlugins)) .add_plugins(bevy_xr_utils::hand_gizmos::HandGizmosPlugin) .add_systems(Startup, setup_scene) - .add_systems(Startup, create_action_entities) - .add_systems(Startup, create_openxr_events.after(create_action_entities)) - .add_systems(Update, sync_active_action_sets) + .add_systems(Startup, create_action_entities.before(XRUtilsActionSystemSet::CreateEvents)) + .add_plugins(XRUtilsActionsPlugin) .add_systems( Update, - sync_and_update_action_states_f32.after(sync_active_action_sets), + read_action_with_marker_component.after(XRUtilsActionSystemSet::SyncActionStates), ) .add_systems( Update, - sync_and_update_action_states_bool.after(sync_active_action_sets), - ) - .add_systems( - Update, - sync_and_update_action_states_vector.after(sync_active_action_sets), - ) - .add_systems( - Update, - read_action_with_marker_component.after(sync_and_update_action_states_f32), - ) - .add_systems( - Update, - handle_flight_input.after(sync_and_update_action_states_f32), + handle_flight_input.after(XRUtilsActionSystemSet::SyncActionStates), ) .run(); } @@ -79,51 +64,6 @@ fn setup_scene( }); } -#[derive(Component)] -struct XRUtilsActionSet { - name: Cow<'static, str>, - pretty_name: Cow<'static, str>, - priority: u32, -} - -#[derive(Component, Clone)] -struct XRUtilsActionSetReference(openxr::ActionSet); - -//I want to use this to indicate when an action set is attached -// #[derive(Component)] -// struct AttachedActionSet; - -#[derive(Component)] -struct ActiveSet; - -#[derive(Component)] -struct XRUtilsAction { - action_name: Cow<'static, str>, - localized_name: Cow<'static, str>, - action_type: bevy_xr::actions::ActionType, -} - -#[derive(Component)] -struct XRUtilsBinding { - profile: Cow<'static, str>, - binding: Cow<'static, str>, -} - -#[derive(Component)] -struct Actionf32Reference { - action: openxr::Action, -} - -#[derive(Component)] -struct ActionBooleference { - action: openxr::Action, -} - -#[derive(Component)] -struct ActionVector2fReference { - action: openxr::Action, -} - #[derive(Component)] struct FlightActionMarker; @@ -165,250 +105,6 @@ fn create_action_entities(mut commands: Commands) { commands.entity(set).add_child(action); } -fn create_openxr_events( - action_sets_query: Query<(&XRUtilsActionSet, &Children, Entity)>, - actions_query: Query<(&XRUtilsAction, &Children)>, - bindings_query: Query<&XRUtilsBinding>, - instance: ResMut, - mut binding_writer: EventWriter, - mut attach_writer: EventWriter, - //not my favorite way of doing this - mut commands: Commands, -) { - //lets create some sets! - //we gonna need a collection of these sets for later - // let mut ActionSets = HashMap::new(); - for (set, children, id) in action_sets_query.iter() { - //create action set - let action_set: openxr::ActionSet = instance - .create_action_set(&set.name, &set.pretty_name, set.priority) - .unwrap(); - //now that we have the action set we need to put it back onto the entity for later - let oxr_action_set = XRUtilsActionSetReference(action_set.clone()); - commands.entity(id).insert(oxr_action_set); - - //since the actions are made from the sets lets go - for &child in children.iter() { - //first get the action entity and stuff - let (create_action, bindings) = actions_query.get(child).unwrap(); - //lets create dat actions - match create_action.action_type { - bevy_xr::actions::ActionType::Bool => { - let action: openxr::Action = action_set - .create_action::( - &create_action.action_name, - &create_action.localized_name, - &[], - ) - .unwrap(); - //please put this in a function so I dont go crazy - //insert a reference for later - commands.entity(child).insert(( - ActionBooleference { - action: action.clone(), - }, - XRUtilsActionState::Bool(ActionStateBool { - current_state: false, - changed_since_last_sync: false, - last_change_time: i64::MIN, - is_active: false, - }), - )); - //since we need actions for bindings lets go!! - for &bind in bindings.iter() { - //interaction profile - //get the binding entity and stuff - let create_binding = bindings_query.get(bind).unwrap(); - let profile = Cow::from(create_binding.profile.clone()); - //bindings - let binding = vec![Cow::from(create_binding.binding.clone())]; - let sugestion = OxrSuggestActionBinding { - action: action.as_raw(), - interaction_profile: profile, - bindings: binding, - }; - //finally send the suggestion - binding_writer.send(sugestion); - } - } - bevy_xr::actions::ActionType::Float => { - let action: openxr::Action = action_set - .create_action::( - &create_action.action_name, - &create_action.localized_name, - &[], - ) - .unwrap(); - - //please put this in a function so I dont go crazy - //insert a reference for later - commands.entity(child).insert(( - Actionf32Reference { - action: action.clone(), - }, - XRUtilsActionState::Float(ActionStateFloat { - current_state: 0.0, - changed_since_last_sync: false, - last_change_time: i64::MIN, - is_active: false, - }), - )); - //since we need actions for bindings lets go!! - for &bind in bindings.iter() { - //interaction profile - //get the binding entity and stuff - let create_binding = bindings_query.get(bind).unwrap(); - let profile = Cow::from(create_binding.profile.clone()); - //bindings - let binding = vec![Cow::from(create_binding.binding.clone())]; - let sugestion = OxrSuggestActionBinding { - action: action.as_raw(), - interaction_profile: profile, - bindings: binding, - }; - //finally send the suggestion - binding_writer.send(sugestion); - } - } - bevy_xr::actions::ActionType::Vector => { - let action: openxr::Action = action_set - .create_action::( - &create_action.action_name, - &create_action.localized_name, - &[], - ) - .unwrap(); - - //please put this in a function so I dont go crazy - //insert a reference for later - commands.entity(child).insert(( - ActionVector2fReference { - action: action.clone(), - }, - XRUtilsActionState::Vector(ActionStateVector { - current_state: [0.0, 0.0], - changed_since_last_sync: false, - last_change_time: i64::MIN, - is_active: false, - }), - )); - //since we need actions for bindings lets go!! - for &bind in bindings.iter() { - //interaction profile - //get the binding entity and stuff - let create_binding = bindings_query.get(bind).unwrap(); - let profile = Cow::from(create_binding.profile.clone()); - //bindings - let binding = vec![Cow::from(create_binding.binding.clone())]; - let sugestion = OxrSuggestActionBinding { - action: action.as_raw(), - interaction_profile: profile, - bindings: binding, - }; - //finally send the suggestion - binding_writer.send(sugestion); - } - } - }; - } - - attach_writer.send(OxrAttachActionSet(action_set)); - } -} - -fn sync_active_action_sets( - session: Res, - active_action_set_query: Query<&XRUtilsActionSetReference, With>, -) { - let active_sets = active_action_set_query - .iter() - .map(|v| ActiveActionSet::from(&v.0)) - .collect::>(); - let sync = session.sync_actions(&active_sets[..]); - match sync { - Ok(_) => info!("sync ok"), - Err(_) => error!("sync error"), - } -} - -fn sync_and_update_action_states_f32( - session: Res, - mut f32_query: Query<(&Actionf32Reference, &mut XRUtilsActionState)>, -) { - //now we do the action state for f32 - for (reference, mut silly_state) in f32_query.iter_mut() { - let state = reference.action.state(&session, Path::NULL); - match state { - Ok(s) => { - info!("we found a state"); - let new_state = XRUtilsActionState::Float(ActionStateFloat { - current_state: s.current_state, - changed_since_last_sync: s.changed_since_last_sync, - last_change_time: s.last_change_time.as_nanos(), - is_active: s.is_active, - }); - - *silly_state = new_state; - } - Err(_) => { - info!("error getting action state"); - } - } - } -} - -fn sync_and_update_action_states_bool( - session: Res, - mut f32_query: Query<(&ActionBooleference, &mut XRUtilsActionState)>, -) { - //now we do the action state for f32 - for (reference, mut silly_state) in f32_query.iter_mut() { - let state = reference.action.state(&session, Path::NULL); - match state { - Ok(s) => { - info!("we found a state"); - let new_state = XRUtilsActionState::Bool(ActionStateBool { - current_state: s.current_state, - changed_since_last_sync: s.changed_since_last_sync, - last_change_time: s.last_change_time.as_nanos(), - is_active: s.is_active, - }); - - *silly_state = new_state; - } - Err(_) => { - info!("error getting action state"); - } - } - } -} - -fn sync_and_update_action_states_vector( - session: Res, - mut vector_query: Query<(&ActionVector2fReference, &mut XRUtilsActionState)>, -) { - //now we do the action state for f32 - for (reference, mut silly_state) in vector_query.iter_mut() { - let state = reference.action.state(&session, Path::NULL); - match state { - Ok(s) => { - info!("we found a state"); - let new_state = XRUtilsActionState::Vector(ActionStateVector { - current_state: [s.current_state.x, s.current_state.y], - changed_since_last_sync: s.changed_since_last_sync, - last_change_time: s.last_change_time.as_nanos(), - is_active: s.is_active, - }); - - *silly_state = new_state; - } - Err(_) => { - info!("error getting action state"); - } - } - } -} - fn read_action_with_marker_component( mut action_query: Query<&XRUtilsActionState, With>, ) { @@ -466,33 +162,3 @@ fn handle_flight_input( } } } - -//the things i do for bad prototyping and lack of understanding -#[derive(Component, Debug)] -pub enum XRUtilsActionState { - Bool(ActionStateBool), - Float(ActionStateFloat), - Vector(ActionStateVector), -} - -#[derive(Debug)] -pub struct ActionStateBool { - pub current_state: bool, - pub changed_since_last_sync: bool, - pub last_change_time: i64, - pub is_active: bool, -} -#[derive(Debug)] -pub struct ActionStateFloat { - pub current_state: f32, - pub changed_since_last_sync: bool, - pub last_change_time: i64, - pub is_active: bool, -} -#[derive(Debug)] -pub struct ActionStateVector { - pub current_state: [f32; 2], - pub changed_since_last_sync: bool, - pub last_change_time: i64, - pub is_active: bool, -} diff --git a/crates/bevy_xr_utils/Cargo.toml b/crates/bevy_xr_utils/Cargo.toml index dfaffaf..8f929ae 100644 --- a/crates/bevy_xr_utils/Cargo.toml +++ b/crates/bevy_xr_utils/Cargo.toml @@ -8,3 +8,5 @@ edition = "2021" [dependencies] bevy.workspace = true bevy_xr.path = "../bevy_xr" +openxr = "0.18.0" +bevy_openxr.path = "../bevy_openxr" \ No newline at end of file diff --git a/crates/bevy_xr_utils/src/lib.rs b/crates/bevy_xr_utils/src/lib.rs index 5c9d6f8..6a1a780 100644 --- a/crates/bevy_xr_utils/src/lib.rs +++ b/crates/bevy_xr_utils/src/lib.rs @@ -1,3 +1,3 @@ // use bevy::prelude::*; pub mod hand_gizmos; - +pub mod xr_utils_actions; diff --git a/crates/bevy_xr_utils/src/xr_utils_actions.rs b/crates/bevy_xr_utils/src/xr_utils_actions.rs new file mode 100644 index 0000000..dac6d07 --- /dev/null +++ b/crates/bevy_xr_utils/src/xr_utils_actions.rs @@ -0,0 +1,360 @@ +use bevy::prelude::*; +use bevy_openxr::{ + action_binding::OxrSuggestActionBinding, + action_set_attaching::OxrAttachActionSet, + resources::{OxrInstance, OxrSession}, +}; +use openxr::{ActiveActionSet, Path, Vector2f}; +use std::borrow::Cow; + +pub struct XRUtilsActionsPlugin; +impl Plugin for XRUtilsActionsPlugin { + fn build(&self, app: &mut App) { + app.add_systems(Startup, create_openxr_events.in_set(XRUtilsActionSystemSet::CreateEvents)); + app.add_systems(Update, sync_active_action_sets); + app.add_systems( + Update, + sync_and_update_action_states_f32 + .in_set(XRUtilsActionSystemSet::SyncActionStates) + .after(sync_active_action_sets), + ); + app.add_systems( + Update, + sync_and_update_action_states_bool + .in_set(XRUtilsActionSystemSet::SyncActionStates) + .after(sync_active_action_sets), + ); + app.add_systems( + Update, + sync_and_update_action_states_vector + .in_set(XRUtilsActionSystemSet::SyncActionStates) + .after(sync_active_action_sets), + ); + } +} + +fn create_openxr_events( + action_sets_query: Query<(&XRUtilsActionSet, &Children, Entity)>, + actions_query: Query<(&XRUtilsAction, &Children)>, + bindings_query: Query<&XRUtilsBinding>, + instance: ResMut, + mut binding_writer: EventWriter, + mut attach_writer: EventWriter, + mut commands: Commands, +) { + //lets create some sets! + for (set, children, id) in action_sets_query.iter() { + //create action set + let action_set: openxr::ActionSet = instance + .create_action_set(&set.name, &set.pretty_name, set.priority) + .unwrap(); + //now that we have the action set we need to put it back onto the entity for later + let oxr_action_set = XRUtilsActionSetReference(action_set.clone()); + commands.entity(id).insert(oxr_action_set); + + //since the actions are made from the sets lets go + for &child in children.iter() { + //first get the action entity and stuff + let (create_action, bindings) = actions_query.get(child).unwrap(); + //lets create dat action + match create_action.action_type { + bevy_xr::actions::ActionType::Bool => { + let action: openxr::Action = action_set + .create_action::( + &create_action.action_name, + &create_action.localized_name, + &[], + ) + .unwrap(); + //please put this in a function so I dont go crazy + //insert a reference for later + commands.entity(child).insert(( + ActionBooleference { + action: action.clone(), + }, + XRUtilsActionState::Bool(ActionStateBool { + current_state: false, + changed_since_last_sync: false, + last_change_time: i64::MIN, + is_active: false, + }), + )); + //since we need actions for bindings lets go!! + for &bind in bindings.iter() { + //interaction profile + //get the binding entity and stuff + let create_binding = bindings_query.get(bind).unwrap(); + let profile = Cow::from(create_binding.profile.clone()); + //bindings + let binding = vec![Cow::from(create_binding.binding.clone())]; + let sugestion = OxrSuggestActionBinding { + action: action.as_raw(), + interaction_profile: profile, + bindings: binding, + }; + //finally send the suggestion + binding_writer.send(sugestion); + } + } + bevy_xr::actions::ActionType::Float => { + let action: openxr::Action = action_set + .create_action::( + &create_action.action_name, + &create_action.localized_name, + &[], + ) + .unwrap(); + + //please put this in a function so I dont go crazy + //insert a reference for later + commands.entity(child).insert(( + Actionf32Reference { + action: action.clone(), + }, + XRUtilsActionState::Float(ActionStateFloat { + current_state: 0.0, + changed_since_last_sync: false, + last_change_time: i64::MIN, + is_active: false, + }), + )); + //since we need actions for bindings lets go!! + for &bind in bindings.iter() { + //interaction profile + //get the binding entity and stuff + let create_binding = bindings_query.get(bind).unwrap(); + let profile = Cow::from(create_binding.profile.clone()); + //bindings + let binding = vec![Cow::from(create_binding.binding.clone())]; + let sugestion = OxrSuggestActionBinding { + action: action.as_raw(), + interaction_profile: profile, + bindings: binding, + }; + //finally send the suggestion + binding_writer.send(sugestion); + } + } + bevy_xr::actions::ActionType::Vector => { + let action: openxr::Action = action_set + .create_action::( + &create_action.action_name, + &create_action.localized_name, + &[], + ) + .unwrap(); + + //please put this in a function so I dont go crazy + //insert a reference for later + commands.entity(child).insert(( + ActionVector2fReference { + action: action.clone(), + }, + XRUtilsActionState::Vector(ActionStateVector { + current_state: [0.0, 0.0], + changed_since_last_sync: false, + last_change_time: i64::MIN, + is_active: false, + }), + )); + //since we need actions for bindings lets go!! + for &bind in bindings.iter() { + //interaction profile + //get the binding entity and stuff + let create_binding = bindings_query.get(bind).unwrap(); + let profile = Cow::from(create_binding.profile.clone()); + //bindings + let binding = vec![Cow::from(create_binding.binding.clone())]; + let sugestion = OxrSuggestActionBinding { + action: action.as_raw(), + interaction_profile: profile, + bindings: binding, + }; + //finally send the suggestion + binding_writer.send(sugestion); + } + } + }; + } + + attach_writer.send(OxrAttachActionSet(action_set)); + } +} + +fn sync_active_action_sets( + session: Res, + active_action_set_query: Query<&XRUtilsActionSetReference, With>, +) { + let active_sets = active_action_set_query + .iter() + .map(|v| ActiveActionSet::from(&v.0)) + .collect::>(); + let sync = session.sync_actions(&active_sets[..]); + match sync { + Ok(_) => info!("sync ok"), + Err(_) => error!("sync error"), + } +} + +fn sync_and_update_action_states_f32( + session: Res, + mut f32_query: Query<(&Actionf32Reference, &mut XRUtilsActionState)>, +) { + //now we do the action state for f32 + for (reference, mut silly_state) in f32_query.iter_mut() { + let state = reference.action.state(&session, Path::NULL); + match state { + Ok(s) => { + info!("we found a state"); + let new_state = XRUtilsActionState::Float(ActionStateFloat { + current_state: s.current_state, + changed_since_last_sync: s.changed_since_last_sync, + last_change_time: s.last_change_time.as_nanos(), + is_active: s.is_active, + }); + + *silly_state = new_state; + } + Err(_) => { + info!("error getting action state"); + } + } + } +} + +fn sync_and_update_action_states_bool( + session: Res, + mut f32_query: Query<(&ActionBooleference, &mut XRUtilsActionState)>, +) { + //now we do the action state for f32 + for (reference, mut silly_state) in f32_query.iter_mut() { + let state = reference.action.state(&session, Path::NULL); + match state { + Ok(s) => { + info!("we found a state"); + let new_state = XRUtilsActionState::Bool(ActionStateBool { + current_state: s.current_state, + changed_since_last_sync: s.changed_since_last_sync, + last_change_time: s.last_change_time.as_nanos(), + is_active: s.is_active, + }); + + *silly_state = new_state; + } + Err(_) => { + info!("error getting action state"); + } + } + } +} + +fn sync_and_update_action_states_vector( + session: Res, + mut vector_query: Query<(&ActionVector2fReference, &mut XRUtilsActionState)>, +) { + //now we do the action state for f32 + for (reference, mut silly_state) in vector_query.iter_mut() { + let state = reference.action.state(&session, Path::NULL); + match state { + Ok(s) => { + info!("we found a state"); + let new_state = XRUtilsActionState::Vector(ActionStateVector { + current_state: [s.current_state.x, s.current_state.y], + changed_since_last_sync: s.changed_since_last_sync, + last_change_time: s.last_change_time.as_nanos(), + is_active: s.is_active, + }); + + *silly_state = new_state; + } + Err(_) => { + info!("error getting action state"); + } + } + } +} + +#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, SystemSet)] +pub enum XRUtilsActionSystemSet { + CreateEvents, + SyncActionStates, +} + +#[derive(Component)] +pub struct XRUtilsActionSet { + pub name: Cow<'static, str>, + pub pretty_name: Cow<'static, str>, + pub priority: u32, +} + +#[derive(Component, Clone)] +pub struct XRUtilsActionSetReference(pub openxr::ActionSet); + +//I want to use this to indicate when an action set is attached +// #[derive(Component)] +// struct AttachedActionSet; + +//this is used to determine if this set should be synced +#[derive(Component)] +pub struct ActiveSet; + +#[derive(Component)] +pub struct XRUtilsAction { + pub action_name: Cow<'static, str>, + pub localized_name: Cow<'static, str>, + pub action_type: bevy_xr::actions::ActionType, +} + +#[derive(Component)] +pub struct XRUtilsBinding { + pub profile: Cow<'static, str>, + pub binding: Cow<'static, str>, +} + +//Prototype action states +//TODO refactor this +#[derive(Component, Debug)] +pub enum XRUtilsActionState { + Bool(ActionStateBool), + Float(ActionStateFloat), + Vector(ActionStateVector), +} + +#[derive(Debug)] +pub struct ActionStateBool { + pub current_state: bool, + pub changed_since_last_sync: bool, + pub last_change_time: i64, + pub is_active: bool, +} +#[derive(Debug)] +pub struct ActionStateFloat { + pub current_state: f32, + pub changed_since_last_sync: bool, + pub last_change_time: i64, + pub is_active: bool, +} +#[derive(Debug)] +pub struct ActionStateVector { + pub current_state: [f32; 2], + pub changed_since_last_sync: bool, + pub last_change_time: i64, + pub is_active: bool, +} + +//prototype action references +//TODO refactor along with action states +#[derive(Component)] +struct Actionf32Reference { + action: openxr::Action, +} + +#[derive(Component)] +struct ActionBooleference { + action: openxr::Action, +} + +#[derive(Component)] +struct ActionVector2fReference { + action: openxr::Action, +}