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:
@@ -13,8 +13,12 @@ bevy = { workspace = true, features = ["bevy_gizmos"] }
|
||||
bevy_mod_xr.workspace = true
|
||||
bevy_mod_openxr.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
bevy = { workspace = true, default-features = true }
|
||||
|
||||
[target.'cfg(not(target_family = "wasm"))'.dependencies]
|
||||
openxr.workspace = true
|
||||
openxr_mndx_xdev_space = "0.1.0"
|
||||
|
||||
[lints.clippy]
|
||||
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;
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
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