Compare commits

..

10 Commits

Author SHA1 Message Date
7936b53a02 chore: cleanup
Some checks failed
CI / Check Ubuntu (push) Has been cancelled
CI / Check Windows (push) Has been cancelled
CI / Check Wasm (push) Has been cancelled
2026-02-19 22:01:09 +01:00
Schmarni
93fe370f12 chore: fix examples and bump version
Signed-off-by: Schmarni <marnistromer@gmail.com>
2026-02-12 07:05:33 +01:00
Schmarni
18879e9f06 chore: bump openxr crate version
Signed-off-by: Schmarni <marnistromer@gmail.com>
2026-02-12 06:51:20 +01:00
Schmarni
728581d8d7 fix(openxr): don't crash when failing to locate views
Signed-off-by: Schmarni <marnistromer@gmail.com>
2026-01-24 20:50:37 +01:00
Schmarni
337cfdb3f8 fix(openxr): fix frametime recovery on WiVRn (and probably other monado runtimes)
Signed-off-by: Schmarni <marnistromer@gmail.com>
2026-01-24 13:21:26 +01:00
WireWhiz
93523de2fd fix duplicate PhysicalDeviceMultiviewFeatures validation error (#201)
* fix duplicate PhysicalDeviceMultiviewFeatures validation error

* remove rogue debug log
2026-01-21 21:27:12 +01:00
Pierce Thompson
3974766f7a feat: Update to Bevy 0.18 (#202) 2026-01-21 21:14:48 +01:00
Schmarni
11bf9442e1 fix(bevy_xr_utils/example): fix tracking utils example
Signed-off-by: Schmarni <marnistromer@gmail.com>
2025-10-19 23:13:05 +02:00
Schmarni
c8e43c666f fix(ci): install wayland since bevy usesbit by default 2025-10-19 21:27:17 +02:00
Schmarni
d94c10c386 chore: bump version
Signed-off-by: Schmarni <marnistromer@gmail.com>
2025-10-19 17:25:06 +02:00
22 changed files with 2777 additions and 1441 deletions

View File

@@ -22,7 +22,7 @@ jobs:
- name: Install bevy dependencies
run: |
sudo apt-get update && sudo apt-get install -y \
g++ pkg-config libx11-dev libasound2-dev libudev-dev libopenxr-loader1 libopenxr-dev
g++ pkg-config libx11-dev libwayland-dev libasound2-dev libudev-dev libopenxr-loader1 libopenxr-dev
- name: Cache cargo dependencies
uses: Swatinem/rust-cache@v2

3631
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,39 +1,41 @@
[workspace.package]
version = "0.3.0"
version = "0.5.0"
edition = "2024"
description = "Community crate for XR in Bevy"
repository = "https://github.com/awtterpip/bevy_oxr"
license = "MIT/Apache-2.0"
[workspace]
resolver = "2"
resolver = "3"
members = ["crates/*", "crates/bevy_openxr/examples/android"]
[workspace.dependencies]
bevy = "0.17"
bevy_ecs = { version = "0.17.0", default-features = false }
bevy_math = { version = "0.17.0", default-features = false }
bevy_render = { version = "0.17.0", default-features = false }
bevy_core_pipeline = { version = "0.17.0", default-features = false }
bevy_window = { version = "0.17.0", default-features = false }
bevy_winit = { version = "0.17.0", default-features = false }
bevy_pbr = { version = "0.17.0", default-features = false }
bevy_app = { version = "0.17.0", default-features = false }
bevy_reflect = { version = "0.17.0", default-features = false }
bevy_log = { version = "0.17.0", default-features = false }
bevy_gizmos = { version = "0.17.0", default-features = false }
bevy_camera = { version = "0.17.0", default-features = false }
bevy_color = { version = "0.17.0", default-features = false }
bevy_transform = { version = "0.17.0", default-features = false }
bevy_derive = { version = "0.17.0", default-features = false }
bevy_platform = { version = "0.17.0", default-features = false }
bevy_mod_xr = { path = "crates/bevy_xr", version = "0.3.0" }
bevy_mod_openxr = { path = "crates/bevy_openxr", version = "0.3.0" }
bevy_xr_utils = { path = "crates/bevy_xr_utils", version = "0.3.0" }
openxr = "0.19.0"
bevy = { version = "0.18", features = ["debug"] }
bevy_ecs = { version = "0.18", default-features = false }
bevy_math = { version = "0.18", default-features = false }
bevy_render = { version = "0.18", default-features = false }
bevy_core_pipeline = { version = "0.18", default-features = false }
bevy_window = { version = "0.18", default-features = false }
bevy_winit = { version = "0.18", default-features = false }
bevy_pbr = { version = "0.18", default-features = false }
bevy_app = { version = "0.18", default-features = false }
bevy_reflect = { version = "0.18", default-features = false }
bevy_log = { version = "0.18", default-features = false }
bevy_gizmos = { version = "0.18", default-features = false }
bevy_camera = { version = "0.18", default-features = false }
bevy_color = { version = "0.18", default-features = false }
bevy_transform = { version = "0.18", default-features = false }
bevy_derive = { version = "0.18", default-features = false }
bevy_platform = { version = "0.18", default-features = false }
bevy_mod_xr = { path = "crates/bevy_xr", version = "0.5.0" }
bevy_mod_openxr = { path = "crates/bevy_openxr", version = "0.5.0" }
bevy_xr_utils = { path = "crates/bevy_xr_utils", version = "0.5.0" }
openxr = "0.21.1"
thiserror = "2.0.3"
wgpu = "26"
wgpu-hal = "26"
wgpu = "27"
wgpu-hal = "27"
# Enable a small amount of optimization in the dev profile.
[profile.dev]

View File

@@ -19,7 +19,7 @@ fn main() {
.add_plugins(bevy_mod_openxr::features::fb_passthrough::OxrFbPassthroughPlugin)
.add_systems(Startup, setup)
.add_systems(Update, modify_camera)
.insert_resource(AmbientLight {
.insert_resource(GlobalAmbientLight {
color: Default::default(),
brightness: 500.0,
affects_lightmapped_meshes: false,

View File

@@ -119,7 +119,7 @@ fn spawn_hands(
actions
.left
.create_space(
session.deref().deref().clone(),
session.deref().deref(),
openxr::Path::NULL,
Posef::IDENTITY,
)

View File

@@ -10,7 +10,7 @@ fn main() -> AppExit {
.add_plugins(bevy_mod_xr::hand_debug_gizmos::HandGizmosPlugin)
.add_systems(Startup, setup)
.add_systems(Update, handle_input)
.insert_resource(AmbientLight::default())
.insert_resource(GlobalAmbientLight::default())
.run()
}

View File

@@ -2,3 +2,15 @@
mod openxr;
#[cfg(not(target_family = "wasm"))]
pub use openxr::*;
pub mod prelude {
pub use crate::{
action_binding::OxrSendActionBindings, action_binding::OxrSuggestActionBinding,
action_set_attaching::OxrAttachActionSet, action_set_syncing::OxrActionSetSyncSet,
action_set_syncing::OxrSyncActionSet, add_xr_plugins, exts::OxrExtensions,
features::handtracking::HandTrackingPlugin, init::OxrInitPlugin, openxr_session_running,
resources::OxrInstance, resources::OxrSessionConfig, session::OxrSession,
spaces::OxrSpaceExt,
};
pub use openxr::EnvironmentBlendMode;
}

View File

@@ -1,5 +1,11 @@
use std::{
ffi::CStr,
ops::{BitAnd, BitOr},
};
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::resource::Resource;
use bevy_log::error;
use openxr::ExtensionSet;
#[derive(Clone, Debug, Eq, PartialEq, Deref, DerefMut, Resource)]
@@ -36,7 +42,41 @@ impl OxrExtensions {
}
/// returns true if all of the extensions enabled are also available in `available_exts`
pub fn is_available(&self, available_exts: &OxrExtensions) -> bool {
self.clone() & available_exts.clone() == *self
self.0.intersection(available_exts) == self.0
}
/// Returns any extensions needed by `required_exts` that aren't available in `self`
pub fn unavailable_exts(&self, required_exts: &Self) -> Vec<String> {
required_exts
.difference(self)
.names()
.into_iter()
.filter_map(|v| {
CStr::from_bytes_with_nul(v)
.inspect_err(|err| error!("failed to convert openxr ext name to CStr: {err}"))
.ok()
})
.filter_map(|v| {
v.to_str()
.inspect_err(|err| error!("openxr ext name is not valid utf8: {err}"))
.ok()
})
.map(|v| v.to_string())
.collect()
}
}
impl BitOr for OxrExtensions {
type Output = Self;
// this is horribly slow, but doesn't require a bunch of code duplication
fn bitor(self, rhs: Self) -> Self::Output {
Self(self.0.names().into_iter().chain(rhs.names()).collect())
}
}
impl BitAnd for OxrExtensions {
type Output = Self;
fn bitand(self, rhs: Self) -> Self::Output {
Self(self.intersection(&rhs))
}
}
impl From<ExtensionSet> for OxrExtensions {
@@ -56,293 +96,3 @@ impl Default for OxrExtensions {
Self(exts)
}
}
macro_rules! unavailable_exts {
(
$exts:ty;
$(
$(
#[$meta:meta]
)*
$ident:ident
),*
$(,)?
) => {
impl $exts {
/// Returns any extensions needed by `required_exts` that aren't available in `self`
pub fn unavailable_exts(&self, required_exts: &Self) -> Vec<std::borrow::Cow<'static, str>> {
let mut exts = vec![];
$(
$(
#[$meta]
)*
if required_exts.0.$ident && !self.0.$ident {
exts.push(std::borrow::Cow::Borrowed(stringify!($ident)))
}
)*
for ext in required_exts.0.other.iter() {
if !self.0.other.contains(ext) {
exts.push(std::borrow::Cow::Owned(ext.clone()))
}
}
exts
}
}
};
}
macro_rules! bitor {
(
$exts:ty;
$(
$(
#[$meta:meta]
)*
$ident:ident
),*
$(,)?
) => {
impl std::ops::BitOr for $exts {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
let mut out = ExtensionSet::default();
$(
$(
#[$meta]
)*
{
out.$ident = self.0.$ident || rhs.0.$ident;
}
)*
out.other = self.0.other;
for ext in rhs.0.other {
if !out.other.contains(&ext) {
out.other.push(ext);
}
}
Self(out)
}
}
};
}
macro_rules! bitand {
(
$exts:ty;
$(
$(
#[$meta:meta]
)*
$ident:ident
),*
$(,)?
) => {
impl std::ops::BitAnd for $exts {
type Output = Self;
fn bitand(self, rhs: Self) -> Self::Output {
let mut out = ExtensionSet::default();
$(
$(
#[$meta]
)*
{
out.$ident = self.0.$ident && rhs.0.$ident;
}
)*
for ext in self.0.other {
if rhs.0.other.contains(&ext) {
out.other.push(ext);
}
}
Self(out)
}
}
};
}
macro_rules! impl_ext {
(
$(
$macro:ident
),*
) => {
$(
$macro! {
OxrExtensions;
almalence_digital_lens_control,
bd_controller_interaction,
epic_view_configuration_fov,
ext_performance_settings,
ext_thermal_query,
ext_debug_utils,
ext_eye_gaze_interaction,
ext_view_configuration_depth_range,
ext_conformance_automation,
ext_hand_tracking,
#[cfg(windows)]
ext_win32_appcontainer_compatible,
ext_dpad_binding,
ext_hand_joints_motion_range,
ext_samsung_odyssey_controller,
ext_hp_mixed_reality_controller,
ext_palm_pose,
ext_uuid,
ext_hand_interaction,
ext_active_action_set_priority,
ext_local_floor,
ext_hand_tracking_data_source,
ext_plane_detection,
ext_future,
ext_user_presence,
fb_composition_layer_image_layout,
fb_composition_layer_alpha_blend,
#[cfg(target_os = "android")]
fb_android_surface_swapchain_create,
fb_swapchain_update_state,
fb_composition_layer_secure_content,
fb_body_tracking,
fb_display_refresh_rate,
fb_color_space,
fb_hand_tracking_mesh,
fb_hand_tracking_aim,
fb_hand_tracking_capsules,
fb_spatial_entity,
fb_foveation,
fb_foveation_configuration,
fb_keyboard_tracking,
fb_triangle_mesh,
fb_passthrough,
fb_render_model,
fb_spatial_entity_query,
fb_spatial_entity_storage,
fb_foveation_vulkan,
#[cfg(target_os = "android")]
fb_swapchain_update_state_android_surface,
fb_swapchain_update_state_opengl_es,
fb_swapchain_update_state_vulkan,
fb_touch_controller_pro,
fb_spatial_entity_sharing,
fb_space_warp,
fb_haptic_amplitude_envelope,
fb_scene,
fb_scene_capture,
fb_spatial_entity_container,
fb_face_tracking,
fb_eye_tracking_social,
fb_passthrough_keyboard_hands,
fb_composition_layer_settings,
fb_touch_controller_proximity,
fb_haptic_pcm,
fb_composition_layer_depth_test,
fb_spatial_entity_storage_batch,
fb_spatial_entity_user,
fb_face_tracking2,
htc_vive_cosmos_controller_interaction,
htc_facial_tracking,
htc_vive_focus3_controller_interaction,
htc_hand_interaction,
htc_vive_wrist_tracker_interaction,
htc_passthrough,
htc_foveation,
htc_anchor,
huawei_controller_interaction,
#[cfg(target_os = "android")]
khr_android_thread_settings,
#[cfg(target_os = "android")]
khr_android_surface_swapchain,
khr_composition_layer_cube,
#[cfg(target_os = "android")]
khr_android_create_instance,
khr_composition_layer_depth,
khr_vulkan_swapchain_format_list,
khr_composition_layer_cylinder,
khr_composition_layer_equirect,
khr_opengl_enable,
khr_opengl_es_enable,
khr_vulkan_enable,
#[cfg(windows)]
khr_d3d11_enable,
#[cfg(windows)]
khr_d3d12_enable,
khr_visibility_mask,
khr_composition_layer_color_scale_bias,
#[cfg(windows)]
khr_win32_convert_performance_counter_time,
khr_convert_timespec_time,
khr_loader_init,
#[cfg(target_os = "android")]
khr_loader_init_android,
khr_vulkan_enable2,
khr_composition_layer_equirect2,
khr_binding_modification,
khr_swapchain_usage_input_attachment_bit,
khr_locate_spaces,
khr_maintenance1,
meta_foveation_eye_tracked,
meta_local_dimming,
meta_passthrough_preferences,
meta_virtual_keyboard,
meta_vulkan_swapchain_create_info,
meta_performance_metrics,
meta_headset_id,
meta_recommended_layer_resolution,
meta_passthrough_color_lut,
meta_spatial_entity_mesh,
meta_automatic_layer_filter,
meta_touch_controller_plus,
meta_environment_depth,
ml_ml2_controller_interaction,
ml_frame_end_info,
ml_global_dimmer,
ml_compat,
ml_marker_understanding,
ml_localization_map,
ml_user_calibration,
mnd_headless,
mnd_swapchain_usage_input_attachment_bit,
msft_unbounded_reference_space,
msft_spatial_anchor,
msft_spatial_graph_bridge,
msft_hand_interaction,
msft_hand_tracking_mesh,
msft_secondary_view_configuration,
msft_first_person_observer,
msft_controller_model,
#[cfg(windows)]
msft_perception_anchor_interop,
#[cfg(windows)]
msft_holographic_window_attachment,
msft_composition_layer_reprojection,
msft_spatial_anchor_persistence,
#[cfg(target_os = "android")]
oculus_android_session_state_enable,
oculus_audio_device_guid,
oculus_external_camera,
oppo_controller_interaction,
qcom_tracking_optimization_settings,
ultraleap_hand_tracking_forearm,
valve_analog_threshold,
varjo_quad_views,
varjo_foveated_rendering,
varjo_composition_layer_depth_test,
varjo_environment_depth_estimation,
varjo_marker_tracking,
varjo_view_offset,
varjo_xr4_controller_interaction,
yvr_controller_interaction,
extx_overlay,
mndx_egl_enable,
mndx_force_feedback_curl,
htcx_vive_tracker_interaction,
}
)*
};
}
impl_ext!(bitor, bitand, unavailable_exts);

View File

@@ -69,7 +69,7 @@ pub fn insert_passthrough(world: &mut World) {
pub fn resume_passthrough(
passthrough: Res<OxrPassthrough>,
passthrough_layer: Res<OxrPassthroughLayer>,
passthrough_layer: Res<OxrPassthroughLayerFB>,
) {
passthrough.start().unwrap();
passthrough_layer.resume().unwrap();
@@ -77,7 +77,7 @@ pub fn resume_passthrough(
pub fn pause_passthrough(
passthrough: Res<OxrPassthrough>,
passthrough_layer: Res<OxrPassthroughLayer>,
passthrough_layer: Res<OxrPassthroughLayerFB>,
) {
passthrough_layer.pause().unwrap();
passthrough.pause().unwrap();
@@ -87,7 +87,7 @@ pub fn create_passthrough(
session: &OxrSession,
flags: openxr::PassthroughFlagsFB,
purpose: openxr::PassthroughLayerPurposeFB,
) -> OxrResult<(OxrPassthrough, OxrPassthroughLayer)> {
) -> OxrResult<(OxrPassthrough, OxrPassthroughLayerFB)> {
let passthrough = session.create_passthrough(flags)?;
let passthrough_layer = session.create_passthrough_layer(&passthrough, purpose)?;

View File

@@ -14,6 +14,7 @@ use crate::{
types::{AppInfo, OxrExtensions, Result, WgpuGraphics},
};
#[allow(clippy::missing_safety_doc)]
/// 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 {
/// Wrap the graphics specific type into the [GraphicsWrap] enum

View File

@@ -1,14 +1,15 @@
use std::ffi::{c_void, CStr, CString};
use std::ffi::{CStr, CString, c_void};
use std::str::FromStr;
use ash::vk::{self, Handle};
use bevy_log::{debug, error};
use bevy_math::UVec2;
use openxr::{sys, Version};
use wgpu::{InstanceFlags, MemoryBudgetThresholds};
use openxr::sys::Handle as _;
use openxr::{Version, sys};
use wgpu::TextureUses;
use wgpu_hal::api::Vulkan;
use wgpu::{ExperimentalFeatures, InstanceFlags, MemoryBudgetThresholds};
use wgpu_hal::Api;
use wgpu_hal::api::Vulkan;
use super::{GraphicsExt, GraphicsType, GraphicsWrap, OxrManualGraphicsConfig};
use crate::error::OxrError;
@@ -54,14 +55,13 @@ unsafe impl GraphicsExt for openxr::Vulkan {
) -> Result<wgpu::Texture> {
let color_image = vk::Image::from_raw(color_image);
let wgpu_hal_texture = unsafe {
let hal_dev =
device
.as_hal::<wgpu_hal::vulkan::Api>()
.ok_or(OxrError::GraphicsBackendMismatch {
item: "Wgpu Device",
backend: "unknown",
expected_backend: "vulkan",
})?;
let hal_dev = device.as_hal::<wgpu_hal::vulkan::Api>().ok_or(
OxrError::GraphicsBackendMismatch {
item: "Wgpu Device",
backend: "unknown",
expected_backend: "vulkan",
},
)?;
hal_dev.texture_from_raw(
color_image,
&wgpu_hal::TextureDescriptor {
@@ -207,16 +207,8 @@ unsafe impl GraphicsExt for openxr::Vulkan {
system_id,
};
let mut out = sys::Session::NULL;
cvt(unsafe { (instance.fp().create_session)(
instance.as_raw(),
&info,
&mut out,
) })?;
Ok(unsafe { openxr::Session::from_raw(
instance.clone(),
out,
Box::new(()),
) })
cvt(unsafe { (instance.fp().create_session)(instance.as_raw(), &info, &mut out) })?;
Ok(unsafe { openxr::Session::from_raw(instance.clone(), out, Box::new(())) })
}
fn init_fallback_graphics(
@@ -300,6 +292,7 @@ fn get_extensions(
instance_exts.extend(&cfg.vk_instance_exts);
device_exts.extend(&cfg.vk_device_exts);
}
instance_exts.dedup();
device_exts.dedup();
@@ -392,12 +385,13 @@ fn init_from_instance_and_dev(
let wgpu_features = wgpu_exposed_adapter.features;
debug!("wgpu features: {wgpu_features:#?}");
let enabled_extensions = wgpu_exposed_adapter
let mut enabled_extensions = wgpu_exposed_adapter
.adapter
.required_device_extensions(wgpu_features);
enabled_extensions.extend(device_exts);
enabled_extensions.dedup();
let (wgpu_open_device, vk_device_ptr, queue_family_index) = {
let extensions_cchar: Vec<_> = device_exts.iter().map(|s| s.as_ptr()).collect();
let mut enabled_phd_features = wgpu_exposed_adapter
.adapter
.physical_device_features(&enabled_extensions, wgpu_features);
@@ -406,17 +400,13 @@ fn init_from_instance_and_dev(
.queue_family_index(family_index)
.queue_priorities(&[1.0]);
let family_infos = [family_info];
let mut physical_device_multiview_features = vk::PhysicalDeviceMultiviewFeatures {
multiview: vk::TRUE,
..Default::default()
};
let ext_names = enabled_extensions
.iter()
.map(|e| e.as_ptr())
.collect::<Vec<_>>();
let info = enabled_phd_features
.add_to_device_create(
vk::DeviceCreateInfo::default()
.queue_create_infos(&family_infos)
.push_next(&mut physical_device_multiview_features),
)
.enabled_extension_names(&extensions_cchar);
.add_to_device_create(vk::DeviceCreateInfo::default().queue_create_infos(&family_infos))
.enabled_extension_names(&ext_names);
let vk_device = create_dev(&info)?;
let vk_device_ptr = vk_device.handle().as_raw() as *const c_void;
@@ -453,6 +443,7 @@ fn init_from_instance_and_dev(
required_limits: limits,
memory_hints: wgpu::MemoryHints::Performance,
trace: wgpu::Trace::Off,
experimental_features: ExperimentalFeatures::enabled(),
},
)
}?;
@@ -475,11 +466,7 @@ fn init_from_instance_and_dev(
}
fn cvt(x: sys::Result) -> openxr::Result<sys::Result> {
if x.into_raw() >= 0 {
Ok(x)
} else {
Err(x)
}
if x.into_raw() >= 0 { Ok(x) } else { Err(x) }
}
fn vulkan_to_wgpu(format: vk::Format) -> Option<wgpu::TextureFormat> {
@@ -811,6 +798,7 @@ fn wgpu_to_vulkan(format: wgpu::TextureFormat) -> Option<vk::Format> {
Tf::EacR11Snorm => F::EAC_R11_SNORM_BLOCK,
Tf::EacRg11Unorm => F::EAC_R11G11_UNORM_BLOCK,
Tf::EacRg11Snorm => F::EAC_R11G11_SNORM_BLOCK,
Tf::P010 => F::G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16,
Tf::Astc { block, channel } => match channel {
AstcChannel::Unorm => match block {
AstcBlock::B4x4 => F::ASTC_4X4_UNORM_BLOCK,

View File

@@ -2,7 +2,7 @@ use std::mem;
use bevy_ecs::world::World;
use bevy_mod_xr::spaces::{XrPrimaryReferenceSpace, XrSpace};
use openxr::{sys, CompositionLayerFlags, Fovf, Posef, Rect2Di};
use openxr::{CompositionLayerFlags, Fovf, Posef, Rect2Di, sys};
use crate::graphics::graphics_match;
use crate::resources::*;
@@ -65,8 +65,8 @@ impl LayerProvider for ProjectionLayer {
impl LayerProvider for PassthroughLayer {
fn get(&self, world: &World) -> Option<Box<dyn CompositionLayer<'_>>> {
Some(Box::new(
CompositionLayerPassthrough::new()
.layer_handle(world.get_resource::<OxrPassthroughLayer>()?)
CompositionLayerPassthroughFB::new()
.layer_handle(world.get_resource::<OxrPassthroughLayerFB>()?)
.layer_flags(CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA),
))
}
@@ -236,16 +236,16 @@ impl Default for CompositionLayerProjection<'_> {
Self::new()
}
}
pub struct CompositionLayerPassthrough {
pub struct CompositionLayerPassthroughFB {
inner: sys::CompositionLayerPassthroughFB,
}
impl Default for CompositionLayerPassthrough {
impl Default for CompositionLayerPassthroughFB {
fn default() -> Self {
Self::new()
}
}
impl CompositionLayerPassthrough {
impl CompositionLayerPassthroughFB {
#[inline]
pub const fn new() -> Self {
Self {
@@ -256,7 +256,7 @@ impl CompositionLayerPassthrough {
}
}
#[inline]
pub fn layer_handle(mut self, layer_handle: &OxrPassthroughLayer) -> Self {
pub fn layer_handle(mut self, layer_handle: &OxrPassthroughLayerFB) -> Self {
self.inner.layer_handle = *layer_handle.inner();
self
}
@@ -266,7 +266,7 @@ impl CompositionLayerPassthrough {
self
}
}
unsafe impl<'a> CompositionLayer<'a> for CompositionLayerPassthrough {
unsafe impl<'a> CompositionLayer<'a> for CompositionLayerPassthroughFB {
fn swapchain(&self) -> Option<&'a OxrSwapchain> {
None
}

View File

@@ -21,7 +21,7 @@ pub struct OxrReferenceSpacePlugin {
impl Default for OxrReferenceSpacePlugin {
fn default() -> Self {
Self {
default_primary_ref_space: openxr::ReferenceSpaceType::STAGE,
default_primary_ref_space: openxr::ReferenceSpaceType::LOCAL,
}
}
}

View File

@@ -7,24 +7,24 @@ use bevy_ecs::{
system::{Commands, Query, Res, ResMut},
world::World,
};
use bevy_log::{debug_span, error, info};
use bevy_log::{debug_span, error, info, warn};
use bevy_render::{
Render, RenderApp,
extract_resource::ExtractResourcePlugin,
pipelined_rendering::PipelinedRenderingPlugin,
texture::{ManualTextureView, ManualTextureViews},
view::ExtractedView,
Render, RenderApp,
};
use bevy_mod_xr::{
camera::{calculate_projection, Fov, XrCamera, XrProjection, XrViewInit},
camera::{Fov, XrCamera, XrProjection, XrViewInit, calculate_projection},
session::{
XrFirst, XrHandleEvents, XrPreDestroySession, XrRenderSystems, XrRootTransform,
XrSessionCreated,
},
spaces::XrPrimaryReferenceSpace,
};
use bevy_transform::{components::Transform, TransformSystems};
use bevy_transform::{TransformSystems, components::Transform};
use openxr::ViewStateFlags;
use crate::{helper_traits::ToTransform as _, init::should_run_frame_loop, resources::*};
@@ -158,10 +158,7 @@ pub fn init_views<const SPAWN_CAMERAS: bool>(
add_texture_view(&mut manual_texture_views, temp_tex, &graphics_info, index);
if SPAWN_CAMERAS {
commands.spawn((
Camera {
target: RenderTarget::TextureView(view_handle),
..Default::default()
},
RenderTarget::TextureView(view_handle),
XrCamera(index),
Projection::custom(XrProjection::default()),
// NoFrustumCulling,
@@ -177,14 +174,14 @@ pub fn wait_frame(mut frame_waiter: ResMut<OxrFrameWaiter>, mut commands: Comman
pub fn update_cameras(
frame_state: Res<OxrFrameState>,
mut cameras: Query<(&mut Camera, &XrCamera)>,
mut cameras: Query<(&mut Camera, &mut RenderTarget, &XrCamera)>,
) {
for (mut camera, xr_camera) in &mut cameras {
camera.target =
for (_, mut target, xr_camera) in &mut cameras {
*target =
RenderTarget::TextureView(ManualTextureViewHandle(XR_TEXTURE_INDEX + xr_camera.0));
}
if frame_state.is_changed() {
for (mut camera, _) in &mut cameras {
for (mut camera, _, _) in &mut cameras {
camera.is_active = frame_state.should_render
}
}
@@ -205,13 +202,16 @@ pub fn locate_views(
} else {
frame_state.predicted_display_time
};
let (flags, xr_views) = session
let Ok((flags, xr_views)) = session
.locate_views(
openxr::ViewConfigurationType::PRIMARY_STEREO,
time,
&ref_space,
)
.expect("Failed to locate views");
.inspect_err(|err| warn!("failed to locate views: {err}"))
else {
return;
};
match (
flags & ViewStateFlags::ORIENTATION_VALID == ViewStateFlags::ORIENTATION_VALID,
@@ -286,7 +286,11 @@ pub fn insert_texture_views(
mut swapchain: ResMut<OxrSwapchain>,
mut manual_texture_views: ResMut<ManualTextureViews>,
graphics_info: Res<OxrCurrentSessionConfig>,
frame_state: Res<OxrFrameState>,
) {
if !frame_state.should_render {
return;
}
let index = swapchain.acquire_image().expect("Failed to acquire image");
let image = &swapchain_images[index as usize];
@@ -296,10 +300,12 @@ pub fn insert_texture_views(
}
}
pub fn wait_image(mut swapchain: ResMut<OxrSwapchain>) {
swapchain
.wait_image(openxr::Duration::INFINITE)
.expect("Failed to wait image");
pub fn wait_image(mut swapchain: ResMut<OxrSwapchain>, state: Res<OxrFrameState>) {
if state.should_render {
swapchain
.wait_image(openxr::Duration::INFINITE)
.expect("Failed to wait image");
}
}
pub fn add_texture_view(
@@ -317,7 +323,7 @@ pub fn add_texture_view(
let view = ManualTextureView {
texture_view: view.into(),
size: info.resolution,
format: info.format,
view_format: info.format,
};
let handle = ManualTextureViewHandle(XR_TEXTURE_INDEX + index);
manual_texture_views.insert(handle, view);
@@ -328,7 +334,10 @@ pub fn begin_frame(mut frame_stream: ResMut<OxrFrameStream>) {
frame_stream.begin().expect("Failed to begin frame");
}
pub fn release_image(mut swapchain: ResMut<OxrSwapchain>) {
pub fn release_image(mut swapchain: ResMut<OxrSwapchain>, state: Res<OxrFrameState>) {
if !state.should_render {
return;
}
#[cfg(target_os = "android")]
{
let ctx = ndk_context::android_context();

View File

@@ -47,7 +47,7 @@ impl OxrEntry {
application_version: app_info.version.to_u32(),
engine_name: "Bevy",
engine_version: Version::BEVY.to_u32(),
api_version: openxr::Version::new(1, 0, 34),
api_version: openxr::Version::new(1, 1, 54),
},
&required_exts.into(),
layers,
@@ -322,11 +322,11 @@ impl OxrPassthrough {
/// Wrapper around [`openxr::Passthrough`].
///
/// Used to create a [`CompositionLayerPassthrough`](crate::layer_builder::CompositionLayerPassthrough), and to [`pause`](openxr::PassthroughLayer::pause) or [`resume`](openxr::PassthroughLayer::resume) rendering of the passthrough layer.
/// Used to create a [`CompositionLayerPassthrough`](crate::layer_builder::CompositionLayerPassthrough), and to [`pause`](openxr::PassthroughLayerFB::pause) or [`resume`](openxr::PassthroughLayerFB::resume) rendering of the passthrough layer.
///
/// See [`openxr::PassthroughLayer`] for available methods.
/// See [`openxr::PassthroughLayerFB`] for available methods.
#[derive(Resource, Deref, DerefMut)]
pub struct OxrPassthroughLayer(pub openxr::PassthroughLayer);
pub struct OxrPassthroughLayerFB(pub openxr::PassthroughLayerFB);
#[derive(Resource, Deref, DerefMut, Default)]
pub struct OxrRenderLayers(pub Vec<Box<dyn LayerProvider + Send + Sync>>);

View File

@@ -1,7 +1,7 @@
use std::ffi::c_void;
use crate::next_chain::{OxrNextChain, OxrNextChainStructBase, OxrNextChainStructProvider};
use crate::resources::{OxrPassthrough, OxrPassthroughLayer, OxrSwapchain};
use crate::resources::{OxrPassthrough, OxrPassthroughLayerFB, OxrSwapchain};
use crate::types::{Result, SwapchainCreateInfo};
use bevy_derive::Deref;
use bevy_ecs::resource::Resource;
@@ -91,8 +91,8 @@ impl OxrSession {
&self,
passthrough: &OxrPassthrough,
purpose: openxr::PassthroughLayerPurposeFB,
) -> Result<OxrPassthroughLayer> {
Ok(OxrPassthroughLayer(graphics_match! {
) -> Result<OxrPassthroughLayerFB> {
Ok(OxrPassthroughLayerFB(graphics_match! {
&self.1;
session => session.create_passthrough_layer(&passthrough.0, passthrough.1, purpose)?
}))

View File

@@ -19,7 +19,7 @@ use bevy_platform::collections::hash_set::HashSet;
use bevy_transform::components::Transform;
use openxr::{
HAND_JOINT_COUNT, HandJointLocation, HandJointLocations, HandJointVelocities,
HandJointVelocity, ReferenceSpaceType, SpaceLocationFlags, SpaceVelocityFlags, sys,
HandJointVelocity, ReferenceSpaceType, SpaceLocationFlags, SpaceVelocityFlags, sys::{self, Handle as _},
};
use std::{mem::MaybeUninit, ptr, sync::Mutex};

View File

@@ -120,7 +120,7 @@ fn on_tracker_add(mut world: DeferredWorld, HookContext { entity, .. }: HookCont
if world
.entity(entity)
.get_components::<Has<Children>>()
.is_some_and(identity)
.is_ok_and(identity)
{
return;
}

View File

@@ -26,7 +26,7 @@ bevy.workspace = true
[target.'cfg(not(target_family = "wasm"))'.dependencies]
openxr.workspace = true
openxr_mndx_xdev_space = "0.1.0"
openxr_mndx_xdev_space = "0.2.0"
[lints.clippy]
too_many_arguments = "allow"

View File

@@ -19,9 +19,9 @@ fn main() {
.add_systems(Update, read_action_with_marker_component)
.add_systems(Update, handle_flight_input)
// Realtime lighting is expensive, use ambient light instead
.insert_resource(AmbientLight {
.insert_resource(GlobalAmbientLight {
brightness: 500.0,
..AmbientLight::default()
..GlobalAmbientLight::default()
})
.run();
}

View File

@@ -11,7 +11,7 @@ fn main() -> AppExit {
exts: {
let mut exts = OxrExtensions::default();
exts.enable_hand_tracking();
exts.other.push("XR_MNDX_xdev_space".to_string());
exts.other.push(c"XR_MNDX_xdev_space".to_bytes_with_nul().to_vec());
exts
},
..Default::default()

View File

@@ -3,9 +3,8 @@
use bevy::prelude::*;
use bevy_mod_openxr::add_xr_plugins;
use bevy_xr_utils::transform_utils::{self, SnapToPosition, SnapToRotation};
use bevy_xr_utils::xr_utils_actions::{
ActiveSet, XRUtilsAction, XRUtilsActionSet, XRUtilsActionState, XRUtilsActionSystems,
XRUtilsActionsPlugin, XRUtilsBinding,
use bevy_xr_utils::actions::{
ActionType, ActiveSet, XRUtilsAction, XRUtilsActionSet, XRUtilsActionState, XRUtilsActionSystems, XRUtilsActionsPlugin, XRUtilsBinding
};
fn main() -> AppExit {
@@ -27,9 +26,9 @@ fn main() -> AppExit {
Update,
send_recenter.after(XRUtilsActionSystems::SyncActionStates),
)
.insert_resource(AmbientLight {
.insert_resource(GlobalAmbientLight {
brightness: 500.0,
..AmbientLight::default()
..GlobalAmbientLight::default()
})
.run()
}
@@ -104,7 +103,7 @@ fn create_action_entities(mut commands: Commands) {
XRUtilsAction {
action_name: "face_red".into(),
localized_name: "face_red_localized".into(),
action_type: bevy_mod_xr::actions::ActionType::Bool,
action_type: ActionType::Bool,
},
FaceRedAction, //lets try a marker component
))
@@ -128,7 +127,7 @@ fn create_action_entities(mut commands: Commands) {
XRUtilsAction {
action_name: "center".into(),
localized_name: "center_localized".into(),
action_type: bevy_mod_xr::actions::ActionType::Bool,
action_type: ActionType::Bool,
},
Center, //lets try a marker component
))
@@ -150,7 +149,7 @@ fn create_action_entities(mut commands: Commands) {
fn send_look_at_red_cube_event(
mut action_query: Query<&XRUtilsActionState, With<FaceRedAction>>,
mut event_writer: EventWriter<SnapToRotation>,
mut event_writer: MessageWriter<SnapToRotation>,
) {
//now for the actual checking
for state in action_query.iter_mut() {
@@ -175,7 +174,7 @@ pub struct Center;
fn send_recenter(
mut action_query: Query<&XRUtilsActionState, With<Center>>,
mut event_writer: EventWriter<SnapToPosition>,
mut event_writer: MessageWriter<SnapToPosition>,
) {
//now for the actual checking
for state in action_query.iter_mut() {