Files
bevy_oxr/crates/bevy_openxr/src/openxr/spaces.rs
Schmarni 6003cc7ac6 add new XrSpace and impl that
Signed-off-by: Schmarni <marnistromer@gmail.com>
2024-06-27 00:32:03 +02:00

519 lines
17 KiB
Rust

use std::{mem::MaybeUninit, ptr, sync::Mutex};
use bevy::{prelude::*, utils::hashbrown::HashSet};
use bevy_xr::{
session::{session_available, session_running},
spaces::{
XrDestroySpace, XrPrimaryReferenceSpace, XrReferenceSpace, XrSpace, XrSpatialTransform,
},
types::XrPose,
};
use openxr::{
sys, HandJointLocation, HandJointLocations, HandJointVelocities, HandJointVelocity,
ReferenceSpaceType, SpaceLocationFlags, HAND_JOINT_COUNT,
};
use crate::{
helper_traits::{ToPosef, ToQuat, ToVec3},
init::{OxrHandleEvents, OxrLast},
resources::{OxrFrameState, OxrInstance, Pipelined},
session::OxrSession,
};
pub struct OxrSpatialPlugin;
impl Plugin for OxrSpatialPlugin {
fn build(&self, app: &mut App) {
app.add_event::<XrDestroySpace>();
app.add_systems(PreUpdate, update_spatial_transforms.run_if(session_running));
app.add_systems(Startup, patch_destroy_space.run_if(session_available));
app.add_systems(OxrLast, destroy_space_event.before(OxrHandleEvents));
}
}
fn destroy_space_event(instance: Res<OxrInstance>, mut events: EventReader<XrDestroySpace>) {
for space in events.read() {
unsafe {
(instance.fp().destroy_space)(space.as_raw_openxr_space());
}
}
}
pub static OXR_DO_NOT_CALL_DESTOY_SPACE_FOR: Mutex<Option<HashSet<u64>>> = Mutex::new(None);
pub static OXR_ORIGINAL_DESTOY_SPACE: Mutex<Option<openxr::sys::pfn::DestroySpace>> =
Mutex::new(None);
fn patch_destroy_space(instance: ResMut<OxrInstance>) {
OXR_DO_NOT_CALL_DESTOY_SPACE_FOR
.lock()
.unwrap()
.replace(HashSet::new());
let raw_instance_ptr = instance.fp() as *const _ as *mut openxr::raw::Instance;
unsafe {
OXR_ORIGINAL_DESTOY_SPACE
.lock()
.unwrap()
.replace((*raw_instance_ptr).destroy_space);
(*raw_instance_ptr).destroy_space = patched_destroy_space;
}
}
unsafe extern "system" fn patched_destroy_space(space: openxr::sys::Space) -> openxr::sys::Result {
if !OXR_DO_NOT_CALL_DESTOY_SPACE_FOR
.lock()
.unwrap()
.as_ref()
.unwrap()
.contains(&space.into_raw())
{
OXR_ORIGINAL_DESTOY_SPACE
.lock()
.unwrap()
.expect("has to be initialized")(space)
} else {
info!("Inject Worked, not destroying space");
openxr::sys::Result::SUCCESS
}
}
fn update_spatial_transforms(
session: Res<OxrSession>,
default_ref_space: Res<XrPrimaryReferenceSpace>,
pipelined: Option<Res<Pipelined>>,
frame_state: Res<OxrFrameState>,
mut query: Query<(
&mut Transform,
&XrSpatialTransform,
Option<&XrReferenceSpace>,
)>,
) {
for (mut transform, spatial, ref_space) in &mut query {
let ref_space = ref_space.unwrap_or(&default_ref_space);
if let Ok(space_location) = session.locate_space(
&spatial.space,
ref_space,
if pipelined.is_some() {
openxr::Time::from_nanos(
frame_state.predicted_display_time.as_nanos()
+ frame_state.predicted_display_period.as_nanos(),
)
} else {
frame_state.predicted_display_time
},
) {
if space_location
.location_flags
.contains(SpaceLocationFlags::POSITION_VALID)
{
transform.translation = spatial
.offset
.transform_point(space_location.pose.position.to_vec3())
}
if space_location
.location_flags
.contains(SpaceLocationFlags::ORIENTATION_VALID)
{
transform.rotation =
spatial.offset.rotation * space_location.pose.orientation.to_quat();
}
}
}
}
impl OxrSession {
pub fn create_action_space<T: openxr::ActionTy>(
&self,
action: &openxr::Action<T>,
subaction_path: openxr::Path,
pose_in_space: XrPose,
) -> openxr::Result<XrSpace> {
let info = sys::ActionSpaceCreateInfo {
ty: sys::ActionSpaceCreateInfo::TYPE,
next: ptr::null(),
action: action.as_raw(),
subaction_path,
pose_in_action_space: pose_in_space.to_posef(),
};
let mut out = sys::Space::NULL;
unsafe {
cvt((self.instance().fp().create_action_space)(
self.as_raw(),
&info,
&mut out,
))?;
Ok(XrSpace::from_raw(out.into_raw()))
}
}
pub fn create_reference_space(
&self,
ref_space_type: ReferenceSpaceType,
pose_in_ref_space: Transform,
) -> openxr::Result<XrReferenceSpace> {
let info = sys::ReferenceSpaceCreateInfo {
ty: sys::ReferenceSpaceCreateInfo::TYPE,
next: ptr::null(),
reference_space_type: ref_space_type,
pose_in_reference_space: pose_in_ref_space.to_posef(),
};
let mut out = sys::Space::NULL;
unsafe {
cvt((self.instance().fp().create_reference_space)(
self.as_raw(),
&info,
&mut out,
))?;
Ok(XrReferenceSpace(XrSpace::from_raw(out.into_raw())))
}
}
}
fn locate_space(
instance: &openxr::Instance,
space: &XrSpace,
base: &XrSpace,
time: openxr::Time,
) -> openxr::Result<openxr::SpaceLocation> {
unsafe {
let mut x = sys::SpaceLocation::out(ptr::null_mut());
cvt((instance.fp().locate_space)(
space.as_raw_openxr_space(),
base.as_raw_openxr_space(),
time,
x.as_mut_ptr(),
))?;
Ok(create_space_location(&x))
}
}
fn locate_space_with_velocity(
instance: &openxr::Instance,
space: &XrSpace,
base: &XrSpace,
time: openxr::Time,
) -> openxr::Result<(openxr::SpaceLocation, openxr::SpaceVelocity)> {
unsafe {
let mut velocity = sys::SpaceVelocity::out(ptr::null_mut());
let mut location = sys::SpaceLocation::out(&mut velocity as *mut _ as _);
cvt((instance.fp().locate_space)(
space.as_raw_openxr_space(),
base.as_raw_openxr_space(),
time,
location.as_mut_ptr(),
))?;
Ok((
create_space_location(&location),
create_space_velocity(&velocity),
))
}
}
pub fn locate_hand_joints(
instance: &openxr::Instance,
tracker: &openxr::HandTracker,
base: &XrSpace,
time: openxr::Time,
) -> openxr::Result<Option<HandJointLocations>> {
unsafe {
let locate_info = sys::HandJointsLocateInfoEXT {
ty: sys::HandJointsLocateInfoEXT::TYPE,
next: ptr::null(),
base_space: base.as_raw_openxr_space(),
time,
};
let mut locations =
MaybeUninit::<[openxr::HandJointLocation; openxr::HAND_JOINT_COUNT]>::uninit();
let mut location_info = sys::HandJointLocationsEXT {
ty: sys::HandJointLocationsEXT::TYPE,
next: ptr::null_mut(),
is_active: false.into(),
joint_count: openxr::HAND_JOINT_COUNT as u32,
joint_locations: locations.as_mut_ptr() as _,
};
cvt((instance
.exts()
.ext_hand_tracking
.as_ref()
.expect("Somehow created HandTracker without XR_EXT_hand_tracking being enabled")
.locate_hand_joints)(
tracker.as_raw(),
&locate_info,
&mut location_info,
))?;
Ok(if location_info.is_active.into() {
Some(locations.assume_init())
} else {
None
})
}
}
pub fn locate_hand_joints_with_velocities(
instance: &openxr::Instance,
tracker: &openxr::HandTracker,
base: &XrSpace,
time: openxr::Time,
) -> openxr::Result<Option<(HandJointLocations, HandJointVelocities)>> {
unsafe {
let locate_info = sys::HandJointsLocateInfoEXT {
ty: sys::HandJointsLocateInfoEXT::TYPE,
next: ptr::null(),
base_space: base.as_raw_openxr_space(),
time,
};
let mut velocities = MaybeUninit::<[HandJointVelocity; HAND_JOINT_COUNT]>::uninit();
let mut velocity_info = sys::HandJointVelocitiesEXT {
ty: sys::HandJointVelocitiesEXT::TYPE,
next: ptr::null_mut(),
joint_count: HAND_JOINT_COUNT as u32,
joint_velocities: velocities.as_mut_ptr() as _,
};
let mut locations = MaybeUninit::<[HandJointLocation; HAND_JOINT_COUNT]>::uninit();
let mut location_info = sys::HandJointLocationsEXT {
ty: sys::HandJointLocationsEXT::TYPE,
next: &mut velocity_info as *mut _ as _,
is_active: false.into(),
joint_count: HAND_JOINT_COUNT as u32,
joint_locations: locations.as_mut_ptr() as _,
};
cvt((instance
.exts()
.ext_hand_tracking
.as_ref()
.expect("Somehow created HandTracker without XR_EXT_hand_tracking being enabled")
.locate_hand_joints)(
tracker.as_raw(),
&locate_info,
&mut location_info,
))?;
Ok(if location_info.is_active.into() {
Some((locations.assume_init(), velocities.assume_init()))
} else {
None
})
}
}
pub fn destroy_space(instance: &openxr::Instance, space: sys::Space) -> openxr::Result<sys::Result> {
let result = unsafe { (instance.fp().destroy_space)(space) };
cvt(result)
}
impl OxrSession {
pub fn destroy_space(&self, space: XrSpace) {
let _ = destroy_space(self.instance(), space.as_raw_openxr_space());
}
pub fn destroy_openxr_space(&self, space: openxr::Space) {
let _ = destroy_space(self.instance(), space.as_raw());
}
pub fn locate_views(
&self,
view_configuration_type: openxr::ViewConfigurationType,
display_time: openxr::Time,
ref_space: &XrReferenceSpace,
) -> openxr::Result<(openxr::ViewStateFlags, Vec<openxr::View>)> {
let info = sys::ViewLocateInfo {
ty: sys::ViewLocateInfo::TYPE,
next: ptr::null(),
view_configuration_type,
display_time,
space: ref_space.as_raw_openxr_space(),
};
let (flags, raw) = unsafe {
let mut out = sys::ViewState::out(ptr::null_mut());
let raw = get_arr_init(sys::View::out(ptr::null_mut()), |cap, count, buf| {
(self.instance().fp().locate_views)(
self.as_raw(),
&info,
out.as_mut_ptr(),
cap,
count,
buf as _,
)
})?;
(out.assume_init().view_state_flags, raw)
};
Ok((
flags,
raw.into_iter()
.map(|x| unsafe { create_view(flags, &x) })
.collect(),
))
}
pub fn locate_space(
&self,
space: &XrSpace,
base: &XrSpace,
time: openxr::Time,
) -> openxr::Result<openxr::SpaceLocation> {
locate_space(self.instance(), space, base, time)
}
pub fn locate_space_with_velocity(
&self,
space: &XrSpace,
base: &XrSpace,
time: openxr::Time,
) -> openxr::Result<(openxr::SpaceLocation, openxr::SpaceVelocity)> {
locate_space_with_velocity(self.instance(), space, base, time)
}
pub fn locate_hand_joints(
&self,
tracker: &openxr::HandTracker,
base: &XrSpace,
time: openxr::Time,
) -> openxr::Result<Option<openxr::HandJointLocations>> {
locate_hand_joints(self.instance(), tracker, base, time)
}
pub fn locate_hand_joints_with_velocities(
&self,
tracker: &openxr::HandTracker,
base: &XrSpace,
time: openxr::Time,
) -> openxr::Result<Option<(HandJointLocations, HandJointVelocities)>> {
locate_hand_joints_with_velocities(self.instance(), tracker, base, time)
}
}
impl OxrInstance {
pub fn locate_space(
&self,
space: &XrSpace,
base: &XrSpace,
time: openxr::Time,
) -> openxr::Result<openxr::SpaceLocation> {
locate_space(self, space, base, time)
}
pub fn locate_space_with_velocity(
&self,
space: &XrSpace,
base: &XrSpace,
time: openxr::Time,
) -> openxr::Result<(openxr::SpaceLocation, openxr::SpaceVelocity)> {
locate_space_with_velocity(self, space, base, time)
}
pub fn locate_hand_joints(
&self,
tracker: &openxr::HandTracker,
base: &XrSpace,
time: openxr::Time,
) -> openxr::Result<Option<openxr::HandJointLocations>> {
locate_hand_joints(self, tracker, base, time)
}
pub fn locate_hand_joints_with_velocities(
&self,
tracker: &openxr::HandTracker,
base: &XrSpace,
time: openxr::Time,
) -> openxr::Result<Option<(HandJointLocations, HandJointVelocities)>> {
locate_hand_joints_with_velocities(self, tracker, base, time)
}
}
/// # Safety
/// This is an Extension trait. DO NOT IMPLEMENT IT!
pub unsafe trait OxrSpaceExt {
fn as_raw_openxr_space(&self) -> sys::Space;
fn from_raw_openxr_space(space: sys::Space) -> Self;
fn from_openxr_space(space: openxr::Space) -> Self;
/// # Safety
/// Session has to be the session from which the space is from
unsafe fn to_openxr_space<T>(self, session: &openxr::Session<T>) -> openxr::Space;
}
unsafe impl OxrSpaceExt for XrSpace {
fn as_raw_openxr_space(&self) -> sys::Space {
sys::Space::from_raw(self.as_raw())
}
fn from_raw_openxr_space(space: sys::Space) -> Self {
let raw = space.into_raw();
OXR_DO_NOT_CALL_DESTOY_SPACE_FOR
.lock()
.unwrap()
.as_mut()
.unwrap()
.insert(raw);
unsafe { XrSpace::from_raw(raw) }
}
fn from_openxr_space(space: openxr::Space) -> Self {
Self::from_raw_openxr_space(space.as_raw())
}
unsafe fn to_openxr_space<T>(self, session: &openxr::Session<T>) -> openxr::Space {
unsafe { openxr::Space::reference_from_raw(session.clone(), self.as_raw_openxr_space()) }
}
}
fn cvt(x: sys::Result) -> openxr::Result<sys::Result> {
if x.into_raw() >= 0 {
Ok(x)
} else {
Err(x)
}
}
unsafe fn create_view(flags: openxr::ViewStateFlags, raw: &MaybeUninit<sys::View>) -> openxr::View {
// Applications *must* not read invalid parts of a poses, i.e. they may be uninitialized
let ptr = raw.as_ptr();
openxr::View {
pose: openxr::Posef {
orientation: flags
.contains(sys::ViewStateFlags::ORIENTATION_VALID)
.then(|| *ptr::addr_of!((*ptr).pose.orientation))
.unwrap_or_default(),
position: flags
.contains(sys::ViewStateFlags::POSITION_VALID)
.then(|| *ptr::addr_of!((*ptr).pose.position))
.unwrap_or_default(),
},
fov: *ptr::addr_of!((*ptr).fov),
}
}
unsafe fn create_space_location(raw: &MaybeUninit<sys::SpaceLocation>) -> openxr::SpaceLocation {
// Applications *must* not read invalid parts of a pose, i.e. they may be uninitialized
let ptr = raw.as_ptr();
let flags = *ptr::addr_of!((*ptr).location_flags);
openxr::SpaceLocation {
location_flags: flags,
pose: openxr::Posef {
orientation: flags
.contains(sys::SpaceLocationFlags::ORIENTATION_VALID)
.then(|| *ptr::addr_of!((*ptr).pose.orientation))
.unwrap_or_default(),
position: flags
.contains(sys::SpaceLocationFlags::POSITION_VALID)
.then(|| *ptr::addr_of!((*ptr).pose.position))
.unwrap_or_default(),
},
}
}
unsafe fn create_space_velocity(raw: &MaybeUninit<sys::SpaceVelocity>) -> openxr::SpaceVelocity {
// Applications *must* not read invalid velocities, i.e. they may be uninitialized
let ptr = raw.as_ptr();
let flags = *ptr::addr_of!((*ptr).velocity_flags);
openxr::SpaceVelocity {
velocity_flags: flags,
linear_velocity: flags
.contains(sys::SpaceVelocityFlags::LINEAR_VALID)
.then(|| *ptr::addr_of!((*ptr).linear_velocity))
.unwrap_or_default(),
angular_velocity: flags
.contains(sys::SpaceVelocityFlags::ANGULAR_VALID)
.then(|| *ptr::addr_of!((*ptr).angular_velocity))
.unwrap_or_default(),
}
}
fn get_arr_init<T: Copy>(
init: T,
mut getter: impl FnMut(u32, &mut u32, *mut T) -> sys::Result,
) -> openxr::Result<Vec<T>> {
let mut output = 0;
cvt(getter(0, &mut output, std::ptr::null_mut()))?;
let mut buffer = vec![init; output as usize];
loop {
match cvt(getter(output, &mut output, buffer.as_mut_ptr() as _)) {
Ok(_) => {
buffer.truncate(output as usize);
return Ok(buffer);
}
Err(sys::Result::ERROR_SIZE_INSUFFICIENT) => {
buffer.resize(output as usize, init);
}
Err(e) => {
return Err(e);
}
}
}
}