Merge pull request #159 from Schmarni-Dev/create_xr_hands

add CI and move more hand related things into bevy_mod_xr
This commit is contained in:
Schmarni
2024-10-22 01:38:29 +02:00
committed by GitHub
10 changed files with 230 additions and 80 deletions

67
.github/workflows/ci.yaml vendored Normal file
View File

@@ -0,0 +1,67 @@
name: CI
on:
push:
branches:
- main
pull_request:
workflow_dispatch:
jobs:
check_linux:
name: Check Ubuntu
runs-on: ubuntu-latest
env:
CARGO_TERM_COLOR: always
steps:
- uses: actions/checkout@v4
- name: Install bevy dependencies
run: |
sudo apt-get update && sudo apt-get install -y \
g++ pkg-config libx11-dev libasound2-dev libudev-dev libopenxr-loader1 libopenxr-dev
- name: Cache cargo dependencies
uses: Swatinem/rust-cache@v2
- name: check
run: cargo check --all --all-targets
check_windows:
name: Check Windows
runs-on: windows-latest
env:
CARGO_TERM_COLOR: always
steps:
- uses: actions/checkout@v4
- name: Cache cargo dependencies
uses: Swatinem/rust-cache@v2
- name: check
run: cargo check --all --all-targets
check_wasm:
name: Check Wasm
runs-on: ubuntu-latest
env:
CARGO_TERM_COLOR: always
steps:
- uses: actions/checkout@v4
- name: Cache cargo dependencies
uses: Swatinem/rust-cache@v2
- name: install wasm toolchain
run: rustup toolchain install stable --profile minimal --target wasm32-unknown-unknown --no-self-update
- run: cargo check --target wasm32-unknown-unknown -p bevy_mod_xr
- run: cargo check --target wasm32-unknown-unknown -p bevy_mod_openxr
- run: cargo check --target wasm32-unknown-unknown -p bevy_mod_webxr
- run: cargo check --target wasm32-unknown-unknown -p bevy_xr_utils

View File

@@ -43,3 +43,7 @@ wgpu = { version = "0.20", features = ["vulkan-portability"] }
openxr = { version = "0.18.0", features = ["mint", "static"] } openxr = { version = "0.18.0", features = ["mint", "static"] }
winapi = { version = "0.3.9", optional = true } winapi = { version = "0.3.9", optional = true }
d3d12 = { version = "0.20", features = ["libloading"], optional = true } d3d12 = { version = "0.20", features = ["libloading"], optional = true }
[lints.clippy]
too_many_arguments = "allow"
type_complexity = "allow"

View File

@@ -1,6 +1,9 @@
use bevy::prelude::*; use bevy::prelude::*;
use bevy_mod_xr::hands::{HandBone, HandBoneRadius}; use bevy_mod_xr::hands::{
use bevy_mod_xr::hands::{LeftHand, RightHand, XrHandBoneEntities, HAND_JOINT_COUNT}; spawn_hand_bones, HandBone, HandBoneRadius, HandSide, SpawnHandTracker,
SpawnHandTrackerCommandExecutor,
};
use bevy_mod_xr::hands::{LeftHand, RightHand, XrHandBoneEntities};
use bevy_mod_xr::session::{XrPreDestroySession, XrSessionCreated, XrTrackingRoot}; use bevy_mod_xr::session::{XrPreDestroySession, XrSessionCreated, XrTrackingRoot};
use bevy_mod_xr::spaces::{ use bevy_mod_xr::spaces::{
XrPrimaryReferenceSpace, XrReferenceSpace, XrSpaceLocationFlags, XrSpaceVelocityFlags, XrPrimaryReferenceSpace, XrReferenceSpace, XrSpaceLocationFlags, XrSpaceVelocityFlags,
@@ -9,11 +12,11 @@ use bevy_mod_xr::spaces::{
use openxr::{SpaceLocationFlags, SpaceVelocityFlags}; use openxr::{SpaceLocationFlags, SpaceVelocityFlags};
use crate::helper_traits::ToVec3; use crate::helper_traits::ToVec3;
use crate::openxr_session_running;
use crate::resources::OxrFrameState; use crate::resources::OxrFrameState;
use crate::resources::Pipelined; use crate::resources::Pipelined;
use crate::session::OxrSession; use crate::session::OxrSession;
use crate::spaces::{OxrSpaceLocationFlags, OxrSpaceVelocityFlags}; use crate::spaces::{OxrSpaceLocationFlags, OxrSpaceVelocityFlags};
use crate::{openxr_session_available, openxr_session_running};
pub struct HandTrackingPlugin { pub struct HandTrackingPlugin {
default_hands: bool, default_hands: bool,
@@ -33,42 +36,24 @@ impl Plugin for HandTrackingPlugin {
app.add_systems(XrPreDestroySession, clean_up_default_hands) app.add_systems(XrPreDestroySession, clean_up_default_hands)
.add_systems(XrSessionCreated, spawn_default_hands); .add_systems(XrSessionCreated, spawn_default_hands);
} }
app.add_systems(Startup, set_spawn_executor.run_if(openxr_session_available));
} }
} }
pub fn spawn_hand_bones<T: Bundle + Clone>( fn set_spawn_executor(mut cmds: Commands) {
cmds: &mut Commands, cmds.insert_resource(SpawnHandTrackerCommandExecutor(handle_tracker_spawn))
bundle: T,
) -> [Entity; HAND_JOINT_COUNT] {
let mut bones: [Entity; HAND_JOINT_COUNT] = [Entity::PLACEHOLDER; HAND_JOINT_COUNT];
// screw you clippy, i don't see a better way to init this array
#[allow(clippy::needless_range_loop)]
for bone in HandBone::get_all_bones().into_iter() {
bones[bone as usize] = cmds
.spawn((
SpatialBundle::default(),
bone,
HandBoneRadius(0.0),
OxrSpaceLocationFlags(openxr::SpaceLocationFlags::default()),
XrSpaceLocationFlags::default(),
))
.insert(bundle.clone())
.id();
}
bones
} }
fn spawn_default_hands( fn handle_tracker_spawn(world: &mut World, tracker: Entity, side: HandSide) {
mut cmds: Commands, let Some(session) = world.get_resource_mut::<OxrSession>() else {
session: Res<OxrSession>, error!("unable to get session while creating hand tracker");
root: Query<Entity, With<XrTrackingRoot>>,
) {
debug!("spawning default hands");
let Ok(root) = root.get_single() else {
error!("unable to get tracking root, skipping hand creation");
return; return;
}; };
let tracker_left = match session.create_hand_tracker(openxr::HandEXT::LEFT) { debug!("spawning hand");
let oxr_tracker = match session.create_hand_tracker(match side {
HandSide::Left => openxr::HandEXT::LEFT,
HandSide::Right => openxr::HandEXT::RIGHT,
}) {
Ok(t) => t, Ok(t) => t,
Err(openxr::sys::Result::ERROR_EXTENSION_NOT_PRESENT) => { Err(openxr::sys::Result::ERROR_EXTENSION_NOT_PRESENT) => {
warn!("Handtracking Extension not loaded, Unable to create Handtracker!"); warn!("Handtracking Extension not loaded, Unable to create Handtracker!");
@@ -79,33 +64,44 @@ fn spawn_default_hands(
return; return;
} }
}; };
let tracker_right = match session.create_hand_tracker(openxr::HandEXT::RIGHT) {
Ok(t) => t, world
Err(openxr::sys::Result::ERROR_EXTENSION_NOT_PRESENT) => { .entity_mut(tracker)
warn!("Handtracking Extension not loaded, Unable to create Handtracker!"); .insert(OxrHandTracker(oxr_tracker));
return; }
}
Err(err) => { fn spawn_default_hands(mut cmds: Commands, root: Query<Entity, With<XrTrackingRoot>>) {
warn!("Error while creating Handtracker: {}", err.to_string()); let Ok(root) = root.get_single() else {
return; error!("unable to get tracking root, skipping handtracker creation");
} return;
}; };
let left_bones = spawn_hand_bones(&mut cmds, (DefaultHandBone, LeftHand)); debug!("spawning default hands");
let right_bones = spawn_hand_bones(&mut cmds, (DefaultHandBone, RightHand)); let left_bones = spawn_hand_bones(&mut cmds, |_| {
(
DefaultHandBone,
LeftHand,
OxrSpaceLocationFlags(openxr::SpaceLocationFlags::default()),
)
});
let right_bones = spawn_hand_bones(&mut cmds, |_| {
(
DefaultHandBone,
RightHand,
OxrSpaceLocationFlags(openxr::SpaceLocationFlags::default()),
)
});
cmds.entity(root).push_children(&left_bones); cmds.entity(root).push_children(&left_bones);
cmds.entity(root).push_children(&right_bones); cmds.entity(root).push_children(&right_bones);
cmds.spawn(( cmds.push(SpawnHandTracker {
DefaultHandTracker, joints: XrHandBoneEntities(left_bones),
OxrHandTracker(tracker_left), tracker_bundle: DefaultHandTracker,
XrHandBoneEntities(left_bones), side: HandSide::Left,
LeftHand, });
)); cmds.push(SpawnHandTracker {
cmds.spawn(( joints: XrHandBoneEntities(right_bones),
DefaultHandTracker, tracker_bundle: DefaultHandTracker,
OxrHandTracker(tracker_right), side: HandSide::Right,
XrHandBoneEntities(right_bones), });
RightHand,
));
} }
#[derive(Component, Clone, Copy)] #[derive(Component, Clone, Copy)]

View File

@@ -17,3 +17,7 @@ wgpu = "0.19.3"
wgpu-hal = "0.19.3" wgpu-hal = "0.19.3"
bevy_mod_xr = { path = "../bevy_xr", version = "0.1.0-rc1" } bevy_mod_xr = { path = "../bevy_xr", version = "0.1.0-rc1" }
[lints.clippy]
too_many_arguments = "allow"
type_complexity = "allow"

View File

@@ -11,3 +11,7 @@ keywords = ["gamedev", "bevy", "Xr", "Vr"]
[dependencies] [dependencies]
bevy.workspace = true bevy.workspace = true
[lints.clippy]
too_many_arguments = "allow"
type_complexity = "allow"

View File

@@ -1,16 +1,49 @@
use bevy::{ use bevy::{
ecs::{component::Component, entity::Entity}, ecs::{component::Component, entity::Entity, world::Command},
hierarchy::BuildWorldChildren,
log::{error, warn},
math::bool, math::bool,
prelude::{Deref, DerefMut}, prelude::{Bundle, Commands, Deref, DerefMut, Resource, SpatialBundle, With, World},
};
use crate::{
session:: XrTrackingRoot,
spaces::XrSpaceLocationFlags,
}; };
pub const HAND_JOINT_COUNT: usize = 26; pub const HAND_JOINT_COUNT: usize = 26;
pub fn spawn_hand_bones<T: Bundle>(
cmds: &mut Commands,
mut get_bundle: impl FnMut(HandBone) -> T,
) -> [Entity; HAND_JOINT_COUNT] {
let mut bones: [Entity; HAND_JOINT_COUNT] = [Entity::PLACEHOLDER; HAND_JOINT_COUNT];
for bone in HandBone::get_all_bones().into_iter() {
bones[bone as usize] = cmds
.spawn((
SpatialBundle::default(),
bone,
HandBoneRadius(0.0),
XrSpaceLocationFlags::default(),
))
.insert((get_bundle)(bone))
.id();
}
bones
}
#[derive(Clone, Copy, Component, Debug)]
pub enum HandSide {
Left,
Right,
}
#[derive(Clone, Copy, Component, Debug)] #[derive(Clone, Copy, Component, Debug)]
pub struct LeftHand; pub struct LeftHand;
#[derive(Clone, Copy, Component, Debug)] #[derive(Clone, Copy, Component, Debug)]
pub struct RightHand; pub struct RightHand;
/// Hand Joint Entities orderd
#[derive(Deref, DerefMut, Component, Clone, Copy)] #[derive(Deref, DerefMut, Component, Clone, Copy)]
pub struct XrHandBoneEntities(pub [Entity; HAND_JOINT_COUNT]); pub struct XrHandBoneEntities(pub [Entity; HAND_JOINT_COUNT]);
@@ -141,3 +174,43 @@ impl HandBone {
] ]
} }
} }
/// Use by a backend to run custom logic when spawning a hand tracker
#[derive(Resource)]
pub struct SpawnHandTrackerCommandExecutor(pub fn(&mut World, Entity, HandSide));
/// `tracker_bundle` is inserted after the backend specific code is run
pub struct SpawnHandTracker<B: Bundle> {
pub joints: XrHandBoneEntities,
pub tracker_bundle: B,
pub side: HandSide,
}
impl<B: Bundle> Command for SpawnHandTracker<B> {
fn apply(self, world: &mut bevy::prelude::World) {
let Some(executor) = world.remove_resource::<SpawnHandTrackerCommandExecutor>() else {
warn!("no SpawnHandTracker executor defined, skipping handtracker creation");
return;
};
let Ok(root) = world
.query_filtered::<Entity, With<XrTrackingRoot>>()
.get_single(world)
else {
error!("unable to get tracking root, skipping handtracker creation");
return;
};
let mut tracker = world.spawn(self.joints);
match &self.side {
HandSide::Left => tracker.insert(LeftHand),
HandSide::Right => tracker.insert(LeftHand),
};
let tracker = tracker.id();
world.entity_mut(root).push_children(&[tracker]);
executor.0(world, tracker, self.side);
if let Some(mut tracker) = world.get_entity_mut(tracker) {
tracker.insert(self.side);
tracker.insert(self.tracker_bundle);
}
world.insert_resource(executor);
}
}

View File

@@ -15,3 +15,7 @@ bevy_mod_openxr = { path = "../bevy_openxr", version = "0.1.0-rc1" }
[target.'cfg(not(target_family = "wasm"))'.dependencies] [target.'cfg(not(target_family = "wasm"))'.dependencies]
openxr = "0.18.0" openxr = "0.18.0"
[lints.clippy]
too_many_arguments = "allow"
type_complexity = "allow"

View File

@@ -1,4 +1,3 @@
// use bevy::prelude::*;
pub mod hand_gizmos; pub mod hand_gizmos;
#[cfg(not(target_family = "wasm"))] #[cfg(not(target_family = "wasm"))]
pub mod tracking_utils; pub mod tracking_utils;

View File

@@ -1,12 +1,6 @@
use bevy::prelude::*; use bevy::prelude::*;
use bevy_mod_openxr::{ use bevy_mod_openxr::{
action_binding::{OxrSendActionBindings, OxrSuggestActionBinding}, action_binding::{OxrSendActionBindings, OxrSuggestActionBinding}, action_set_attaching::OxrAttachActionSet, action_set_syncing::{OxrActionSetSyncSet, OxrSyncActionSet}, helper_traits::{ToQuat, ToVec3}, openxr_session_available, openxr_session_running, resources::{OxrFrameState, OxrInstance, Pipelined}, session::OxrSession, spaces::{OxrSpaceLocationFlags, OxrSpaceSyncSet}
action_set_attaching::OxrAttachActionSet,
action_set_syncing::{OxrActionSetSyncSet, OxrSyncActionSet},
helper_traits::{ToQuat, ToVec3},
resources::{OxrFrameState, OxrInstance, Pipelined},
session::OxrSession,
spaces::{OxrSpaceLocationFlags, OxrSpaceSyncSet},
}; };
use bevy_mod_xr::{ use bevy_mod_xr::{
session::{session_available, session_running, XrSessionCreated, XrTrackingRoot}, session::{session_available, session_running, XrSessionCreated, XrTrackingRoot},
@@ -48,7 +42,7 @@ impl Plugin for TrackingUtilitiesPlugin {
PreUpdate, PreUpdate,
update_head_transforms update_head_transforms
.in_set(OxrSpaceSyncSet) .in_set(OxrSpaceSyncSet)
.run_if(session_running), .run_if(openxr_session_running),
); );
//external //external
app.add_systems(PreUpdate, update_view.after(update_head_transforms)); app.add_systems(PreUpdate, update_view.after(update_head_transforms));
@@ -66,12 +60,12 @@ impl Plugin for TrackingUtilitiesPlugin {
PreUpdate, PreUpdate,
sync_actions sync_actions
.before(OxrActionSetSyncSet) .before(OxrActionSetSyncSet)
.run_if(session_running), .run_if(openxr_session_running),
); );
//attach sets //attach sets
app.add_systems(XrSessionCreated, attach_set); app.add_systems(XrSessionCreated, attach_set);
//create actions //create actions
app.add_systems(Startup, create_actions.run_if(session_available)); app.add_systems(Startup, create_actions.run_if(openxr_session_available));
app.add_systems(PreUpdate, update_left_grip.after(OxrSpaceSyncSet)); app.add_systems(PreUpdate, update_left_grip.after(OxrSpaceSyncSet));
app.add_systems(PreUpdate, update_right_grip.after(OxrSpaceSyncSet)); app.add_systems(PreUpdate, update_right_grip.after(OxrSpaceSyncSet));

View File

@@ -55,11 +55,13 @@
//! //!
use bevy::prelude::*; use bevy::prelude::*;
use bevy_mod_openxr::{ use bevy_mod_openxr::{
action_binding::OxrSuggestActionBinding, action_set_attaching::OxrAttachActionSet, action_binding::OxrSuggestActionBinding,
action_set_syncing::OxrActionSetSyncSet, action_set_syncing::OxrSyncActionSet, action_set_attaching::OxrAttachActionSet,
resources::OxrInstance, session::OxrSession, action_set_syncing::{OxrActionSetSyncSet, OxrSyncActionSet},
openxr_session_available, openxr_session_running,
resources::OxrInstance,
session::OxrSession,
}; };
use bevy_mod_xr::session::{session_available, session_running};
use openxr::{Path, Vector2f}; use openxr::{Path, Vector2f};
use std::borrow::Cow; use std::borrow::Cow;
@@ -69,37 +71,40 @@ impl Plugin for XRUtilsActionsPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.configure_sets( app.configure_sets(
Startup, Startup,
XRUtilsActionSystemSet::CreateEvents.run_if(session_available), XRUtilsActionSystemSet::CreateEvents.run_if(openxr_session_available),
); );
app.configure_sets( app.configure_sets(
PreUpdate, PreUpdate,
XRUtilsActionSystemSet::SyncActionStates.run_if(session_running), XRUtilsActionSystemSet::SyncActionStates.run_if(openxr_session_running),
); );
app.add_systems( app.add_systems(
Startup, Startup,
create_openxr_events create_openxr_events
.in_set(XRUtilsActionSystemSet::CreateEvents) .in_set(XRUtilsActionSystemSet::CreateEvents)
.run_if(session_available), .run_if(openxr_session_available),
);
app.add_systems(
Update,
sync_active_action_sets.run_if(openxr_session_running),
); );
app.add_systems(Update, sync_active_action_sets.run_if(session_running));
app.add_systems( app.add_systems(
PreUpdate, PreUpdate,
sync_and_update_action_states_f32 sync_and_update_action_states_f32
.run_if(session_running) .run_if(openxr_session_running)
.in_set(XRUtilsActionSystemSet::SyncActionStates) .in_set(XRUtilsActionSystemSet::SyncActionStates)
.after(OxrActionSetSyncSet), .after(OxrActionSetSyncSet),
); );
app.add_systems( app.add_systems(
PreUpdate, PreUpdate,
sync_and_update_action_states_bool sync_and_update_action_states_bool
.run_if(session_running) .run_if(openxr_session_running)
.in_set(XRUtilsActionSystemSet::SyncActionStates) .in_set(XRUtilsActionSystemSet::SyncActionStates)
.after(OxrActionSetSyncSet), .after(OxrActionSetSyncSet),
); );
app.add_systems( app.add_systems(
PreUpdate, PreUpdate,
sync_and_update_action_states_vector sync_and_update_action_states_vector
.run_if(session_running) .run_if(openxr_session_running)
.in_set(XRUtilsActionSystemSet::SyncActionStates) .in_set(XRUtilsActionSystemSet::SyncActionStates)
.after(OxrActionSetSyncSet), .after(OxrActionSetSyncSet),
); );