move to single crate for webxr and openxr
This commit is contained in:
168
Cargo.toml
168
Cargo.toml
@@ -1,98 +1,114 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "bevy_oxr"
|
name = "bevy_xr"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Community crate for OpenXR in Bevy"
|
description = "Community crate for XR in Bevy"
|
||||||
repository = "https://github.com/awtterpip/bevy_oxr"
|
repository = "https://github.com/awtterpip/bevy_oxr"
|
||||||
license = "MIT/Apache-2.0"
|
license = "MIT/Apache-2.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["linked", "vulkan"]
|
default = ["vulkan"]
|
||||||
linked = ["openxr/linked"]
|
|
||||||
vulkan = ["dep:ash"]
|
vulkan = ["dep:ash"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bevy_openxr.path = "./crates/bevy_openxr"
|
|
||||||
bevy_xr.path = "./crates/bevy_xr"
|
|
||||||
anyhow = "1.0.79"
|
|
||||||
async-std = "1.12.0"
|
|
||||||
bevy = "0.13.0"
|
bevy = "0.13.0"
|
||||||
paste = "1.0.14"
|
|
||||||
thiserror = "1.0.57"
|
|
||||||
wgpu = "0.19.3"
|
wgpu = "0.19.3"
|
||||||
wgpu-hal = "0.19.3"
|
wgpu-hal = "0.19.3"
|
||||||
winit = "0.28.7"
|
thiserror = "1.0.57"
|
||||||
|
bevy_xr_api.path = "bevy_xr_api"
|
||||||
[target.'cfg(target_family = "unix")'.dependencies]
|
|
||||||
openxr = { version = "0.17.1", features = ["mint"] }
|
|
||||||
|
|
||||||
[target.'cfg(target_family = "windows")'.dependencies]
|
|
||||||
openxr = { version = "0.17.1", features = ["mint", "static"] }
|
|
||||||
|
|
||||||
[target.'cfg(not(target_family = "wasm"))'.dependencies]
|
[target.'cfg(not(target_family = "wasm"))'.dependencies]
|
||||||
ash = { version = "0.37.3", optional = true }
|
ash = { version = "0.37.3", optional = true }
|
||||||
|
|
||||||
[target.'cfg(target_family = "wasm")'.dependencies]
|
[target.'cfg(target_family = "unix")'.dependencies]
|
||||||
js-sys = "0.3"
|
openxr = { version = "0.18.0", features = ["mint"] }
|
||||||
wasm-bindgen = "0.2.91"
|
|
||||||
glow = "0.12.1"
|
[target.'cfg(target_family = "windows")'.dependencies]
|
||||||
web-sys = { version = "0.3.67", features = [
|
openxr = { version = "0.18.0", features = ["mint", "static"] }
|
||||||
# STANDARD
|
|
||||||
'console',
|
|
||||||
'Document',
|
# [dependencies]
|
||||||
'Element',
|
# bevy_openxr.path = "./crates/bevy_openxr"
|
||||||
'Headers',
|
# bevy_xr.path = "./crates/bevy_xr"
|
||||||
'Navigator',
|
# anyhow = "1.0.79"
|
||||||
'Window',
|
# async-std = "1.12.0"
|
||||||
# IO
|
# bevy = "0.13.0"
|
||||||
# 'Url',
|
# paste = "1.0.14"
|
||||||
# WEBGL
|
# thiserror = "1.0.57"
|
||||||
'Gpu',
|
# wgpu = "0.19.3"
|
||||||
'HtmlCanvasElement',
|
# wgpu-hal = "0.19.3"
|
||||||
'WebGl2RenderingContext',
|
# winit = "0.28.7"
|
||||||
'WebGlFramebuffer',
|
|
||||||
'GamepadHapticActuator',
|
# [target.'cfg(target_family = "unix")'.dependencies]
|
||||||
## XR
|
# openxr = { version = "0.17.1", features = ["mint"] }
|
||||||
'DomPointReadOnly',
|
|
||||||
'XrWebGlLayer',
|
# [target.'cfg(target_family = "windows")'.dependencies]
|
||||||
'XrBoundedReferenceSpace',
|
# openxr = { version = "0.17.1", features = ["mint", "static"] }
|
||||||
'XrEye',
|
|
||||||
'XrFrame',
|
# [target.'cfg(not(target_family = "wasm"))'.dependencies]
|
||||||
'XrHandedness',
|
# ash = { version = "0.37.3", optional = true }
|
||||||
'XrInputSource',
|
|
||||||
'XrInputSourceArray',
|
# [target.'cfg(target_family = "wasm")'.dependencies]
|
||||||
'XrInputSourceEvent',
|
# js-sys = "0.3"
|
||||||
'XrInputSourceEventInit',
|
# wasm-bindgen = "0.2.91"
|
||||||
'XrInputSourcesChangeEvent',
|
# glow = "0.12.1"
|
||||||
'XrJointPose',
|
# web-sys = { version = "0.3.67", features = [
|
||||||
'XrJointSpace',
|
# # STANDARD
|
||||||
'XrPose',
|
# 'console',
|
||||||
'XrReferenceSpace',
|
# 'Document',
|
||||||
'XrReferenceSpaceEvent',
|
# 'Element',
|
||||||
'XrReferenceSpaceEventInit',
|
# 'Headers',
|
||||||
'XrReferenceSpaceType',
|
# 'Navigator',
|
||||||
'XrRenderState',
|
# 'Window',
|
||||||
'XrRenderStateInit',
|
# # IO
|
||||||
'XrRigidTransform',
|
# # 'Url',
|
||||||
'XrSession',
|
# # WEBGL
|
||||||
'XrSessionEvent',
|
# 'Gpu',
|
||||||
'XrSessionEventInit',
|
# 'HtmlCanvasElement',
|
||||||
'XrSessionInit',
|
# 'WebGl2RenderingContext',
|
||||||
'XrSessionMode',
|
# 'WebGlFramebuffer',
|
||||||
'XrSpace',
|
# 'GamepadHapticActuator',
|
||||||
'XrTargetRayMode',
|
# ## XR
|
||||||
'XrView',
|
# 'DomPointReadOnly',
|
||||||
'XrViewerPose',
|
# 'XrWebGlLayer',
|
||||||
'XrViewport',
|
# 'XrBoundedReferenceSpace',
|
||||||
'XrVisibilityState',
|
# 'XrEye',
|
||||||
'XrWebGlLayer',
|
# 'XrFrame',
|
||||||
'XrWebGlLayerInit',
|
# 'XrHandedness',
|
||||||
'XrSystem',
|
# 'XrInputSource',
|
||||||
] }
|
# 'XrInputSourceArray',
|
||||||
wasm-bindgen-futures = "0.4"
|
# 'XrInputSourceEvent',
|
||||||
|
# 'XrInputSourceEventInit',
|
||||||
|
# 'XrInputSourcesChangeEvent',
|
||||||
|
# 'XrJointPose',
|
||||||
|
# 'XrJointSpace',
|
||||||
|
# 'XrPose',
|
||||||
|
# 'XrReferenceSpace',
|
||||||
|
# 'XrReferenceSpaceEvent',
|
||||||
|
# 'XrReferenceSpaceEventInit',
|
||||||
|
# 'XrReferenceSpaceType',
|
||||||
|
# 'XrRenderState',
|
||||||
|
# 'XrRenderStateInit',
|
||||||
|
# 'XrRigidTransform',
|
||||||
|
# 'XrSession',
|
||||||
|
# 'XrSessionEvent',
|
||||||
|
# 'XrSessionEventInit',
|
||||||
|
# 'XrSessionInit',
|
||||||
|
# 'XrSessionMode',
|
||||||
|
# 'XrSpace',
|
||||||
|
# 'XrTargetRayMode',
|
||||||
|
# 'XrView',
|
||||||
|
# 'XrViewerPose',
|
||||||
|
# 'XrViewport',
|
||||||
|
# 'XrVisibilityState',
|
||||||
|
# 'XrWebGlLayer',
|
||||||
|
# 'XrWebGlLayerInit',
|
||||||
|
# 'XrSystem',
|
||||||
|
# ] }
|
||||||
|
# wasm-bindgen-futures = "0.4"
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["crates/*"]
|
members = ["bevy_xr_api"]
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
bevy = "0.13.0"
|
bevy = "0.13.0"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "bevy_xr"
|
name = "bevy_xr_api"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "bevy_openxr"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["vulkan"]
|
|
||||||
vulkan = ["dep:ash"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
thiserror = "1.0.57"
|
|
||||||
wgpu = "0.19.3"
|
|
||||||
wgpu-hal = "0.19.3"
|
|
||||||
|
|
||||||
bevy_xr.path = "../bevy_xr"
|
|
||||||
bevy.workspace = true
|
|
||||||
|
|
||||||
|
|
||||||
ash = { version = "0.37.3", optional = true }
|
|
||||||
|
|
||||||
[target.'cfg(target_family = "unix")'.dependencies]
|
|
||||||
openxr = { version = "0.18.0", features = ["mint"] }
|
|
||||||
|
|
||||||
[target.'cfg(target_family = "windows")'.dependencies]
|
|
||||||
openxr = { version = "0.18.0", features = ["mint", "static"] }
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
//! A simple 3D scene with light shining over a cube sitting on a plane.
|
|
||||||
|
|
||||||
use std::any::TypeId;
|
|
||||||
|
|
||||||
use bevy::prelude::*;
|
|
||||||
use bevy_openxr::{
|
|
||||||
actions::{create_action_sets, ActionApp},
|
|
||||||
add_xr_plugins, resources::{TypedAction, XrActions, XrInstance},
|
|
||||||
};
|
|
||||||
use bevy_xr::actions::{Action, ActionState};
|
|
||||||
use openxr::Binding;
|
|
||||||
|
|
||||||
#[derive(Action)]
|
|
||||||
#[action(action_type = bool, name = "jump")]
|
|
||||||
pub struct Jump;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
App::new()
|
|
||||||
.add_plugins(add_xr_plugins(DefaultPlugins))
|
|
||||||
.add_systems(Startup, setup.after(create_action_sets))
|
|
||||||
.add_systems(Update, read_action_state)
|
|
||||||
.register_action::<Jump>()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// set up a simple 3D scene
|
|
||||||
fn setup(
|
|
||||||
mut commands: Commands,
|
|
||||||
mut meshes: ResMut<Assets<Mesh>>,
|
|
||||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
|
||||||
actions: Res<XrActions>,
|
|
||||||
instance: Res<XrInstance>,
|
|
||||||
) {
|
|
||||||
let TypedAction::Bool(action) = actions.get(&TypeId::of::<Jump>()).unwrap() else {
|
|
||||||
unreachable!()
|
|
||||||
};
|
|
||||||
instance.suggest_interaction_profile_bindings(instance.string_to_path("/interaction_profiles/oculus/touch_controller").unwrap(), &[
|
|
||||||
Binding::new(action, instance.string_to_path("/user/hand/right/input/a/click").unwrap())
|
|
||||||
]).unwrap();
|
|
||||||
// circular base
|
|
||||||
commands.spawn(PbrBundle {
|
|
||||||
mesh: meshes.add(Circle::new(4.0)),
|
|
||||||
material: materials.add(Color::WHITE),
|
|
||||||
transform: Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)),
|
|
||||||
..default()
|
|
||||||
});
|
|
||||||
// cube
|
|
||||||
commands.spawn(PbrBundle {
|
|
||||||
mesh: meshes.add(Cuboid::new(1.0, 1.0, 1.0)),
|
|
||||||
material: materials.add(Color::rgb_u8(124, 144, 255)),
|
|
||||||
transform: Transform::from_xyz(0.0, 0.5, 0.0),
|
|
||||||
..default()
|
|
||||||
});
|
|
||||||
// light
|
|
||||||
commands.spawn(PointLightBundle {
|
|
||||||
point_light: PointLight {
|
|
||||||
shadows_enabled: true,
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
transform: Transform::from_xyz(4.0, 8.0, 4.0),
|
|
||||||
..default()
|
|
||||||
});
|
|
||||||
// // camera
|
|
||||||
// commands.spawn(XrCameraBundle {
|
|
||||||
// transform: Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
|
|
||||||
// camera: Camera {
|
|
||||||
// target: RenderTarget::TextureView(ManualTextureViewHandle(XR_TEXTURE_INDEX + 1)),
|
|
||||||
// ..default()
|
|
||||||
// },
|
|
||||||
// ..default()
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_action_state(
|
|
||||||
state: Res<ActionState<Jump>>
|
|
||||||
) {
|
|
||||||
info!("{}", state.pressed())
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,205 +0,0 @@
|
|||||||
use std::any::TypeId;
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
use crate::init::XrPreUpdateSet;
|
|
||||||
use crate::resources::*;
|
|
||||||
use crate::types::*;
|
|
||||||
use bevy::app::{App, Plugin, PreUpdate, Startup};
|
|
||||||
use bevy::ecs::schedule::common_conditions::resource_added;
|
|
||||||
use bevy::ecs::schedule::IntoSystemConfigs;
|
|
||||||
use bevy::ecs::system::{Commands, Res, ResMut};
|
|
||||||
use bevy::input::InputSystem;
|
|
||||||
use bevy::log::error;
|
|
||||||
use bevy::math::{vec2, Vec2};
|
|
||||||
use bevy::utils::hashbrown::HashMap;
|
|
||||||
use bevy_xr::actions::ActionPlugin;
|
|
||||||
use bevy_xr::actions::{Action, ActionList, ActionState};
|
|
||||||
use bevy_xr::session::session_available;
|
|
||||||
use bevy_xr::session::session_running;
|
|
||||||
|
|
||||||
pub struct XrActionPlugin;
|
|
||||||
|
|
||||||
impl Plugin for XrActionPlugin {
|
|
||||||
fn build(&self, app: &mut App) {
|
|
||||||
app.add_systems(Startup, create_action_sets.run_if(session_available))
|
|
||||||
.add_systems(
|
|
||||||
PreUpdate,
|
|
||||||
sync_actions.run_if(session_running).before(InputSystem),
|
|
||||||
)
|
|
||||||
.add_systems(
|
|
||||||
PreUpdate,
|
|
||||||
attach_action_sets
|
|
||||||
.after(XrPreUpdateSet::HandleEvents)
|
|
||||||
.run_if(resource_added::<XrSession>),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_action_sets(
|
|
||||||
instance: Res<XrInstance>,
|
|
||||||
action_list: Res<ActionList>,
|
|
||||||
mut commands: Commands,
|
|
||||||
) {
|
|
||||||
let (action_set, actions) =
|
|
||||||
initialize_action_sets(&instance, &action_list).expect("Failed to initialize action set");
|
|
||||||
|
|
||||||
commands.insert_resource(action_set);
|
|
||||||
commands.insert_resource(actions);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn attach_action_sets(mut action_set: ResMut<XrActionSet>, session: Res<XrSession>) {
|
|
||||||
session
|
|
||||||
.attach_action_sets(&[&action_set])
|
|
||||||
.expect("Failed to attach action sets");
|
|
||||||
action_set.attach();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sync_actions(session: Res<XrSession>, action_set: Res<XrActionSet>) {
|
|
||||||
session
|
|
||||||
.sync_actions(&[openxr::ActiveActionSet::new(&action_set)])
|
|
||||||
.expect("Failed to sync actions");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn initialize_action_sets(
|
|
||||||
instance: &XrInstance,
|
|
||||||
action_info: &ActionList,
|
|
||||||
) -> Result<(XrActionSet, XrActions)> {
|
|
||||||
let action_set = instance.create_action_set("actions", "actions", 0)?;
|
|
||||||
let mut actions = HashMap::new();
|
|
||||||
for action_info in action_info.0.iter() {
|
|
||||||
use bevy_xr::actions::ActionType::*;
|
|
||||||
let action = match action_info.action_type {
|
|
||||||
Bool => TypedAction::Bool(action_set.create_action(
|
|
||||||
action_info.name,
|
|
||||||
action_info.pretty_name,
|
|
||||||
&[],
|
|
||||||
)?),
|
|
||||||
Float => TypedAction::Float(action_set.create_action(
|
|
||||||
action_info.name,
|
|
||||||
action_info.pretty_name,
|
|
||||||
&[],
|
|
||||||
)?),
|
|
||||||
Vector => TypedAction::Vector(action_set.create_action(
|
|
||||||
action_info.name,
|
|
||||||
action_info.pretty_name,
|
|
||||||
&[],
|
|
||||||
)?),
|
|
||||||
};
|
|
||||||
actions.insert(action_info.type_id, action);
|
|
||||||
}
|
|
||||||
Ok((XrActionSet::new(action_set), XrActions(actions)))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct XrActionUpdatePlugin<A: Action>(PhantomData<A>);
|
|
||||||
|
|
||||||
impl<A> Plugin for XrActionUpdatePlugin<A>
|
|
||||||
where
|
|
||||||
A: Action,
|
|
||||||
A::ActionType: XrActionTy,
|
|
||||||
{
|
|
||||||
fn build(&self, app: &mut App) {
|
|
||||||
app.add_systems(PreUpdate, update_action_state::<A>.in_set(InputSystem).run_if(session_running));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<A: Action> Default for XrActionUpdatePlugin<A> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self(Default::default())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait XrActionTy: Sized {
|
|
||||||
fn get_action_state(
|
|
||||||
action: &TypedAction,
|
|
||||||
session: &XrSession,
|
|
||||||
subaction_path: Option<openxr::Path>,
|
|
||||||
) -> Option<Self>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl XrActionTy for bool {
|
|
||||||
fn get_action_state(
|
|
||||||
action: &TypedAction,
|
|
||||||
session: &XrSession,
|
|
||||||
subaction_path: Option<openxr::Path>,
|
|
||||||
) -> Option<Self> {
|
|
||||||
match action {
|
|
||||||
TypedAction::Bool(action) => action
|
|
||||||
.state(session, subaction_path.unwrap_or(openxr::Path::NULL))
|
|
||||||
.ok()
|
|
||||||
.map(|state| state.current_state),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl XrActionTy for f32 {
|
|
||||||
fn get_action_state(
|
|
||||||
action: &TypedAction,
|
|
||||||
session: &XrSession,
|
|
||||||
subaction_path: Option<openxr::Path>,
|
|
||||||
) -> Option<Self> {
|
|
||||||
match action {
|
|
||||||
TypedAction::Float(action) => action
|
|
||||||
.state(session, subaction_path.unwrap_or(openxr::Path::NULL))
|
|
||||||
.ok()
|
|
||||||
.map(|state| state.current_state),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl XrActionTy for Vec2 {
|
|
||||||
fn get_action_state(
|
|
||||||
action: &TypedAction,
|
|
||||||
session: &XrSession,
|
|
||||||
subaction_path: Option<openxr::Path>,
|
|
||||||
) -> Option<Self> {
|
|
||||||
match action {
|
|
||||||
TypedAction::Vector(action) => action
|
|
||||||
.state(session, subaction_path.unwrap_or(openxr::Path::NULL))
|
|
||||||
.ok()
|
|
||||||
.map(|state| vec2(state.current_state.x, state.current_state.y)),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update_action_state<A>(
|
|
||||||
mut action_state: ResMut<ActionState<A>>,
|
|
||||||
session: Res<XrSession>,
|
|
||||||
actions: Res<XrActions>,
|
|
||||||
) where
|
|
||||||
A: Action,
|
|
||||||
A::ActionType: XrActionTy,
|
|
||||||
{
|
|
||||||
if let Some(action) = actions.get(&TypeId::of::<A>()) {
|
|
||||||
if let Some(state) = A::ActionType::get_action_state(action, &session, None) {
|
|
||||||
action_state.set(state);
|
|
||||||
} else {
|
|
||||||
error!(
|
|
||||||
"Failed to update value for action '{}'",
|
|
||||||
std::any::type_name::<A>()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ActionApp {
|
|
||||||
fn register_action<A>(&mut self) -> &mut Self
|
|
||||||
where
|
|
||||||
A: Action,
|
|
||||||
A::ActionType: XrActionTy;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ActionApp for App {
|
|
||||||
fn register_action<A>(&mut self) -> &mut Self
|
|
||||||
where
|
|
||||||
A: Action,
|
|
||||||
A::ActionType: XrActionTy,
|
|
||||||
{
|
|
||||||
self.add_plugins((
|
|
||||||
ActionPlugin::<A>::default(),
|
|
||||||
XrActionUpdatePlugin::<A>::default(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,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_xr::add_xr_plugins;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
.add_plugins(DefaultPlugins)
|
.add_plugins(add_xr_plugins(DefaultPlugins))
|
||||||
.add_systems(Startup, setup)
|
.add_systems(Startup, setup)
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,2 +1,7 @@
|
|||||||
pub use bevy_openxr;
|
#[cfg(not(target_family = "wasm"))]
|
||||||
pub use bevy_xr;
|
pub mod oxr;
|
||||||
|
#[cfg(target_family = "wasm")]
|
||||||
|
pub mod webxr;
|
||||||
|
|
||||||
|
#[cfg(not(target_family = "wasm"))]
|
||||||
|
pub use oxr::add_xr_plugins;
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
use crate::graphics::GraphicsBackend;
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
use super::graphics::GraphicsBackend;
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum XrError {
|
pub enum OXrError {
|
||||||
#[error("OpenXR error: {0}")]
|
#[error("OpenXR error: {0}")]
|
||||||
OpenXrError(#[from] openxr::sys::Result),
|
OpenXrError(#[from] openxr::sys::Result),
|
||||||
#[error("OpenXR loading error: {0}")]
|
#[error("OpenXR loading error: {0}")]
|
||||||
@@ -17,10 +19,6 @@ pub enum XrError {
|
|||||||
WgpuRequestDeviceError(#[from] wgpu::RequestDeviceError),
|
WgpuRequestDeviceError(#[from] wgpu::RequestDeviceError),
|
||||||
#[error("Unsupported texture format: {0:?}")]
|
#[error("Unsupported texture format: {0:?}")]
|
||||||
UnsupportedTextureFormat(wgpu::TextureFormat),
|
UnsupportedTextureFormat(wgpu::TextureFormat),
|
||||||
#[error("Vulkan error: {0}")]
|
|
||||||
VulkanError(#[from] ash::vk::Result),
|
|
||||||
#[error("Vulkan loading error: {0}")]
|
|
||||||
VulkanLoadingError(#[from] ash::LoadingError),
|
|
||||||
#[error("Graphics backend '{0:?}' is not available")]
|
#[error("Graphics backend '{0:?}' is not available")]
|
||||||
UnavailableBackend(GraphicsBackend),
|
UnavailableBackend(GraphicsBackend),
|
||||||
#[error("No compatible backend available")]
|
#[error("No compatible backend available")]
|
||||||
@@ -45,9 +43,55 @@ pub enum XrError {
|
|||||||
},
|
},
|
||||||
#[error("Failed to create CString: {0}")]
|
#[error("Failed to create CString: {0}")]
|
||||||
NulError(#[from] std::ffi::NulError),
|
NulError(#[from] std::ffi::NulError),
|
||||||
|
#[error("Graphics init error: {0}")]
|
||||||
|
InitError(InitError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Vec<Cow<'static, str>>> for XrError {
|
pub use init_error::InitError;
|
||||||
|
|
||||||
|
/// This module is needed because thiserror does not allow conditional compilation within enums for some reason,
|
||||||
|
/// so graphics api specific errors are implemented here.
|
||||||
|
mod init_error {
|
||||||
|
use super::OXrError;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum InitError {
|
||||||
|
#[cfg(feature = "vulkan")]
|
||||||
|
VulkanError(ash::vk::Result),
|
||||||
|
#[cfg(feature = "vulkan")]
|
||||||
|
VulkanLoadingError(ash::LoadingError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for InitError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
#[cfg(feature = "vulkan")]
|
||||||
|
InitError::VulkanError(error) => write!(f, "Vulkan error: {}", error),
|
||||||
|
#[cfg(feature = "vulkan")]
|
||||||
|
InitError::VulkanLoadingError(error) => {
|
||||||
|
write!(f, "Vulkan loading error: {}", error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "vulkan")]
|
||||||
|
impl From<ash::vk::Result> for OXrError {
|
||||||
|
fn from(value: ash::vk::Result) -> Self {
|
||||||
|
Self::InitError(InitError::VulkanError(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "vulkan")]
|
||||||
|
impl From<ash::LoadingError> for OXrError {
|
||||||
|
fn from(value: ash::LoadingError) -> Self {
|
||||||
|
Self::InitError(InitError::VulkanLoadingError(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vec<Cow<'static, str>>> for OXrError {
|
||||||
fn from(value: Vec<Cow<'static, str>>) -> Self {
|
fn from(value: Vec<Cow<'static, str>>) -> Self {
|
||||||
Self::UnavailableExtensions(UnavailableExts(value))
|
Self::UnavailableExtensions(UnavailableExts(value))
|
||||||
}
|
}
|
||||||
@@ -2,8 +2,8 @@ use bevy::prelude::{Deref, DerefMut};
|
|||||||
use openxr::ExtensionSet;
|
use openxr::ExtensionSet;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Deref, DerefMut)]
|
#[derive(Clone, Debug, Eq, PartialEq, Deref, DerefMut)]
|
||||||
pub struct XrExtensions(ExtensionSet);
|
pub struct OXrExtensions(ExtensionSet);
|
||||||
impl XrExtensions {
|
impl OXrExtensions {
|
||||||
pub fn raw_mut(&mut self) -> &mut ExtensionSet {
|
pub fn raw_mut(&mut self) -> &mut ExtensionSet {
|
||||||
&mut self.0
|
&mut self.0
|
||||||
}
|
}
|
||||||
@@ -27,21 +27,21 @@ impl XrExtensions {
|
|||||||
self
|
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: &XrExtensions) -> bool {
|
pub fn is_available(&self, available_exts: &OXrExtensions) -> bool {
|
||||||
self.clone() & available_exts.clone() == *self
|
self.clone() & available_exts.clone() == *self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl From<ExtensionSet> for XrExtensions {
|
impl From<ExtensionSet> for OXrExtensions {
|
||||||
fn from(value: ExtensionSet) -> Self {
|
fn from(value: ExtensionSet) -> Self {
|
||||||
Self(value)
|
Self(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl From<XrExtensions> for ExtensionSet {
|
impl From<OXrExtensions> for ExtensionSet {
|
||||||
fn from(val: XrExtensions) -> Self {
|
fn from(val: OXrExtensions) -> Self {
|
||||||
val.0
|
val.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Default for XrExtensions {
|
impl Default for OXrExtensions {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let exts = ExtensionSet::default();
|
let exts = ExtensionSet::default();
|
||||||
//exts.ext_hand_tracking = true;
|
//exts.ext_hand_tracking = true;
|
||||||
@@ -165,7 +165,7 @@ macro_rules! impl_ext {
|
|||||||
) => {
|
) => {
|
||||||
$(
|
$(
|
||||||
$macro! {
|
$macro! {
|
||||||
XrExtensions;
|
OXrExtensions;
|
||||||
almalence_digital_lens_control,
|
almalence_digital_lens_control,
|
||||||
bd_controller_interaction,
|
bd_controller_interaction,
|
||||||
epic_view_configuration_fov,
|
epic_view_configuration_fov,
|
||||||
@@ -5,32 +5,51 @@ use std::any::TypeId;
|
|||||||
|
|
||||||
use bevy::math::UVec2;
|
use bevy::math::UVec2;
|
||||||
|
|
||||||
use crate::extensions::XrExtensions;
|
use crate::oxr::types::{AppInfo, OXrExtensions, Result, WgpuGraphics};
|
||||||
use crate::types::*;
|
|
||||||
|
|
||||||
|
/// 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 {
|
||||||
/// Wrap the graphics specific type into the [GraphicsWrap] enum
|
/// Wrap the graphics specific type into the [GraphicsWrap] enum
|
||||||
fn wrap<T: GraphicsType>(item: T::Inner<Self>) -> GraphicsWrap<T>;
|
fn wrap<T: GraphicsType>(item: T::Inner<Self>) -> GraphicsWrap<T>;
|
||||||
|
/// Returns all of the required openxr extensions to use this graphics API.
|
||||||
|
fn required_exts() -> OXrExtensions;
|
||||||
/// Convert from wgpu format to the graphics format
|
/// Convert from wgpu format to the graphics format
|
||||||
fn from_wgpu_format(format: wgpu::TextureFormat) -> Option<Self::Format>;
|
fn from_wgpu_format(format: wgpu::TextureFormat) -> Option<Self::Format>;
|
||||||
/// Convert from the graphics format to wgpu format
|
/// Convert from the graphics format to wgpu format
|
||||||
fn to_wgpu_format(format: Self::Format) -> Option<wgpu::TextureFormat>;
|
fn into_wgpu_format(format: Self::Format) -> Option<wgpu::TextureFormat>;
|
||||||
/// Initialize graphics for this backend
|
/// Convert an API specific swapchain image to a [`Texture`](wgpu::Texture).
|
||||||
fn init_graphics(
|
///
|
||||||
app_info: &AppInfo,
|
/// # Safety
|
||||||
instance: &openxr::Instance,
|
///
|
||||||
system_id: openxr::SystemId,
|
/// The `image` argument must be a valid handle.
|
||||||
) -> Result<(WgpuGraphics, Self::SessionCreateInfo)>;
|
|
||||||
/// Convert a swapchain function
|
|
||||||
unsafe fn to_wgpu_img(
|
unsafe fn to_wgpu_img(
|
||||||
image: Self::SwapchainImage,
|
image: Self::SwapchainImage,
|
||||||
device: &wgpu::Device,
|
device: &wgpu::Device,
|
||||||
format: wgpu::TextureFormat,
|
format: wgpu::TextureFormat,
|
||||||
resolution: UVec2,
|
resolution: UVec2,
|
||||||
) -> Result<wgpu::Texture>;
|
) -> Result<wgpu::Texture>;
|
||||||
fn required_exts() -> XrExtensions;
|
/// Initialize graphics for this backend and return a [`WgpuGraphics`] for bevy and an API specific [Self::SessionCreateInfo] for openxr
|
||||||
|
fn init_graphics(
|
||||||
|
app_info: &AppInfo,
|
||||||
|
instance: &openxr::Instance,
|
||||||
|
system_id: openxr::SystemId,
|
||||||
|
) -> Result<(WgpuGraphics, Self::SessionCreateInfo)>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A type that can be used in [`GraphicsWrap`].
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// pub struct XrSession(GraphicsWrap<XrSession>);
|
||||||
|
///
|
||||||
|
/// impl GraphicsType for XrSession {
|
||||||
|
/// type Inner<G: GraphicsExt> = openxr::Session<G>;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// In this example, `GraphicsWrap<XrSession>` is now an enum with variants for each graphics API. The enum can be matched to get the graphics specific inner type.
|
||||||
pub trait GraphicsType {
|
pub trait GraphicsType {
|
||||||
type Inner<G: GraphicsExt>;
|
type Inner<G: GraphicsExt>;
|
||||||
}
|
}
|
||||||
@@ -39,12 +58,13 @@ impl GraphicsType for () {
|
|||||||
type Inner<G: GraphicsExt> = ();
|
type Inner<G: GraphicsExt> = ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This is a special variant of [GraphicsWrap] using the unit struct as the inner type. This is to simply represent a graphics backend without storing data.
|
||||||
pub type GraphicsBackend = GraphicsWrap<()>;
|
pub type GraphicsBackend = GraphicsWrap<()>;
|
||||||
|
|
||||||
impl GraphicsBackend {
|
impl GraphicsBackend {
|
||||||
const ALL: &'static [Self] = &[Self::Vulkan(())];
|
const ALL: &'static [Self] = &[Self::Vulkan(())];
|
||||||
|
|
||||||
pub fn available_backends(exts: &XrExtensions) -> Vec<Self> {
|
pub fn available_backends(exts: &OXrExtensions) -> Vec<Self> {
|
||||||
Self::ALL
|
Self::ALL
|
||||||
.iter()
|
.iter()
|
||||||
.copied()
|
.copied()
|
||||||
@@ -52,11 +72,11 @@ impl GraphicsBackend {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_available(&self, exts: &XrExtensions) -> bool {
|
pub fn is_available(&self, exts: &OXrExtensions) -> bool {
|
||||||
self.required_exts().is_available(exts)
|
self.required_exts().is_available(exts)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn required_exts(&self) -> XrExtensions {
|
pub fn required_exts(&self) -> OXrExtensions {
|
||||||
graphics_match!(
|
graphics_match!(
|
||||||
self;
|
self;
|
||||||
_ => Api::required_exts()
|
_ => Api::required_exts()
|
||||||
@@ -64,6 +84,7 @@ impl GraphicsBackend {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This struct is for creating agnostic objects for OpenXR graphics API specific structs.
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum GraphicsWrap<T: GraphicsType> {
|
pub enum GraphicsWrap<T: GraphicsType> {
|
||||||
#[cfg(feature = "vulkan")]
|
#[cfg(feature = "vulkan")]
|
||||||
@@ -97,6 +118,33 @@ impl<T: GraphicsType> GraphicsWrap<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This macro can be used to quickly run the same code for every variant of [GraphicsWrap].
|
||||||
|
/// The first argument should be an expression that returns either a reference or owned value of [GraphicsWrap].
|
||||||
|
/// The second argument should be a match arm with the pattern for the inner type.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// pub struct OXrFrameStream(GraphicsWrap<XrFrameStream>);
|
||||||
|
///
|
||||||
|
/// impl GraphicsType for OXrFrameStream {
|
||||||
|
/// // Here is the inner type
|
||||||
|
/// type Inner<G: GraphicsExt> = openxr::FrameStream<G>;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn begin(frame_stream: &mut XrFrameStream) {
|
||||||
|
/// graphics_match! {
|
||||||
|
/// // get the inner 'GraphicsWrap' struct
|
||||||
|
/// &mut frame_stream.0;
|
||||||
|
/// // now we can handle every match arm with a single arm
|
||||||
|
/// // important: the first pattern here represents the inner type of `GraphicsWrap`
|
||||||
|
/// // it is already destructured for you.
|
||||||
|
/// stream => stream.begin();
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Additionally, if you want the type that implements `GraphicsExt` in the scope of the match body, you can access that type through the type alias `Api`.
|
||||||
macro_rules! graphics_match {
|
macro_rules! graphics_match {
|
||||||
(
|
(
|
||||||
$field:expr;
|
$field:expr;
|
||||||
@@ -104,7 +152,7 @@ macro_rules! graphics_match {
|
|||||||
) => {
|
) => {
|
||||||
match $field {
|
match $field {
|
||||||
#[cfg(feature = "vulkan")]
|
#[cfg(feature = "vulkan")]
|
||||||
$crate::graphics::GraphicsWrap::Vulkan($var) => {
|
$crate::oxr::graphics::GraphicsWrap::Vulkan($var) => {
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
type Api = openxr::Vulkan;
|
type Api = openxr::Vulkan;
|
||||||
graphics_match!(@arm_impl Vulkan; $expr $(=> $($return)*)?)
|
graphics_match!(@arm_impl Vulkan; $expr $(=> $($return)*)?)
|
||||||
@@ -117,7 +165,7 @@ macro_rules! graphics_match {
|
|||||||
$variant:ident;
|
$variant:ident;
|
||||||
$expr:expr => $wrap_ty:ty
|
$expr:expr => $wrap_ty:ty
|
||||||
) => {
|
) => {
|
||||||
GraphicsWrap::<$wrap_ty>::$variant($expr)
|
$crate::oxr::graphics::GraphicsWrap::<$wrap_ty>::$variant($expr)
|
||||||
};
|
};
|
||||||
|
|
||||||
(
|
(
|
||||||
@@ -7,11 +7,9 @@ use openxr::Version;
|
|||||||
use wgpu_hal::api::Vulkan;
|
use wgpu_hal::api::Vulkan;
|
||||||
use wgpu_hal::Api;
|
use wgpu_hal::Api;
|
||||||
|
|
||||||
use crate::error::XrError;
|
use super::{GraphicsExt, GraphicsType, GraphicsWrap};
|
||||||
use crate::extensions::XrExtensions;
|
use crate::oxr::error::OXrError;
|
||||||
use crate::types::*;
|
use crate::oxr::types::{AppInfo, OXrExtensions, Result, WgpuGraphics};
|
||||||
|
|
||||||
use super::GraphicsExt;
|
|
||||||
|
|
||||||
#[cfg(not(target_os = "android"))]
|
#[cfg(not(target_os = "android"))]
|
||||||
const VK_TARGET_VERSION: Version = Version::new(1, 2, 0);
|
const VK_TARGET_VERSION: Version = Version::new(1, 2, 0);
|
||||||
@@ -26,14 +24,74 @@ const VK_TARGET_VERSION_ASH: u32 = ash::vk::make_api_version(
|
|||||||
);
|
);
|
||||||
|
|
||||||
unsafe impl GraphicsExt for openxr::Vulkan {
|
unsafe impl GraphicsExt for openxr::Vulkan {
|
||||||
|
fn wrap<T: GraphicsType>(item: T::Inner<Self>) -> GraphicsWrap<T> {
|
||||||
|
GraphicsWrap::Vulkan(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn required_exts() -> OXrExtensions {
|
||||||
|
let mut extensions = openxr::ExtensionSet::default();
|
||||||
|
extensions.khr_vulkan_enable2 = true;
|
||||||
|
extensions.into()
|
||||||
|
}
|
||||||
|
|
||||||
fn from_wgpu_format(format: wgpu::TextureFormat) -> Option<Self::Format> {
|
fn from_wgpu_format(format: wgpu::TextureFormat) -> Option<Self::Format> {
|
||||||
wgpu_to_vulkan(format).map(|f| f.as_raw() as _)
|
wgpu_to_vulkan(format).map(|f| f.as_raw() as _)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_wgpu_format(format: Self::Format) -> Option<wgpu::TextureFormat> {
|
fn into_wgpu_format(format: Self::Format) -> Option<wgpu::TextureFormat> {
|
||||||
vulkan_to_wgpu(ash::vk::Format::from_raw(format as _))
|
vulkan_to_wgpu(ash::vk::Format::from_raw(format as _))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe fn to_wgpu_img(
|
||||||
|
color_image: Self::SwapchainImage,
|
||||||
|
device: &wgpu::Device,
|
||||||
|
format: wgpu::TextureFormat,
|
||||||
|
resolution: UVec2,
|
||||||
|
) -> Result<wgpu::Texture> {
|
||||||
|
let color_image = ash::vk::Image::from_raw(color_image);
|
||||||
|
let wgpu_hal_texture = unsafe {
|
||||||
|
<wgpu_hal::vulkan::Api as wgpu_hal::Api>::Device::texture_from_raw(
|
||||||
|
color_image,
|
||||||
|
&wgpu_hal::TextureDescriptor {
|
||||||
|
label: Some("VR Swapchain"),
|
||||||
|
size: wgpu::Extent3d {
|
||||||
|
width: resolution.x,
|
||||||
|
height: resolution.y,
|
||||||
|
depth_or_array_layers: 2,
|
||||||
|
},
|
||||||
|
mip_level_count: 1,
|
||||||
|
sample_count: 1,
|
||||||
|
dimension: wgpu::TextureDimension::D2,
|
||||||
|
format: format,
|
||||||
|
usage: wgpu_hal::TextureUses::COLOR_TARGET | wgpu_hal::TextureUses::COPY_DST,
|
||||||
|
memory_flags: wgpu_hal::MemoryFlags::empty(),
|
||||||
|
view_formats: vec![],
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let texture = unsafe {
|
||||||
|
device.create_texture_from_hal::<wgpu_hal::vulkan::Api>(
|
||||||
|
wgpu_hal_texture,
|
||||||
|
&wgpu::TextureDescriptor {
|
||||||
|
label: Some("VR Swapchain"),
|
||||||
|
size: wgpu::Extent3d {
|
||||||
|
width: resolution.x,
|
||||||
|
height: resolution.y,
|
||||||
|
depth_or_array_layers: 2,
|
||||||
|
},
|
||||||
|
mip_level_count: 1,
|
||||||
|
sample_count: 1,
|
||||||
|
dimension: wgpu::TextureDimension::D2,
|
||||||
|
format: format,
|
||||||
|
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST,
|
||||||
|
view_formats: &[],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
};
|
||||||
|
Ok(texture)
|
||||||
|
}
|
||||||
|
|
||||||
fn init_graphics(
|
fn init_graphics(
|
||||||
app_info: &AppInfo,
|
app_info: &AppInfo,
|
||||||
instance: &openxr::Instance,
|
instance: &openxr::Instance,
|
||||||
@@ -48,7 +106,7 @@ unsafe impl GraphicsExt for openxr::Vulkan {
|
|||||||
reqs.min_api_version_supported,
|
reqs.min_api_version_supported,
|
||||||
reqs.max_api_version_supported.major() + 1
|
reqs.max_api_version_supported.major() + 1
|
||||||
);
|
);
|
||||||
return Err(XrError::FailedGraphicsRequirements);
|
return Err(OXrError::FailedGraphicsRequirements);
|
||||||
};
|
};
|
||||||
let vk_entry = unsafe { ash::Entry::load() }?;
|
let vk_entry = unsafe { ash::Entry::load() }?;
|
||||||
let flags = wgpu::InstanceFlags::empty();
|
let flags = wgpu::InstanceFlags::empty();
|
||||||
@@ -107,7 +165,7 @@ unsafe impl GraphicsExt for openxr::Vulkan {
|
|||||||
VK_TARGET_VERSION.minor(),
|
VK_TARGET_VERSION.minor(),
|
||||||
VK_TARGET_VERSION.patch()
|
VK_TARGET_VERSION.patch()
|
||||||
);
|
);
|
||||||
return Err(XrError::FailedGraphicsRequirements);
|
return Err(OXrError::FailedGraphicsRequirements);
|
||||||
}
|
}
|
||||||
|
|
||||||
let wgpu_vk_instance = unsafe {
|
let wgpu_vk_instance = unsafe {
|
||||||
@@ -131,7 +189,7 @@ unsafe impl GraphicsExt for openxr::Vulkan {
|
|||||||
|
|
||||||
let Some(wgpu_exposed_adapter) = wgpu_vk_instance.expose_adapter(vk_physical_device) else {
|
let Some(wgpu_exposed_adapter) = wgpu_vk_instance.expose_adapter(vk_physical_device) else {
|
||||||
error!("WGPU failed to provide an adapter");
|
error!("WGPU failed to provide an adapter");
|
||||||
return Err(XrError::FailedGraphicsRequirements);
|
return Err(OXrError::FailedGraphicsRequirements);
|
||||||
};
|
};
|
||||||
|
|
||||||
let enabled_extensions = wgpu_exposed_adapter
|
let enabled_extensions = wgpu_exposed_adapter
|
||||||
@@ -234,66 +292,6 @@ unsafe impl GraphicsExt for openxr::Vulkan {
|
|||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn to_wgpu_img(
|
|
||||||
color_image: Self::SwapchainImage,
|
|
||||||
device: &wgpu::Device,
|
|
||||||
format: wgpu::TextureFormat,
|
|
||||||
resolution: UVec2,
|
|
||||||
) -> Result<wgpu::Texture> {
|
|
||||||
let color_image = ash::vk::Image::from_raw(color_image);
|
|
||||||
let wgpu_hal_texture = unsafe {
|
|
||||||
<wgpu_hal::vulkan::Api as wgpu_hal::Api>::Device::texture_from_raw(
|
|
||||||
color_image,
|
|
||||||
&wgpu_hal::TextureDescriptor {
|
|
||||||
label: Some("VR Swapchain"),
|
|
||||||
size: wgpu::Extent3d {
|
|
||||||
width: resolution.x,
|
|
||||||
height: resolution.y,
|
|
||||||
depth_or_array_layers: 2,
|
|
||||||
},
|
|
||||||
mip_level_count: 1,
|
|
||||||
sample_count: 1,
|
|
||||||
dimension: wgpu::TextureDimension::D2,
|
|
||||||
format: format,
|
|
||||||
usage: wgpu_hal::TextureUses::COLOR_TARGET | wgpu_hal::TextureUses::COPY_DST,
|
|
||||||
memory_flags: wgpu_hal::MemoryFlags::empty(),
|
|
||||||
view_formats: vec![],
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
let texture = unsafe {
|
|
||||||
device.create_texture_from_hal::<wgpu_hal::vulkan::Api>(
|
|
||||||
wgpu_hal_texture,
|
|
||||||
&wgpu::TextureDescriptor {
|
|
||||||
label: Some("VR Swapchain"),
|
|
||||||
size: wgpu::Extent3d {
|
|
||||||
width: resolution.x,
|
|
||||||
height: resolution.y,
|
|
||||||
depth_or_array_layers: 2,
|
|
||||||
},
|
|
||||||
mip_level_count: 1,
|
|
||||||
sample_count: 1,
|
|
||||||
dimension: wgpu::TextureDimension::D2,
|
|
||||||
format: format,
|
|
||||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST,
|
|
||||||
view_formats: &[],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
};
|
|
||||||
Ok(texture)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn required_exts() -> XrExtensions {
|
|
||||||
let mut extensions = openxr::ExtensionSet::default();
|
|
||||||
extensions.khr_vulkan_enable2 = true;
|
|
||||||
extensions.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn wrap<T: super::GraphicsType>(item: T::Inner<Self>) -> super::GraphicsWrap<T> {
|
|
||||||
super::GraphicsWrap::Vulkan(item)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn vulkan_to_wgpu(format: ash::vk::Format) -> Option<wgpu::TextureFormat> {
|
fn vulkan_to_wgpu(format: ash::vk::Format) -> Option<wgpu::TextureFormat> {
|
||||||
@@ -1,41 +1,51 @@
|
|||||||
use bevy::math::uvec2;
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy::render::extract_resource::ExtractResourcePlugin;
|
use bevy::render::extract_resource::ExtractResourcePlugin;
|
||||||
use bevy::render::renderer::{
|
use bevy::render::renderer::RenderAdapter;
|
||||||
RenderAdapter, RenderAdapterInfo, RenderDevice, RenderInstance, RenderQueue,
|
use bevy::render::renderer::RenderAdapterInfo;
|
||||||
};
|
use bevy::render::renderer::RenderDevice;
|
||||||
|
use bevy::render::renderer::RenderInstance;
|
||||||
|
use bevy::render::renderer::RenderQueue;
|
||||||
use bevy::render::settings::RenderCreation;
|
use bevy::render::settings::RenderCreation;
|
||||||
use bevy::render::{MainWorld, Render, RenderApp, RenderPlugin, RenderSet};
|
use bevy::render::MainWorld;
|
||||||
|
use bevy::render::Render;
|
||||||
|
use bevy::render::RenderApp;
|
||||||
|
use bevy::render::RenderPlugin;
|
||||||
|
use bevy::render::RenderSet;
|
||||||
use bevy::transform::TransformSystem;
|
use bevy::transform::TransformSystem;
|
||||||
use bevy::winit::{UpdateMode, WinitSettings};
|
use bevy::winit::UpdateMode;
|
||||||
use bevy_xr::session::{
|
use bevy::winit::WinitSettings;
|
||||||
handle_session, session_available, session_running, status_equals, BeginXrSession,
|
use bevy_xr_api::session::handle_session;
|
||||||
CreateXrSession, DestroyXrSession, EndXrSession, XrSharedStatus, XrStatus,
|
use bevy_xr_api::session::session_available;
|
||||||
};
|
use bevy_xr_api::session::session_running;
|
||||||
|
use bevy_xr_api::session::status_equals;
|
||||||
|
use bevy_xr_api::session::BeginXrSession;
|
||||||
|
use bevy_xr_api::session::CreateXrSession;
|
||||||
|
use bevy_xr_api::session::DestroyXrSession;
|
||||||
|
use bevy_xr_api::session::EndXrSession;
|
||||||
|
use bevy_xr_api::session::XrSharedStatus;
|
||||||
|
use bevy_xr_api::session::XrStatus;
|
||||||
|
|
||||||
use crate::graphics::*;
|
use crate::oxr::error::OXrError;
|
||||||
use crate::resources::*;
|
use crate::oxr::graphics::*;
|
||||||
use crate::types::*;
|
use crate::oxr::resources::*;
|
||||||
|
use crate::oxr::types::*;
|
||||||
|
|
||||||
pub fn session_started(started: Option<Res<XrSessionStarted>>) -> bool {
|
pub fn session_started(started: Option<Res<OXrSessionStarted>>) -> bool {
|
||||||
started.is_some_and(|started| started.get())
|
started.is_some_and(|started| started.get())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, SystemSet)]
|
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, SystemSet)]
|
||||||
pub enum XrPreUpdateSet {
|
pub enum OXrPreUpdateSet {
|
||||||
PollEvents,
|
PollEvents,
|
||||||
HandleEvents,
|
HandleEvents,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component)]
|
pub struct OXrInitPlugin {
|
||||||
pub struct XrTrackingRoot;
|
|
||||||
|
|
||||||
pub struct XrInitPlugin {
|
|
||||||
/// Information about the app this is being used to build.
|
/// Information about the app this is being used to build.
|
||||||
pub app_info: AppInfo,
|
pub app_info: AppInfo,
|
||||||
/// Extensions wanted for this session.
|
/// Extensions wanted for this session.
|
||||||
// TODO!() This should be changed to take a simpler list of features wanted that this crate supports. i.e. hand tracking
|
// TODO!() This should be changed to take a simpler list of features wanted that this crate supports. i.e. hand tracking
|
||||||
pub exts: XrExtensions,
|
pub exts: OXrExtensions,
|
||||||
/// List of blend modes the openxr session can use. If [None], pick the first available blend mode.
|
/// List of blend modes the openxr session can use. If [None], pick the first available blend mode.
|
||||||
pub blend_modes: Option<Vec<EnvironmentBlendMode>>,
|
pub blend_modes: Option<Vec<EnvironmentBlendMode>>,
|
||||||
/// List of backends the openxr session can use. If [None], pick the first available backend.
|
/// List of backends the openxr session can use. If [None], pick the first available backend.
|
||||||
@@ -48,7 +58,10 @@ pub struct XrInitPlugin {
|
|||||||
pub synchronous_pipeline_compilation: bool,
|
pub synchronous_pipeline_compilation: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Plugin for XrInitPlugin {
|
#[derive(Component)]
|
||||||
|
pub struct OXrTrackingRoot;
|
||||||
|
|
||||||
|
impl Plugin for OXrInitPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
match self.init_xr() {
|
match self.init_xr() {
|
||||||
Ok((
|
Ok((
|
||||||
@@ -70,9 +83,9 @@ impl Plugin for XrInitPlugin {
|
|||||||
),
|
),
|
||||||
synchronous_pipeline_compilation: self.synchronous_pipeline_compilation,
|
synchronous_pipeline_compilation: self.synchronous_pipeline_compilation,
|
||||||
},
|
},
|
||||||
ExtractResourcePlugin::<XrCleanupSession>::default(),
|
ExtractResourcePlugin::<OXrCleanupSession>::default(),
|
||||||
ExtractResourcePlugin::<XrTime>::default(),
|
ExtractResourcePlugin::<OXrTime>::default(),
|
||||||
ExtractResourcePlugin::<XrRootTransform>::default(),
|
ExtractResourcePlugin::<OXrRootTransform>::default(),
|
||||||
))
|
))
|
||||||
.add_systems(First, reset_per_frame_resources)
|
.add_systems(First, reset_per_frame_resources)
|
||||||
.add_systems(
|
.add_systems(
|
||||||
@@ -80,7 +93,7 @@ impl Plugin for XrInitPlugin {
|
|||||||
(
|
(
|
||||||
poll_events
|
poll_events
|
||||||
.run_if(session_available)
|
.run_if(session_available)
|
||||||
.in_set(XrPreUpdateSet::PollEvents),
|
.in_set(OXrPreUpdateSet::PollEvents),
|
||||||
(
|
(
|
||||||
(create_xr_session, apply_deferred)
|
(create_xr_session, apply_deferred)
|
||||||
.chain()
|
.chain()
|
||||||
@@ -96,7 +109,7 @@ impl Plugin for XrInitPlugin {
|
|||||||
.run_if(on_event::<DestroyXrSession>())
|
.run_if(on_event::<DestroyXrSession>())
|
||||||
.run_if(status_equals(XrStatus::Exiting)),
|
.run_if(status_equals(XrStatus::Exiting)),
|
||||||
)
|
)
|
||||||
.in_set(XrPreUpdateSet::HandleEvents),
|
.in_set(OXrPreUpdateSet::HandleEvents),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.add_systems(
|
.add_systems(
|
||||||
@@ -110,24 +123,24 @@ impl Plugin for XrInitPlugin {
|
|||||||
focused_mode: UpdateMode::Continuous,
|
focused_mode: UpdateMode::Continuous,
|
||||||
unfocused_mode: UpdateMode::Continuous,
|
unfocused_mode: UpdateMode::Continuous,
|
||||||
})
|
})
|
||||||
.init_resource::<XrCleanupSession>()
|
.init_resource::<OXrCleanupSession>()
|
||||||
.init_resource::<XrRootTransform>()
|
.init_resource::<OXrRootTransform>()
|
||||||
.insert_non_send_resource(session_create_info);
|
.insert_non_send_resource(session_create_info);
|
||||||
|
|
||||||
app.world
|
app.world
|
||||||
.spawn((TransformBundle::default(), XrTrackingRoot));
|
.spawn((TransformBundle::default(), OXrTrackingRoot));
|
||||||
|
|
||||||
let render_app = app.sub_app_mut(RenderApp);
|
let render_app = app.sub_app_mut(RenderApp);
|
||||||
render_app
|
render_app
|
||||||
.insert_resource(instance)
|
.insert_resource(instance)
|
||||||
.insert_resource(system_id)
|
.insert_resource(system_id)
|
||||||
.insert_resource(status)
|
.insert_resource(status)
|
||||||
.init_resource::<XrRootTransform>()
|
.init_resource::<OXrRootTransform>()
|
||||||
.init_resource::<XrCleanupSession>()
|
.init_resource::<OXrCleanupSession>()
|
||||||
.add_systems(
|
.add_systems(
|
||||||
Render,
|
Render,
|
||||||
destroy_xr_session_render
|
destroy_xr_session_render
|
||||||
.run_if(resource_equals(XrCleanupSession(true)))
|
.run_if(resource_equals(OXrCleanupSession(true)))
|
||||||
.after(RenderSet::ExtractCommands),
|
.after(RenderSet::ExtractCommands),
|
||||||
)
|
)
|
||||||
.add_systems(
|
.add_systems(
|
||||||
@@ -151,12 +164,12 @@ impl Plugin for XrInitPlugin {
|
|||||||
app.configure_sets(
|
app.configure_sets(
|
||||||
PreUpdate,
|
PreUpdate,
|
||||||
(
|
(
|
||||||
XrPreUpdateSet::PollEvents.before(handle_session),
|
OXrPreUpdateSet::PollEvents.before(handle_session),
|
||||||
XrPreUpdateSet::HandleEvents.after(handle_session),
|
OXrPreUpdateSet::HandleEvents.after(handle_session),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
let session_started = XrSessionStarted::default();
|
let session_started = OXrSessionStarted::default();
|
||||||
|
|
||||||
app.insert_resource(session_started.clone());
|
app.insert_resource(session_started.clone());
|
||||||
|
|
||||||
@@ -167,24 +180,24 @@ impl Plugin for XrInitPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_root_transform(
|
pub fn update_root_transform(
|
||||||
mut root_transform: ResMut<XrRootTransform>,
|
mut root_transform: ResMut<OXrRootTransform>,
|
||||||
root: Query<&GlobalTransform, With<XrTrackingRoot>>,
|
root: Query<&GlobalTransform, With<OXrTrackingRoot>>,
|
||||||
) {
|
) {
|
||||||
let transform = root.single();
|
let transform = root.single();
|
||||||
|
|
||||||
root_transform.0 = *transform;
|
root_transform.0 = *transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn xr_entry() -> Result<XrEntry> {
|
fn xr_entry() -> Result<OXrEntry> {
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let entry = openxr::Entry::linked();
|
let entry = openxr::Entry::linked();
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
let entry = unsafe { openxr::Entry::load()? };
|
let entry = unsafe { openxr::Entry::load()? };
|
||||||
Ok(XrEntry(entry))
|
Ok(OXrEntry(entry))
|
||||||
}
|
}
|
||||||
|
|
||||||
impl XrInitPlugin {
|
impl OXrInitPlugin {
|
||||||
fn init_xr(&self) -> Result<(XrInstance, XrSystemId, WgpuGraphics, XrSessionCreateInfo)> {
|
fn init_xr(&self) -> Result<(OXrInstance, OXrSystemId, WgpuGraphics, SessionConfigInfo)> {
|
||||||
let entry = xr_entry()?;
|
let entry = xr_entry()?;
|
||||||
|
|
||||||
let available_exts = entry.enumerate_extensions()?;
|
let available_exts = entry.enumerate_extensions()?;
|
||||||
@@ -211,7 +224,7 @@ impl XrInitPlugin {
|
|||||||
} else {
|
} else {
|
||||||
available_backends.first().copied()
|
available_backends.first().copied()
|
||||||
}
|
}
|
||||||
.ok_or(XrError::NoAvailableBackend)?;
|
.ok_or(OXrError::NoAvailableBackend)?;
|
||||||
|
|
||||||
let exts = self.exts.clone() & available_exts;
|
let exts = self.exts.clone() & available_exts;
|
||||||
|
|
||||||
@@ -243,7 +256,7 @@ impl XrInitPlugin {
|
|||||||
|
|
||||||
let (graphics, graphics_info) = instance.init_graphics(system_id)?;
|
let (graphics, graphics_info) = instance.init_graphics(system_id)?;
|
||||||
|
|
||||||
let session_create_info = XrSessionCreateInfo {
|
let session_create_info = SessionConfigInfo {
|
||||||
blend_modes: self.blend_modes.clone(),
|
blend_modes: self.blend_modes.clone(),
|
||||||
formats: self.formats.clone(),
|
formats: self.formats.clone(),
|
||||||
resolutions: self.resolutions.clone(),
|
resolutions: self.resolutions.clone(),
|
||||||
@@ -252,7 +265,7 @@ impl XrInitPlugin {
|
|||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
instance,
|
instance,
|
||||||
XrSystemId(system_id),
|
OXrSystemId(system_id),
|
||||||
graphics,
|
graphics,
|
||||||
session_create_info,
|
session_create_info,
|
||||||
))
|
))
|
||||||
@@ -261,22 +274,22 @@ impl XrInitPlugin {
|
|||||||
|
|
||||||
fn init_xr_session(
|
fn init_xr_session(
|
||||||
device: &wgpu::Device,
|
device: &wgpu::Device,
|
||||||
instance: &XrInstance,
|
instance: &OXrInstance,
|
||||||
system_id: openxr::SystemId,
|
system_id: openxr::SystemId,
|
||||||
XrSessionCreateInfo {
|
SessionConfigInfo {
|
||||||
blend_modes,
|
blend_modes,
|
||||||
formats,
|
formats,
|
||||||
resolutions,
|
resolutions,
|
||||||
graphics_info,
|
graphics_info,
|
||||||
}: XrSessionCreateInfo,
|
}: SessionConfigInfo,
|
||||||
) -> Result<(
|
) -> Result<(
|
||||||
XrSession,
|
OXrSession,
|
||||||
XrFrameWaiter,
|
OXrFrameWaiter,
|
||||||
XrFrameStream,
|
OXrFrameStream,
|
||||||
XrSwapchain,
|
OXrSwapchain,
|
||||||
XrSwapchainImages,
|
OXrSwapchainImages,
|
||||||
XrGraphicsInfo,
|
OXrGraphicsInfo,
|
||||||
XrStage,
|
OXrStage,
|
||||||
)> {
|
)> {
|
||||||
let (session, frame_waiter, frame_stream) =
|
let (session, frame_waiter, frame_stream) =
|
||||||
unsafe { instance.create_session(system_id, graphics_info)? };
|
unsafe { instance.create_session(system_id, graphics_info)? };
|
||||||
@@ -284,7 +297,7 @@ fn init_xr_session(
|
|||||||
// TODO!() support other view configurations
|
// TODO!() support other view configurations
|
||||||
let available_view_configurations = instance.enumerate_view_configurations(system_id)?;
|
let available_view_configurations = instance.enumerate_view_configurations(system_id)?;
|
||||||
if !available_view_configurations.contains(&openxr::ViewConfigurationType::PRIMARY_STEREO) {
|
if !available_view_configurations.contains(&openxr::ViewConfigurationType::PRIMARY_STEREO) {
|
||||||
return Err(XrError::NoAvailableViewConfiguration);
|
return Err(OXrError::NoAvailableViewConfiguration);
|
||||||
}
|
}
|
||||||
|
|
||||||
let view_configuration_type = openxr::ViewConfigurationType::PRIMARY_STEREO;
|
let view_configuration_type = openxr::ViewConfigurationType::PRIMARY_STEREO;
|
||||||
@@ -320,7 +333,7 @@ fn init_xr_session(
|
|||||||
} else {
|
} else {
|
||||||
if let Some(config) = view_configuration_views.first() {
|
if let Some(config) = view_configuration_views.first() {
|
||||||
Some((
|
Some((
|
||||||
uvec2(
|
UVec2::new(
|
||||||
config.recommended_image_rect_width,
|
config.recommended_image_rect_width,
|
||||||
config.recommended_image_rect_height,
|
config.recommended_image_rect_height,
|
||||||
),
|
),
|
||||||
@@ -330,7 +343,7 @@ fn init_xr_session(
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.ok_or(XrError::NoAvailableViewConfiguration)?;
|
.ok_or(OXrError::NoAvailableViewConfiguration)?;
|
||||||
|
|
||||||
let available_formats = session.enumerate_swapchain_formats()?;
|
let available_formats = session.enumerate_swapchain_formats()?;
|
||||||
|
|
||||||
@@ -345,7 +358,7 @@ fn init_xr_session(
|
|||||||
} else {
|
} else {
|
||||||
available_formats.first().copied()
|
available_formats.first().copied()
|
||||||
}
|
}
|
||||||
.ok_or(XrError::NoAvailableFormat)?;
|
.ok_or(OXrError::NoAvailableFormat)?;
|
||||||
|
|
||||||
let swapchain = session.create_swapchain(SwapchainCreateInfo {
|
let swapchain = session.create_swapchain(SwapchainCreateInfo {
|
||||||
create_flags: SwapchainCreateFlags::EMPTY,
|
create_flags: SwapchainCreateFlags::EMPTY,
|
||||||
@@ -378,15 +391,15 @@ fn init_xr_session(
|
|||||||
} else {
|
} else {
|
||||||
available_blend_modes.first().copied()
|
available_blend_modes.first().copied()
|
||||||
}
|
}
|
||||||
.ok_or(XrError::NoAvailableBackend)?;
|
.ok_or(OXrError::NoAvailableBackend)?;
|
||||||
|
|
||||||
let stage = XrStage(
|
let stage = OXrStage(
|
||||||
session
|
session
|
||||||
.create_reference_space(openxr::ReferenceSpaceType::STAGE, openxr::Posef::IDENTITY)?
|
.create_reference_space(openxr::ReferenceSpaceType::STAGE, openxr::Posef::IDENTITY)?
|
||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let graphics_info = XrGraphicsInfo {
|
let graphics_info = OXrGraphicsInfo {
|
||||||
blend_mode,
|
blend_mode,
|
||||||
resolution,
|
resolution,
|
||||||
format,
|
format,
|
||||||
@@ -406,19 +419,19 @@ fn init_xr_session(
|
|||||||
/// This is used solely to transport resources from the main world to the render world.
|
/// This is used solely to transport resources from the main world to the render world.
|
||||||
#[derive(Resource)]
|
#[derive(Resource)]
|
||||||
struct XrRenderResources {
|
struct XrRenderResources {
|
||||||
session: XrSession,
|
session: OXrSession,
|
||||||
frame_stream: XrFrameStream,
|
frame_stream: OXrFrameStream,
|
||||||
swapchain: XrSwapchain,
|
swapchain: OXrSwapchain,
|
||||||
images: XrSwapchainImages,
|
images: OXrSwapchainImages,
|
||||||
graphics_info: XrGraphicsInfo,
|
graphics_info: OXrGraphicsInfo,
|
||||||
stage: XrStage,
|
stage: OXrStage,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_xr_session(
|
pub fn create_xr_session(
|
||||||
device: Res<RenderDevice>,
|
device: Res<RenderDevice>,
|
||||||
instance: Res<XrInstance>,
|
instance: Res<OXrInstance>,
|
||||||
create_info: NonSend<XrSessionCreateInfo>,
|
create_info: NonSend<SessionConfigInfo>,
|
||||||
system_id: Res<XrSystemId>,
|
system_id: Res<OXrSystemId>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
match init_xr_session(
|
match init_xr_session(
|
||||||
@@ -446,7 +459,7 @@ pub fn create_xr_session(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn begin_xr_session(session: Res<XrSession>, session_started: Res<XrSessionStarted>) {
|
pub fn begin_xr_session(session: Res<OXrSession>, session_started: Res<OXrSessionStarted>) {
|
||||||
let _span = info_span!("xr_begin_session");
|
let _span = info_span!("xr_begin_session");
|
||||||
session
|
session
|
||||||
.begin(openxr::ViewConfigurationType::PRIMARY_STEREO)
|
.begin(openxr::ViewConfigurationType::PRIMARY_STEREO)
|
||||||
@@ -454,7 +467,7 @@ pub fn begin_xr_session(session: Res<XrSession>, session_started: Res<XrSessionS
|
|||||||
session_started.set(true);
|
session_started.set(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn end_xr_session(session: Res<XrSession>, session_started: Res<XrSessionStarted>) {
|
pub fn end_xr_session(session: Res<OXrSession>, session_started: Res<OXrSessionStarted>) {
|
||||||
let _span = info_span!("xr_end_session");
|
let _span = info_span!("xr_end_session");
|
||||||
session.end().expect("Failed to end session");
|
session.end().expect("Failed to end session");
|
||||||
session_started.set(false);
|
session_started.set(false);
|
||||||
@@ -483,7 +496,7 @@ pub fn transfer_xr_resources(mut commands: Commands, mut world: ResMut<MainWorld
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Polls any OpenXR events and handles them accordingly
|
/// Polls any OpenXR events and handles them accordingly
|
||||||
pub fn poll_events(instance: Res<XrInstance>, status: Res<XrSharedStatus>) {
|
pub fn poll_events(instance: Res<OXrInstance>, status: Res<XrSharedStatus>) {
|
||||||
let _span = info_span!("xr_poll_events");
|
let _span = info_span!("xr_poll_events");
|
||||||
let mut buffer = Default::default();
|
let mut buffer = Default::default();
|
||||||
while let Some(event) = instance
|
while let Some(event) = instance
|
||||||
@@ -519,24 +532,24 @@ pub fn poll_events(instance: Res<XrInstance>, status: Res<XrSharedStatus>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset_per_frame_resources(mut cleanup: ResMut<XrCleanupSession>) {
|
pub fn reset_per_frame_resources(mut cleanup: ResMut<OXrCleanupSession>) {
|
||||||
**cleanup = false;
|
**cleanup = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn destroy_xr_session(mut commands: Commands) {
|
pub fn destroy_xr_session(mut commands: Commands) {
|
||||||
commands.remove_resource::<XrSession>();
|
commands.remove_resource::<OXrSession>();
|
||||||
commands.remove_resource::<XrFrameWaiter>();
|
commands.remove_resource::<OXrFrameWaiter>();
|
||||||
commands.remove_resource::<XrSwapchainImages>();
|
commands.remove_resource::<OXrSwapchainImages>();
|
||||||
commands.remove_resource::<XrGraphicsInfo>();
|
commands.remove_resource::<OXrGraphicsInfo>();
|
||||||
commands.remove_resource::<XrStage>();
|
commands.remove_resource::<OXrStage>();
|
||||||
commands.insert_resource(XrCleanupSession(true));
|
commands.insert_resource(OXrCleanupSession(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn destroy_xr_session_render(world: &mut World) {
|
pub fn destroy_xr_session_render(world: &mut World) {
|
||||||
world.remove_resource::<XrSwapchain>();
|
world.remove_resource::<OXrSwapchain>();
|
||||||
world.remove_resource::<XrFrameStream>();
|
world.remove_resource::<OXrFrameStream>();
|
||||||
world.remove_resource::<XrStage>();
|
world.remove_resource::<OXrStage>();
|
||||||
world.remove_resource::<XrSwapchainImages>();
|
world.remove_resource::<OXrSwapchainImages>();
|
||||||
world.remove_resource::<XrGraphicsInfo>();
|
world.remove_resource::<OXrGraphicsInfo>();
|
||||||
world.remove_resource::<XrSession>();
|
world.remove_resource::<OXrSession>();
|
||||||
}
|
}
|
||||||
@@ -2,13 +2,13 @@ use std::mem;
|
|||||||
|
|
||||||
use openxr::{sys, CompositionLayerFlags, Fovf, Posef, Rect2Di, Space};
|
use openxr::{sys, CompositionLayerFlags, Fovf, Posef, Rect2Di, Space};
|
||||||
|
|
||||||
use crate::graphics::graphics_match;
|
use crate::oxr::graphics::graphics_match;
|
||||||
use crate::resources::XrSwapchain;
|
use crate::oxr::resources::OXrSwapchain;
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct SwapchainSubImage<'a> {
|
pub struct SwapchainSubImage<'a> {
|
||||||
inner: sys::SwapchainSubImage,
|
inner: sys::SwapchainSubImage,
|
||||||
swapchain: Option<&'a XrSwapchain>,
|
swapchain: Option<&'a OXrSwapchain>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> SwapchainSubImage<'a> {
|
impl<'a> SwapchainSubImage<'a> {
|
||||||
@@ -30,7 +30,7 @@ impl<'a> SwapchainSubImage<'a> {
|
|||||||
&self.inner
|
&self.inner
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn swapchain(mut self, value: &'a XrSwapchain) -> Self {
|
pub fn swapchain(mut self, value: &'a OXrSwapchain) -> Self {
|
||||||
graphics_match!(
|
graphics_match!(
|
||||||
&value.0;
|
&value.0;
|
||||||
swap => self.inner.swapchain = swap.as_raw()
|
swap => self.inner.swapchain = swap.as_raw()
|
||||||
@@ -59,7 +59,7 @@ impl<'a> Default for SwapchainSubImage<'a> {
|
|||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct CompositionLayerProjectionView<'a> {
|
pub struct CompositionLayerProjectionView<'a> {
|
||||||
inner: sys::CompositionLayerProjectionView,
|
inner: sys::CompositionLayerProjectionView,
|
||||||
swapchain: Option<&'a XrSwapchain>,
|
swapchain: Option<&'a OXrSwapchain>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> CompositionLayerProjectionView<'a> {
|
impl<'a> CompositionLayerProjectionView<'a> {
|
||||||
@@ -104,13 +104,13 @@ impl<'a> Default for CompositionLayerProjectionView<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub unsafe trait CompositionLayer<'a> {
|
pub unsafe trait CompositionLayer<'a> {
|
||||||
fn swapchain(&self) -> Option<&'a XrSwapchain>;
|
fn swapchain(&self) -> Option<&'a OXrSwapchain>;
|
||||||
fn header(&self) -> &'a sys::CompositionLayerBaseHeader;
|
fn header(&self) -> &'a sys::CompositionLayerBaseHeader;
|
||||||
}
|
}
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct CompositionLayerProjection<'a> {
|
pub struct CompositionLayerProjection<'a> {
|
||||||
inner: sys::CompositionLayerProjection,
|
inner: sys::CompositionLayerProjection,
|
||||||
swapchain: Option<&'a XrSwapchain>,
|
swapchain: Option<&'a OXrSwapchain>,
|
||||||
views: Vec<sys::CompositionLayerProjectionView>,
|
views: Vec<sys::CompositionLayerProjectionView>,
|
||||||
}
|
}
|
||||||
impl<'a> CompositionLayerProjection<'a> {
|
impl<'a> CompositionLayerProjection<'a> {
|
||||||
@@ -154,7 +154,7 @@ impl<'a> CompositionLayerProjection<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
unsafe impl<'a> CompositionLayer<'a> for CompositionLayerProjection<'a> {
|
unsafe impl<'a> CompositionLayer<'a> for CompositionLayerProjection<'a> {
|
||||||
fn swapchain(&self) -> Option<&'a XrSwapchain> {
|
fn swapchain(&self) -> Option<&'a OXrSwapchain> {
|
||||||
self.swapchain
|
self.swapchain
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,18 +1,5 @@
|
|||||||
use actions::XrActionPlugin;
|
|
||||||
use bevy::{
|
|
||||||
app::{PluginGroup, PluginGroupBuilder},
|
|
||||||
render::{pipelined_rendering::PipelinedRenderingPlugin, RenderPlugin},
|
|
||||||
utils::default,
|
|
||||||
window::{PresentMode, Window, WindowPlugin},
|
|
||||||
};
|
|
||||||
use bevy_xr::camera::XrCameraPlugin;
|
|
||||||
use init::XrInitPlugin;
|
|
||||||
use render::XrRenderPlugin;
|
|
||||||
|
|
||||||
pub mod actions;
|
|
||||||
pub mod camera;
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod extensions;
|
mod exts;
|
||||||
pub mod graphics;
|
pub mod graphics;
|
||||||
pub mod init;
|
pub mod init;
|
||||||
pub mod layer_builder;
|
pub mod layer_builder;
|
||||||
@@ -20,13 +7,25 @@ pub mod render;
|
|||||||
pub mod resources;
|
pub mod resources;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
|
// use actions::XrActionPlugin;
|
||||||
|
use bevy::{
|
||||||
|
app::{PluginGroup, PluginGroupBuilder},
|
||||||
|
render::{pipelined_rendering::PipelinedRenderingPlugin, RenderPlugin},
|
||||||
|
utils::default,
|
||||||
|
window::{PresentMode, Window, WindowPlugin},
|
||||||
|
};
|
||||||
|
use bevy_xr_api::camera::XrCameraPlugin;
|
||||||
|
use bevy_xr_api::session::XrSessionPlugin;
|
||||||
|
use init::OXrInitPlugin;
|
||||||
|
use render::XrRenderPlugin;
|
||||||
|
|
||||||
pub fn add_xr_plugins<G: PluginGroup>(plugins: G) -> PluginGroupBuilder {
|
pub fn add_xr_plugins<G: PluginGroup>(plugins: G) -> PluginGroupBuilder {
|
||||||
plugins
|
plugins
|
||||||
.build()
|
.build()
|
||||||
.disable::<RenderPlugin>()
|
.disable::<RenderPlugin>()
|
||||||
.disable::<PipelinedRenderingPlugin>()
|
.disable::<PipelinedRenderingPlugin>()
|
||||||
.add_before::<RenderPlugin, _>(bevy_xr::session::XrSessionPlugin)
|
.add_before::<RenderPlugin, _>(XrSessionPlugin)
|
||||||
.add_before::<RenderPlugin, _>(XrInitPlugin {
|
.add_before::<RenderPlugin, _>(OXrInitPlugin {
|
||||||
app_info: default(),
|
app_info: default(),
|
||||||
exts: default(),
|
exts: default(),
|
||||||
blend_modes: default(),
|
blend_modes: default(),
|
||||||
@@ -37,7 +36,7 @@ pub fn add_xr_plugins<G: PluginGroup>(plugins: G) -> PluginGroupBuilder {
|
|||||||
})
|
})
|
||||||
.add(XrRenderPlugin)
|
.add(XrRenderPlugin)
|
||||||
.add(XrCameraPlugin)
|
.add(XrCameraPlugin)
|
||||||
.add(XrActionPlugin)
|
// .add(XrActionPlugin)
|
||||||
.set(WindowPlugin {
|
.set(WindowPlugin {
|
||||||
#[cfg(not(target_os = "android"))]
|
#[cfg(not(target_os = "android"))]
|
||||||
primary_window: Some(Window {
|
primary_window: Some(Window {
|
||||||
@@ -9,28 +9,28 @@ use bevy::{
|
|||||||
},
|
},
|
||||||
transform::TransformSystem,
|
transform::TransformSystem,
|
||||||
};
|
};
|
||||||
use bevy_xr::camera::{XrCamera, XrCameraBundle, XrProjection};
|
use bevy_xr_api::camera::{XrCamera, XrCameraBundle, XrProjection};
|
||||||
use openxr::{CompositionLayerFlags, ViewStateFlags};
|
use openxr::{CompositionLayerFlags, ViewStateFlags};
|
||||||
|
|
||||||
use crate::init::{session_started, XrPreUpdateSet};
|
use crate::oxr::init::{session_started, OXrPreUpdateSet};
|
||||||
use crate::layer_builder::*;
|
use crate::oxr::layer_builder::*;
|
||||||
use crate::resources::*;
|
use crate::oxr::resources::*;
|
||||||
|
|
||||||
pub struct XrRenderPlugin;
|
pub struct XrRenderPlugin;
|
||||||
|
|
||||||
impl Plugin for XrRenderPlugin {
|
impl Plugin for XrRenderPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_plugins((ExtractResourcePlugin::<XrViews>::default(),))
|
app.add_plugins((ExtractResourcePlugin::<OXrViews>::default(),))
|
||||||
.add_systems(
|
.add_systems(
|
||||||
PreUpdate,
|
PreUpdate,
|
||||||
(
|
(
|
||||||
init_views.run_if(resource_added::<XrGraphicsInfo>),
|
init_views.run_if(resource_added::<OXrGraphicsInfo>),
|
||||||
wait_frame.run_if(session_started),
|
wait_frame.run_if(session_started),
|
||||||
locate_views.run_if(session_started),
|
locate_views.run_if(session_started),
|
||||||
update_views.run_if(session_started),
|
update_views.run_if(session_started),
|
||||||
)
|
)
|
||||||
.chain()
|
.chain()
|
||||||
.after(XrPreUpdateSet::HandleEvents),
|
.after(OXrPreUpdateSet::HandleEvents),
|
||||||
)
|
)
|
||||||
.add_systems(
|
.add_systems(
|
||||||
PostUpdate,
|
PostUpdate,
|
||||||
@@ -65,9 +65,9 @@ pub const XR_TEXTURE_INDEX: u32 = 3383858418;
|
|||||||
// TODO: have cameras initialized externally and then recieved by this function.
|
// TODO: have cameras initialized externally and then recieved by this function.
|
||||||
/// This is needed to properly initialize the texture views so that bevy will set them to the correct resolution despite them being updated in the render world.
|
/// This is needed to properly initialize the texture views so that bevy will set them to the correct resolution despite them being updated in the render world.
|
||||||
pub fn init_views(
|
pub fn init_views(
|
||||||
graphics_info: Res<XrGraphicsInfo>,
|
graphics_info: Res<OXrGraphicsInfo>,
|
||||||
mut manual_texture_views: ResMut<ManualTextureViews>,
|
mut manual_texture_views: ResMut<ManualTextureViews>,
|
||||||
swapchain_images: Res<XrSwapchainImages>,
|
swapchain_images: Res<OXrSwapchainImages>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
let _span = info_span!("xr_init_views");
|
let _span = info_span!("xr_init_views");
|
||||||
@@ -93,22 +93,22 @@ pub fn init_views(
|
|||||||
));
|
));
|
||||||
views.push(default());
|
views.push(default());
|
||||||
}
|
}
|
||||||
commands.insert_resource(XrViews(views));
|
commands.insert_resource(OXrViews(views));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wait_frame(mut frame_waiter: ResMut<XrFrameWaiter>, mut commands: Commands) {
|
pub fn wait_frame(mut frame_waiter: ResMut<OXrFrameWaiter>, mut commands: Commands) {
|
||||||
let _span = info_span!("xr_wait_frame");
|
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");
|
||||||
// Here we insert the predicted display time for when this frame will be displayed.
|
// Here we insert the predicted display time for when this frame will be displayed.
|
||||||
// TODO: don't add predicted_display_period if pipelined rendering plugin not enabled
|
// TODO: don't add predicted_display_period if pipelined rendering plugin not enabled
|
||||||
commands.insert_resource(XrTime(state.predicted_display_time));
|
commands.insert_resource(OXrTime(state.predicted_display_time));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn locate_views(
|
pub fn locate_views(
|
||||||
session: Res<XrSession>,
|
session: Res<OXrSession>,
|
||||||
stage: Res<XrStage>,
|
stage: Res<OXrStage>,
|
||||||
time: Res<XrTime>,
|
time: Res<OXrTime>,
|
||||||
mut openxr_views: ResMut<XrViews>,
|
mut openxr_views: ResMut<OXrViews>,
|
||||||
) {
|
) {
|
||||||
let _span = info_span!("xr_locate_views");
|
let _span = info_span!("xr_locate_views");
|
||||||
let (flags, xr_views) = session
|
let (flags, xr_views) = session
|
||||||
@@ -125,7 +125,7 @@ pub fn locate_views(
|
|||||||
flags & ViewStateFlags::ORIENTATION_VALID == ViewStateFlags::ORIENTATION_VALID,
|
flags & ViewStateFlags::ORIENTATION_VALID == ViewStateFlags::ORIENTATION_VALID,
|
||||||
flags & ViewStateFlags::POSITION_VALID == ViewStateFlags::POSITION_VALID,
|
flags & ViewStateFlags::POSITION_VALID == ViewStateFlags::POSITION_VALID,
|
||||||
) {
|
) {
|
||||||
(true, true) => *openxr_views = XrViews(xr_views),
|
(true, true) => *openxr_views = OXrViews(xr_views),
|
||||||
(true, false) => {
|
(true, false) => {
|
||||||
for (i, view) in openxr_views.iter_mut().enumerate() {
|
for (i, view) in openxr_views.iter_mut().enumerate() {
|
||||||
view.pose.orientation = xr_views[i].pose.orientation;
|
view.pose.orientation = xr_views[i].pose.orientation;
|
||||||
@@ -142,7 +142,7 @@ pub fn locate_views(
|
|||||||
|
|
||||||
pub fn update_views(
|
pub fn update_views(
|
||||||
mut query: Query<(&mut Transform, &mut XrProjection, &XrCamera)>,
|
mut query: Query<(&mut Transform, &mut XrProjection, &XrCamera)>,
|
||||||
views: ResMut<XrViews>,
|
views: ResMut<OXrViews>,
|
||||||
) {
|
) {
|
||||||
for (mut transform, mut projection, camera) in query.iter_mut() {
|
for (mut transform, mut projection, camera) in query.iter_mut() {
|
||||||
let Some(view) = views.get(camera.0 as usize) else {
|
let Some(view) = views.get(camera.0 as usize) else {
|
||||||
@@ -162,8 +162,8 @@ pub fn update_views(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_views_render_world(
|
pub fn update_views_render_world(
|
||||||
views: Res<XrViews>,
|
views: Res<OXrViews>,
|
||||||
root: Res<XrRootTransform>,
|
root: Res<OXrRootTransform>,
|
||||||
mut query: Query<(&mut ExtractedView, &XrCamera)>,
|
mut query: Query<(&mut ExtractedView, &XrCamera)>,
|
||||||
) {
|
) {
|
||||||
for (mut extracted_view, camera) in query.iter_mut() {
|
for (mut extracted_view, camera) in query.iter_mut() {
|
||||||
@@ -280,10 +280,10 @@ fn calculate_projection(near_z: f32, fov: openxr::Fovf) -> Mat4 {
|
|||||||
/// # Safety
|
/// # Safety
|
||||||
/// Images inserted into texture views here should not be written to until [`wait_image`] is ran
|
/// Images inserted into texture views here should not be written to until [`wait_image`] is ran
|
||||||
pub fn insert_texture_views(
|
pub fn insert_texture_views(
|
||||||
swapchain_images: Res<XrSwapchainImages>,
|
swapchain_images: Res<OXrSwapchainImages>,
|
||||||
mut swapchain: ResMut<XrSwapchain>,
|
mut swapchain: ResMut<OXrSwapchain>,
|
||||||
mut manual_texture_views: ResMut<ManualTextureViews>,
|
mut manual_texture_views: ResMut<ManualTextureViews>,
|
||||||
graphics_info: Res<XrGraphicsInfo>,
|
graphics_info: Res<OXrGraphicsInfo>,
|
||||||
) {
|
) {
|
||||||
let _span = info_span!("xr_insert_texture_views");
|
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");
|
||||||
@@ -294,7 +294,7 @@ pub fn insert_texture_views(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wait_image(mut swapchain: ResMut<XrSwapchain>) {
|
pub fn wait_image(mut swapchain: ResMut<OXrSwapchain>) {
|
||||||
swapchain
|
swapchain
|
||||||
.wait_image(openxr::Duration::INFINITE)
|
.wait_image(openxr::Duration::INFINITE)
|
||||||
.expect("Failed to wait image");
|
.expect("Failed to wait image");
|
||||||
@@ -303,7 +303,7 @@ pub fn wait_image(mut swapchain: ResMut<XrSwapchain>) {
|
|||||||
pub fn add_texture_view(
|
pub fn add_texture_view(
|
||||||
manual_texture_views: &mut ManualTextureViews,
|
manual_texture_views: &mut ManualTextureViews,
|
||||||
texture: &wgpu::Texture,
|
texture: &wgpu::Texture,
|
||||||
info: &XrGraphicsInfo,
|
info: &OXrGraphicsInfo,
|
||||||
index: u32,
|
index: u32,
|
||||||
) -> ManualTextureViewHandle {
|
) -> ManualTextureViewHandle {
|
||||||
let view = texture.create_view(&wgpu::TextureViewDescriptor {
|
let view = texture.create_view(&wgpu::TextureViewDescriptor {
|
||||||
@@ -322,17 +322,17 @@ pub fn add_texture_view(
|
|||||||
handle
|
handle
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn begin_frame(mut frame_stream: ResMut<XrFrameStream>) {
|
pub fn begin_frame(mut frame_stream: ResMut<OXrFrameStream>) {
|
||||||
frame_stream.begin().expect("Failed to begin frame")
|
frame_stream.begin().expect("Failed to begin frame")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn end_frame(
|
pub fn end_frame(
|
||||||
mut frame_stream: ResMut<XrFrameStream>,
|
mut frame_stream: ResMut<OXrFrameStream>,
|
||||||
mut swapchain: ResMut<XrSwapchain>,
|
mut swapchain: ResMut<OXrSwapchain>,
|
||||||
stage: Res<XrStage>,
|
stage: Res<OXrStage>,
|
||||||
display_time: Res<XrTime>,
|
display_time: Res<OXrTime>,
|
||||||
graphics_info: Res<XrGraphicsInfo>,
|
graphics_info: Res<OXrGraphicsInfo>,
|
||||||
openxr_views: Res<XrViews>,
|
openxr_views: Res<OXrViews>,
|
||||||
) {
|
) {
|
||||||
let _span = info_span!("xr_end_frame");
|
let _span = info_span!("xr_end_frame");
|
||||||
swapchain.release_image().unwrap();
|
swapchain.release_image().unwrap();
|
||||||
@@ -1,35 +1,36 @@
|
|||||||
use std::any::TypeId;
|
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::error::XrError;
|
|
||||||
use crate::graphics::*;
|
|
||||||
use crate::layer_builder::CompositionLayer;
|
|
||||||
use crate::types::*;
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy::render::extract_resource::ExtractResource;
|
use bevy::render::extract_resource::ExtractResource;
|
||||||
use bevy::utils::HashMap;
|
|
||||||
use openxr::AnyGraphics;
|
use openxr::AnyGraphics;
|
||||||
|
|
||||||
#[derive(Deref, Clone)]
|
use crate::oxr::error::OXrError;
|
||||||
pub struct XrEntry(pub openxr::Entry);
|
use crate::oxr::graphics::*;
|
||||||
|
use crate::oxr::layer_builder::CompositionLayer;
|
||||||
|
use crate::oxr::types::*;
|
||||||
|
|
||||||
impl XrEntry {
|
/// Wrapper around the entry point to the OpenXR API
|
||||||
pub fn enumerate_extensions(&self) -> Result<XrExtensions> {
|
#[derive(Deref, Clone)]
|
||||||
|
pub struct OXrEntry(pub openxr::Entry);
|
||||||
|
|
||||||
|
impl OXrEntry {
|
||||||
|
/// Enumerate available extensions for this OpenXR runtime.
|
||||||
|
pub fn enumerate_extensions(&self) -> Result<OXrExtensions> {
|
||||||
Ok(self.0.enumerate_extensions().map(Into::into)?)
|
Ok(self.0.enumerate_extensions().map(Into::into)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_instance(
|
pub fn create_instance(
|
||||||
&self,
|
&self,
|
||||||
app_info: AppInfo,
|
app_info: AppInfo,
|
||||||
exts: XrExtensions,
|
exts: OXrExtensions,
|
||||||
layers: &[&str],
|
layers: &[&str],
|
||||||
backend: GraphicsBackend,
|
backend: GraphicsBackend,
|
||||||
) -> Result<XrInstance> {
|
) -> Result<OXrInstance> {
|
||||||
let available_exts = self.enumerate_extensions()?;
|
let available_exts = self.enumerate_extensions()?;
|
||||||
|
|
||||||
if !backend.is_available(&available_exts) {
|
if !backend.is_available(&available_exts) {
|
||||||
return Err(XrError::UnavailableBackend(backend));
|
return Err(OXrError::UnavailableBackend(backend));
|
||||||
}
|
}
|
||||||
|
|
||||||
let required_exts = exts | backend.required_exts();
|
let required_exts = exts | backend.required_exts();
|
||||||
@@ -45,7 +46,7 @@ impl XrEntry {
|
|||||||
layers,
|
layers,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(XrInstance(instance, backend, app_info))
|
Ok(OXrInstance(instance, backend, app_info))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn available_backends(&self) -> Result<Vec<GraphicsBackend>> {
|
pub fn available_backends(&self) -> Result<Vec<GraphicsBackend>> {
|
||||||
@@ -55,39 +56,47 @@ impl XrEntry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Wrapper around [openxr::Instance] with additional data for safety.
|
||||||
#[derive(Resource, Deref, Clone)]
|
#[derive(Resource, Deref, Clone)]
|
||||||
pub struct XrInstance(
|
pub struct OXrInstance(
|
||||||
#[deref] pub openxr::Instance,
|
#[deref] pub openxr::Instance,
|
||||||
pub(crate) GraphicsBackend,
|
pub(crate) GraphicsBackend,
|
||||||
pub(crate) AppInfo,
|
pub(crate) AppInfo,
|
||||||
);
|
);
|
||||||
|
|
||||||
impl XrInstance {
|
impl OXrInstance {
|
||||||
|
pub fn into_inner(self) -> openxr::Instance {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize graphics. This is used to create [WgpuGraphics] for the bevy app and to get the [SessionCreateInfo] to make an XR session.
|
||||||
pub fn init_graphics(
|
pub fn init_graphics(
|
||||||
&self,
|
&self,
|
||||||
system_id: openxr::SystemId,
|
system_id: openxr::SystemId,
|
||||||
) -> Result<(WgpuGraphics, XrSessionGraphicsInfo)> {
|
) -> Result<(WgpuGraphics, SessionCreateInfo)> {
|
||||||
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, XrSessionGraphicsInfo(Api::wrap(session_info))))
|
Ok((graphics, SessionCreateInfo(Api::wrap(session_info))))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates an [OXrSession]
|
||||||
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// `info` must contain valid handles for the graphics api
|
/// `info` must contain valid handles for the graphics api
|
||||||
pub unsafe fn create_session(
|
pub unsafe fn create_session(
|
||||||
&self,
|
&self,
|
||||||
system_id: openxr::SystemId,
|
system_id: openxr::SystemId,
|
||||||
info: XrSessionGraphicsInfo,
|
info: SessionCreateInfo,
|
||||||
) -> Result<(XrSession, XrFrameWaiter, XrFrameStream)> {
|
) -> Result<(OXrSession, OXrFrameWaiter, OXrFrameStream)> {
|
||||||
if !info.0.using_graphics_of_val(&self.1) {
|
if !info.0.using_graphics_of_val(&self.1) {
|
||||||
return Err(XrError::GraphicsBackendMismatch {
|
return Err(OXrError::GraphicsBackendMismatch {
|
||||||
item: std::any::type_name::<XrSessionGraphicsInfo>(),
|
item: std::any::type_name::<SessionCreateInfo>(),
|
||||||
backend: info.0.graphics_name(),
|
backend: info.0.graphics_name(),
|
||||||
expected_backend: self.1.graphics_name(),
|
expected_backend: self.1.graphics_name(),
|
||||||
});
|
});
|
||||||
@@ -96,59 +105,61 @@ impl XrInstance {
|
|||||||
info.0;
|
info.0;
|
||||||
info => {
|
info => {
|
||||||
let (session, frame_waiter, frame_stream) = self.0.create_session::<Api>(system_id, &info)?;
|
let (session, frame_waiter, frame_stream) = self.0.create_session::<Api>(system_id, &info)?;
|
||||||
Ok((session.into(), XrFrameWaiter(frame_waiter), XrFrameStream(Api::wrap(frame_stream))))
|
Ok((session.into(), OXrFrameWaiter(frame_waiter), OXrFrameStream(Api::wrap(frame_stream))))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
/// Graphics agnostic wrapper around [openxr::Session]
|
||||||
pub struct XrSessionGraphicsInfo(pub(crate) GraphicsWrap<Self>);
|
|
||||||
|
|
||||||
impl GraphicsType for XrSessionGraphicsInfo {
|
|
||||||
type Inner<G: GraphicsExt> = G::SessionCreateInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Resource, Deref, Clone)]
|
#[derive(Resource, Deref, Clone)]
|
||||||
pub struct XrSession(
|
pub struct OXrSession(
|
||||||
#[deref] pub(crate) openxr::Session<AnyGraphics>,
|
#[deref] pub openxr::Session<AnyGraphics>,
|
||||||
pub(crate) GraphicsWrap<Self>,
|
pub GraphicsWrap<Self>,
|
||||||
);
|
);
|
||||||
|
|
||||||
impl GraphicsType for XrSession {
|
impl GraphicsType for OXrSession {
|
||||||
type Inner<G: GraphicsExt> = openxr::Session<G>;
|
type Inner<G: GraphicsExt> = openxr::Session<G>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<G: GraphicsExt> From<openxr::Session<G>> for XrSession {
|
impl<G: GraphicsExt> From<openxr::Session<G>> for OXrSession {
|
||||||
fn from(value: openxr::Session<G>) -> Self {
|
fn from(session: openxr::Session<G>) -> Self {
|
||||||
Self(value.clone().into_any_graphics(), G::wrap(value))
|
Self::new(session)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl XrSession {
|
impl OXrSession {
|
||||||
|
pub fn new<G: GraphicsExt>(session: openxr::Session<G>) -> Self {
|
||||||
|
Self(session.clone().into_any_graphics(), G::wrap(session))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enumerate all available swapchain formats.
|
||||||
pub fn enumerate_swapchain_formats(&self) -> Result<Vec<wgpu::TextureFormat>> {
|
pub fn enumerate_swapchain_formats(&self) -> Result<Vec<wgpu::TextureFormat>> {
|
||||||
graphics_match!(
|
graphics_match!(
|
||||||
&self.1;
|
&self.1;
|
||||||
session => Ok(session.enumerate_swapchain_formats()?.into_iter().filter_map(Api::to_wgpu_format).collect())
|
session => Ok(session.enumerate_swapchain_formats()?.into_iter().filter_map(Api::into_wgpu_format).collect())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_swapchain(&self, info: SwapchainCreateInfo) -> Result<XrSwapchain> {
|
/// Creates an [OXrSwapchain].
|
||||||
Ok(XrSwapchain(graphics_match!(
|
pub fn create_swapchain(&self, info: SwapchainCreateInfo) -> Result<OXrSwapchain> {
|
||||||
|
Ok(OXrSwapchain(graphics_match!(
|
||||||
&self.1;
|
&self.1;
|
||||||
session => session.create_swapchain(&info.try_into()?)? => XrSwapchain
|
session => session.create_swapchain(&info.try_into()?)? => OXrSwapchain
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Graphics agnostic wrapper around [openxr::FrameStream]
|
||||||
#[derive(Resource)]
|
#[derive(Resource)]
|
||||||
pub struct XrFrameStream(pub(crate) GraphicsWrap<Self>);
|
pub struct OXrFrameStream(pub GraphicsWrap<Self>);
|
||||||
|
|
||||||
impl GraphicsType for XrFrameStream {
|
impl GraphicsType for OXrFrameStream {
|
||||||
type Inner<G: GraphicsExt> = openxr::FrameStream<G>;
|
type Inner<G: GraphicsExt> = openxr::FrameStream<G>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl XrFrameStream {
|
impl OXrFrameStream {
|
||||||
|
/// Indicate that graphics device work is beginning.
|
||||||
pub fn begin(&mut self) -> openxr::Result<()> {
|
pub fn begin(&mut self) -> openxr::Result<()> {
|
||||||
graphics_match!(
|
graphics_match!(
|
||||||
&mut self.0;
|
&mut self.0;
|
||||||
@@ -156,6 +167,10 @@ impl XrFrameStream {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Indicate that all graphics work for the frame has been submitted
|
||||||
|
///
|
||||||
|
/// `layers` is an array of references to any type of composition layer,
|
||||||
|
/// e.g. [`CompositionLayerProjection`](crate::oxr::layer_builder::CompositionLayerProjection)
|
||||||
pub fn end(
|
pub fn end(
|
||||||
&mut self,
|
&mut self,
|
||||||
display_time: openxr::Time,
|
display_time: openxr::Time,
|
||||||
@@ -187,17 +202,20 @@ impl XrFrameStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Handle for waiting to render a frame. Check [`FrameWaiter`](openxr::FrameWaiter) for available methods.
|
||||||
#[derive(Resource, Deref, DerefMut)]
|
#[derive(Resource, Deref, DerefMut)]
|
||||||
pub struct XrFrameWaiter(pub openxr::FrameWaiter);
|
pub struct OXrFrameWaiter(pub openxr::FrameWaiter);
|
||||||
|
|
||||||
|
/// Graphics agnostic wrapper around [openxr::Swapchain]
|
||||||
#[derive(Resource)]
|
#[derive(Resource)]
|
||||||
pub struct XrSwapchain(pub(crate) GraphicsWrap<Self>);
|
pub struct OXrSwapchain(pub GraphicsWrap<Self>);
|
||||||
|
|
||||||
impl GraphicsType for XrSwapchain {
|
impl GraphicsType for OXrSwapchain {
|
||||||
type Inner<G: GraphicsExt> = openxr::Swapchain<G>;
|
type Inner<G: GraphicsExt> = openxr::Swapchain<G>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl XrSwapchain {
|
impl OXrSwapchain {
|
||||||
|
/// Determine the index of the next image to render to in the swapchain image array
|
||||||
pub fn acquire_image(&mut self) -> Result<u32> {
|
pub fn acquire_image(&mut self) -> Result<u32> {
|
||||||
graphics_match!(
|
graphics_match!(
|
||||||
&mut self.0;
|
&mut self.0;
|
||||||
@@ -205,6 +223,7 @@ impl XrSwapchain {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Wait for the compositor to finish reading from the oldest unwaited acquired image
|
||||||
pub fn wait_image(&mut self, timeout: openxr::Duration) -> Result<()> {
|
pub fn wait_image(&mut self, timeout: openxr::Duration) -> Result<()> {
|
||||||
graphics_match!(
|
graphics_match!(
|
||||||
&mut self.0;
|
&mut self.0;
|
||||||
@@ -212,6 +231,7 @@ impl XrSwapchain {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Release the oldest acquired image
|
||||||
pub fn release_image(&mut self) -> Result<()> {
|
pub fn release_image(&mut self) -> Result<()> {
|
||||||
graphics_match!(
|
graphics_match!(
|
||||||
&mut self.0;
|
&mut self.0;
|
||||||
@@ -219,12 +239,13 @@ impl XrSwapchain {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enumerates swapchain images and converts them to wgpu [`Texture`](wgpu::Texture)s.
|
||||||
pub fn enumerate_images(
|
pub fn enumerate_images(
|
||||||
&self,
|
&self,
|
||||||
device: &wgpu::Device,
|
device: &wgpu::Device,
|
||||||
format: wgpu::TextureFormat,
|
format: wgpu::TextureFormat,
|
||||||
resolution: UVec2,
|
resolution: UVec2,
|
||||||
) -> Result<XrSwapchainImages> {
|
) -> Result<OXrSwapchainImages> {
|
||||||
graphics_match!(
|
graphics_match!(
|
||||||
&self.0;
|
&self.0;
|
||||||
swap => {
|
swap => {
|
||||||
@@ -234,43 +255,39 @@ impl XrSwapchain {
|
|||||||
images.push(Api::to_wgpu_img(image, device, format, resolution)?);
|
images.push(Api::to_wgpu_img(image, device, format, resolution)?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(XrSwapchainImages(images.into()))
|
Ok(OXrSwapchainImages(images.into()))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deref, Clone, Resource)]
|
/// Stores the generated swapchain images.
|
||||||
pub struct XrStage(pub Arc<openxr::Space>);
|
|
||||||
|
|
||||||
#[derive(Debug, Deref, Resource, Clone)]
|
#[derive(Debug, Deref, Resource, Clone)]
|
||||||
pub struct XrSwapchainImages(pub Arc<Vec<wgpu::Texture>>);
|
pub struct OXrSwapchainImages(pub Arc<Vec<wgpu::Texture>>);
|
||||||
|
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Deref, DerefMut, Resource, ExtractResource)]
|
/// Thread safe wrapper around [openxr::Space] representing the stage.
|
||||||
pub struct XrTime(pub openxr::Time);
|
#[derive(Deref, Clone, Resource)]
|
||||||
|
pub struct OXrStage(pub Arc<openxr::Space>);
|
||||||
|
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Resource)]
|
/// Stores the latest generated [OXrViews]
|
||||||
pub struct XrSwapchainInfo {
|
#[derive(Clone, Resource, ExtractResource, Deref, DerefMut)]
|
||||||
pub format: wgpu::TextureFormat,
|
pub struct OXrViews(pub Vec<openxr::View>);
|
||||||
pub resolution: UVec2,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/// Wrapper around [openxr::SystemId] to allow it to be stored as a resource.
|
||||||
#[derive(Debug, Copy, Clone, Deref, Default, Eq, PartialEq, Ord, PartialOrd, Hash, Resource)]
|
#[derive(Debug, Copy, Clone, Deref, Default, Eq, PartialEq, Ord, PartialOrd, Hash, Resource)]
|
||||||
pub struct XrSystemId(pub openxr::SystemId);
|
pub struct OXrSystemId(pub openxr::SystemId);
|
||||||
|
|
||||||
|
/// Resource storing graphics info for the currently running session.
|
||||||
#[derive(Clone, Copy, Resource)]
|
#[derive(Clone, Copy, Resource)]
|
||||||
pub struct XrGraphicsInfo {
|
pub struct OXrGraphicsInfo {
|
||||||
pub blend_mode: EnvironmentBlendMode,
|
pub blend_mode: EnvironmentBlendMode,
|
||||||
pub resolution: UVec2,
|
pub resolution: UVec2,
|
||||||
pub format: wgpu::TextureFormat,
|
pub format: wgpu::TextureFormat,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Resource, ExtractResource, Deref, DerefMut)]
|
|
||||||
pub struct XrViews(pub Vec<openxr::View>);
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
/// This is used to store information from startup that is needed to create the session after the instance has been created.
|
/// This is used to store information from startup that is needed to create the session after the instance has been created.
|
||||||
pub struct XrSessionCreateInfo {
|
pub struct SessionConfigInfo {
|
||||||
/// List of blend modes the openxr session can use. If [None], pick the first available blend mode.
|
/// List of blend modes the openxr session can use. If [None], pick the first available blend mode.
|
||||||
pub blend_modes: Option<Vec<EnvironmentBlendMode>>,
|
pub blend_modes: Option<Vec<EnvironmentBlendMode>>,
|
||||||
/// List of formats the openxr session can use. If [None], pick the first available format
|
/// List of formats the openxr session can use. If [None], pick the first available format
|
||||||
@@ -278,13 +295,13 @@ pub struct XrSessionCreateInfo {
|
|||||||
/// List of resolutions that the openxr swapchain can use. If [None] pick the first available resolution.
|
/// List of resolutions that the openxr swapchain can use. If [None] pick the first available resolution.
|
||||||
pub resolutions: Option<Vec<UVec2>>,
|
pub resolutions: Option<Vec<UVec2>>,
|
||||||
/// Graphics info used to create a session.
|
/// Graphics info used to create a session.
|
||||||
pub graphics_info: XrSessionGraphicsInfo,
|
pub graphics_info: SessionCreateInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Resource, Clone, Default)]
|
#[derive(Resource, Clone, Default)]
|
||||||
pub struct XrSessionStarted(Arc<AtomicBool>);
|
pub struct OXrSessionStarted(Arc<AtomicBool>);
|
||||||
|
|
||||||
impl XrSessionStarted {
|
impl OXrSessionStarted {
|
||||||
pub fn set(&self, val: bool) {
|
pub fn set(&self, val: bool) {
|
||||||
self.0.store(val, Ordering::SeqCst);
|
self.0.store(val, Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
@@ -294,36 +311,14 @@ impl XrSessionStarted {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The calculated display time for the app. Passed through the pipeline.
|
||||||
|
#[derive(Copy, Clone, Eq, PartialEq, Deref, DerefMut, Resource, ExtractResource)]
|
||||||
|
pub struct OXrTime(pub openxr::Time);
|
||||||
|
|
||||||
|
/// The root transform's global position for late latching in the render world.
|
||||||
#[derive(ExtractResource, Resource, Clone, Copy, Default)]
|
#[derive(ExtractResource, Resource, Clone, Copy, Default)]
|
||||||
pub struct XrRootTransform(pub GlobalTransform);
|
pub struct OXrRootTransform(pub GlobalTransform);
|
||||||
|
|
||||||
#[derive(ExtractResource, Resource, Clone, Copy, Default, Deref, DerefMut, PartialEq)]
|
#[derive(ExtractResource, Resource, Clone, Copy, Default, Deref, DerefMut, PartialEq)]
|
||||||
/// This is inserted into the world to signify if the session should be cleaned up.
|
/// This is inserted into the world to signify if the session should be cleaned up.
|
||||||
pub struct XrCleanupSession(pub bool);
|
pub struct OXrCleanupSession(pub bool);
|
||||||
|
|
||||||
#[derive(Resource, Clone, Deref)]
|
|
||||||
pub struct XrActionSet(#[deref] pub openxr::ActionSet, bool);
|
|
||||||
|
|
||||||
impl XrActionSet {
|
|
||||||
pub fn new(action_set: openxr::ActionSet) -> Self {
|
|
||||||
Self(action_set, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn attach(&mut self) {
|
|
||||||
self.1 = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_attached(&self) -> bool {
|
|
||||||
self.1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub enum TypedAction {
|
|
||||||
Bool(openxr::Action<bool>),
|
|
||||||
Float(openxr::Action<f32>),
|
|
||||||
Vector(openxr::Action<openxr::Vector2f>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Resource, Clone, Deref)]
|
|
||||||
pub struct XrActions(pub HashMap<TypeId, TypedAction>);
|
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
pub use crate::error::XrError;
|
use super::error::OXrError;
|
||||||
pub use crate::extensions::XrExtensions;
|
use super::graphics::{GraphicsExt, GraphicsType, GraphicsWrap};
|
||||||
use crate::graphics::GraphicsExt;
|
|
||||||
|
|
||||||
pub use openxr::{
|
pub use crate::oxr::exts::OXrExtensions;
|
||||||
ApiLayerProperties, EnvironmentBlendMode, SwapchainCreateFlags, SwapchainUsageFlags,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, XrError>;
|
pub use openxr::{EnvironmentBlendMode, SwapchainCreateFlags, SwapchainUsageFlags};
|
||||||
|
|
||||||
|
pub type Result<T> = std::result::Result<T, OXrError>;
|
||||||
|
|
||||||
|
/// A container for all required graphics objects needed for a bevy app.
|
||||||
pub struct WgpuGraphics(
|
pub struct WgpuGraphics(
|
||||||
pub wgpu::Device,
|
pub wgpu::Device,
|
||||||
pub wgpu::Queue,
|
pub wgpu::Queue,
|
||||||
@@ -18,11 +18,13 @@ pub struct WgpuGraphics(
|
|||||||
pub wgpu::Instance,
|
pub wgpu::Instance,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// A version number that can be stored inside of a u32
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||||
pub struct Version(pub u8, pub u8, pub u16);
|
pub struct Version(pub u8, pub u8, pub u16);
|
||||||
|
|
||||||
impl Version {
|
impl Version {
|
||||||
pub const BEVY: Self = Self(0, 12, 1);
|
/// Bevy's version number
|
||||||
|
pub const BEVY: Self = Self(0, 13, 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;
|
||||||
@@ -31,21 +33,29 @@ impl Version {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Info needed about an app for OpenXR
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct AppInfo {
|
pub struct AppInfo {
|
||||||
pub name: Cow<'static, str>,
|
pub name: Cow<'static, str>,
|
||||||
pub version: Version,
|
pub version: Version,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AppInfo {
|
||||||
|
/// The default app info for a generic bevy app
|
||||||
|
pub const BEVY: Self = Self {
|
||||||
|
name: Cow::Borrowed("Bevy"),
|
||||||
|
version: Version::BEVY,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for AppInfo {
|
impl Default for AppInfo {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self::BEVY
|
||||||
name: "Bevy".into(),
|
|
||||||
version: Version::BEVY,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Info needed to create a swapchain.
|
||||||
|
/// This is an API agnostic version of [openxr::SwapchainCreateInfo] used for some of this library's functions
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct SwapchainCreateInfo {
|
pub struct SwapchainCreateInfo {
|
||||||
pub create_flags: SwapchainCreateFlags,
|
pub create_flags: SwapchainCreateFlags,
|
||||||
@@ -60,14 +70,14 @@ pub struct SwapchainCreateInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<G: GraphicsExt> TryFrom<SwapchainCreateInfo> for openxr::SwapchainCreateInfo<G> {
|
impl<G: GraphicsExt> TryFrom<SwapchainCreateInfo> for openxr::SwapchainCreateInfo<G> {
|
||||||
type Error = XrError;
|
type Error = OXrError;
|
||||||
|
|
||||||
fn try_from(value: SwapchainCreateInfo) -> Result<Self> {
|
fn try_from(value: SwapchainCreateInfo) -> Result<Self> {
|
||||||
Ok(openxr::SwapchainCreateInfo {
|
Ok(openxr::SwapchainCreateInfo {
|
||||||
create_flags: value.create_flags,
|
create_flags: value.create_flags,
|
||||||
usage_flags: value.usage_flags,
|
usage_flags: value.usage_flags,
|
||||||
format: G::from_wgpu_format(value.format)
|
format: G::from_wgpu_format(value.format)
|
||||||
.ok_or(XrError::UnsupportedTextureFormat(value.format))?,
|
.ok_or(OXrError::UnsupportedTextureFormat(value.format))?,
|
||||||
sample_count: value.sample_count,
|
sample_count: value.sample_count,
|
||||||
width: value.width,
|
width: value.width,
|
||||||
height: value.height,
|
height: value.height,
|
||||||
@@ -77,3 +87,12 @@ impl<G: GraphicsExt> TryFrom<SwapchainCreateInfo> for openxr::SwapchainCreateInf
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Info needed to create a session. Mostly contains graphics info.
|
||||||
|
/// This is an API agnostic version of [openxr::Graphics::SessionCreateInfo] used for some of this library's functions
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SessionCreateInfo(pub GraphicsWrap<Self>);
|
||||||
|
|
||||||
|
impl GraphicsType for SessionCreateInfo {
|
||||||
|
type Inner<G: GraphicsExt> = G::SessionCreateInfo;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user