somewhat working actions
This commit is contained in:
@@ -1,12 +1,37 @@
|
||||
//! A simple 3D scene with light shining over a cube sitting on a plane.
|
||||
|
||||
use std::any::TypeId;
|
||||
|
||||
use bevy::prelude::*;
|
||||
use bevy_openxr::add_xr_plugins;
|
||||
use bevy_openxr::{
|
||||
actions::{create_action_sets, ActionApp},
|
||||
add_xr_plugins, resources::{TypedAction, XrActions, XrInstance},
|
||||
};
|
||||
use bevy_xr::actions::{Action, ActionInfo, ActionState, ActionType};
|
||||
use openxr::Binding;
|
||||
|
||||
pub struct Jump;
|
||||
|
||||
impl Action for Jump {
|
||||
type ActionType = bool;
|
||||
|
||||
fn info() -> ActionInfo {
|
||||
ActionInfo {
|
||||
pretty_name: "jump",
|
||||
name: "jump",
|
||||
action_type: ActionType::Bool,
|
||||
type_id: TypeId::of::<Self>(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(add_xr_plugins(DefaultPlugins))
|
||||
.add_systems(Startup, setup)
|
||||
.add_systems(Startup, setup.after(create_action_sets))
|
||||
.add_systems(Update, read_action_state)
|
||||
.register_action::<Jump>()
|
||||
.run();
|
||||
}
|
||||
|
||||
@@ -15,7 +40,15 @@ fn setup(
|
||||
mut commands: Commands,
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||
actions: Res<XrActions>,
|
||||
instance: Res<XrInstance>,
|
||||
) {
|
||||
let TypedAction::Bool(action) = actions.get(&TypeId::of::<Jump>()).unwrap() else {
|
||||
unreachable!()
|
||||
};
|
||||
instance.suggest_interaction_profile_bindings(instance.string_to_path("/interaction_profiles/oculus/touch_controller").unwrap(), &[
|
||||
Binding::new(action, instance.string_to_path("/user/hand/right/input/a/click").unwrap())
|
||||
]).unwrap();
|
||||
// circular base
|
||||
commands.spawn(PbrBundle {
|
||||
mesh: meshes.add(Circle::new(4.0)),
|
||||
@@ -49,3 +82,10 @@ fn setup(
|
||||
// ..default()
|
||||
// });
|
||||
}
|
||||
|
||||
fn read_action_state(
|
||||
state: Res<ActionState<Jump>>
|
||||
) {
|
||||
info!("{}", state.pressed())
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
use std::any::TypeId;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::init::XrPreUpdateSet;
|
||||
use crate::resources::*;
|
||||
use crate::types::*;
|
||||
use bevy::app::{App, Plugin, PreUpdate, Startup};
|
||||
use bevy::ecs::schedule::common_conditions::resource_added;
|
||||
use bevy::ecs::schedule::IntoSystemConfigs;
|
||||
use bevy::ecs::system::{Commands, Res, ResMut};
|
||||
use bevy::input::InputSystem;
|
||||
use bevy::log::error;
|
||||
use bevy::math::{vec2, Vec2};
|
||||
use bevy::utils::hashbrown::HashMap;
|
||||
use bevy_xr::actions::ActionPlugin;
|
||||
use bevy_xr::actions::{Action, ActionList, ActionState};
|
||||
use bevy_xr::session::session_available;
|
||||
use bevy_xr::session::session_running;
|
||||
|
||||
pub struct XrActionPlugin;
|
||||
|
||||
impl Plugin for XrActionPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(Startup, create_action_sets.run_if(session_available))
|
||||
.add_systems(
|
||||
PreUpdate,
|
||||
sync_actions.run_if(session_running).before(InputSystem),
|
||||
)
|
||||
.add_systems(
|
||||
PreUpdate,
|
||||
attach_action_sets
|
||||
.after(XrPreUpdateSet::HandleEvents)
|
||||
.run_if(resource_added::<XrSession>),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_action_sets(
|
||||
instance: Res<XrInstance>,
|
||||
action_list: Res<ActionList>,
|
||||
mut commands: Commands,
|
||||
) {
|
||||
let (action_set, actions) =
|
||||
initialize_action_sets(&instance, &action_list).expect("Failed to initialize action set");
|
||||
|
||||
commands.insert_resource(action_set);
|
||||
commands.insert_resource(actions);
|
||||
}
|
||||
|
||||
pub fn attach_action_sets(mut action_set: ResMut<XrActionSet>, session: Res<XrSession>) {
|
||||
session
|
||||
.attach_action_sets(&[&action_set])
|
||||
.expect("Failed to attach action sets");
|
||||
action_set.attach();
|
||||
}
|
||||
|
||||
pub fn sync_actions(session: Res<XrSession>, action_set: Res<XrActionSet>) {
|
||||
session
|
||||
.sync_actions(&[openxr::ActiveActionSet::new(&action_set)])
|
||||
.expect("Failed to sync actions");
|
||||
}
|
||||
|
||||
fn initialize_action_sets(
|
||||
instance: &XrInstance,
|
||||
action_info: &ActionList,
|
||||
) -> Result<(XrActionSet, XrActions)> {
|
||||
let action_set = instance.create_action_set("actions", "actions", 0)?;
|
||||
let mut actions = HashMap::new();
|
||||
for action_info in action_info.0.iter() {
|
||||
use bevy_xr::actions::ActionType::*;
|
||||
let action = match action_info.action_type {
|
||||
Bool => TypedAction::Bool(action_set.create_action(
|
||||
action_info.name,
|
||||
action_info.pretty_name,
|
||||
&[],
|
||||
)?),
|
||||
Float => TypedAction::Float(action_set.create_action(
|
||||
action_info.name,
|
||||
action_info.pretty_name,
|
||||
&[],
|
||||
)?),
|
||||
Vector => TypedAction::Vector(action_set.create_action(
|
||||
action_info.name,
|
||||
action_info.pretty_name,
|
||||
&[],
|
||||
)?),
|
||||
};
|
||||
actions.insert(action_info.type_id, action);
|
||||
}
|
||||
Ok((XrActionSet::new(action_set), XrActions(actions)))
|
||||
}
|
||||
|
||||
pub struct XrActionUpdatePlugin<A: Action>(PhantomData<A>);
|
||||
|
||||
impl<A> Plugin for XrActionUpdatePlugin<A>
|
||||
where
|
||||
A: Action,
|
||||
A::ActionType: XrActionTy,
|
||||
{
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(PreUpdate, update_action_state::<A>.in_set(InputSystem).run_if(session_running));
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Action> Default for XrActionUpdatePlugin<A> {
|
||||
fn default() -> Self {
|
||||
Self(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait XrActionTy: Sized {
|
||||
fn get_action_state(
|
||||
action: &TypedAction,
|
||||
session: &XrSession,
|
||||
subaction_path: Option<openxr::Path>,
|
||||
) -> Option<Self>;
|
||||
}
|
||||
|
||||
impl XrActionTy for bool {
|
||||
fn get_action_state(
|
||||
action: &TypedAction,
|
||||
session: &XrSession,
|
||||
subaction_path: Option<openxr::Path>,
|
||||
) -> Option<Self> {
|
||||
match action {
|
||||
TypedAction::Bool(action) => action
|
||||
.state(session, subaction_path.unwrap_or(openxr::Path::NULL))
|
||||
.ok()
|
||||
.map(|state| state.current_state),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl XrActionTy for f32 {
|
||||
fn get_action_state(
|
||||
action: &TypedAction,
|
||||
session: &XrSession,
|
||||
subaction_path: Option<openxr::Path>,
|
||||
) -> Option<Self> {
|
||||
match action {
|
||||
TypedAction::Float(action) => action
|
||||
.state(session, subaction_path.unwrap_or(openxr::Path::NULL))
|
||||
.ok()
|
||||
.map(|state| state.current_state),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl XrActionTy for Vec2 {
|
||||
fn get_action_state(
|
||||
action: &TypedAction,
|
||||
session: &XrSession,
|
||||
subaction_path: Option<openxr::Path>,
|
||||
) -> Option<Self> {
|
||||
match action {
|
||||
TypedAction::Vector(action) => action
|
||||
.state(session, subaction_path.unwrap_or(openxr::Path::NULL))
|
||||
.ok()
|
||||
.map(|state| vec2(state.current_state.x, state.current_state.y)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_action_state<A>(
|
||||
mut action_state: ResMut<ActionState<A>>,
|
||||
session: Res<XrSession>,
|
||||
actions: Res<XrActions>,
|
||||
) where
|
||||
A: Action,
|
||||
A::ActionType: XrActionTy,
|
||||
{
|
||||
if let Some(action) = actions.get(&TypeId::of::<A>()) {
|
||||
if let Some(state) = A::ActionType::get_action_state(action, &session, None) {
|
||||
action_state.set(state);
|
||||
} else {
|
||||
error!(
|
||||
"Failed to update value for action '{}'",
|
||||
std::any::type_name::<A>()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ActionApp {
|
||||
fn register_action<A>(&mut self) -> &mut Self
|
||||
where
|
||||
A: Action,
|
||||
A::ActionType: XrActionTy;
|
||||
}
|
||||
|
||||
impl ActionApp for App {
|
||||
fn register_action<A>(&mut self) -> &mut Self
|
||||
where
|
||||
A: Action,
|
||||
A::ActionType: XrActionTy,
|
||||
{
|
||||
self.add_plugins((
|
||||
ActionPlugin::<A>::default(),
|
||||
XrActionUpdatePlugin::<A>::default(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use actions::XrActionPlugin;
|
||||
use bevy::{
|
||||
app::{PluginGroup, PluginGroupBuilder},
|
||||
render::{pipelined_rendering::PipelinedRenderingPlugin, RenderPlugin},
|
||||
@@ -36,6 +37,7 @@ pub fn add_xr_plugins<G: PluginGroup>(plugins: G) -> PluginGroupBuilder {
|
||||
})
|
||||
.add(XrRenderPlugin)
|
||||
.add(XrCameraPlugin)
|
||||
.add(XrActionPlugin)
|
||||
.set(WindowPlugin {
|
||||
#[cfg(not(target_os = "android"))]
|
||||
primary_window: Some(Window {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::any::TypeId;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -7,6 +8,7 @@ use crate::layer_builder::CompositionLayer;
|
||||
use crate::types::*;
|
||||
use bevy::prelude::*;
|
||||
use bevy::render::extract_resource::ExtractResource;
|
||||
use bevy::utils::HashMap;
|
||||
use openxr::AnyGraphics;
|
||||
|
||||
#[derive(Deref, Clone)]
|
||||
@@ -298,3 +300,30 @@ pub struct XrRootTransform(pub GlobalTransform);
|
||||
#[derive(ExtractResource, Resource, Clone, Copy, Default, Deref, DerefMut, PartialEq)]
|
||||
/// This is inserted into the world to signify if the session should be cleaned up.
|
||||
pub struct XrCleanupSession(pub bool);
|
||||
|
||||
#[derive(Resource, Clone, Deref)]
|
||||
pub struct XrActionSet(#[deref] pub openxr::ActionSet, bool);
|
||||
|
||||
impl XrActionSet {
|
||||
pub fn new(action_set: openxr::ActionSet) -> Self {
|
||||
Self(action_set, false)
|
||||
}
|
||||
|
||||
pub fn attach(&mut self) {
|
||||
self.1 = true;
|
||||
}
|
||||
|
||||
pub fn is_attached(&self) -> bool {
|
||||
self.1
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum TypedAction {
|
||||
Bool(openxr::Action<bool>),
|
||||
Float(openxr::Action<f32>),
|
||||
Vector(openxr::Action<openxr::Vector2f>),
|
||||
}
|
||||
|
||||
#[derive(Resource, Clone, Deref)]
|
||||
pub struct XrActions(pub HashMap<TypeId, TypedAction>);
|
||||
Reference in New Issue
Block a user