Merge pull request #163 from Schmarni-Dev/0.15rc

update to bevy 0.15
This commit is contained in:
Schmarni
2025-01-01 22:37:46 +01:00
committed by GitHub
39 changed files with 1448 additions and 1321 deletions

1528
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -10,10 +10,17 @@ resolver = "2"
members = ["crates/*", "crates/bevy_openxr/examples/android"] members = ["crates/*", "crates/bevy_openxr/examples/android"]
[workspace.dependencies] [workspace.dependencies]
bevy = { version = "0.14.0", default-features = false, features = [ bevy = { version = "0.15", default-features = false, features = [
"bevy_render", "bevy_render",
"bevy_core_pipeline", "bevy_core_pipeline",
"bevy_winit", "bevy_winit",
"bevy_pbr", "bevy_pbr",
"x11", "x11",
] } ] }
bevy_mod_xr.path = "crates/bevy_xr"
bevy_mod_openxr.path = "crates/bevy_openxr"
bevy_xr_utils.path = "crates/bevy_xr_utils"
openxr = "0.19.0"
thiserror = "2.0.3"
wgpu = "23"
wgpu-hal = "23"

View File

@@ -10,11 +10,11 @@ keywords = ["gamedev", "bevy", "Xr", "Vr", "OpenXR"]
[features] [features]
default = ["vulkan", "d3d12", "passthrough"] default = ["vulkan", "d3d12", "passthrough"]
vulkan = ["dep:ash"] vulkan = ["dep:ash"]
d3d12 = ["wgpu/dx12", "wgpu-hal/dx12", "dep:winapi", "dep:d3d12"] d3d12 = ["wgpu/dx12", "wgpu-hal/dx12", "dep:winapi"]
passthrough = [] passthrough = []
[dev-dependencies] [dev-dependencies]
bevy_xr_utils.path = "../bevy_xr_utils" bevy_xr_utils.workspace = true
bevy = { workspace = true, default-features = true } bevy = { workspace = true, default-features = true }
[target.'cfg(target_os = "android")'.dependencies] [target.'cfg(target_os = "android")'.dependencies]
@@ -27,22 +27,20 @@ bevy.workspace = true
# all other dependencies are placed under this since on wasm, this crate is completely empty # all other dependencies are placed under this since on wasm, this crate is completely empty
[target.'cfg(not(target_family = "wasm"))'.dependencies] [target.'cfg(not(target_family = "wasm"))'.dependencies]
openxr = "0.18.0" bevy_mod_xr.workspace = true
thiserror = "1.0.57" openxr.workspace = true
wgpu = "0.20" thiserror.workspace = true
wgpu-hal = "0.21" wgpu.workspace = true
bevy_mod_xr = { path = "../bevy_xr", version = "0.1.0" } wgpu-hal.workspace = true
ash = { version = "0.38", optional = true }
ash = { version = "0.37.3", optional = true }
[target.'cfg(target_family = "unix")'.dependencies] [target.'cfg(target_family = "unix")'.dependencies]
openxr = { version = "0.18.0", features = ["mint"] } openxr = { workspace = true, features = ["mint"] }
wgpu = { version = "0.20", features = ["vulkan-portability"] } wgpu = { workspace = true, features = ["vulkan-portability"] }
[target.'cfg(target_family = "windows")'.dependencies] [target.'cfg(target_family = "windows")'.dependencies]
openxr = { version = "0.18.0", features = ["mint", "static"] } openxr = { workspace=true, 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 }
[lints.clippy] [lints.clippy]
too_many_arguments = "allow" too_many_arguments = "allow"

View File

@@ -1,13 +1,25 @@
//! A simple 3D scene with light shining over a cube sitting on a plane. //! A simple 3D scene with light shining over a cube sitting on a plane.
use bevy::prelude::*; use bevy::{prelude::*, render::pipelined_rendering::PipelinedRenderingPlugin};
use bevy_mod_openxr::add_xr_plugins; use bevy_mod_openxr::{add_xr_plugins, init::OxrInitPlugin};
use openxr::EnvironmentBlendMode;
fn main() { fn main() {
App::new() App::new()
.add_plugins(add_xr_plugins(DefaultPlugins)) .add_plugins(
add_xr_plugins(DefaultPlugins.build().disable::<PipelinedRenderingPlugin>()).set(
OxrInitPlugin {
blend_modes: Some(vec![
EnvironmentBlendMode::ALPHA_BLEND,
EnvironmentBlendMode::ADDITIVE,
]),
..Default::default()
},
),
)
.add_plugins(bevy_xr_utils::hand_gizmos::HandGizmosPlugin) .add_plugins(bevy_xr_utils::hand_gizmos::HandGizmosPlugin)
.add_systems(Startup, setup) .add_systems(Startup, setup)
.insert_resource(ClearColor(Color::NONE))
.run(); .run();
} }
@@ -18,30 +30,27 @@ fn setup(
mut materials: ResMut<Assets<StandardMaterial>>, mut materials: ResMut<Assets<StandardMaterial>>,
) { ) {
// circular base // circular base
commands.spawn(PbrBundle { commands.spawn((
mesh: meshes.add(Circle::new(4.0)), Mesh3d(meshes.add(Circle::new(4.0))),
material: materials.add(Color::WHITE), MeshMaterial3d(materials.add(Color::WHITE)),
transform: Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)), Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)),
..default() ));
});
// cube // cube
commands.spawn(PbrBundle { commands.spawn((
mesh: meshes.add(Cuboid::new(1.0, 1.0, 1.0)), Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))),
material: materials.add(Color::srgb_u8(124, 144, 255)), MeshMaterial3d(materials.add(Color::srgb_u8(124, 144, 255))),
transform: Transform::from_xyz(0.0, 0.5, 0.0), Transform::from_xyz(0.0, 0.5, 0.0),
..default() ));
});
// light // light
commands.spawn(PointLightBundle { commands.spawn((
point_light: PointLight { PointLight {
shadows_enabled: true, shadows_enabled: true,
..default() ..default()
}, },
transform: Transform::from_xyz(4.0, 8.0, 4.0), Transform::from_xyz(4.0, 8.0, 4.0),
..default() ));
}); commands.spawn((
commands.spawn(Camera3dBundle { Camera3d::default(),
transform: Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y), Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
..default() ));
});
} }

View File

@@ -34,24 +34,22 @@ fn setup_scene(
mut materials: ResMut<Assets<StandardMaterial>>, mut materials: ResMut<Assets<StandardMaterial>>,
) { ) {
// circular base // circular base
commands.spawn(PbrBundle { commands.spawn((
mesh: meshes.add(Circle::new(4.0)), Mesh3d(meshes.add(Circle::new(4.0))),
material: materials.add(Color::WHITE), MeshMaterial3d(materials.add(Color::WHITE)),
transform: Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)), Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)),
..default() ));
});
// cube // cube
commands.spawn(PbrBundle { commands.spawn((
mesh: meshes.add(Cuboid::new(1.0, 1.0, 1.0)), Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))),
material: materials.add(Color::srgb_u8(124, 144, 255)), MeshMaterial3d(materials.add(Color::srgb_u8(124, 144, 255))),
transform: Transform::from_xyz(0.0, 0.5, 0.0), Transform::from_xyz(0.0, 0.5, 0.0),
..default() ));
});
commands.spawn(Camera3dBundle { commands.spawn((
transform: Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y), Camera3d::default(),
..default() Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
}); ));
} }
#[derive(Component)] #[derive(Component)]
@@ -145,7 +143,7 @@ fn handle_flight_input(
let locomotion_vector = reference_quat.mul_vec3(input_vector); let locomotion_vector = reference_quat.mul_vec3(input_vector);
root_position.translation += root_position.translation +=
locomotion_vector * speed * time.delta_seconds(); locomotion_vector * speed * time.delta_secs();
} }
None => return, None => return,
} }

View File

@@ -7,9 +7,9 @@ publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
bevy_mod_openxr.path = "../.." bevy_mod_openxr.workspace = true
bevy = { workspace = true, default-features = true } bevy = { workspace = true, default-features = true }
bevy_xr_utils.path = "../../../bevy_xr_utils" bevy_xr_utils.workspace = true
[build-dependencies] [build-dependencies]
reqwest = { version = "0.12", features = ["blocking"] } reqwest = { version = "0.12", features = ["blocking"] }

View File

@@ -21,8 +21,8 @@ fn main() {
synchronous_pipeline_compilation: default(), synchronous_pipeline_compilation: default(),
})) }))
.add_plugins(bevy_xr_utils::hand_gizmos::HandGizmosPlugin) .add_plugins(bevy_xr_utils::hand_gizmos::HandGizmosPlugin)
.insert_resource(Msaa::Off)
.add_systems(Startup, setup) .add_systems(Startup, setup)
.add_systems(Update, modify_msaa)
.insert_resource(AmbientLight { .insert_resource(AmbientLight {
color: Default::default(), color: Default::default(),
brightness: 500.0, brightness: 500.0,
@@ -31,6 +31,15 @@ fn main() {
.run(); .run();
} }
#[derive(Component)]
struct MsaaModified;
fn modify_msaa(cams: Query<Entity, (With<Camera>, Without<MsaaModified>)>, mut commands: Commands) {
for cam in &cams {
commands.entity(cam).insert(Msaa::Off).insert(MsaaModified);
}
}
/// set up a simple 3D scene /// set up a simple 3D scene
fn setup( fn setup(
mut commands: Commands, mut commands: Commands,
@@ -40,19 +49,17 @@ fn setup(
let mut white: StandardMaterial = Color::WHITE.into(); let mut white: StandardMaterial = Color::WHITE.into();
white.unlit = true; white.unlit = true;
// circular base // circular base
commands.spawn(PbrBundle { commands.spawn((
mesh: meshes.add(Circle::new(4.0)), Mesh3d(meshes.add(Circle::new(4.0))),
material: materials.add(white), MeshMaterial3d(materials.add(white)),
transform: Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)), Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)),
..default() ));
});
let mut cube_mat: StandardMaterial = Color::srgb_u8(124, 144, 255).into(); let mut cube_mat: StandardMaterial = Color::srgb_u8(124, 144, 255).into();
cube_mat.unlit = true; cube_mat.unlit = true;
// cube // cube
commands.spawn(PbrBundle { commands.spawn((
mesh: meshes.add(Cuboid::new(1.0, 1.0, 1.0)), Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))),
material: materials.add(cube_mat), MeshMaterial3d(materials.add(cube_mat)),
transform: Transform::from_xyz(0.0, 0.5, 0.0), Transform::from_xyz(0.0, 0.5, 0.0),
..default() ));
});
} }

View File

@@ -1,10 +1,11 @@
//! A simple 3D scene with light shining over a cube sitting on a plane. //! A simple 3D scene with light shining over a cube sitting on a plane.
use bevy::prelude::*; use bevy::prelude::*;
use bevy_mod_openxr::{add_xr_plugins, init::OxrInitPlugin, types::OxrExtensions}; use bevy_mod_openxr::{
use bevy_mod_xr::session::XrState; add_xr_plugins, features::overlay::OxrOverlaySessionEvent, init::OxrInitPlugin,
// use openxr::EnvironmentBlendMode; types::OxrExtensions,
// use wgpu::TextureFormat; };
use openxr::EnvironmentBlendMode;
fn main() { fn main() {
App::new() App::new()
@@ -12,32 +13,32 @@ fn main() {
exts: { exts: {
let mut exts = OxrExtensions::default(); let mut exts = OxrExtensions::default();
exts.enable_hand_tracking(); exts.enable_hand_tracking();
exts.other.push("XR_EXTX_overlay\0".into()); exts.extx_overlay = true;
exts exts
}, },
// blend_modes: Some({ blend_modes: Some({
// let mut v = Vec::new(); vec![
// v.push(EnvironmentBlendMode::ALPHA_BLEND); EnvironmentBlendMode::ALPHA_BLEND,
// v.push(EnvironmentBlendMode::ADDITIVE); EnvironmentBlendMode::OPAQUE,
// v.push(EnvironmentBlendMode::OPAQUE); ]
// v }),
// }),
// formats: Some({
// let mut v = Vec::new();
// // v.push(TextureFormat::Rgba8Uint);
// v.push(TextureFormat::Rgba8Unorm);
// v.push(TextureFormat::Rgba8UnormSrgb);
// v
// }),
..OxrInitPlugin::default() ..OxrInitPlugin::default()
})) }))
.insert_resource(ClearColor(Color::NONE)) .insert_resource(ClearColor(Color::NONE))
.add_plugins(bevy_xr_utils::hand_gizmos::HandGizmosPlugin) .add_plugins(bevy_xr_utils::hand_gizmos::HandGizmosPlugin)
.add_systems(Startup, setup) .add_systems(Startup, setup)
.add_systems(Update, handle_input) .add_systems(Update, handle_input)
.add_systems(Update, print_main_session_changes)
.run(); .run();
} }
fn print_main_session_changes(mut events: EventReader<OxrOverlaySessionEvent>) {
for event in events.read() {
let OxrOverlaySessionEvent::MainSessionVisibilityChanged { visible, flags: _ } = event;
info!("main session visible: {visible}");
}
}
fn handle_input( fn handle_input(
keys: Res<ButtonInput<KeyCode>>, keys: Res<ButtonInput<KeyCode>>,
mut end: EventWriter<bevy_mod_xr::session::XrEndSessionEvent>, mut end: EventWriter<bevy_mod_xr::session::XrEndSessionEvent>,
@@ -45,7 +46,6 @@ fn handle_input(
mut begin: EventWriter<bevy_mod_xr::session::XrBeginSessionEvent>, mut begin: EventWriter<bevy_mod_xr::session::XrBeginSessionEvent>,
mut create: EventWriter<bevy_mod_xr::session::XrCreateSessionEvent>, mut create: EventWriter<bevy_mod_xr::session::XrCreateSessionEvent>,
mut request_exit: EventWriter<bevy_mod_xr::session::XrRequestExitEvent>, mut request_exit: EventWriter<bevy_mod_xr::session::XrRequestExitEvent>,
state: Res<XrState>,
) { ) {
if keys.just_pressed(KeyCode::KeyE) { if keys.just_pressed(KeyCode::KeyE) {
info!("sending end"); info!("sending end");
@@ -67,9 +67,6 @@ fn handle_input(
info!("sending request exit"); info!("sending request exit");
request_exit.send_default(); request_exit.send_default();
} }
if keys.just_pressed(KeyCode::KeyI) {
info!("current state: {:?}", *state);
}
} }
/// set up a simple 3D scene /// set up a simple 3D scene
@@ -78,31 +75,22 @@ fn setup(
mut meshes: ResMut<Assets<Mesh>>, mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>, mut materials: ResMut<Assets<StandardMaterial>>,
) { ) {
// circular base
// commands.spawn(PbrBundle {
// mesh: meshes.add(Circle::new(4.0)),
// material: materials.add(Color::WHITE),
// transform: Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)),
// ..default()
// });
// cube // cube
commands.spawn(PbrBundle { commands.spawn((
mesh: meshes.add(Cuboid::new(1.0, 1.0, 1.0)), Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))),
material: materials.add(Color::srgb_u8(124, 144, 255)), MeshMaterial3d(materials.add(Color::srgb_u8(124, 144, 255))),
transform: Transform::from_xyz(0.0, 2.5, 0.0), Transform::from_xyz(0.0, 2.5, 0.0),
..default() ));
});
// light // light
commands.spawn(PointLightBundle { commands.spawn((
point_light: PointLight { PointLight {
shadows_enabled: true, shadows_enabled: true,
..default() ..default()
}, },
transform: Transform::from_xyz(4.0, 8.0, 4.0), Transform::from_xyz(4.0, 8.0, 4.0),
..default() ));
}); commands.spawn((
commands.spawn(Camera3dBundle { Camera3d::default(),
transform: Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y), Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
..default() ));
});
} }

View File

@@ -5,15 +5,14 @@ use bevy_mod_openxr::{
action_binding::{OxrSendActionBindings, OxrSuggestActionBinding}, action_binding::{OxrSendActionBindings, OxrSuggestActionBinding},
action_set_attaching::OxrAttachActionSet, action_set_attaching::OxrAttachActionSet,
action_set_syncing::{OxrActionSetSyncSet, OxrSyncActionSet}, action_set_syncing::{OxrActionSetSyncSet, OxrSyncActionSet},
add_xr_plugins, add_xr_plugins, openxr_session_running,
resources::OxrInstance, resources::OxrInstance,
session::OxrSession, session::OxrSession,
spaces::OxrSpaceExt, spaces::OxrSpaceExt,
}; };
use bevy_mod_xr::{ use bevy_mod_xr::{
session::{session_available, session_running, XrSessionCreated, XrTrackingRoot}, session::{session_available, XrSessionCreated},
spaces::XrSpace, spaces::XrSpace,
types::XrPose,
}; };
use openxr::Posef; use openxr::Posef;
@@ -26,7 +25,7 @@ fn main() {
PreUpdate, PreUpdate,
sync_actions sync_actions
.before(OxrActionSetSyncSet) .before(OxrActionSetSyncSet)
.run_if(session_running), .run_if(openxr_session_running),
); );
app.add_systems(OxrSendActionBindings, suggest_action_bindings); app.add_systems(OxrSendActionBindings, suggest_action_bindings);
app.add_systems(Startup, create_actions.run_if(session_available)); app.add_systems(Startup, create_actions.run_if(session_available));
@@ -55,32 +54,29 @@ fn setup(
mut materials: ResMut<Assets<StandardMaterial>>, mut materials: ResMut<Assets<StandardMaterial>>,
) { ) {
// circular base // circular base
commands.spawn(PbrBundle { commands.spawn((
mesh: meshes.add(Circle::new(4.0)), Mesh3d(meshes.add(Circle::new(4.0))),
material: materials.add(Color::WHITE), MeshMaterial3d(materials.add(Color::WHITE)),
transform: Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)), Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)),
..default() ));
});
// cube // cube
commands.spawn(PbrBundle { commands.spawn((
mesh: meshes.add(Cuboid::new(1.0, 1.0, 1.0)), Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))),
material: materials.add(Color::srgb_u8(124, 144, 255)), MeshMaterial3d(materials.add(Color::srgb_u8(124, 144, 255))),
transform: Transform::from_xyz(0.0, 0.5, 0.0), Transform::from_xyz(0.0, 0.5, 0.0),
..default() ));
});
// light // light
commands.spawn(PointLightBundle { commands.spawn((
point_light: PointLight { PointLight {
shadows_enabled: true, shadows_enabled: true,
..default() ..default()
}, },
transform: Transform::from_xyz(4.0, 8.0, 4.0), Transform::from_xyz(4.0, 8.0, 4.0),
..default() ));
}); commands.spawn((
commands.spawn(Camera3dBundle { Camera3d::default(),
transform: Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y), Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
..default() ));
});
} }
fn suggest_action_bindings( fn suggest_action_bindings(
actions: Res<ControllerActions>, actions: Res<ControllerActions>,
@@ -112,7 +108,6 @@ fn create_actions(instance: Res<OxrInstance>, mut cmds: Commands) {
fn spawn_hands( fn spawn_hands(
actions: Res<ControllerActions>, actions: Res<ControllerActions>,
mut cmds: Commands, mut cmds: Commands,
root: Query<Entity, With<XrTrackingRoot>>,
session: Res<OxrSession>, session: Res<OxrSession>,
mut meshes: ResMut<Assets<Mesh>>, mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>, mut materials: ResMut<Assets<StandardMaterial>>,
@@ -130,34 +125,20 @@ fn spawn_hands(
.unwrap(), .unwrap(),
); );
let right_space = session let right_space = session
.create_action_space(&actions.right, openxr::Path::NULL, XrPose::IDENTITY) .create_action_space(&actions.right, openxr::Path::NULL, Isometry3d::IDENTITY)
.unwrap(); .unwrap();
let left = cmds cmds.spawn((
.spawn(( Mesh3d(meshes.add(Cuboid::new(0.1, 0.1, 0.05))),
PbrBundle { MeshMaterial3d(materials.add(Color::srgb_u8(124, 144, 255))),
mesh: meshes.add(Cuboid::new(0.1, 0.1, 0.05)),
material: materials.add(Color::srgb_u8(124, 144, 255)),
transform: Transform::from_xyz(0.0, 0.5, 0.0),
..default()
},
left_space, left_space,
Controller, Controller,
)) ));
.id(); cmds.spawn((
let right = cmds Mesh3d(meshes.add(Cuboid::new(0.1, 0.1, 0.05))),
.spawn(( MeshMaterial3d(materials.add(Color::srgb_u8(124, 144, 255))),
PbrBundle {
mesh: meshes.add(Cuboid::new(0.1, 0.1, 0.05)),
material: materials.add(Color::srgb_u8(124, 144, 255)),
transform: Transform::from_xyz(0.0, 0.5, 0.0),
..default()
},
right_space, right_space,
Controller, Controller,
)) ));
.id();
cmds.entity(root.single()).push_children(&[left, right]);
} }
#[derive(Component)] #[derive(Component)]

View File

@@ -1,12 +1,19 @@
//! A simple 3D scene with light shining over a cube sitting on a plane. //! A simple 3D scene with light shining over a cube sitting on a plane.
use bevy::prelude::*; use bevy::prelude::*;
use bevy_mod_openxr::add_xr_plugins; use bevy_mod_openxr::{add_xr_plugins, init::OxrInitPlugin};
use bevy_mod_xr::session::{XrSessionPlugin, XrState}; use bevy_mod_xr::session::{XrSessionPlugin, XrState};
fn main() { fn main() {
App::new() App::new()
.add_plugins(add_xr_plugins(DefaultPlugins).set(XrSessionPlugin { auto_handle: true })) .add_plugins(
add_xr_plugins(DefaultPlugins)
.set(XrSessionPlugin { auto_handle: true })
.set(OxrInitPlugin {
blend_modes: Some(vec![openxr::EnvironmentBlendMode::OPAQUE]),
..Default::default()
}),
)
.add_plugins(bevy_xr_utils::hand_gizmos::HandGizmosPlugin) .add_plugins(bevy_xr_utils::hand_gizmos::HandGizmosPlugin)
.add_systems(Startup, setup) .add_systems(Startup, setup)
.add_systems(Update, handle_input) .add_systems(Update, handle_input)
@@ -55,21 +62,19 @@ fn setup(
mut materials: ResMut<Assets<StandardMaterial>>, mut materials: ResMut<Assets<StandardMaterial>>,
) { ) {
// circular base // circular base
commands.spawn(PbrBundle { commands.spawn((
mesh: meshes.add(Circle::new(4.0)), Mesh3d(meshes.add(Circle::new(4.0))),
material: materials.add(Color::WHITE), MeshMaterial3d(materials.add(Color::WHITE)),
transform: Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)), Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)),
..default() ));
});
// cube // cube
commands.spawn(PbrBundle { commands.spawn((
mesh: meshes.add(Cuboid::new(1.0, 1.0, 1.0)), Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))),
material: materials.add(Color::srgb_u8(124, 144, 255)), MeshMaterial3d(materials.add(Color::srgb_u8(124, 144, 255))),
transform: Transform::from_xyz(0.0, 0.5, 0.0), Transform::from_xyz(0.0, 0.5, 0.0),
..default() ));
}); commands.spawn((
commands.spawn(Camera3dBundle { Camera3d::default(),
transform: Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y), Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
..default() ));
});
} }

View File

@@ -2,7 +2,7 @@
use bevy::prelude::*; use bevy::prelude::*;
use bevy_mod_openxr::{action_binding::OxrSendActionBindings, add_xr_plugins}; use bevy_mod_openxr::{action_binding::OxrSendActionBindings, add_xr_plugins};
use bevy_mod_xr::session::XrSessionCreated; use bevy_mod_xr::session::{XrSessionCreated, XrTracker};
use bevy_xr_utils::tracking_utils::{ use bevy_xr_utils::tracking_utils::{
suggest_action_bindings, TrackingUtilitiesPlugin, XrTrackedLeftGrip, XrTrackedLocalFloor, XrTrackedRightGrip, XrTrackedStage, XrTrackedView suggest_action_bindings, TrackingUtilitiesPlugin, XrTrackedLeftGrip, XrTrackedLocalFloor, XrTrackedRightGrip, XrTrackedStage, XrTrackedView
}; };
@@ -30,25 +30,23 @@ fn setup(
mut materials: ResMut<Assets<StandardMaterial>>, mut materials: ResMut<Assets<StandardMaterial>>,
) { ) {
// circular base // circular base
commands.spawn(PbrBundle { commands.spawn((
mesh: meshes.add(Circle::new(4.0)), Mesh3d(meshes.add(Circle::new(4.0))),
material: materials.add(Color::WHITE), MeshMaterial3d(materials.add(Color::WHITE)),
transform: Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)), Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)),
..default() ));
});
// light // light
commands.spawn(PointLightBundle { commands.spawn((
point_light: PointLight { PointLight {
shadows_enabled: true, shadows_enabled: true,
..default() ..default()
}, },
transform: Transform::from_xyz(4.0, 8.0, 4.0), Transform::from_xyz(4.0, 8.0, 4.0),
..default() ));
}); commands.spawn((
commands.spawn(Camera3dBundle { Camera3d::default(),
transform: Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y), Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
..default() ));
});
} }
fn spawn_hands( fn spawn_hands(
@@ -56,65 +54,42 @@ fn spawn_hands(
mut meshes: ResMut<Assets<Mesh>>, mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>, mut materials: ResMut<Assets<StandardMaterial>>,
) { ) {
let left = cmds cmds.spawn((
.spawn(( Mesh3d(meshes.add(Cuboid::new(0.1, 0.1, 0.05))),
PbrBundle { MeshMaterial3d(materials.add(Color::srgb_u8(124, 144, 255))),
mesh: meshes.add(Cuboid::new(0.1, 0.1, 0.05)), Transform::from_xyz(0.0, 0.5, 0.0),
material: materials.add(Color::srgb_u8(124, 144, 255)),
transform: Transform::from_xyz(0.0, 0.5, 0.0),
..default()
},
XrTrackedLeftGrip, XrTrackedLeftGrip,
)) XrTracker,
.id(); ));
let bundle = ( cmds.spawn((
PbrBundle { Mesh3d(meshes.add(Cuboid::new(0.1, 0.1, 0.05))),
mesh: meshes.add(Cuboid::new(0.1, 0.1, 0.05)), MeshMaterial3d(materials.add(Color::srgb_u8(124, 144, 255))),
material: materials.add(Color::srgb_u8(124, 144, 255)), Transform::from_xyz(0.0, 0.5, 0.0),
transform: Transform::from_xyz(0.0, 0.5, 0.0),
..default()
},
XrTrackedRightGrip, XrTrackedRightGrip,
); XrTracker,
let right = cmds.spawn(bundle).id(); ));
//head //head
cmds.spawn((
let head = cmds Mesh3d(meshes.add(Cuboid::new(0.2, 0.2, 0.2))),
.spawn(( MeshMaterial3d(materials.add(Color::srgb_u8(255, 144, 144))),
PbrBundle { Transform::from_xyz(0.0, 0.0, 0.0),
mesh: meshes.add(Cuboid::new(0.2, 0.2, 0.2)),
material: materials.add(Color::srgb_u8(255, 144, 144)),
transform: Transform::from_xyz(0.0, 0.0, 0.0),
..default()
},
XrTrackedView, XrTrackedView,
)) XrTracker,
.id(); ));
//local_floor emulated //local_floor emulated
let local_floor = cmds cmds.spawn((
.spawn(( Mesh3d(meshes.add(Cuboid::new(0.5, 0.1, 0.5))),
PbrBundle { MeshMaterial3d(materials.add(Color::srgb_u8(144, 255, 144))),
mesh: meshes.add(Cuboid::new(0.5, 0.1, 0.5)), Transform::from_xyz(0.0, 0.0, 0.0),
material: materials.add(Color::srgb_u8(144, 255, 144)),
transform: Transform::from_xyz(0.0, 0.0, 0.0),
..default()
},
XrTrackedLocalFloor, XrTrackedLocalFloor,
)) XrTracker,
.id(); ));
let stage = cmds cmds.spawn((
.spawn(( Mesh3d(meshes.add(Cuboid::new(0.5, 0.1, 0.5))),
PbrBundle { MeshMaterial3d(materials.add(Color::srgb_u8(144, 255, 255))),
mesh: meshes.add(Cuboid::new(0.5, 0.1, 0.5)), Transform::from_xyz(0.0, 0.0, 0.0),
material: materials.add(Color::srgb_u8(144, 255, 255)),
transform: Transform::from_xyz(0.0, 0.0, 0.0),
..default()
},
XrTrackedStage, XrTrackedStage,
)) XrTracker,
.id(); ));
cmds.entity(stage)
.push_children(&[left, right, head, local_floor]);
} }

View File

@@ -41,52 +41,46 @@ fn setup(
mut materials: ResMut<Assets<StandardMaterial>>, mut materials: ResMut<Assets<StandardMaterial>>,
) { ) {
// circular base // circular base
commands.spawn(PbrBundle { commands.spawn((
mesh: meshes.add(Circle::new(4.0)), Mesh3d(meshes.add(Circle::new(4.0))),
material: materials.add(Color::WHITE), MeshMaterial3d(materials.add(Color::WHITE)),
transform: Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)), Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)),
..default() ));
});
// red cube // red cube
commands.spawn(PbrBundle { commands.spawn((
mesh: meshes.add(Cuboid::new(1.0, 1.0, 1.0)), Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))),
material: materials.add(Color::srgb_u8(252, 44, 3)), MeshMaterial3d(materials.add(Color::srgb_u8(252, 44, 3))),
transform: Transform::from_xyz(4.0, 0.5, 0.0).with_scale(Vec3::splat(0.5)), Transform::from_xyz(4.0, 0.5, 0.0).with_scale(Vec3::splat(0.5)),
..default() ));
});
// blue cube // blue cube
commands.spawn(PbrBundle { commands.spawn((
mesh: meshes.add(Cuboid::new(1.0, 1.0, 1.0)), Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))),
material: materials.add(Color::srgb_u8(3, 28, 252)), MeshMaterial3d(materials.add(Color::srgb_u8(3, 28, 252))),
transform: Transform::from_xyz(-4.0, 0.5, 0.0).with_scale(Vec3::splat(0.5)), Transform::from_xyz(-4.0, 0.5, 0.0).with_scale(Vec3::splat(0.5)),
..default() ));
});
// green cube // green cube
commands.spawn(PbrBundle { commands.spawn((
mesh: meshes.add(Cuboid::new(1.0, 1.0, 1.0)), Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))),
material: materials.add(Color::srgb_u8(3, 252, 32)), MeshMaterial3d(materials.add(Color::srgb_u8(3, 252, 32))),
transform: Transform::from_xyz(0.0, 0.5, 4.0).with_scale(Vec3::splat(0.5)), Transform::from_xyz(0.0, 0.5, 4.0).with_scale(Vec3::splat(0.5)),
..default() ));
});
// white cube // white cube
commands.spawn(PbrBundle { commands.spawn((
mesh: meshes.add(Cuboid::new(1.0, 1.0, 1.0)), Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))),
material: materials.add(Color::srgb_u8(250, 250, 250)), MeshMaterial3d(materials.add(Color::srgb_u8(250, 250, 250))),
transform: Transform::from_xyz(0.0, 0.5, -4.0).with_scale(Vec3::splat(0.5)), Transform::from_xyz(0.0, 0.5, -4.0).with_scale(Vec3::splat(0.5)),
..default() ));
});
// black cube // black cube
commands.spawn(PbrBundle { commands.spawn((
mesh: meshes.add(Cuboid::new(1.0, 1.0, 1.0)), Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))),
material: materials.add(Color::srgb_u8(0, 0, 0)), MeshMaterial3d(materials.add(Color::srgb_u8(0, 0, 0))),
transform: Transform::from_xyz(0.0, 0.1, 0.0).with_scale(Vec3::splat(0.2)), Transform::from_xyz(0.0, 0.1, 0.0).with_scale(Vec3::splat(0.2)),
..default() ));
});
commands.spawn(Camera3dBundle { commands.spawn((
transform: Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y), Camera3d::default(),
..default() Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
}); ));
} }
#[derive(Component)] #[derive(Component)]

View File

@@ -16,7 +16,7 @@ impl Plugin for OxrActionBindingPlugin {
app.add_event::<OxrSuggestActionBinding>(); app.add_event::<OxrSuggestActionBinding>();
app.add_systems( app.add_systems(
Update, Update,
run_action_binding_sugestion.run_if(on_event::<XrSessionCreatedEvent>()), run_action_binding_sugestion.run_if(on_event::<XrSessionCreatedEvent>),
); );
} }
} }
@@ -25,7 +25,7 @@ impl Plugin for OxrActionBindingPlugin {
// Event to allow requesting binding suggestion for new actions // Event to allow requesting binding suggestion for new actions
pub(crate) fn run_action_binding_sugestion(world: &mut World) { pub(crate) fn run_action_binding_sugestion(world: &mut World) {
world.run_schedule(OxrSendActionBindings); world.run_schedule(OxrSendActionBindings);
world.run_system_once(bind_actions); _ = world.run_system_once(bind_actions);
} }
fn bind_actions(instance: Res<OxrInstance>, mut actions: EventReader<OxrSuggestActionBinding>) { fn bind_actions(instance: Res<OxrInstance>, mut actions: EventReader<OxrSuggestActionBinding>) {

View File

@@ -8,7 +8,7 @@ impl Plugin for OxrActionAttachingPlugin {
app.add_systems( app.add_systems(
PostUpdate, PostUpdate,
attach_sets attach_sets
.run_if(on_event::<XrSessionCreatedEvent>()) .run_if(on_event::<XrSessionCreatedEvent>)
.after(run_action_binding_sugestion), .after(run_action_binding_sugestion),
); );
} }

View File

@@ -22,7 +22,6 @@ fn sync_sets(session: Res<OxrSession>, mut events: EventReader<OxrSyncActionSet>
.map(|v| &v.0) .map(|v| &v.0)
.map(openxr::ActiveActionSet::new) .map(openxr::ActiveActionSet::new)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
if sets.is_empty() { if sets.is_empty() {
return; return;
} }

View File

@@ -1,4 +1,7 @@
use bevy::{ecs::system::Resource, prelude::{Deref, DerefMut}}; use bevy::{
ecs::system::Resource,
prelude::{Deref, DerefMut},
};
use openxr::ExtensionSet; use openxr::ExtensionSet;
#[derive(Clone, Debug, Eq, PartialEq, Deref, DerefMut, Resource)] #[derive(Clone, Debug, Eq, PartialEq, Deref, DerefMut, Resource)]
@@ -29,6 +32,10 @@ impl OxrExtensions {
self.0.ext_hand_tracking = false; self.0.ext_hand_tracking = false;
self self
} }
pub fn enable_extx_overlay(&mut self) -> &mut Self {
self.0.extx_overlay = true;
self
}
/// returns true if all of the extensions enabled are also available in `available_exts` /// returns true if all of the extensions enabled are also available in `available_exts`
pub fn is_available(&self, available_exts: &OxrExtensions) -> bool { pub fn is_available(&self, available_exts: &OxrExtensions) -> bool {
self.clone() & available_exts.clone() == *self self.clone() & available_exts.clone() == *self
@@ -192,6 +199,8 @@ macro_rules! impl_ext {
ext_local_floor, ext_local_floor,
ext_hand_tracking_data_source, ext_hand_tracking_data_source,
ext_plane_detection, ext_plane_detection,
ext_future,
ext_user_presence,
fb_composition_layer_image_layout, fb_composition_layer_image_layout,
fb_composition_layer_alpha_blend, fb_composition_layer_alpha_blend,
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
@@ -234,6 +243,7 @@ macro_rules! impl_ext {
fb_composition_layer_depth_test, fb_composition_layer_depth_test,
fb_spatial_entity_storage_batch, fb_spatial_entity_storage_batch,
fb_spatial_entity_user, fb_spatial_entity_user,
fb_face_tracking2,
htc_vive_cosmos_controller_interaction, htc_vive_cosmos_controller_interaction,
htc_facial_tracking, htc_facial_tracking,
htc_vive_focus3_controller_interaction, htc_vive_focus3_controller_interaction,
@@ -241,6 +251,7 @@ macro_rules! impl_ext {
htc_vive_wrist_tracker_interaction, htc_vive_wrist_tracker_interaction,
htc_passthrough, htc_passthrough,
htc_foveation, htc_foveation,
htc_anchor,
huawei_controller_interaction, huawei_controller_interaction,
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
khr_android_thread_settings, khr_android_thread_settings,
@@ -272,6 +283,8 @@ macro_rules! impl_ext {
khr_composition_layer_equirect2, khr_composition_layer_equirect2,
khr_binding_modification, khr_binding_modification,
khr_swapchain_usage_input_attachment_bit, khr_swapchain_usage_input_attachment_bit,
khr_locate_spaces,
khr_maintenance1,
meta_foveation_eye_tracked, meta_foveation_eye_tracked,
meta_local_dimming, meta_local_dimming,
meta_passthrough_preferences, meta_passthrough_preferences,
@@ -279,11 +292,18 @@ macro_rules! impl_ext {
meta_vulkan_swapchain_create_info, meta_vulkan_swapchain_create_info,
meta_performance_metrics, meta_performance_metrics,
meta_headset_id, meta_headset_id,
meta_recommended_layer_resolution,
meta_passthrough_color_lut, meta_passthrough_color_lut,
meta_spatial_entity_mesh,
meta_automatic_layer_filter,
meta_touch_controller_plus,
meta_environment_depth,
ml_ml2_controller_interaction, ml_ml2_controller_interaction,
ml_frame_end_info, ml_frame_end_info,
ml_global_dimmer, ml_global_dimmer,
ml_compat, ml_compat,
ml_marker_understanding,
ml_localization_map,
ml_user_calibration, ml_user_calibration,
mnd_headless, mnd_headless,
mnd_swapchain_usage_input_attachment_bit, mnd_swapchain_usage_input_attachment_bit,
@@ -315,7 +335,12 @@ macro_rules! impl_ext {
varjo_environment_depth_estimation, varjo_environment_depth_estimation,
varjo_marker_tracking, varjo_marker_tracking,
varjo_view_offset, varjo_view_offset,
varjo_xr4_controller_interaction,
yvr_controller_interaction, yvr_controller_interaction,
extx_overlay,
mndx_egl_enable,
mndx_force_feedback_curl,
htcx_vive_tracker_interaction,
} }
)* )*

View File

@@ -1,10 +1,10 @@
use bevy::prelude::*; use bevy::prelude::*;
use bevy_mod_xr::hands::{ use bevy_mod_xr::hands::{
spawn_hand_bones, HandBone, HandBoneRadius, HandSide, SpawnHandTracker, spawn_hand_bones, HandBone, HandSide, SpawnHandTracker, SpawnHandTrackerCommandExecutor,
SpawnHandTrackerCommandExecutor, XrHandBoneRadius,
}; };
use bevy_mod_xr::hands::{LeftHand, RightHand, XrHandBoneEntities}; use bevy_mod_xr::hands::{LeftHand, RightHand, XrHandBoneEntities};
use bevy_mod_xr::session::{XrPreDestroySession, XrSessionCreated, XrTrackingRoot}; use bevy_mod_xr::session::{XrPreDestroySession, XrSessionCreated};
use bevy_mod_xr::spaces::{ use bevy_mod_xr::spaces::{
XrPrimaryReferenceSpace, XrReferenceSpace, XrSpaceLocationFlags, XrSpaceVelocityFlags, XrPrimaryReferenceSpace, XrReferenceSpace, XrSpaceLocationFlags, XrSpaceVelocityFlags,
XrVelocity, XrVelocity,
@@ -70,11 +70,7 @@ fn handle_tracker_spawn(world: &mut World, tracker: Entity, side: HandSide) {
.insert(OxrHandTracker(oxr_tracker)); .insert(OxrHandTracker(oxr_tracker));
} }
fn spawn_default_hands(mut cmds: Commands, root: Query<Entity, With<XrTrackingRoot>>) { fn spawn_default_hands(mut cmds: Commands) {
let Ok(root) = root.get_single() else {
error!("unable to get tracking root, skipping handtracker creation");
return;
};
debug!("spawning default hands"); debug!("spawning default hands");
let left_bones = spawn_hand_bones(&mut cmds, |_| { let left_bones = spawn_hand_bones(&mut cmds, |_| {
( (
@@ -90,14 +86,12 @@ fn spawn_default_hands(mut cmds: Commands, root: Query<Entity, With<XrTrackingRo
OxrSpaceLocationFlags(openxr::SpaceLocationFlags::default()), OxrSpaceLocationFlags(openxr::SpaceLocationFlags::default()),
) )
}); });
cmds.entity(root).push_children(&left_bones); cmds.queue(SpawnHandTracker {
cmds.entity(root).push_children(&right_bones);
cmds.push(SpawnHandTracker {
joints: XrHandBoneEntities(left_bones), joints: XrHandBoneEntities(left_bones),
tracker_bundle: DefaultHandTracker, tracker_bundle: DefaultHandTracker,
side: HandSide::Left, side: HandSide::Left,
}); });
cmds.push(SpawnHandTracker { cmds.queue(SpawnHandTracker {
joints: XrHandBoneEntities(right_bones), joints: XrHandBoneEntities(right_bones),
tracker_bundle: DefaultHandTracker, tracker_bundle: DefaultHandTracker,
side: HandSide::Right, side: HandSide::Right,
@@ -134,7 +128,7 @@ fn locate_hands(
session: Res<OxrSession>, session: Res<OxrSession>,
mut bone_query: Query<( mut bone_query: Query<(
&HandBone, &HandBone,
&mut HandBoneRadius, &mut XrHandBoneRadius,
&mut Transform, &mut Transform,
Option<&mut XrVelocity>, Option<&mut XrVelocity>,
&mut OxrSpaceLocationFlags, &mut OxrSpaceLocationFlags,

View File

@@ -55,7 +55,7 @@ fn add_overlay_info_to_chain(
exts: Res<OxrEnabledExtensions>, exts: Res<OxrEnabledExtensions>,
settings: Res<OxrOverlaySettings>, settings: Res<OxrOverlaySettings>,
) { ) {
if exts.other.contains(&"XR_EXTX_overlay\0".to_string()) { if exts.extx_overlay {
chain.push(OxrSessionCreateInfoOverlay::new( chain.push(OxrSessionCreateInfoOverlay::new(
settings.flags, settings.flags,
settings.session_layer_placement, settings.session_layer_placement,

View File

@@ -1,5 +1,5 @@
#[cfg(all(feature = "d3d12", windows))] // #[cfg(all(feature = "d3d12", windows))]
mod d3d12; // mod d3d12;
#[cfg(feature = "vulkan")] #[cfg(feature = "vulkan")]
pub mod vulkan; pub mod vulkan;
@@ -8,7 +8,10 @@ use std::any::TypeId;
use bevy::math::UVec2; use bevy::math::UVec2;
use openxr::{FrameStream, FrameWaiter, Session}; use openxr::{FrameStream, FrameWaiter, Session};
use crate::{session::OxrSessionCreateNextChain, types::{AppInfo, OxrExtensions, Result, WgpuGraphics}}; use crate::{
session::OxrSessionCreateNextChain,
types::{AppInfo, OxrExtensions, Result, WgpuGraphics},
};
/// This is an extension trait to the [`Graphics`](openxr::Graphics) trait and is how the graphics API should be interacted with. /// This is an extension trait to the [`Graphics`](openxr::Graphics) trait and is how the graphics API should be interacted with.
pub unsafe trait GraphicsExt: openxr::Graphics { pub unsafe trait GraphicsExt: openxr::Graphics {
@@ -74,8 +77,8 @@ impl GraphicsBackend {
const ALL: &'static [Self] = &[ const ALL: &'static [Self] = &[
#[cfg(feature = "vulkan")] #[cfg(feature = "vulkan")]
Self::Vulkan(()), Self::Vulkan(()),
#[cfg(all(feature = "d3d12", windows))] // #[cfg(all(feature = "d3d12", windows))]
Self::D3D12(()), // Self::D3D12(()),
]; ];
pub fn available_backends(exts: &OxrExtensions) -> Vec<Self> { pub fn available_backends(exts: &OxrExtensions) -> Vec<Self> {
@@ -103,8 +106,8 @@ impl GraphicsBackend {
pub enum GraphicsWrap<T: GraphicsType> { pub enum GraphicsWrap<T: GraphicsType> {
#[cfg(feature = "vulkan")] #[cfg(feature = "vulkan")]
Vulkan(T::Inner<openxr::Vulkan>), Vulkan(T::Inner<openxr::Vulkan>),
#[cfg(all(feature = "d3d12", windows))] // #[cfg(all(feature = "d3d12", windows))]
D3D12(T::Inner<openxr::D3D12>), // D3D12(T::Inner<openxr::D3D12>),
} }
impl<T: GraphicsType> GraphicsWrap<T> { impl<T: GraphicsType> GraphicsWrap<T> {
@@ -173,12 +176,12 @@ macro_rules! graphics_match {
type Api = openxr::Vulkan; type Api = openxr::Vulkan;
graphics_match!(@arm_impl Vulkan; $expr $(=> $($return)*)?) graphics_match!(@arm_impl Vulkan; $expr $(=> $($return)*)?)
}, },
#[cfg(all(feature = "d3d12", windows))] // #[cfg(all(feature = "d3d12", windows))]
$crate::graphics::GraphicsWrap::D3D12($var) => { // $crate::graphics::GraphicsWrap::D3D12($var) => {
#[allow(unused)] // #[allow(unused)]
type Api = openxr::D3D12; // type Api = openxr::D3D12;
graphics_match!(@arm_impl D3D12; $expr $(=> $($return)*)?) // graphics_match!(@arm_impl D3D12; $expr $(=> $($return)*)?)
}, // },
} }
}; };

View File

@@ -21,7 +21,7 @@ const VK_TARGET_VERSION_ASH: u32 = ash::vk::make_api_version(
0, 0,
VK_TARGET_VERSION.major() as u32, VK_TARGET_VERSION.major() as u32,
VK_TARGET_VERSION.minor() as u32, VK_TARGET_VERSION.minor() as u32,
VK_TARGET_VERSION.patch() as u32, VK_TARGET_VERSION.patch(),
); );
unsafe impl GraphicsExt for openxr::Vulkan { unsafe impl GraphicsExt for openxr::Vulkan {
@@ -63,7 +63,7 @@ unsafe impl GraphicsExt for openxr::Vulkan {
mip_level_count: 1, mip_level_count: 1,
sample_count: 1, sample_count: 1,
dimension: wgpu::TextureDimension::D2, dimension: wgpu::TextureDimension::D2,
format: format, format,
usage: wgpu_hal::TextureUses::COLOR_TARGET | wgpu_hal::TextureUses::COPY_DST, usage: wgpu_hal::TextureUses::COLOR_TARGET | wgpu_hal::TextureUses::COPY_DST,
memory_flags: wgpu_hal::MemoryFlags::empty(), memory_flags: wgpu_hal::MemoryFlags::empty(),
view_formats: vec![], view_formats: vec![],
@@ -84,7 +84,7 @@ unsafe impl GraphicsExt for openxr::Vulkan {
mip_level_count: 1, mip_level_count: 1,
sample_count: 1, sample_count: 1,
dimension: wgpu::TextureDimension::D2, dimension: wgpu::TextureDimension::D2,
format: format, format,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST, usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST,
view_formats: &[], view_formats: &[],
}, },
@@ -113,20 +113,20 @@ unsafe impl GraphicsExt for openxr::Vulkan {
let flags = wgpu::InstanceFlags::empty(); let flags = wgpu::InstanceFlags::empty();
let extensions = let extensions =
<Vulkan as Api>::Instance::desired_extensions(&vk_entry, VK_TARGET_VERSION_ASH, flags)?; <Vulkan as Api>::Instance::desired_extensions(&vk_entry, VK_TARGET_VERSION_ASH, flags)?;
let device_extensions = vec![ let device_extensions = [
ash::extensions::khr::Swapchain::name(), ash::khr::swapchain::NAME,
ash::extensions::khr::DrawIndirectCount::name(), ash::khr::draw_indirect_count::NAME,
#[cfg(target_os = "android")] // #[cfg(target_os = "android")]
ash::extensions::khr::TimelineSemaphore::name(), ash::khr::timeline_semaphore::NAME,
ash::vk::KhrImagelessFramebufferFn::name(), ash::khr::imageless_framebuffer::NAME,
ash::vk::KhrImageFormatListFn::name(), ash::khr::image_format_list::NAME,
]; ];
let vk_instance = unsafe { let vk_instance = unsafe {
let extensions_cchar: Vec<_> = extensions.iter().map(|s| s.as_ptr()).collect(); let extensions_cchar: Vec<_> = extensions.iter().map(|s| s.as_ptr()).collect();
let app_name = CString::new(app_info.name.clone().into_owned())?; let app_name = CString::new(app_info.name.clone().into_owned())?;
let vk_app_info = ash::vk::ApplicationInfo::builder() let vk_app_info = ash::vk::ApplicationInfo::default()
.application_name(&app_name) .application_name(&app_name)
.application_version(1) .application_version(1)
.engine_name(&app_name) .engine_name(&app_name)
@@ -136,8 +136,9 @@ unsafe impl GraphicsExt for openxr::Vulkan {
let vk_instance = instance let vk_instance = instance
.create_vulkan_instance( .create_vulkan_instance(
system_id, system_id,
#[allow(clippy::missing_transmute_annotations)]
std::mem::transmute(vk_entry.static_fn().get_instance_proc_addr), std::mem::transmute(vk_entry.static_fn().get_instance_proc_addr),
&ash::vk::InstanceCreateInfo::builder() &ash::vk::InstanceCreateInfo::default()
.application_info(&vk_app_info) .application_info(&vk_app_info)
.enabled_extension_names(&extensions_cchar) as *const _ .enabled_extension_names(&extensions_cchar) as *const _
as *const _, as *const _,
@@ -181,7 +182,7 @@ unsafe impl GraphicsExt for openxr::Vulkan {
extensions, extensions,
flags, flags,
false, false,
Some(Box::new(())), None,
)? )?
}; };
@@ -205,26 +206,26 @@ unsafe impl GraphicsExt for openxr::Vulkan {
.adapter .adapter
.physical_device_features(&enabled_extensions, wgpu_features); .physical_device_features(&enabled_extensions, wgpu_features);
let family_index = 0; let family_index = 0;
let family_info = ash::vk::DeviceQueueCreateInfo::builder() let family_info = ash::vk::DeviceQueueCreateInfo::default()
.queue_family_index(family_index) .queue_family_index(family_index)
.queue_priorities(&[1.0]) .queue_priorities(&[1.0]);
.build();
let family_infos = [family_info]; let family_infos = [family_info];
let info = enabled_phd_features let mut physical_device_multiview_features = ash::vk::PhysicalDeviceMultiviewFeatures {
.add_to_device_create_builder(
ash::vk::DeviceCreateInfo::builder()
.queue_create_infos(&family_infos)
.push_next(&mut ash::vk::PhysicalDeviceMultiviewFeatures {
multiview: ash::vk::TRUE, multiview: ash::vk::TRUE,
..Default::default() ..Default::default()
}), };
let info = enabled_phd_features
.add_to_device_create(
ash::vk::DeviceCreateInfo::default()
.queue_create_infos(&family_infos)
.push_next(&mut physical_device_multiview_features),
) )
.enabled_extension_names(&extensions_cchar) .enabled_extension_names(&extensions_cchar);
.build();
let vk_device = unsafe { let vk_device = unsafe {
let vk_device = instance let vk_device = instance
.create_vulkan_device( .create_vulkan_device(
system_id, system_id,
#[allow(clippy::missing_transmute_annotations)]
std::mem::transmute(vk_entry.static_fn().get_instance_proc_addr), std::mem::transmute(vk_entry.static_fn().get_instance_proc_addr),
vk_physical_device.as_raw() as _, vk_physical_device.as_raw() as _,
&info as *const _ as *const _, &info as *const _ as *const _,
@@ -241,9 +242,10 @@ unsafe impl GraphicsExt for openxr::Vulkan {
let wgpu_open_device = unsafe { let wgpu_open_device = unsafe {
wgpu_exposed_adapter.adapter.device_from_raw( wgpu_exposed_adapter.adapter.device_from_raw(
vk_device, vk_device,
true, None,
&enabled_extensions, &enabled_extensions,
wgpu_features, wgpu_features,
&wgpu::MemoryHints::Performance,
family_info.queue_family_index, family_info.queue_family_index,
0, 0,
) )
@@ -273,6 +275,7 @@ unsafe impl GraphicsExt for openxr::Vulkan {
max_push_constant_size: 4, max_push_constant_size: 4,
..Default::default() ..Default::default()
}, },
memory_hints: wgpu::MemoryHints::Performance,
}, },
None, None,
) )
@@ -320,7 +323,7 @@ unsafe impl GraphicsExt for openxr::Vulkan {
ty: sys::SessionCreateInfo::TYPE, ty: sys::SessionCreateInfo::TYPE,
next: &binding as *const _ as *const _, next: &binding as *const _ as *const _,
create_flags: Default::default(), create_flags: Default::default(),
system_id: system_id, system_id,
}; };
let mut out = sys::Session::NULL; let mut out = sys::Session::NULL;
cvt((instance.fp().create_session)( cvt((instance.fp().create_session)(
@@ -379,7 +382,7 @@ fn vulkan_to_wgpu(format: ash::vk::Format) -> Option<wgpu::TextureFormat> {
F::R8G8B8A8_SINT => Tf::Rgba8Sint, F::R8G8B8A8_SINT => Tf::Rgba8Sint,
F::A2B10G10R10_UINT_PACK32 => Tf::Rgb10a2Uint, F::A2B10G10R10_UINT_PACK32 => Tf::Rgb10a2Uint,
F::A2B10G10R10_UNORM_PACK32 => Tf::Rgb10a2Unorm, F::A2B10G10R10_UNORM_PACK32 => Tf::Rgb10a2Unorm,
F::B10G11R11_UFLOAT_PACK32 => Tf::Rg11b10Float, F::B10G11R11_UFLOAT_PACK32 => Tf::Rg11b10Ufloat,
F::R32G32_UINT => Tf::Rg32Uint, F::R32G32_UINT => Tf::Rg32Uint,
F::R32G32_SINT => Tf::Rg32Sint, F::R32G32_SINT => Tf::Rg32Sint,
F::R32G32_SFLOAT => Tf::Rg32Float, F::R32G32_SFLOAT => Tf::Rg32Float,
@@ -630,7 +633,7 @@ fn wgpu_to_vulkan(format: wgpu::TextureFormat) -> Option<ash::vk::Format> {
Tf::Rgba8Sint => F::R8G8B8A8_SINT, Tf::Rgba8Sint => F::R8G8B8A8_SINT,
Tf::Rgb10a2Uint => F::A2B10G10R10_UINT_PACK32, Tf::Rgb10a2Uint => F::A2B10G10R10_UINT_PACK32,
Tf::Rgb10a2Unorm => F::A2B10G10R10_UNORM_PACK32, Tf::Rgb10a2Unorm => F::A2B10G10R10_UNORM_PACK32,
Tf::Rg11b10Float => F::B10G11R11_UFLOAT_PACK32, Tf::Rg11b10Ufloat => F::B10G11R11_UFLOAT_PACK32,
Tf::Rg32Uint => F::R32G32_UINT, Tf::Rg32Uint => F::R32G32_UINT,
Tf::Rg32Sint => F::R32G32_SINT, Tf::Rg32Sint => F::R32G32_SINT,
Tf::Rg32Float => F::R32G32_SFLOAT, Tf::Rg32Float => F::R32G32_SFLOAT,
@@ -724,4 +727,3 @@ fn wgpu_to_vulkan(format: wgpu::TextureFormat) -> Option<ash::vk::Format> {
}, },
}) })
} }

View File

@@ -1,5 +1,4 @@
use bevy::prelude::*; use bevy::{math::Vec3A, prelude::*};
use bevy_mod_xr::types::XrPose;
pub trait ToPosef { pub trait ToPosef {
fn to_posef(&self) -> openxr::Posef; fn to_posef(&self) -> openxr::Posef;
@@ -7,8 +6,8 @@ pub trait ToPosef {
pub trait ToTransform { pub trait ToTransform {
fn to_transform(&self) -> Transform; fn to_transform(&self) -> Transform;
} }
pub trait ToXrPose { pub trait ToIsometry3d {
fn to_xr_pose(&self) -> XrPose; fn to_xr_pose(&self) -> Isometry3d;
} }
pub trait ToQuaternionf { pub trait ToQuaternionf {
fn to_quaternionf(&self) -> openxr::Quaternionf; fn to_quaternionf(&self) -> openxr::Quaternionf;
@@ -42,15 +41,15 @@ impl ToTransform for openxr::Posef {
.with_rotation(self.orientation.to_quat()) .with_rotation(self.orientation.to_quat())
} }
} }
impl ToXrPose for openxr::Posef { impl ToIsometry3d for openxr::Posef {
fn to_xr_pose(&self) -> XrPose { fn to_xr_pose(&self) -> Isometry3d {
XrPose { Isometry3d {
translation: self.position.to_vec3(), translation: self.position.to_vec3().into(),
rotation: self.orientation.to_quat(), rotation: self.orientation.to_quat(),
} }
} }
} }
impl ToPosef for XrPose { impl ToPosef for Isometry3d {
fn to_posef(&self) -> openxr::Posef { fn to_posef(&self) -> openxr::Posef {
openxr::Posef { openxr::Posef {
orientation: self.rotation.to_quaternionf(), orientation: self.rotation.to_quaternionf(),
@@ -90,6 +89,15 @@ impl ToVector3f for Vec3 {
} }
} }
} }
impl ToVector3f for Vec3A {
fn to_vector3f(&self) -> openxr::Vector3f {
openxr::Vector3f {
x: self.x,
y: self.y,
z: self.z,
}
}
}
impl ToVec3 for openxr::Vector3f { impl ToVec3 for openxr::Vector3f {
fn to_vec3(&self) -> Vec3 { fn to_vec3(&self) -> Vec3 {
Vec3 { Vec3 {

View File

@@ -113,7 +113,7 @@ impl Plugin for OxrInitPlugin {
( (
create_xr_session create_xr_session
.run_if(state_equals(XrState::Available)) .run_if(state_equals(XrState::Available))
.run_if(on_event::<XrCreateSessionEvent>()), .run_if(on_event::<XrCreateSessionEvent>),
( (
destroy_xr_session, destroy_xr_session,
(|v: Res<XrDestroySessionRender>| { (|v: Res<XrDestroySessionRender>| {
@@ -122,16 +122,16 @@ impl Plugin for OxrInitPlugin {
}), }),
) )
.run_if(state_matches!(XrState::Exiting { .. })) .run_if(state_matches!(XrState::Exiting { .. }))
.run_if(on_event::<XrDestroySessionEvent>()), .run_if(on_event::<XrDestroySessionEvent>),
begin_xr_session begin_xr_session
.run_if(state_equals(XrState::Ready)) .run_if(state_equals(XrState::Ready))
.run_if(on_event::<XrBeginSessionEvent>()), .run_if(on_event::<XrBeginSessionEvent>),
end_xr_session end_xr_session
.run_if(state_equals(XrState::Stopping)) .run_if(state_equals(XrState::Stopping))
.run_if(on_event::<XrEndSessionEvent>()), .run_if(on_event::<XrEndSessionEvent>),
request_exit_xr_session request_exit_xr_session
.run_if(session_created) .run_if(session_created)
.run_if(on_event::<XrRequestExitEvent>()), .run_if(on_event::<XrRequestExitEvent>),
) )
.in_set(XrHandleEvents::SessionStateUpdateEvents), .in_set(XrHandleEvents::SessionStateUpdateEvents),
) )
@@ -146,9 +146,6 @@ impl Plugin for OxrInitPlugin {
.insert_non_send_resource(session_create_info) .insert_non_send_resource(session_create_info)
.init_non_send_resource::<OxrSessionCreateNextChain>(); .init_non_send_resource::<OxrSessionCreateNextChain>();
app.world_mut()
.spawn((SpatialBundle::default(), XrTrackingRoot));
app.world_mut() app.world_mut()
.resource_mut::<Events<XrStateChanged>>() .resource_mut::<Events<XrStateChanged>>()
.send(XrStateChanged(XrState::Available)); .send(XrStateChanged(XrState::Available));
@@ -178,7 +175,7 @@ impl Plugin for OxrInitPlugin {
}) })
.run_if( .run_if(
resource_exists::<XrDestroySessionRender> resource_exists::<XrDestroySessionRender>
.and_then(|v: Res<XrDestroySessionRender>| v.0.load(Ordering::Relaxed)), .and(|v: Res<XrDestroySessionRender>| v.0.load(Ordering::Relaxed)),
) )
.chain(), .chain(),
); );
@@ -467,7 +464,7 @@ pub fn create_xr_session(world: &mut World) {
let system_id = world.resource::<OxrSystemId>(); let system_id = world.resource::<OxrSystemId>();
match init_xr_session( match init_xr_session(
device.wgpu_device(), device.wgpu_device(),
&instance, instance,
**system_id, **system_id,
&mut chain, &mut chain,
create_info.clone(), create_info.clone(),
@@ -475,8 +472,8 @@ pub fn create_xr_session(world: &mut World) {
Ok((session, frame_waiter, frame_stream, swapchain, images, graphics_info)) => { Ok((session, frame_waiter, frame_stream, swapchain, images, graphics_info)) => {
world.insert_resource(session.clone()); world.insert_resource(session.clone());
world.insert_resource(frame_waiter); world.insert_resource(frame_waiter);
world.insert_resource(images.clone()); world.insert_resource(images);
world.insert_resource(graphics_info.clone()); world.insert_resource(graphics_info);
world.insert_resource(OxrRenderResources { world.insert_resource(OxrRenderResources {
session, session,
frame_stream, frame_stream,
@@ -511,12 +508,13 @@ pub fn begin_xr_session(
world: &mut World, world: &mut World,
// session: Res<OxrSession>, mut session_started: ResMut<OxrSessionStarted> // session: Res<OxrSession>, mut session_started: ResMut<OxrSessionStarted>
) { ) {
let _span = info_span!("xr_begin_session"); let _span = debug_span!("xr_begin_session").entered();
world world
.get_resource::<OxrSession>() .get_resource::<OxrSession>()
.unwrap() .unwrap()
.begin(openxr::ViewConfigurationType::PRIMARY_STEREO) .begin(openxr::ViewConfigurationType::PRIMARY_STEREO)
.expect("Failed to begin session"); .expect("Failed to begin session");
drop(_span);
world.get_resource_mut::<OxrSessionStarted>().unwrap().0 = true; world.get_resource_mut::<OxrSessionStarted>().unwrap().0 = true;
world.run_schedule(XrPostSessionBegin); world.run_schedule(XrPostSessionBegin);
} }
@@ -527,7 +525,7 @@ pub fn end_xr_session(
) { ) {
// Maybe this could be an event? // Maybe this could be an event?
world.run_schedule(XrPreSessionEnd); world.run_schedule(XrPreSessionEnd);
let _span = info_span!("xr_end_session"); let _span = debug_span!("xr_end_session").entered();
world world
.get_resource::<OxrSession>() .get_resource::<OxrSession>()
.unwrap() .unwrap()

View File

@@ -37,14 +37,14 @@ impl LayerProvider for ProjectionLayer {
Some(Box::new( Some(Box::new(
CompositionLayerProjection::new() CompositionLayerProjection::new()
.layer_flags(CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA) .layer_flags(CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA)
.space(&stage) .space(stage)
.views(&[ .views(&[
CompositionLayerProjectionView::new() CompositionLayerProjectionView::new()
.pose(openxr_views.0[0].pose) .pose(openxr_views.0[0].pose)
.fov(openxr_views.0[0].fov) .fov(openxr_views.0[0].fov)
.sub_image( .sub_image(
SwapchainSubImage::new() SwapchainSubImage::new()
.swapchain(&swapchain) .swapchain(swapchain)
.image_array_index(0) .image_array_index(0)
.image_rect(rect), .image_rect(rect),
), ),
@@ -53,7 +53,7 @@ impl LayerProvider for ProjectionLayer {
.fov(openxr_views.0[1].fov) .fov(openxr_views.0[1].fov)
.sub_image( .sub_image(
SwapchainSubImage::new() SwapchainSubImage::new()
.swapchain(&swapchain) .swapchain(swapchain)
.image_array_index(1) .image_array_index(1)
.image_rect(rect), .image_rect(rect),
), ),
@@ -235,9 +235,15 @@ impl<'a> Default for CompositionLayerProjection<'a> {
pub struct CompositionLayerPassthrough { pub struct CompositionLayerPassthrough {
inner: sys::CompositionLayerPassthroughFB, inner: sys::CompositionLayerPassthroughFB,
} }
impl Default for CompositionLayerPassthrough {
fn default() -> Self {
Self::new()
}
}
impl CompositionLayerPassthrough { impl CompositionLayerPassthrough {
#[inline] #[inline]
pub fn new() -> Self { pub const fn new() -> Self {
Self { Self {
inner: openxr::sys::CompositionLayerPassthroughFB { inner: openxr::sys::CompositionLayerPassthroughFB {
ty: openxr::sys::CompositionLayerPassthroughFB::TYPE, ty: openxr::sys::CompositionLayerPassthroughFB::TYPE,

View File

@@ -60,8 +60,8 @@ pub fn add_xr_plugins<G: PluginGroup>(plugins: G) -> PluginGroupBuilder {
.build() .build()
.disable::<RenderPlugin>() .disable::<RenderPlugin>()
// .disable::<PipelinedRenderingPlugin>() // .disable::<PipelinedRenderingPlugin>()
.add_before::<RenderPlugin, _>(XrSessionPlugin { auto_handle: true }) .add_before::<RenderPlugin>(XrSessionPlugin { auto_handle: true })
.add_before::<RenderPlugin, _>(OxrInitPlugin::default()) .add_before::<RenderPlugin>(OxrInitPlugin::default())
.add(OxrEventsPlugin) .add(OxrEventsPlugin)
.add(OxrReferenceSpacePlugin::default()) .add(OxrReferenceSpacePlugin::default())
.add(OxrRenderPlugin) .add(OxrRenderPlugin)

View File

@@ -19,7 +19,7 @@ impl Plugin for OxrEventsPlugin {
} }
/// Polls any OpenXR events and handles them accordingly /// Polls any OpenXR events and handles them accordingly
pub fn poll_events(world: &mut World) { pub fn poll_events(world: &mut World) {
let _span = info_span!("xr_poll_events"); let _span = debug_span!("xr_poll_events").entered();
let instance = world.resource::<OxrInstance>().clone(); let instance = world.resource::<OxrInstance>().clone();
let handlers = world.remove_resource::<OxrEventHandlers>().unwrap(); let handlers = world.remove_resource::<OxrEventHandlers>().unwrap();
let mut buffer = EventDataBuffer::default(); let mut buffer = EventDataBuffer::default();
@@ -45,15 +45,16 @@ use super::{openxr_session_available, resources::OxrInstance};
pub struct OxrEventHandlers { pub struct OxrEventHandlers {
pub handlers: Vec<OxrEventHandler>, pub handlers: Vec<OxrEventHandler>,
} }
pub type OxrEventHandler = SystemId<OxrEvent, ()>; pub type OxrEventHandler = SystemId<In<OxrEvent>, ()>;
pub struct OxrEvent { pub struct OxrEvent {
event: Rc<RefCell<Option<Event<'static>>>>, event: Rc<RefCell<Option<Event<'static>>>>,
} }
impl OxrEvent { impl OxrEvent {
pub(crate) fn new<'a>(event: Rc<RefCell<Option<Event<'a>>>>) -> Self { pub(crate) fn new(event: Rc<RefCell<Option<Event<'_>>>>) -> Self {
Self { Self {
#[allow(clippy::missing_transmute_annotations)]
event: unsafe { mem::transmute(event) }, event: unsafe { mem::transmute(event) },
} }
} }
@@ -63,19 +64,19 @@ impl OxrEvent {
/// don't Store the [Event] anywhere!! /// don't Store the [Event] anywhere!!
#[allow(clippy::needless_lifetimes)] #[allow(clippy::needless_lifetimes)]
pub unsafe fn get<'a>(&'a self) -> Option<Event<'a>> { pub unsafe fn get<'a>(&'a self) -> Option<Event<'a>> {
self.event.borrow().clone() *self.event.borrow()
} }
} }
pub trait OxrEventHandlerExt { pub trait OxrEventHandlerExt {
fn add_oxr_event_handler<M>( fn add_oxr_event_handler<M>(
&mut self, &mut self,
system: impl IntoSystem<OxrEvent, (), M> + 'static, system: impl IntoSystem<In<OxrEvent>, (), M> + 'static,
) -> &mut Self; ) -> &mut Self;
} }
impl OxrEventHandlerExt for App { impl OxrEventHandlerExt for App {
fn add_oxr_event_handler<M>( fn add_oxr_event_handler<M>(
&mut self, &mut self,
system: impl IntoSystem<OxrEvent, (), M> + 'static, system: impl IntoSystem<In<OxrEvent>, (), M> + 'static,
) -> &mut Self { ) -> &mut Self {
self.init_resource::<OxrEventHandlers>(); self.init_resource::<OxrEventHandlers>();
let id = self.register_system(system); let id = self.register_system(system);

View File

@@ -1,5 +1,4 @@
use bevy::{ use bevy::{
ecs::query::QuerySingleError,
prelude::*, prelude::*,
render::{ render::{
camera::{ManualTextureView, ManualTextureViewHandle, ManualTextureViews, RenderTarget}, camera::{ManualTextureView, ManualTextureViewHandle, ManualTextureViews, RenderTarget},
@@ -11,10 +10,8 @@ use bevy::{
transform::TransformSystem, transform::TransformSystem,
}; };
use bevy_mod_xr::{ use bevy_mod_xr::{
camera::{XrCamera, XrCameraBundle, XrProjection}, camera::{XrCamera, XrProjection},
session::{ session::{XrFirst, XrHandleEvents, XrPreDestroySession, XrRenderSet, XrRootTransform},
XrFirst, XrHandleEvents, XrPreDestroySession, XrRenderSet, XrRootTransform, XrTrackingRoot,
},
spaces::XrPrimaryReferenceSpace, spaces::XrPrimaryReferenceSpace,
}; };
use openxr::ViewStateFlags; use openxr::ViewStateFlags;
@@ -34,10 +31,6 @@ impl Plugin for OxrRenderPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
if app.is_plugin_added::<PipelinedRenderingPlugin>() { if app.is_plugin_added::<PipelinedRenderingPlugin>() {
app.init_resource::<Pipelined>(); app.init_resource::<Pipelined>();
// if let Some(sub_app) = app.remove_sub_app(RenderExtractApp) {
// app.insert_sub_app(RenderExtractApp, SubApp::new(sub_app.app, update_rendering));
// }
} }
app.add_plugins(( app.add_plugins((
@@ -140,43 +133,26 @@ pub fn init_views(
graphics_info: Res<OxrGraphicsInfo>, graphics_info: Res<OxrGraphicsInfo>,
mut manual_texture_views: ResMut<ManualTextureViews>, mut manual_texture_views: ResMut<ManualTextureViews>,
swapchain_images: Res<OxrSwapchainImages>, swapchain_images: Res<OxrSwapchainImages>,
root: Query<Entity, With<XrTrackingRoot>>,
mut commands: Commands, mut commands: Commands,
) { ) {
let _span = info_span!("xr_init_views");
let temp_tex = swapchain_images.first().unwrap(); let temp_tex = swapchain_images.first().unwrap();
// this for loop is to easily add support for quad or mono views in the future. // this for loop is to easily add support for quad or mono views in the future.
for index in 0..2 { for index in 0..2 {
info!("{}", graphics_info.resolution); let _span = debug_span!("xr_init_view").entered();
info!("XrCamera resolution: {}", graphics_info.resolution);
let view_handle = let view_handle =
add_texture_view(&mut manual_texture_views, temp_tex, &graphics_info, index); add_texture_view(&mut manual_texture_views, temp_tex, &graphics_info, index);
commands.spawn((
let cam = commands Camera {
.spawn((XrCameraBundle {
camera: Camera {
target: RenderTarget::TextureView(view_handle), target: RenderTarget::TextureView(view_handle),
..Default::default() ..Default::default()
}, },
view: XrCamera(index), XrCamera(index),
..Default::default() ));
},))
.id();
match root.get_single() {
Ok(root) => {
commands.entity(root).add_child(cam);
}
Err(QuerySingleError::NoEntities(_)) => {
warn!("No XrTrackingRoot!");
}
Err(QuerySingleError::MultipleEntities(_)) => {
warn!("Multiple XrTrackingRoots! this is not allowed");
}
}
} }
} }
pub fn wait_frame(mut frame_waiter: ResMut<OxrFrameWaiter>, mut commands: Commands) { pub fn wait_frame(mut frame_waiter: ResMut<OxrFrameWaiter>, mut commands: Commands) {
let _span = info_span!("xr_wait_frame");
let state = frame_waiter.wait().expect("Failed to wait frame"); let state = frame_waiter.wait().expect("Failed to wait frame");
commands.insert_resource(OxrFrameState(state)); commands.insert_resource(OxrFrameState(state));
} }
@@ -385,11 +361,11 @@ pub fn insert_texture_views(
mut manual_texture_views: ResMut<ManualTextureViews>, mut manual_texture_views: ResMut<ManualTextureViews>,
graphics_info: Res<OxrGraphicsInfo>, graphics_info: Res<OxrGraphicsInfo>,
) { ) {
let _span = info_span!("xr_insert_texture_views");
let index = swapchain.acquire_image().expect("Failed to acquire image"); let index = swapchain.acquire_image().expect("Failed to acquire image");
let image = &swapchain_images[index as usize]; let image = &swapchain_images[index as usize];
for i in 0..2 { for i in 0..2 {
let _span = debug_span!("xr_insert_texture_view").entered();
add_texture_view(&mut manual_texture_views, image, &graphics_info, i); add_texture_view(&mut manual_texture_views, image, &graphics_info, i);
} }
} }
@@ -423,23 +399,21 @@ pub fn add_texture_view(
} }
pub fn begin_frame(mut frame_stream: ResMut<OxrFrameStream>) { pub fn begin_frame(mut frame_stream: ResMut<OxrFrameStream>) {
let _span = info_span!("xr_begin_frame");
frame_stream.begin().expect("Failed to begin frame"); frame_stream.begin().expect("Failed to begin frame");
} }
pub fn release_image(mut swapchain: ResMut<OxrSwapchain>) { pub fn release_image(mut swapchain: ResMut<OxrSwapchain>) {
let _span = info_span!("xr_release_image");
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
{ {
let ctx = ndk_context::android_context(); let ctx = ndk_context::android_context();
let vm = unsafe { jni::JavaVM::from_raw(ctx.vm().cast()) }.unwrap(); let vm = unsafe { jni::JavaVM::from_raw(ctx.vm().cast()) }.unwrap();
let env = vm.attach_current_thread_as_daemon(); let env = vm.attach_current_thread_as_daemon();
} }
let _span = debug_span!("xr_release_image").entered();
swapchain.release_image().unwrap(); swapchain.release_image().unwrap();
} }
pub fn end_frame(world: &mut World) { pub fn end_frame(world: &mut World) {
let _span = info_span!("xr_end_frame");
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
{ {
let ctx = ndk_context::android_context(); let ctx = ndk_context::android_context();
@@ -449,6 +423,7 @@ pub fn end_frame(world: &mut World) {
world.resource_scope::<OxrFrameStream, ()>(|world, mut frame_stream| { world.resource_scope::<OxrFrameStream, ()>(|world, mut frame_stream| {
let mut layers = vec![]; let mut layers = vec![];
let frame_state = world.resource::<OxrFrameState>(); let frame_state = world.resource::<OxrFrameState>();
let _span = debug_span!("get layers").entered();
if frame_state.should_render { if frame_state.should_render {
for layer in world.resource::<OxrRenderLayers>().iter() { for layer in world.resource::<OxrRenderLayers>().iter() {
if let Some(layer) = layer.get(world) { if let Some(layer) = layer.get(world) {
@@ -456,7 +431,9 @@ pub fn end_frame(world: &mut World) {
} }
} }
} }
drop(_span);
let layers: Vec<_> = layers.iter().map(Box::as_ref).collect(); let layers: Vec<_> = layers.iter().map(Box::as_ref).collect();
let _span = debug_span!("xr_end_frame").entered();
if let Err(e) = frame_stream.end( if let Err(e) = frame_stream.end(
frame_state.predicted_display_time, frame_state.predicted_display_time,
world.resource::<OxrGraphicsInfo>().blend_mode, world.resource::<OxrGraphicsInfo>().blend_mode,

View File

@@ -43,6 +43,7 @@ impl OxrEntry {
application_version: app_info.version.to_u32(), application_version: app_info.version.to_u32(),
engine_name: "Bevy", engine_name: "Bevy",
engine_version: Version::BEVY.to_u32(), engine_version: Version::BEVY.to_u32(),
api_version: openxr::Version::new(1, 1, 36),
}, },
&required_exts.into(), &required_exts.into(),
layers, layers,
@@ -108,7 +109,7 @@ impl OxrInstance {
graphics_match!( graphics_match!(
self.1; self.1;
_ => { _ => {
let (graphics, session_info) = Api::init_graphics(&self.2, &self, system_id)?; let (graphics, session_info) = Api::init_graphics(&self.2, self, system_id)?;
Ok((graphics, SessionCreateInfo(Api::wrap(session_info)))) Ok((graphics, SessionCreateInfo(Api::wrap(session_info))))
} }
@@ -185,7 +186,7 @@ impl OxrFrameStream {
stream => { stream => {
let mut new_layers = vec![]; let mut new_layers = vec![];
for (i, layer) in layers.into_iter().enumerate() { for (i, layer) in layers.iter().enumerate() {
if let Some(swapchain) = layer.swapchain() { if let Some(swapchain) = layer.swapchain() {
if !swapchain.0.using_graphics::<Api>() { if !swapchain.0.using_graphics::<Api>() {
error!( error!(
@@ -196,7 +197,10 @@ impl OxrFrameStream {
continue; continue;
} }
} }
new_layers.push(unsafe { std::mem::transmute(layer.header()) }); new_layers.push(unsafe {
#[allow(clippy::missing_transmute_annotations)]
std::mem::transmute(layer.header())
});
} }
Ok(stream.end(display_time, environment_blend_mode, new_layers.as_slice())?) Ok(stream.end(display_time, environment_blend_mode, new_layers.as_slice())?)

View File

@@ -7,7 +7,6 @@ use bevy_mod_xr::{
XrDestroySpace, XrPrimaryReferenceSpace, XrReferenceSpace, XrSpace, XrSpaceLocationFlags, XrDestroySpace, XrPrimaryReferenceSpace, XrReferenceSpace, XrSpace, XrSpaceLocationFlags,
XrSpaceVelocityFlags, XrVelocity, XrSpaceVelocityFlags, XrVelocity,
}, },
types::XrPose,
}; };
use openxr::{ use openxr::{
sys, HandJointLocation, HandJointLocations, HandJointVelocities, HandJointVelocity, sys, HandJointLocation, HandJointLocations, HandJointVelocities, HandJointVelocity,
@@ -51,28 +50,11 @@ impl Plugin for OxrSpatialPlugin {
.in_set(OxrSpaceSyncSet) .in_set(OxrSpaceSyncSet)
.run_if(openxr_session_running), .run_if(openxr_session_running),
) )
.observe(add_location_flags) .register_required_components::<XrSpaceLocationFlags, OxrSpaceLocationFlags>()
.observe(add_velocity_flags); .register_required_components::<XrSpaceVelocityFlags, OxrSpaceVelocityFlags>();
} }
} }
fn add_velocity_flags(event: Trigger<OnAdd, XrVelocity>, mut cmds: Commands) {
if event.entity() == Entity::PLACEHOLDER {
error!("called add_location_flags observer without entity");
return;
}
cmds.entity(event.entity())
.insert(OxrSpaceLocationFlags(openxr::SpaceLocationFlags::default()));
}
fn add_location_flags(event: Trigger<OnAdd, XrSpace>, mut cmds: Commands) {
if event.entity() == Entity::PLACEHOLDER {
error!("called add_location_flags observer without entity");
return;
}
cmds.entity(event.entity())
.insert(OxrSpaceLocationFlags(openxr::SpaceLocationFlags::default()));
}
fn destroy_space_event(instance: Res<OxrInstance>, mut events: EventReader<XrDestroySpace>) { fn destroy_space_event(instance: Res<OxrInstance>, mut events: EventReader<XrDestroySpace>) {
for space in events.read() { for space in events.read() {
match instance.destroy_space(space.0) { match instance.destroy_space(space.0) {
@@ -119,7 +101,7 @@ unsafe extern "system" fn patched_destroy_space(space: openxr::sys::Space) -> op
} }
} }
#[derive(Clone, Copy, Component)] #[derive(Clone, Copy, Component, Default)]
pub struct OxrSpaceLocationFlags(pub openxr::SpaceLocationFlags); pub struct OxrSpaceLocationFlags(pub openxr::SpaceLocationFlags);
impl OxrSpaceLocationFlags { impl OxrSpaceLocationFlags {
pub fn pos_valid(&self) -> bool { pub fn pos_valid(&self) -> bool {
@@ -135,7 +117,7 @@ impl OxrSpaceLocationFlags {
self.0.contains(SpaceLocationFlags::ORIENTATION_TRACKED) self.0.contains(SpaceLocationFlags::ORIENTATION_TRACKED)
} }
} }
#[derive(Clone, Copy, Component)] #[derive(Clone, Copy, Component, Default)]
pub struct OxrSpaceVelocityFlags(pub openxr::SpaceVelocityFlags); pub struct OxrSpaceVelocityFlags(pub openxr::SpaceVelocityFlags);
impl OxrSpaceVelocityFlags { impl OxrSpaceVelocityFlags {
pub fn linear_valid(&self) -> bool { pub fn linear_valid(&self) -> bool {
@@ -231,7 +213,7 @@ impl OxrSession {
&self, &self,
action: &openxr::Action<T>, action: &openxr::Action<T>,
subaction_path: openxr::Path, subaction_path: openxr::Path,
pose_in_space: XrPose, pose_in_space: Isometry3d,
) -> openxr::Result<XrSpace> { ) -> openxr::Result<XrSpace> {
let info = sys::ActionSpaceCreateInfo { let info = sys::ActionSpaceCreateInfo {
ty: sys::ActionSpaceCreateInfo::TYPE, ty: sys::ActionSpaceCreateInfo::TYPE,

View File

@@ -24,7 +24,7 @@ pub struct Version(pub u8, pub u8, pub u16);
impl Version { impl Version {
/// Bevy's version number /// Bevy's version number
pub const BEVY: Self = Self(0, 13, 0); pub const BEVY: Self = Self(0, 15, 0);
pub const fn to_u32(self) -> u32 { pub const fn to_u32(self) -> u32 {
let major = (self.0 as u32) << 24; let major = (self.0 as u32) << 24;

View File

@@ -12,11 +12,11 @@ bevy.workspace = true
# all dependencies are placed under this since on anything but wasm, this crate is completely empty # all dependencies are placed under this since on anything but wasm, this crate is completely empty
[target.'cfg(target_family = "wasm")'.dependencies] [target.'cfg(target_family = "wasm")'.dependencies]
thiserror = "1.0.57" thiserror.workspace = true
wgpu = "0.19.3" wgpu.workspace = true
wgpu-hal = "0.19.3" wgpu-hal.workspace = true
bevy_mod_xr = { path = "../bevy_xr", version = "0.1.0-rc1" } bevy_mod_xr.workspace = true
[lints.clippy] [lints.clippy]
too_many_arguments = "allow" too_many_arguments = "allow"

View File

@@ -94,11 +94,11 @@ impl<A: Action<ActionType = bool>> ActionState<A> {
} }
pub fn just_pressed(&self) -> bool { pub fn just_pressed(&self) -> bool {
self.previous_state == false && self.current_state == true !self.previous_state && self.current_state
} }
pub fn just_released(&self) -> bool { pub fn just_released(&self) -> bool {
self.previous_state == true && self.current_state == false self.previous_state && !self.current_state
} }
pub fn press(&mut self) { pub fn press(&mut self) {

View File

@@ -1,25 +1,24 @@
use core::panic;
use bevy::app::{App, Plugin, PostUpdate}; use bevy::app::{App, Plugin, PostUpdate};
use bevy::core_pipeline::core_3d::graph::Core3d;
use bevy::core_pipeline::core_3d::Camera3d; use bevy::core_pipeline::core_3d::Camera3d;
use bevy::core_pipeline::tonemapping::{DebandDither, Tonemapping}; use bevy::ecs::component::{Component, StorageType};
use bevy::ecs::bundle::Bundle;
use bevy::ecs::component::Component;
use bevy::ecs::reflect::ReflectComponent; use bevy::ecs::reflect::ReflectComponent;
use bevy::ecs::schedule::IntoSystemConfigs; use bevy::ecs::schedule::IntoSystemConfigs;
use bevy::math::{Mat4, Vec3A}; use bevy::math::{Mat4, Vec3A};
use bevy::pbr::{build_directional_light_cascades, clear_directional_light_cascades, SimulationLightSystems}; use bevy::pbr::{
build_directional_light_cascades, clear_directional_light_cascades, SimulationLightSystems,
};
use bevy::prelude::Projection;
use bevy::reflect::std_traits::ReflectDefault; use bevy::reflect::std_traits::ReflectDefault;
use bevy::reflect::Reflect; use bevy::reflect::Reflect;
use bevy::render::camera::{ use bevy::render::camera::{CameraProjection, CameraProjectionPlugin};
Camera, CameraMainTextureUsages, CameraProjection, CameraProjectionPlugin, CameraRenderGraph,
Exposure,
};
use bevy::render::extract_component::{ExtractComponent, ExtractComponentPlugin}; use bevy::render::extract_component::{ExtractComponent, ExtractComponentPlugin};
use bevy::render::primitives::Frustum; use bevy::render::view::{update_frusta, VisibilitySystems};
use bevy::render::view::{update_frusta, ColorGrading, VisibilitySystems, VisibleEntities};
use bevy::transform::components::{GlobalTransform, Transform};
use bevy::transform::TransformSystem; use bevy::transform::TransformSystem;
use crate::session::XrTracker;
pub struct XrCameraPlugin; pub struct XrCameraPlugin;
impl Plugin for XrCameraPlugin { impl Plugin for XrCameraPlugin {
@@ -44,12 +43,21 @@ impl Plugin for XrCameraPlugin {
} }
} }
#[derive(Debug, Clone, Component, Reflect, ExtractComponent)] #[derive(Debug, Clone, Reflect, ExtractComponent)]
#[reflect(Component, Default)] #[reflect(Component, Default)]
pub struct XrProjection { pub struct XrProjection {
pub projection_matrix: Mat4, pub projection_matrix: Mat4,
pub near: f32, pub near: f32,
} }
impl Component for XrProjection {
const STORAGE_TYPE: StorageType = StorageType::Table;
fn register_component_hooks(hooks: &mut bevy::ecs::component::ComponentHooks) {
hooks.on_add(|mut world, entity, _| {
world.commands().entity(entity).remove::<Projection>();
});
}
}
impl Default for XrProjection { impl Default for XrProjection {
fn default() -> Self { fn default() -> Self {
@@ -62,16 +70,15 @@ impl Default for XrProjection {
/// Marker component for an XR view. It is the backends responsibility to update this. /// Marker component for an XR view. It is the backends responsibility to update this.
#[derive(Clone, Copy, Component, ExtractComponent, Debug, Default)] #[derive(Clone, Copy, Component, ExtractComponent, Debug, Default)]
#[require(Camera3d, XrProjection, XrTracker)]
pub struct XrCamera(pub u32); pub struct XrCamera(pub u32);
impl CameraProjection for XrProjection { impl CameraProjection for XrProjection {
fn update(&mut self, _width: f32, _height: f32) {} fn update(&mut self, _width: f32, _height: f32) {}
fn far(&self) -> f32 { fn far(&self) -> f32 {
let far = self.projection_matrix.to_cols_array()[14] self.projection_matrix.to_cols_array()[14]
/ (self.projection_matrix.to_cols_array()[10] + 1.0); / (self.projection_matrix.to_cols_array()[10] + 1.0)
far
} }
// TODO calculate this properly // TODO calculate this properly
@@ -99,43 +106,8 @@ impl CameraProjection for XrProjection {
fn get_clip_from_view(&self) -> Mat4 { fn get_clip_from_view(&self) -> Mat4 {
self.projection_matrix self.projection_matrix
} }
}
#[derive(Bundle)] fn get_clip_from_view_for_sub(&self, _sub_view: &bevy::render::camera::SubCameraView) -> Mat4 {
pub struct XrCameraBundle { panic!("sub view not supported for xr camera");
pub camera: Camera,
pub camera_render_graph: CameraRenderGraph,
pub projection: XrProjection,
pub visible_entities: VisibleEntities,
pub frustum: Frustum,
pub transform: Transform,
pub global_transform: GlobalTransform,
pub camera_3d: Camera3d,
pub tonemapping: Tonemapping,
pub dither: DebandDither,
pub color_grading: ColorGrading,
pub exposure: Exposure,
pub main_texture_usages: CameraMainTextureUsages,
pub view: XrCamera,
}
impl Default for XrCameraBundle {
fn default() -> Self {
Self {
camera_render_graph: CameraRenderGraph::new(Core3d),
camera: Default::default(),
projection: Default::default(),
visible_entities: Default::default(),
frustum: Default::default(),
transform: Default::default(),
global_transform: Default::default(),
camera_3d: Default::default(),
tonemapping: Default::default(),
color_grading: Default::default(),
exposure: Default::default(),
main_texture_usages: Default::default(),
dither: DebandDither::Enabled,
view: XrCamera(0),
}
} }
} }

View File

@@ -1,15 +1,11 @@
use bevy::{ use bevy::{
ecs::{component::Component, entity::Entity, world::Command}, ecs::{component::Component, entity::Entity, world::Command},
hierarchy::BuildWorldChildren, log::warn,
log::{error, warn},
math::bool, math::bool,
prelude::{Bundle, Commands, Deref, DerefMut, Resource, SpatialBundle, With, World}, prelude::{Bundle, Commands, Deref, DerefMut, Resource, Transform, Visibility, World},
}; };
use crate::{ use crate::{session::XrTracker, spaces::XrSpaceLocationFlags};
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>( pub fn spawn_hand_bones<T: Bundle>(
@@ -18,15 +14,7 @@ pub fn spawn_hand_bones<T: Bundle>(
) -> [Entity; HAND_JOINT_COUNT] { ) -> [Entity; HAND_JOINT_COUNT] {
let mut bones: [Entity; HAND_JOINT_COUNT] = [Entity::PLACEHOLDER; HAND_JOINT_COUNT]; let mut bones: [Entity; HAND_JOINT_COUNT] = [Entity::PLACEHOLDER; HAND_JOINT_COUNT];
for bone in HandBone::get_all_bones().into_iter() { for bone in HandBone::get_all_bones().into_iter() {
bones[bone as usize] = cmds bones[bone as usize] = cmds.spawn((bone, (get_bundle)(bone))).id();
.spawn((
SpatialBundle::default(),
bone,
HandBoneRadius(0.0),
XrSpaceLocationFlags::default(),
))
.insert((get_bundle)(bone))
.id();
} }
bones bones
} }
@@ -48,11 +36,18 @@ pub struct RightHand;
pub struct XrHandBoneEntities(pub [Entity; HAND_JOINT_COUNT]); pub struct XrHandBoneEntities(pub [Entity; HAND_JOINT_COUNT]);
#[repr(transparent)] #[repr(transparent)]
#[derive(Clone, Copy, Component, Debug, DerefMut, Deref)] #[derive(Clone, Copy, Component, Debug, DerefMut, Deref, Default)]
pub struct HandBoneRadius(pub f32); pub struct XrHandBoneRadius(pub f32);
#[repr(u8)] #[repr(u8)]
#[derive(Clone, Copy, Component, Debug)] #[derive(Clone, Copy, Component, Debug)]
#[require(
XrSpaceLocationFlags,
XrHandBoneRadius,
Transform,
Visibility,
XrTracker
)]
pub enum HandBone { pub enum HandBone {
Palm = 0, Palm = 0,
Wrist = 1, Wrist = 1,
@@ -192,22 +187,14 @@ impl<B: Bundle> Command for SpawnHandTracker<B> {
warn!("no SpawnHandTracker executor defined, skipping handtracker creation"); warn!("no SpawnHandTracker executor defined, skipping handtracker creation");
return; 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); let mut tracker = world.spawn(self.joints);
match &self.side { match &self.side {
HandSide::Left => tracker.insert(LeftHand), HandSide::Left => tracker.insert((XrTracker, LeftHand)),
HandSide::Right => tracker.insert(RightHand), HandSide::Right => tracker.insert((XrTracker, RightHand)),
}; };
let tracker = tracker.id(); let tracker = tracker.id();
world.entity_mut(root).push_children(&[tracker]);
executor.0(world, tracker, self.side); executor.0(world, tracker, self.side);
if let Some(mut tracker) = world.get_entity_mut(tracker) { if let Ok(mut tracker) = world.get_entity_mut(tracker) {
tracker.insert(self.side); tracker.insert(self.side);
tracker.insert(self.tracker_bundle); tracker.insert(self.tracker_bundle);
} }

View File

@@ -1,7 +1,9 @@
use std::convert::identity;
use std::sync::atomic::AtomicBool; use std::sync::atomic::AtomicBool;
use std::sync::Arc; use std::sync::Arc;
use bevy::app::{AppExit, MainScheduleOrder}; use bevy::app::{AppExit, MainScheduleOrder};
use bevy::ecs::component::StorageType;
use bevy::ecs::schedule::ScheduleLabel; use bevy::ecs::schedule::ScheduleLabel;
use bevy::prelude::*; use bevy::prelude::*;
use bevy::render::extract_resource::{ExtractResource, ExtractResourcePlugin}; use bevy::render::extract_resource::{ExtractResource, ExtractResourcePlugin};
@@ -84,7 +86,33 @@ pub struct XrRootTransform(pub GlobalTransform);
/// Component used to specify the entity we should use as the tracking root. /// Component used to specify the entity we should use as the tracking root.
#[derive(Component)] #[derive(Component)]
#[require(Transform, Visibility)]
pub struct XrTrackingRoot; pub struct XrTrackingRoot;
#[derive(Resource)]
struct TrackingRootRes(Entity);
/// Makes the entity a child of the XrTrackingRoot if the entity has no parent
#[derive(Clone, Copy, Hash, PartialEq, Eq, Reflect, Debug, Default)]
pub struct XrTracker;
impl Component for XrTracker {
const STORAGE_TYPE: StorageType = StorageType::SparseSet;
fn register_component_hooks(hooks: &mut bevy::ecs::component::ComponentHooks) {
hooks.on_add(|mut world, entity, _| {
if world
.entity(entity)
.get_components::<Has<Parent>>()
.is_some_and(identity)
{
return;
}
let Some(root) = world.get_resource::<TrackingRootRes>().map(|r| r.0) else {
return;
};
world.commands().entity(root).add_child(entity);
});
}
}
pub struct XrSessionPlugin { pub struct XrSessionPlugin {
pub auto_handle: bool, pub auto_handle: bool,
@@ -121,10 +149,12 @@ impl Plugin for XrSessionPlugin {
.add_systems( .add_systems(
XrFirst, XrFirst,
exits_session_on_app_exit exits_session_on_app_exit
.run_if(on_event::<AppExit>()) .run_if(on_event::<AppExit>)
.run_if(session_created) .run_if(session_created)
.in_set(XrHandleEvents::ExitEvents), .in_set(XrHandleEvents::ExitEvents),
); );
let root = app.world_mut().spawn(XrTrackingRoot).id();
app.world_mut().insert_resource(TrackingRootRes(root));
app.world_mut() app.world_mut()
.resource_mut::<MainScheduleOrder>() .resource_mut::<MainScheduleOrder>()
.labels .labels
@@ -153,7 +183,7 @@ impl Plugin for XrSessionPlugin {
XrFirst, XrFirst,
exits_session_on_app_exit exits_session_on_app_exit
.before(XrHandleEvents::ExitEvents) .before(XrHandleEvents::ExitEvents)
.run_if(on_event::<AppExit>().and_then(session_running)), .run_if(on_event::<AppExit>.and(session_running)),
); );
let render_app = app.sub_app_mut(RenderApp); let render_app = app.sub_app_mut(RenderApp);

View File

@@ -1,15 +1,18 @@
use bevy::{ use bevy::{
ecs::component::StorageType,
prelude::*, prelude::*,
render::{extract_component::ExtractComponent, extract_resource::ExtractResource}, render::{extract_component::ExtractComponent, extract_resource::ExtractResource},
}; };
use crate::session::XrTracker;
/// Any Spaces will be invalid after the owning session exits /// Any Spaces will be invalid after the owning session exits
#[repr(transparent)] #[repr(transparent)]
#[derive(Clone, Copy, Hash, PartialEq, Eq, Reflect, Debug, ExtractComponent)] #[derive(Component, Clone, Copy, Hash, PartialEq, Eq, Reflect, Debug, ExtractComponent)]
#[require(XrSpaceLocationFlags, Transform, Visibility, XrTracker)]
pub struct XrSpace(u64); pub struct XrSpace(u64);
#[derive(Clone, Copy, Reflect, Debug, ExtractComponent, Default)] #[derive(Component, Clone, Copy, Reflect, Debug, ExtractComponent, Default)]
#[require(XrSpaceVelocityFlags)]
pub struct XrVelocity { pub struct XrVelocity {
/// Velocity of a space relative to it's reference space /// Velocity of a space relative to it's reference space
pub linear: Vec3, pub linear: Vec3,
@@ -69,28 +72,3 @@ impl XrSpace {
self.0 self.0
} }
} }
impl Component for XrSpace {
const STORAGE_TYPE: StorageType = StorageType::Table;
fn register_component_hooks(hooks: &mut bevy::ecs::component::ComponentHooks) {
hooks.on_add(|mut world, entity, _| {
world
.commands()
.entity(entity)
.insert(XrSpaceLocationFlags::default());
});
}
}
impl Component for XrVelocity {
const STORAGE_TYPE: StorageType = StorageType::Table;
fn register_component_hooks(hooks: &mut bevy::ecs::component::ComponentHooks) {
hooks.on_add(|mut world, entity, _| {
world
.commands()
.entity(entity)
.insert(XrSpaceVelocityFlags::default());
});
}
}

View File

@@ -1,26 +1,4 @@
use bevy::{ use bevy::math::Isometry3d;
math::{Quat, Vec3},
reflect::Reflect,
transform::components::Transform,
};
#[derive(Clone, Copy, PartialEq, Reflect, Debug)] #[deprecated = "Use Isometry3d instead"]
pub struct XrPose { pub type XrPose = Isometry3d;
pub translation: Vec3,
pub rotation: Quat,
}
impl Default for XrPose {
fn default() -> Self {
Self::IDENTITY
}
}
impl XrPose {
pub const IDENTITY: XrPose = XrPose {
translation: Vec3::ZERO,
rotation: Quat::IDENTITY,
};
pub const fn to_transform(self) -> Transform {
Transform::from_translation(self.translation).with_rotation(self.rotation)
}
}

View File

@@ -10,11 +10,11 @@ description = "utils for bevy_mod_xr and bevy_mod_openxr"
[dependencies] [dependencies]
bevy = { workspace = true, features = ["bevy_gizmos"] } bevy = { workspace = true, features = ["bevy_gizmos"] }
bevy_mod_xr = { path = "../bevy_xr", version = "0.1.0" } bevy_mod_xr.workspace = true
bevy_mod_openxr = { path = "../bevy_openxr", version = "0.1.0" } bevy_mod_openxr.workspace = true
[target.'cfg(not(target_family = "wasm"))'.dependencies] [target.'cfg(not(target_family = "wasm"))'.dependencies]
openxr = "0.18.0" openxr.workspace = true
[lints.clippy] [lints.clippy]
too_many_arguments = "allow" too_many_arguments = "allow"

View File

@@ -1,6 +1,7 @@
use bevy::color::palettes::css; use bevy::color::palettes::css;
use bevy::{prelude::*, transform::TransformSystem}; use bevy::{prelude::*, transform::TransformSystem};
use bevy_mod_xr::hands::{HandBone, HandBoneRadius}; use bevy_mod_xr::hands::{HandBone, XrHandBoneRadius};
use bevy_mod_xr::spaces::XrSpaceLocationFlags;
pub struct HandGizmosPlugin; pub struct HandGizmosPlugin;
impl Plugin for HandGizmosPlugin { impl Plugin for HandGizmosPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
@@ -12,11 +13,24 @@ impl Plugin for HandGizmosPlugin {
} }
fn draw_hand_gizmos( fn draw_hand_gizmos(
mut gizmos: Gizmos, mut gizmos: Gizmos,
query: Query<(&GlobalTransform, &HandBone, &HandBoneRadius)>, query: Query<(
&GlobalTransform,
&HandBone,
&XrHandBoneRadius,
&XrSpaceLocationFlags,
)>,
) { ) {
for (transform, bone, radius) in &query { for (transform, bone, radius, flags) in &query {
if (!flags.position_tracked) || (!flags.rotation_tracked) {
continue;
}
let pose = transform.compute_transform(); let pose = transform.compute_transform();
gizmos.sphere(pose.translation, pose.rotation, **radius, gizmo_color(bone)); let pose = Isometry3d {
translation: pose.translation.into(),
rotation: pose.rotation,
};
gizmos.sphere(pose, **radius, gizmo_color(bone));
gizmos.axes(pose, **radius);
} }
} }

View File

@@ -1,11 +1,17 @@
use bevy::prelude::*; use bevy::prelude::*;
use bevy_mod_openxr::{ use bevy_mod_openxr::{
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_binding::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},
}; };
use bevy_mod_xr::{ use bevy_mod_xr::{
session::{session_available, session_running, XrSessionCreated, XrTrackingRoot}, session::{XrSessionCreated, XrTracker, XrTrackingRoot},
spaces::{XrPrimaryReferenceSpace, XrReferenceSpace}, spaces::{XrPrimaryReferenceSpace, XrReferenceSpace},
types::XrPose,
}; };
use openxr::Posef; use openxr::Posef;
@@ -74,18 +80,14 @@ impl Plugin for TrackingUtilitiesPlugin {
//stage //stage
fn update_stage( fn update_stage(
mut root_query: Query<&mut Transform, (With<XrTrackingRoot>, Without<XrTrackedStage>)>, root_query: Query<&Transform, (With<XrTrackingRoot>, Without<XrTrackedStage>)>,
mut stage_query: Query<&mut Transform, (With<XrTrackedStage>, Without<XrTrackingRoot>)>, mut stage_query: Query<&mut Transform, (With<XrTrackedStage>, Without<XrTrackingRoot>)>,
) { ) {
let tracking_root_transform = root_query.get_single_mut(); if let Ok(root) = root_query.get_single() {
match tracking_root_transform { for mut transform in &mut stage_query {
Ok(root) => { *transform = *root;
for (mut transform) in &mut stage_query {
*transform = root.clone();
} }
} }
Err(_) => (),
}
} }
//view //view
@@ -128,14 +130,11 @@ fn update_view(
mut view_query: Query<&mut Transform, (With<XrTrackedView>, Without<HeadXRSpace>)>, mut view_query: Query<&mut Transform, (With<XrTrackedView>, Without<HeadXRSpace>)>,
) { ) {
let head_transform = head_query.get_single_mut(); let head_transform = head_query.get_single_mut();
match head_transform { if let Ok(root) = head_transform {
Ok(root) => { for mut transform in &mut view_query {
for (mut transform) in &mut view_query { *transform = *root;
*transform = root.clone();
} }
} }
Err(_) => (),
}
} }
//local floor //local floor
@@ -144,20 +143,17 @@ fn update_local_floor_transforms(
mut local_floor: Query<&mut Transform, (With<XrTrackedLocalFloor>, Without<HeadXRSpace>)>, mut local_floor: Query<&mut Transform, (With<XrTrackedLocalFloor>, Without<HeadXRSpace>)>,
) { ) {
let head_transform = head_space.get_single_mut(); let head_transform = head_space.get_single_mut();
match head_transform { if let Ok(head) = head_transform {
Ok(head) => { let mut calc_floor = *head;
let mut calc_floor = head.clone();
calc_floor.translation.y = 0.0; calc_floor.translation.y = 0.0;
//TODO: use yaw //TODO: use yaw
let (y, x, z) = calc_floor.rotation.to_euler(EulerRot::YXZ); let (y, _, _) = calc_floor.rotation.to_euler(EulerRot::YXZ);
let new_rot = Quat::from_rotation_y(y); let new_rot = Quat::from_rotation_y(y);
calc_floor.rotation = new_rot; calc_floor.rotation = new_rot;
for (mut transform) in &mut local_floor { for mut transform in &mut local_floor {
*transform = calc_floor; *transform = calc_floor;
} }
} }
Err(_) => (),
}
} }
//left grip //left grip
@@ -169,14 +165,11 @@ fn update_left_grip(
mut tracked_left_grip: Query<&mut Transform, (With<XrTrackedLeftGrip>, Without<LeftGrip>)>, mut tracked_left_grip: Query<&mut Transform, (With<XrTrackedLeftGrip>, Without<LeftGrip>)>,
) { ) {
let head_transform = left_grip.get_single_mut(); let head_transform = left_grip.get_single_mut();
match head_transform { if let Ok(head) = head_transform {
Ok(head) => { for mut transform in &mut tracked_left_grip {
for (mut transform) in &mut tracked_left_grip { *transform = *head;
*transform = head.clone();
} }
} }
Err(_) => (),
}
} }
//right grip //right grip
@@ -188,14 +181,11 @@ fn update_right_grip(
mut tracked_right_grip: Query<&mut Transform, (With<XrTrackedRightGrip>, Without<RightGrip>)>, mut tracked_right_grip: Query<&mut Transform, (With<XrTrackedRightGrip>, Without<RightGrip>)>,
) { ) {
let head_transform = right_grip.get_single_mut(); let head_transform = right_grip.get_single_mut();
match head_transform { if let Ok(head) = head_transform {
Ok(head) => { for mut transform in &mut tracked_right_grip {
for (mut transform) in &mut tracked_right_grip { *transform = *head;
*transform = head.clone();
} }
} }
Err(_) => (),
}
} }
//tracking rig //tracking rig
@@ -209,33 +199,28 @@ pub struct ControllerActions {
fn spawn_tracking_rig( fn spawn_tracking_rig(
actions: Res<ControllerActions>, actions: Res<ControllerActions>,
mut cmds: Commands, mut cmds: Commands,
root: Query<Entity, With<XrTrackingRoot>>,
session: Res<OxrSession>, session: Res<OxrSession>,
) { ) {
//head //head
let head_space = session let head_space = session
.create_reference_space(openxr::ReferenceSpaceType::VIEW, Transform::IDENTITY) .create_reference_space(openxr::ReferenceSpaceType::VIEW, Transform::IDENTITY)
.unwrap(); .unwrap();
let head = cmds cmds.spawn((
.spawn((SpatialBundle::default(), HeadXRSpace(head_space))) Transform::default(),
.id(); Visibility::default(),
XrTracker,
HeadXRSpace(head_space),
));
// let local_floor = cmds.spawn((SpatialBundle::default(), LocalFloor)).id(); // let local_floor = cmds.spawn((SpatialBundle::default(), LocalFloor)).id();
let left_space = session let left_space = session
.create_action_space(&actions.left, openxr::Path::NULL, XrPose::IDENTITY) .create_action_space(&actions.left, openxr::Path::NULL, Isometry3d::IDENTITY)
.unwrap(); .unwrap();
let right_space = session let right_space = session
.create_action_space(&actions.right, openxr::Path::NULL, XrPose::IDENTITY) .create_action_space(&actions.right, openxr::Path::NULL, Isometry3d::IDENTITY)
.unwrap(); .unwrap();
let left = cmds cmds.spawn((left_space, LeftGrip));
.spawn((SpatialBundle::default(), left_space, LeftGrip)) cmds.spawn((right_space, RightGrip));
.id();
let right = cmds
.spawn((SpatialBundle::default(), right_space, RightGrip))
.id();
cmds.entity(root.single())
.push_children(&[head, left, right]);
} }
//bindings //bindings