1use bevy_app::{App, Plugin, PreUpdate, Startup, Update};
57use bevy_ecs::{component::Component, entity::Entity, hierarchy::Children, message::MessageWriter, query::With, schedule::{IntoScheduleConfigs as _, SystemSet}, system::{Commands, Query, Res, ResMut}};
58use bevy_log::info;
59use bevy_mod_openxr::{
60 action_binding::OxrSuggestActionBinding,
61 action_set_attaching::OxrAttachActionSet,
62 action_set_syncing::{OxrActionSetSyncSet, OxrSyncActionSet},
63 openxr_session_available, openxr_session_running,
64 resources::OxrInstance,
65 session::OxrSession,
66};
67use openxr::{Path, Vector2f};
68
69use std::borrow::Cow;
70
71pub struct XRUtilsActionsPlugin;
72impl Plugin for XRUtilsActionsPlugin {
73 fn build(&self, app: &mut App) {
74 app.configure_sets(
75 Startup,
76 XRUtilsActionSystems::CreateEvents.run_if(openxr_session_available),
77 );
78 app.configure_sets(
79 PreUpdate,
80 XRUtilsActionSystems::SyncActionStates.run_if(openxr_session_running),
81 );
82 app.add_systems(
83 Startup,
84 create_openxr_events
85 .in_set(XRUtilsActionSystems::CreateEvents)
86 .run_if(openxr_session_available),
87 );
88 app.add_systems(
89 Update,
90 sync_active_action_sets.run_if(openxr_session_running),
91 );
92 app.add_systems(
93 PreUpdate,
94 sync_and_update_action_states_f32
95 .run_if(openxr_session_running)
96 .in_set(XRUtilsActionSystems::SyncActionStates)
97 .after(OxrActionSetSyncSet),
98 );
99 app.add_systems(
100 PreUpdate,
101 sync_and_update_action_states_bool
102 .run_if(openxr_session_running)
103 .in_set(XRUtilsActionSystems::SyncActionStates)
104 .after(OxrActionSetSyncSet),
105 );
106 app.add_systems(
107 PreUpdate,
108 sync_and_update_action_states_vector
109 .run_if(openxr_session_running)
110 .in_set(XRUtilsActionSystems::SyncActionStates)
111 .after(OxrActionSetSyncSet),
112 );
113 }
114}
115
116fn create_openxr_events(
117 action_sets_query: Query<(&XRUtilsActionSet, &Children, Entity)>,
118 actions_query: Query<(&XRUtilsAction, &Children)>,
119 bindings_query: Query<&XRUtilsBinding>,
120 instance: ResMut<OxrInstance>,
121 mut binding_writer: MessageWriter<OxrSuggestActionBinding>,
122 mut attach_writer: MessageWriter<OxrAttachActionSet>,
123 mut commands: Commands,
124) {
125 for (set, children, id) in action_sets_query.iter() {
127 let action_set: openxr::ActionSet = instance
129 .create_action_set(&set.name, &set.pretty_name, set.priority)
130 .unwrap();
131 let oxr_action_set = XRUtilsActionSetReference(action_set.clone());
133 commands.entity(id).insert(oxr_action_set);
134
135 for child in children.iter().copied() {
137 let (create_action, bindings) = actions_query.get(child).unwrap();
139 match create_action.action_type {
141 ActionType::Bool => {
142 let action: openxr::Action<bool> = action_set
143 .create_action::<bool>(
144 &create_action.action_name,
145 &create_action.localized_name,
146 &[],
147 )
148 .unwrap();
149 commands.entity(child).insert((
152 ActionBooleference {
153 action: action.clone(),
154 },
155 XRUtilsActionState::Bool(ActionStateBool {
156 current_state: false,
157 changed_since_last_sync: false,
158 last_change_time: i64::MIN,
159 is_active: false,
160 }),
161 ));
162 for bind in bindings.iter().copied() {
164 let create_binding = bindings_query.get(bind).unwrap();
167 let profile = create_binding.profile.clone();
168 let binding = vec![create_binding.binding.clone()];
170 let sugestion = OxrSuggestActionBinding {
171 action: action.as_raw(),
172 interaction_profile: profile,
173 bindings: binding,
174 };
175 binding_writer.write(sugestion);
177 }
178 }
179 ActionType::Float => {
180 let action: openxr::Action<f32> = action_set
181 .create_action::<f32>(
182 &create_action.action_name,
183 &create_action.localized_name,
184 &[],
185 )
186 .unwrap();
187
188 commands.entity(child).insert((
191 Actionf32Reference {
192 action: action.clone(),
193 },
194 XRUtilsActionState::Float(ActionStateFloat {
195 current_state: 0.0,
196 changed_since_last_sync: false,
197 last_change_time: i64::MIN,
198 is_active: false,
199 }),
200 ));
201 for bind in bindings.iter().copied() {
203 let create_binding = bindings_query.get(bind).unwrap();
206 let profile = create_binding.profile.clone();
207 let binding = vec![create_binding.binding.clone()];
209 let sugestion = OxrSuggestActionBinding {
210 action: action.as_raw(),
211 interaction_profile: profile,
212 bindings: binding,
213 };
214 binding_writer.write(sugestion);
216 }
217 }
218 ActionType::Vector => {
219 let action: openxr::Action<Vector2f> = action_set
220 .create_action::<Vector2f>(
221 &create_action.action_name,
222 &create_action.localized_name,
223 &[],
224 )
225 .unwrap();
226
227 commands.entity(child).insert((
230 ActionVector2fReference {
231 action: action.clone(),
232 },
233 XRUtilsActionState::Vector(ActionStateVector {
234 current_state: [0.0, 0.0],
235 changed_since_last_sync: false,
236 last_change_time: i64::MIN,
237 is_active: false,
238 }),
239 ));
240 for bind in bindings.iter().copied() {
242 let create_binding = bindings_query.get(bind).unwrap();
245 let profile = create_binding.profile.clone();
246 let binding = vec![create_binding.binding.clone()];
248 let sugestion = OxrSuggestActionBinding {
249 action: action.as_raw(),
250 interaction_profile: profile,
251 bindings: binding,
252 };
253 binding_writer.write(sugestion);
255 }
256 }
257 };
258 }
259
260 attach_writer.write(OxrAttachActionSet(action_set));
261 }
262}
263
264fn sync_active_action_sets(
265 mut sync_set: MessageWriter<OxrSyncActionSet>,
266 active_action_set_query: Query<&XRUtilsActionSetReference, With<ActiveSet>>,
267) {
268 for set in &active_action_set_query {
269 sync_set.write(OxrSyncActionSet(set.0.clone()));
270 }
271}
272
273fn sync_and_update_action_states_f32(
274 session: Res<OxrSession>,
275 mut f32_query: Query<(&Actionf32Reference, &mut XRUtilsActionState)>,
276) {
277 for (reference, mut silly_state) in f32_query.iter_mut() {
279 let state = reference.action.state(&session, Path::NULL);
280 match state {
281 Ok(s) => {
282 let new_state = XRUtilsActionState::Float(ActionStateFloat {
283 current_state: s.current_state,
284 changed_since_last_sync: s.changed_since_last_sync,
285 last_change_time: s.last_change_time.as_nanos(),
286 is_active: s.is_active,
287 });
288
289 *silly_state = new_state;
290 }
291 Err(_) => {
292 info!("error getting action state");
293 }
294 }
295 }
296}
297
298fn sync_and_update_action_states_bool(
299 session: Res<OxrSession>,
300 mut f32_query: Query<(&ActionBooleference, &mut XRUtilsActionState)>,
301) {
302 for (reference, mut silly_state) in f32_query.iter_mut() {
304 let state = reference.action.state(&session, Path::NULL);
305 match state {
306 Ok(s) => {
307 let new_state = XRUtilsActionState::Bool(ActionStateBool {
308 current_state: s.current_state,
309 changed_since_last_sync: s.changed_since_last_sync,
310 last_change_time: s.last_change_time.as_nanos(),
311 is_active: s.is_active,
312 });
313
314 *silly_state = new_state;
315 }
316 Err(_) => {
317 info!("error getting action state");
318 }
319 }
320 }
321}
322
323fn sync_and_update_action_states_vector(
324 session: Res<OxrSession>,
325 mut vector_query: Query<(&ActionVector2fReference, &mut XRUtilsActionState)>,
326) {
327 for (reference, mut silly_state) in vector_query.iter_mut() {
329 let state = reference.action.state(&session, Path::NULL);
330 match state {
331 Ok(s) => {
332 let new_state = XRUtilsActionState::Vector(ActionStateVector {
333 current_state: [s.current_state.x, s.current_state.y],
334 changed_since_last_sync: s.changed_since_last_sync,
335 last_change_time: s.last_change_time.as_nanos(),
336 is_active: s.is_active,
337 });
338
339 *silly_state = new_state;
340 }
341 Err(_) => {
342 info!("error getting action state");
343 }
344 }
345 }
346}
347
348#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
349pub enum ActionType {
350 Bool,
351 Float,
352 Vector,
353}
354
355#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, SystemSet)]
356pub enum XRUtilsActionSystems {
357 CreateEvents,
359 SyncActionStates,
361}
362
363#[derive(Component)]
364pub struct XRUtilsActionSet {
365 pub name: Cow<'static, str>,
366 pub pretty_name: Cow<'static, str>,
367 pub priority: u32,
368}
369
370#[derive(Component, Clone)]
371pub struct XRUtilsActionSetReference(pub openxr::ActionSet);
372
373#[derive(Component)]
379pub struct ActiveSet;
380
381#[derive(Component)]
382pub struct XRUtilsAction {
383 pub action_name: Cow<'static, str>,
384 pub localized_name: Cow<'static, str>,
385 pub action_type: ActionType,
386}
387
388#[derive(Component)]
389pub struct XRUtilsBinding {
390 pub profile: Cow<'static, str>,
391 pub binding: Cow<'static, str>,
392}
393
394#[derive(Component, Debug)]
397pub enum XRUtilsActionState {
398 Bool(ActionStateBool),
399 Float(ActionStateFloat),
400 Vector(ActionStateVector),
401}
402
403#[derive(Debug)]
404pub struct ActionStateBool {
405 pub current_state: bool,
406 pub changed_since_last_sync: bool,
407 pub last_change_time: i64,
408 pub is_active: bool,
409}
410#[derive(Debug)]
411pub struct ActionStateFloat {
412 pub current_state: f32,
413 pub changed_since_last_sync: bool,
414 pub last_change_time: i64,
415 pub is_active: bool,
416}
417#[derive(Debug)]
418pub struct ActionStateVector {
419 pub current_state: [f32; 2],
420 pub changed_since_last_sync: bool,
421 pub last_change_time: i64,
422 pub is_active: bool,
423}
424
425#[derive(Component)]
428struct Actionf32Reference {
429 action: openxr::Action<f32>,
430}
431
432#[derive(Component)]
433struct ActionBooleference {
434 action: openxr::Action<bool>,
435}
436
437#[derive(Component)]
438struct ActionVector2fReference {
439 action: openxr::Action<Vector2f>,
440}