move to api crate
This commit is contained in:
111
xr_api/src/api.rs
Normal file
111
xr_api/src/api.rs
Normal file
@@ -0,0 +1,111 @@
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Entry(Rc<dyn EntryTrait>);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Instance(Rc<dyn InstanceTrait>);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Session(Rc<dyn SessionTrait>);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Input(Rc<dyn InputTrait>);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Action<A: ActionType>(A::Inner);
|
||||
|
||||
impl Deref for Entry {
|
||||
type Target = dyn EntryTrait;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&*self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Instance {
|
||||
type Target = dyn InstanceTrait;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&*self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Session {
|
||||
type Target = dyn SessionTrait;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&*self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Input {
|
||||
type Target = dyn InputTrait;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&*self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<O, A> Deref for Action<A>
|
||||
where
|
||||
A: ActionType,
|
||||
A::Inner: Deref<Target = O>,
|
||||
{
|
||||
type Target = O;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&*self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EntryTrait + 'static> From<T> for Entry {
|
||||
fn from(value: T) -> Self {
|
||||
Self(Rc::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: InstanceTrait + 'static> From<T> for Instance {
|
||||
fn from(value: T) -> Self {
|
||||
Self(Rc::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: SessionTrait + 'static> From<T> for Session {
|
||||
fn from(value: T) -> Self {
|
||||
Self(Rc::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: InputTrait + 'static> From<T> for Input {
|
||||
fn from(value: T) -> Self {
|
||||
Self(Rc::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: HapticTrait + 'static> From<T> for Action<Haptic> {
|
||||
fn from(value: T) -> Self {
|
||||
Self(Rc::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ActionInputTrait<f32> + 'static> From<T> for Action<f32> {
|
||||
fn from(value: T) -> Self {
|
||||
Self(Rc::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ActionInputTrait<Pose> + 'static> From<T> for Action<Pose> {
|
||||
fn from(value: T) -> Self {
|
||||
Self(Rc::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ActionInputTrait<bool> + 'static> From<T> for Action<bool> {
|
||||
fn from(value: T) -> Self {
|
||||
Self(Rc::new(value))
|
||||
}
|
||||
}
|
||||
67
xr_api/src/api_traits.rs
Normal file
67
xr_api/src/api_traits.rs
Normal file
@@ -0,0 +1,67 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
pub trait EntryTrait {
|
||||
/// Return currently available extensions
|
||||
fn available_extensions(&self) -> Result<ExtensionSet>;
|
||||
/// Create an [Instance] with the enabled extensions.
|
||||
fn create_instance(&self, exts: ExtensionSet) -> Result<Instance>;
|
||||
}
|
||||
|
||||
pub trait InstanceTrait {
|
||||
/// Returns the [Entry] used to create this.
|
||||
fn entry(&self) -> Entry;
|
||||
/// Returns an [ExtensionSet] listing all enabled extensions.
|
||||
fn enabled_extensions(&self) -> ExtensionSet;
|
||||
/// Creates a [Session] with the requested properties
|
||||
fn create_session(&self, info: SessionCreateInfo) -> Result<Session>;
|
||||
}
|
||||
|
||||
pub trait SessionTrait {
|
||||
/// Returns the [Instance] used to create this.
|
||||
fn instance(&self) -> &Instance;
|
||||
/// Request input modules with the specified bindings.
|
||||
fn create_input(&self, bindings: Bindings) -> Result<Input>;
|
||||
/// Blocks until a rendering frame is available and then begins it.
|
||||
fn begin_frame(&self) -> Result<()>;
|
||||
/// Submits rendering work for this frame.
|
||||
fn end_frame(&self) -> Result<()>;
|
||||
}
|
||||
|
||||
pub trait InputTrait {
|
||||
fn get_haptics(&self, path: ActionId) -> Result<Action<Haptic>>;
|
||||
fn get_pose(&self, path: ActionId) -> Result<Action<Pose>>;
|
||||
fn get_float(&self, path: ActionId) -> Result<Action<f32>>;
|
||||
fn get_bool(&self, path: ActionId) -> Result<Action<bool>>;
|
||||
}
|
||||
|
||||
pub trait ActionTrait {
|
||||
fn id(&self) -> ActionId;
|
||||
}
|
||||
|
||||
pub trait ActionInputTrait<A> {}
|
||||
|
||||
pub trait HapticTrait {}
|
||||
|
||||
impl<T: InstanceTrait> EntryTrait for T {
|
||||
fn available_extensions(&self) -> Result<ExtensionSet> {
|
||||
self.entry().available_extensions()
|
||||
}
|
||||
|
||||
fn create_instance(&self, exts: ExtensionSet) -> Result<Instance> {
|
||||
self.entry().create_instance(exts)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: SessionTrait> InstanceTrait for T {
|
||||
fn entry(&self) -> Entry {
|
||||
self.instance().entry()
|
||||
}
|
||||
|
||||
fn enabled_extensions(&self) -> ExtensionSet {
|
||||
self.instance().enabled_extensions()
|
||||
}
|
||||
|
||||
fn create_session(&self, info: SessionCreateInfo) -> Result<Session> {
|
||||
self.instance().create_session(info)
|
||||
}
|
||||
}
|
||||
4
xr_api/src/backend/mod.rs
Normal file
4
xr_api/src/backend/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
pub mod oxr;
|
||||
#[cfg(target_family = "wasm")]
|
||||
pub mod webxr;
|
||||
30
xr_api/src/backend/oxr.rs
Normal file
30
xr_api/src/backend/oxr.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
pub struct OXrEntry(openxr::Entry);
|
||||
|
||||
impl EntryTrait for OXrEntry {
|
||||
fn available_extensions(&self) -> Result<ExtensionSet> {
|
||||
// self.0.enumerate_extensions();
|
||||
Ok(ExtensionSet::default())
|
||||
}
|
||||
|
||||
fn create_instance(&self, exts: ExtensionSet) -> Result<Instance> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OXrInstance(openxr::Instance);
|
||||
|
||||
impl InstanceTrait for OXrInstance {
|
||||
fn entry(&self) -> Entry {
|
||||
OXrEntry(self.0.entry().clone()).into()
|
||||
}
|
||||
|
||||
fn enabled_extensions(&self) -> ExtensionSet {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn create_session(&self, info: SessionCreateInfo) -> Result<Session> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
0
xr_api/src/backend/oxr/utils.rs
Normal file
0
xr_api/src/backend/oxr/utils.rs
Normal file
181
xr_api/src/backend/webxr.rs
Normal file
181
xr_api/src/backend/webxr.rs
Normal file
@@ -0,0 +1,181 @@
|
||||
use std::sync::{
|
||||
mpsc::{channel, Sender},
|
||||
Mutex,
|
||||
};
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use wasm_bindgen::{closure::Closure, JsCast};
|
||||
use web_sys::{js_sys, XrFrame, XrInputSource};
|
||||
|
||||
mod utils;
|
||||
|
||||
use utils::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WebXrEntry(web_sys::XrSystem);
|
||||
|
||||
impl EntryTrait for WebXrEntry {
|
||||
fn available_extensions(&self) -> Result<ExtensionSet> {
|
||||
Ok(ExtensionSet::default())
|
||||
}
|
||||
|
||||
fn create_instance(&self, exts: ExtensionSet) -> Result<Instance> {
|
||||
Ok(WebXrInstance {
|
||||
entry: self.clone(),
|
||||
exts,
|
||||
}
|
||||
.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WebXrInstance {
|
||||
entry: WebXrEntry,
|
||||
exts: ExtensionSet,
|
||||
}
|
||||
|
||||
impl InstanceTrait for WebXrInstance {
|
||||
fn entry(&self) -> Entry {
|
||||
self.entry.clone().into()
|
||||
}
|
||||
|
||||
fn enabled_extensions(&self) -> ExtensionSet {
|
||||
self.exts
|
||||
}
|
||||
|
||||
fn create_session(&self, info: SessionCreateInfo) -> Result<Session> {
|
||||
Ok(WebXrSession {
|
||||
instance: self.clone().into(),
|
||||
session: self
|
||||
.entry
|
||||
.0
|
||||
.request_session(web_sys::XrSessionMode::ImmersiveVr)
|
||||
.resolve()
|
||||
.map_err(|_| XrError::Placeholder)?,
|
||||
end_frame_sender: Mutex::default(),
|
||||
}
|
||||
.into())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WebXrSession {
|
||||
instance: Instance,
|
||||
session: web_sys::XrSession,
|
||||
end_frame_sender: Mutex<Option<Sender<()>>>,
|
||||
}
|
||||
|
||||
impl SessionTrait for WebXrSession {
|
||||
fn instance(&self) -> &Instance {
|
||||
&self.instance
|
||||
}
|
||||
|
||||
fn create_input(&self, bindings: Bindings) -> Result<Input> {
|
||||
Ok(WebXrInput {
|
||||
devices: self.session.input_sources(),
|
||||
bindings,
|
||||
}
|
||||
.into())
|
||||
}
|
||||
|
||||
fn begin_frame(&self) -> Result<()> {
|
||||
let mut end_frame_sender = self.end_frame_sender.lock().unwrap();
|
||||
if end_frame_sender.is_some() {
|
||||
Err(XrError::Placeholder)?
|
||||
}
|
||||
let (tx, rx) = channel::<()>();
|
||||
let (tx_end, rx_end) = channel::<()>();
|
||||
*end_frame_sender = Some(tx_end);
|
||||
let on_frame: Closure<dyn FnMut(f64, XrFrame)> =
|
||||
Closure::new(move |time: f64, frame: XrFrame| {
|
||||
tx.send(()).ok();
|
||||
rx_end.recv().ok();
|
||||
});
|
||||
|
||||
self.session
|
||||
.request_animation_frame(on_frame.as_ref().unchecked_ref());
|
||||
|
||||
rx.recv().ok();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end_frame(&self) -> Result<()> {
|
||||
let mut end_frame_sender = self.end_frame_sender.lock().unwrap();
|
||||
match std::mem::take(&mut *end_frame_sender) {
|
||||
Some(sender) => sender.send(()).ok(),
|
||||
None => Err(XrError::Placeholder)?,
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WebXrInput {
|
||||
devices: web_sys::XrInputSourceArray,
|
||||
bindings: Bindings,
|
||||
}
|
||||
|
||||
impl From<web_sys::XrHandedness> for Handedness {
|
||||
fn from(value: web_sys::XrHandedness) -> Self {
|
||||
match value {
|
||||
web_sys::XrHandedness::None => Handedness::None,
|
||||
web_sys::XrHandedness::Left => Handedness::Left,
|
||||
web_sys::XrHandedness::Right => Handedness::Right,
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WebXrInput {
|
||||
fn get_controller(&self, handedness: Handedness) -> Option<web_sys::XrInputSource> {
|
||||
js_sys::try_iter(&self.devices).ok()??.find_map(|dev| {
|
||||
if let Ok(dev) = dev {
|
||||
let dev: XrInputSource = dev.into();
|
||||
if Into::<Handedness>::into(dev.handedness()) == handedness {
|
||||
Some(dev)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl InputTrait for WebXrInput {
|
||||
fn get_haptics(&self, path: ActionId) -> Result<Action<Haptic>> {
|
||||
let haptics = self
|
||||
.get_controller(path.handedness)
|
||||
.ok_or(XrError::Placeholder)?
|
||||
.gamepad()
|
||||
.ok_or(XrError::Placeholder)?
|
||||
.haptic_actuators()
|
||||
.iter()
|
||||
.next()
|
||||
.ok_or(XrError::Placeholder)?
|
||||
.into();
|
||||
Ok(WebXrHaptics(haptics, path).into())
|
||||
}
|
||||
|
||||
fn get_pose(&self, path: ActionId) -> Result<Action<Pose>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn get_float(&self, path: ActionId) -> Result<Action<f32>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn get_bool(&self, path: ActionId) -> Result<Action<bool>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WebXrHaptics(web_sys::GamepadHapticActuator, ActionId);
|
||||
|
||||
impl ActionTrait for WebXrHaptics {
|
||||
fn id(&self) -> ActionId {
|
||||
self.1
|
||||
}
|
||||
}
|
||||
|
||||
impl HapticTrait for WebXrHaptics {}
|
||||
17
xr_api/src/backend/webxr/utils.rs
Normal file
17
xr_api/src/backend/webxr/utils.rs
Normal file
@@ -0,0 +1,17 @@
|
||||
use wasm_bindgen::JsValue;
|
||||
use wasm_bindgen_futures::JsFuture;
|
||||
use web_sys::js_sys::Promise;
|
||||
|
||||
pub trait PromiseRes {
|
||||
fn resolve<T: From<JsValue>>(self) -> Result<T, JsValue>;
|
||||
}
|
||||
|
||||
impl PromiseRes for Promise {
|
||||
fn resolve<T: From<JsValue>>(self) -> Result<T, JsValue> {
|
||||
resolve_promise(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_promise<T: From<JsValue>>(promise: Promise) -> Result<T, JsValue> {
|
||||
futures::executor::block_on(async move { JsFuture::from(promise).await.map(Into::into) })
|
||||
}
|
||||
16
xr_api/src/error.rs
Normal file
16
xr_api/src/error.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, XrError>;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum XrError {
|
||||
Placeholder,
|
||||
}
|
||||
|
||||
impl Display for XrError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
12
xr_api/src/lib.rs
Normal file
12
xr_api/src/lib.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
pub mod api;
|
||||
pub mod api_traits;
|
||||
pub mod backend;
|
||||
pub mod error;
|
||||
pub mod types;
|
||||
|
||||
pub mod prelude {
|
||||
pub use super::api::*;
|
||||
pub use super::api_traits::*;
|
||||
pub use super::error::*;
|
||||
pub use super::types::*;
|
||||
}
|
||||
51
xr_api/src/types.rs
Normal file
51
xr_api/src/types.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::api_traits::{ActionInputTrait, HapticTrait};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
pub struct ExtensionSet {}
|
||||
|
||||
pub enum SessionCreateInfo {}
|
||||
|
||||
pub struct Bindings {}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub struct ActionId {
|
||||
pub handedness: Handedness,
|
||||
pub device: Device,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum Handedness {
|
||||
Left,
|
||||
Right,
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum Device {
|
||||
Controller,
|
||||
}
|
||||
|
||||
pub struct Haptic;
|
||||
pub struct Pose;
|
||||
|
||||
pub trait ActionType {
|
||||
type Inner;
|
||||
}
|
||||
|
||||
impl ActionType for Haptic {
|
||||
type Inner = Rc<dyn HapticTrait>;
|
||||
}
|
||||
|
||||
impl ActionType for Pose {
|
||||
type Inner = Rc<dyn ActionInputTrait<Pose>>;
|
||||
}
|
||||
|
||||
impl ActionType for f32 {
|
||||
type Inner = Rc<dyn ActionInputTrait<f32>>;
|
||||
}
|
||||
|
||||
impl ActionType for bool {
|
||||
type Inner = Rc<dyn ActionInputTrait<bool>>;
|
||||
}
|
||||
Reference in New Issue
Block a user