feat(bevy_xr_utils): add GenericTracker component and implement XR_MNDX_xdev_space as a backend
Signed-off-by: Schmarni <marnistromer@gmail.com>
This commit is contained in:
10
Cargo.lock
generated
10
Cargo.lock
generated
@@ -1394,6 +1394,7 @@ dependencies = [
|
|||||||
"bevy_mod_openxr",
|
"bevy_mod_openxr",
|
||||||
"bevy_mod_xr",
|
"bevy_mod_xr",
|
||||||
"openxr",
|
"openxr",
|
||||||
|
"openxr_mndx_xdev_space",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3904,6 +3905,15 @@ dependencies = [
|
|||||||
"mint",
|
"mint",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "openxr_mndx_xdev_space"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1589b0afd8fdf8d5d143457d1d3edf763c6f0e79254435351245a39975711f25"
|
||||||
|
dependencies = [
|
||||||
|
"openxr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "orbclient"
|
name = "orbclient"
|
||||||
version = "0.3.48"
|
version = "0.3.48"
|
||||||
|
|||||||
@@ -13,8 +13,12 @@ bevy = { workspace = true, features = ["bevy_gizmos"] }
|
|||||||
bevy_mod_xr.workspace = true
|
bevy_mod_xr.workspace = true
|
||||||
bevy_mod_openxr.workspace = true
|
bevy_mod_openxr.workspace = true
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
bevy = { workspace = true, default-features = true }
|
||||||
|
|
||||||
[target.'cfg(not(target_family = "wasm"))'.dependencies]
|
[target.'cfg(not(target_family = "wasm"))'.dependencies]
|
||||||
openxr.workspace = true
|
openxr.workspace = true
|
||||||
|
openxr_mndx_xdev_space = "0.1.0"
|
||||||
|
|
||||||
[lints.clippy]
|
[lints.clippy]
|
||||||
too_many_arguments = "allow"
|
too_many_arguments = "allow"
|
||||||
|
|||||||
67
crates/bevy_xr_utils/examples/mndx_xdev_spaces.rs
Normal file
67
crates/bevy_xr_utils/examples/mndx_xdev_spaces.rs
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
use bevy::prelude::*;
|
||||||
|
use bevy_mod_openxr::{add_xr_plugins, exts::OxrExtensions, init::OxrInitPlugin, resources::OxrSessionConfig};
|
||||||
|
use bevy_mod_xr::hand_debug_gizmos::HandGizmosPlugin;
|
||||||
|
use bevy_xr_utils::{
|
||||||
|
generic_tracker::GenericTrackerGizmoPlugin, mndx_xdev_spaces_trackers::MonadoXDevSpacesPlugin,
|
||||||
|
};
|
||||||
|
use openxr::EnvironmentBlendMode;
|
||||||
|
fn main() -> AppExit {
|
||||||
|
App::new()
|
||||||
|
.add_plugins(add_xr_plugins(DefaultPlugins).set(OxrInitPlugin {
|
||||||
|
exts: {
|
||||||
|
let mut exts = OxrExtensions::default();
|
||||||
|
exts.enable_hand_tracking();
|
||||||
|
exts.other.push("XR_MNDX_xdev_space".to_string());
|
||||||
|
exts
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
}))
|
||||||
|
.insert_resource(OxrSessionConfig {
|
||||||
|
blend_mode_preference: vec![
|
||||||
|
EnvironmentBlendMode::ALPHA_BLEND,
|
||||||
|
EnvironmentBlendMode::ADDITIVE,
|
||||||
|
EnvironmentBlendMode::OPAQUE,
|
||||||
|
],
|
||||||
|
..default()
|
||||||
|
})
|
||||||
|
.insert_resource(ClearColor(Color::NONE))
|
||||||
|
.add_plugins((
|
||||||
|
HandGizmosPlugin,
|
||||||
|
GenericTrackerGizmoPlugin,
|
||||||
|
MonadoXDevSpacesPlugin,
|
||||||
|
))
|
||||||
|
.add_systems(Startup, setup)
|
||||||
|
.run()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// set up a simple 3D scene
|
||||||
|
fn setup(
|
||||||
|
mut commands: Commands,
|
||||||
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||||
|
) {
|
||||||
|
// circular base
|
||||||
|
commands.spawn((
|
||||||
|
Mesh3d(meshes.add(Circle::new(4.0))),
|
||||||
|
MeshMaterial3d(materials.add(Color::WHITE)),
|
||||||
|
Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)),
|
||||||
|
));
|
||||||
|
// cube
|
||||||
|
commands.spawn((
|
||||||
|
Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))),
|
||||||
|
MeshMaterial3d(materials.add(Color::srgb_u8(124, 144, 255))),
|
||||||
|
Transform::from_xyz(0.0, 0.5, 0.0),
|
||||||
|
));
|
||||||
|
// light
|
||||||
|
commands.spawn((
|
||||||
|
PointLight {
|
||||||
|
shadows_enabled: true,
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
Transform::from_xyz(4.0, 8.0, 4.0),
|
||||||
|
));
|
||||||
|
commands.spawn((
|
||||||
|
Camera3d::default(),
|
||||||
|
Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
|
||||||
|
));
|
||||||
|
}
|
||||||
23
crates/bevy_xr_utils/src/generic_tracker.rs
Normal file
23
crates/bevy_xr_utils/src/generic_tracker.rs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
use bevy::{color::palettes::css, prelude::*};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Component)]
|
||||||
|
#[require(Transform)]
|
||||||
|
pub struct GenericTracker;
|
||||||
|
|
||||||
|
pub struct GenericTrackerGizmoPlugin;
|
||||||
|
|
||||||
|
impl Plugin for GenericTrackerGizmoPlugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
app.add_systems(
|
||||||
|
PostUpdate,
|
||||||
|
draw_gizmos.after(TransformSystem::TransformPropagate),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_gizmos(query: Query<&GlobalTransform, With<GenericTracker>>, mut gizmos: Gizmos) {
|
||||||
|
for transform in query {
|
||||||
|
gizmos.axes(*transform, 0.05);
|
||||||
|
gizmos.sphere(transform.to_isometry(), 0.05, css::PINK);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,3 +4,5 @@ pub mod tracking_utils;
|
|||||||
pub mod transform_utils;
|
pub mod transform_utils;
|
||||||
#[cfg(not(target_family = "wasm"))]
|
#[cfg(not(target_family = "wasm"))]
|
||||||
pub mod xr_utils_actions;
|
pub mod xr_utils_actions;
|
||||||
|
pub mod generic_tracker;
|
||||||
|
pub mod mndx_xdev_spaces_trackers;
|
||||||
|
|||||||
119
crates/bevy_xr_utils/src/mndx_xdev_spaces_trackers.rs
Normal file
119
crates/bevy_xr_utils/src/mndx_xdev_spaces_trackers.rs
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
use std::convert::identity;
|
||||||
|
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use bevy_mod_openxr::{
|
||||||
|
resources::{OxrInstance, OxrSystemId},
|
||||||
|
session::OxrSession,
|
||||||
|
spaces::OxrSpaceExt,
|
||||||
|
};
|
||||||
|
use bevy_mod_xr::{
|
||||||
|
session::{XrPreDestroySession, XrSessionCreated},
|
||||||
|
spaces::XrSpace,
|
||||||
|
};
|
||||||
|
use openxr_mndx_xdev_space::{InstanceXDevExtensionMNDX, SessionXDevExtensionMNDX, XDev, XDevList};
|
||||||
|
|
||||||
|
use crate::generic_tracker::GenericTracker;
|
||||||
|
|
||||||
|
pub struct MonadoXDevSpacesPlugin;
|
||||||
|
impl Plugin for MonadoXDevSpacesPlugin {
|
||||||
|
fn build(&self, _app: &mut App) {}
|
||||||
|
fn finish(&self, app: &mut App) {
|
||||||
|
let Some((instance, system_id)) =
|
||||||
|
app.world()
|
||||||
|
.get_resource::<OxrInstance>()
|
||||||
|
.and_then(|instance| {
|
||||||
|
app.world()
|
||||||
|
.get_resource::<OxrSystemId>()
|
||||||
|
.map(|system_id| (instance, system_id))
|
||||||
|
})
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if !instance
|
||||||
|
.supports_mndx_xdev_spaces(**system_id)
|
||||||
|
.is_ok_and(identity)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
app.add_systems(XrSessionCreated, session_created);
|
||||||
|
app.add_systems(PreUpdate, update_xdev_list);
|
||||||
|
app.add_systems(
|
||||||
|
XrPreDestroySession,
|
||||||
|
(despawn_xdev_trackers, |mut cmds: Commands| {
|
||||||
|
cmds.remove_resource::<PrimaryXDevList>()
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_xdev_list(mut xdev_list: ResMut<PrimaryXDevList>, mut cmds: Commands) {
|
||||||
|
let Ok(new_gen) = xdev_list
|
||||||
|
.get_generation()
|
||||||
|
.inspect_err(|err| error!("unable to get xdev list generation: {err}"))
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if new_gen != xdev_list.generation {
|
||||||
|
xdev_list.generation = new_gen;
|
||||||
|
cmds.run_system_cached(despawn_xdev_trackers);
|
||||||
|
cmds.run_system_cached(create_xdev_trackers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn session_created(session: Res<OxrSession>, mut cmds: Commands) {
|
||||||
|
let list = match session.get_xdev_list() {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(err) => {
|
||||||
|
error!("unable to create xdev list: {err}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
cmds.insert_resource(PrimaryXDevList {
|
||||||
|
generation: list.get_generation().unwrap(),
|
||||||
|
list,
|
||||||
|
});
|
||||||
|
cmds.run_system_cached(despawn_xdev_trackers);
|
||||||
|
cmds.run_system_cached(create_xdev_trackers);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn despawn_xdev_trackers(
|
||||||
|
xdev_query: Query<(Entity, &XrSpace), With<XDevTracker>>,
|
||||||
|
mut cmds: Commands,
|
||||||
|
session: Res<OxrSession>,
|
||||||
|
) {
|
||||||
|
for (e, space) in &xdev_query {
|
||||||
|
cmds.entity(e).despawn();
|
||||||
|
if let Err(err) = session.destroy_space(*space) {
|
||||||
|
error!("unable to destroy xdev XrSpace: {err}");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_xdev_trackers(xdev_list: Res<PrimaryXDevList>, mut cmds: Commands) {
|
||||||
|
let xdevs = match xdev_list.enumerate_xdevs() {
|
||||||
|
Err(err) => {
|
||||||
|
error!("Unable to enumerate xdevs: {err}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Ok(v) => v,
|
||||||
|
};
|
||||||
|
for xdev in xdevs
|
||||||
|
.into_iter()
|
||||||
|
.filter(XDev::can_create_space)
|
||||||
|
.filter(|v| v.name().contains("Tracker"))
|
||||||
|
{
|
||||||
|
info!("new XDev Tracker: {}", xdev.name());
|
||||||
|
let xr_space =
|
||||||
|
XrSpace::from_openxr_space(xdev.create_space(openxr::Posef::IDENTITY).unwrap());
|
||||||
|
cmds.spawn((xr_space, GenericTracker, XDevTracker));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Component, Debug)]
|
||||||
|
pub struct XDevTracker;
|
||||||
|
#[derive(Deref, DerefMut, Resource)]
|
||||||
|
pub struct PrimaryXDevList {
|
||||||
|
#[deref]
|
||||||
|
pub list: XDevList,
|
||||||
|
pub generation: u64,
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user