diff --git a/xr_api/Cargo.toml b/xr_api/Cargo.toml index d5ca4e8..c8544ca 100644 --- a/xr_api/Cargo.toml +++ b/xr_api/Cargo.toml @@ -11,6 +11,7 @@ linked = ["openxr/linked"] ash = "0.37.3" futures = "0.3.29" glam = "0.24.1" +paste = "1.0.14" thiserror = "1.0.51" tracing = "0.1.40" wgpu = "0.17.1" diff --git a/xr_api/src/api_traits.rs b/xr_api/src/api_traits.rs index ee4d3ff..f8affd5 100644 --- a/xr_api/src/api_traits.rs +++ b/xr_api/src/api_traits.rs @@ -1,8 +1,10 @@ -use glam::UVec2; +use glam::{UVec2, Vec2}; use wgpu::{Adapter, AdapterInfo, Device, Queue, TextureView}; use crate::prelude::*; +use self::path::{InputComponent, UntypedActionPath}; + pub trait EntryTrait { /// Return currently available extensions fn available_extensions(&self) -> Result; @@ -52,27 +54,29 @@ pub trait ViewTrait { pub trait InputTrait { /// Get the haptic action at the specified path. - fn get_haptics(&self, path: ActionPath) -> Result>; + fn create_action_haptics(&self, name: &str, path: UntypedActionPath) -> Result>; /// Get the pose action at the specified path. - fn get_pose(&self, path: ActionPath) -> Result>; + fn create_action_pose(&self, name: &str, path: UntypedActionPath) -> Result>; /// Get the float action at the specified path. - fn get_float(&self, path: ActionPath) -> Result>; + fn create_action_float(&self, name: &str, path: UntypedActionPath) -> Result>; /// Get the boolean action at the specified path. - fn get_bool(&self, path: ActionPath) -> Result>; + fn create_action_bool(&self, name: &str, path: UntypedActionPath) -> Result>; + /// Get the Vec2 action at the specified path. + fn create_action_vec2(&self, name: &str, path: UntypedActionPath) -> Result>; } // This impl is moved outside of the trait to ensure that InputTrait stays object safe. impl dyn InputTrait { /// Get the action at the specified path. - pub fn get_action(&self, path: ActionPath) -> Result> { - A::get(self, path) + pub fn create_action( + &self, + name: &str, + path: ActionPath

, + ) -> Result> { + P::PathType::get(self, name, path.untyped()) } } -pub trait ActionTrait { - fn id(&self) -> ActionPath; -} - /// Represents input actions, such as bools, floats, and poses pub trait ActionInputTrait { fn get(&self) -> A; diff --git a/xr_api/src/backend/oxr.rs b/xr_api/src/backend/oxr.rs index 058aa61..e81cbcf 100644 --- a/xr_api/src/backend/oxr.rs +++ b/xr_api/src/backend/oxr.rs @@ -1,7 +1,7 @@ mod graphics; mod utils; -use std::sync::Mutex; +use std::{rc::Rc, sync::Mutex}; use glam::{Mat4, UVec2}; use openxr::EnvironmentBlendMode; @@ -70,11 +70,26 @@ impl InstanceTrait for OXrInstance { } } +enum UntypedOXrAction { + Haptics(openxr::Action), + Pose(openxr::Action), + Float(openxr::Action), + Bool(openxr::Action), + Vec2(openxr::Action), +} + +#[derive(Default)] +struct BindingState { + sets_attached: bool, + bindings: Vec<(UntypedOXrAction, openxr::Path)>, +} + pub struct OXrSession { pub(crate) instance: Instance, // this could definitely be done better pub(crate) inner_instance: openxr::Instance, pub(crate) session: openxr::Session, + pub(crate) action_sets: Mutex>, pub(crate) render_resources: Mutex< Option<( wgpu::Device, @@ -84,6 +99,7 @@ pub struct OXrSession { wgpu::Instance, )>, >, + pub(crate) bindings: Rc>, pub(crate) frame_state: Mutex, pub(crate) views: Mutex<[openxr::View; 2]>, pub(crate) frame_waiter: Mutex, @@ -99,7 +115,6 @@ impl SessionTrait for OXrSession { fn instance(&self) -> &Instance { &self.instance } - fn get_render_resources( &self, ) -> Option<( @@ -113,6 +128,10 @@ impl SessionTrait for OXrSession { } fn create_input(&self, bindings: Bindings) -> Result { + let action_set = self + .inner_instance + .create_action_set("xr_input", "XR Input", 0)?; + self.action_sets.lock().unwrap().push(action_set.clone()); todo!() } @@ -239,6 +258,71 @@ impl SessionTrait for OXrSession { } } +pub struct OXrInput { + inner_instance: openxr::Instance, + action_set: openxr::ActionSet, + bindings: Rc>, +} + +impl OXrInput { + fn create_action( + &self, + name: &str, + handed: bool, + ) -> openxr::Result> { + if handed { + let left_path = self.inner_instance.string_to_path("/user/hand/left")?; + let right_path = self.inner_instance.string_to_path("/user/hand/right")?; + self.action_set + .create_action::(name, name, &[left_path, right_path]) + } else { + self.action_set.create_action(name, name, &[]) + } + } +} + +impl InputTrait for OXrInput { + fn create_action_haptics( + &self, + name: &str, + path: path::UntypedActionPath, + ) -> Result> { + todo!() + } + + fn create_action_pose( + &self, + name: &str, + path: path::UntypedActionPath, + ) -> Result> { + todo!() + } + + fn create_action_float( + &self, + name: &str, + path: path::UntypedActionPath, + ) -> Result> { + todo!() + } + + fn create_action_bool( + &self, + name: &str, + path: path::UntypedActionPath, + ) -> Result> { + todo!() + } + + fn create_action_vec2( + &self, + name: &str, + path: path::UntypedActionPath, + ) -> Result> { + todo!() + } +} + pub struct OXrView { texture: Mutex>, view: openxr::View, @@ -361,25 +445,3 @@ impl ViewTrait for OXrView { self.format } } - -pub struct OXrInput { - action_set: openxr::ActionSet, -} - -impl InputTrait for OXrInput { - fn get_haptics(&self, path: ActionPath) -> Result> { - todo!() - } - - fn get_pose(&self, path: ActionPath) -> Result> { - todo!() - } - - fn get_float(&self, path: ActionPath) -> Result> { - todo!() - } - - fn get_bool(&self, path: ActionPath) -> Result> { - todo!() - } -} diff --git a/xr_api/src/backend/oxr/graphics.rs b/xr_api/src/backend/oxr/graphics.rs index 47f3cdf..d5bf110 100644 --- a/xr_api/src/backend/oxr/graphics.rs +++ b/xr_api/src/backend/oxr/graphics.rs @@ -149,7 +149,7 @@ impl SwapchainInner { return Ok(()); } let mut stream = self.stream.lock().unwrap(); - if true { + if should_render { stream.end( predicted_display_time, environment_blend_mode, diff --git a/xr_api/src/backend/oxr/graphics/vulkan.rs b/xr_api/src/backend/oxr/graphics/vulkan.rs index 6dd786c..8a0f613 100644 --- a/xr_api/src/backend/oxr/graphics/vulkan.rs +++ b/xr_api/src/backend/oxr/graphics/vulkan.rs @@ -318,6 +318,8 @@ pub fn init_oxr_graphics( head: session .create_reference_space(openxr::ReferenceSpaceType::VIEW, openxr::Posef::IDENTITY)?, format: swapchain_format, + action_sets: Default::default(), + bindings: Default::default(), }) } diff --git a/xr_api/src/backend/oxr/utils.rs b/xr_api/src/backend/oxr/utils.rs index f17f442..a3a6698 100644 --- a/xr_api/src/backend/oxr/utils.rs +++ b/xr_api/src/backend/oxr/utils.rs @@ -4,7 +4,7 @@ use openxr::Posef; use crate::{error::XrError, prelude::Pose}; impl From for XrError { - fn from(value: openxr::sys::Result) -> Self { + fn from(_: openxr::sys::Result) -> Self { XrError::Placeholder } } diff --git a/xr_api/src/lib.rs b/xr_api/src/lib.rs index b6ee74c..b0c372f 100644 --- a/xr_api/src/lib.rs +++ b/xr_api/src/lib.rs @@ -9,12 +9,14 @@ mod api; pub mod api_traits; pub mod backend; pub mod error; +pub mod path; pub mod types; pub mod prelude { pub use super::api::*; pub use super::api_traits::*; pub use super::error::*; + pub use super::path::{self, ActionPath}; pub use super::types::*; } diff --git a/xr_api/src/path.rs b/xr_api/src/path.rs new file mode 100644 index 0000000..516891d --- /dev/null +++ b/xr_api/src/path.rs @@ -0,0 +1,246 @@ +use std::marker::PhantomData; + +use glam::Vec2; + +use crate::prelude::ActionType; + +pub struct ActionPath { + pub(crate) input: InputId, + pub(crate) comp: PathComponent, + pub(crate) hand: Option, + _data: PhantomData

, +} + +pub struct UntypedActionPath { + pub(crate) input: InputId, + pub(crate) comp: PathComponent, + pub(crate) hand: Option, +} + +impl From> for UntypedActionPath { + fn from(value: ActionPath

) -> Self { + value.untyped() + } +} + +impl ActionPath

{ + const fn new(input: InputId, comp: PathComponent, hand: Option) -> Self { + Self { + input, + comp, + hand, + _data: PhantomData, + } + } + + pub fn untyped(self) -> UntypedActionPath { + UntypedActionPath { + input: self.input, + comp: self.comp, + hand: self.hand, + } + } +} + +pub(crate) enum Hand { + Left, + Right, +} + +pub(crate) enum PathComponent { + Click, + Touch, + Value, + Axes, + Pose, + Haptic, +} + +pub struct Click; + +impl Click { + const COMP: PathComponent = PathComponent::Click; +} + +pub struct Touch; + +impl Touch { + const COMP: PathComponent = PathComponent::Touch; +} + +pub struct Value; + +impl Value { + const COMP: PathComponent = PathComponent::Value; +} + +pub struct Axes; + +impl Axes { + const COMP: PathComponent = PathComponent::Axes; +} + +pub struct Pose; + +impl Pose { + const COMP: PathComponent = PathComponent::Pose; +} + +pub struct Haptic; + +impl Haptic { + const COMP: PathComponent = PathComponent::Haptic; +} + +pub trait InputComponent { + type PathType: ActionType; +} + +impl InputComponent for Click { + type PathType = bool; +} + +impl InputComponent for Touch { + type PathType = bool; +} + +impl InputComponent for Value { + type PathType = bool; +} + +impl InputComponent for Axes { + type PathType = Vec2; +} + +impl InputComponent for Pose { + type PathType = crate::types::Pose; +} + +impl InputComponent for Haptic { + type PathType = crate::types::Haptic; +} + +macro_rules! input_ids { + ( + $(#[$id_meta:meta])* + $id:ident; + handed { + $( + $(#[$inner_handed_meta:meta])* + $inner_handed:ident { + $( + $comp_name_handed:ident, + )* + } + )* + } + $( + $(#[$inner_meta:meta])* + $inner:ident { + $( + $comp_name:ident, + )* + } + )* + ) => { + $( + #[$id_meta] + )* + paste::paste! { + pub(crate) enum $id { + $( + $inner, + )* + $( + [<$inner_handed Left>], + [<$inner_handed Right>], + )* + } + } + + pub mod left { + const LEFT: bool = true; + $( + pub type $inner_handed = super::$inner_handed; + )* + } + + pub mod right { + const RIGHT: bool = false; + $( + pub type $inner_handed = super::$inner_handed; + )* + } + + $( + $( + #[$inner_handed_meta] + )* + pub struct $inner_handed; + impl $inner_handed { + paste::paste! { + $( + pub const [<$comp_name_handed:snake:upper>]: ActionPath<$comp_name_handed> = ActionPath::<$comp_name_handed>::new($id::[<$inner_handed Left>], $comp_name_handed::COMP, Some(Hand::Left)); + )* + } + } + impl $inner_handed { + paste::paste! { + $( + pub const [<$comp_name_handed:snake:upper>]: ActionPath<$comp_name_handed> = ActionPath::<$comp_name_handed>::new($id::[<$inner_handed Right>], $comp_name_handed::COMP, Some(Hand::Right)); + )* + } + } + + )* + + $( + $( + #[$inner_meta] + )* + pub struct $inner; + + impl $inner { + paste::paste! { + $( + pub const [<$comp_name:snake:upper>]: ActionPath<$comp_name> = ActionPath::<$comp_name>::new($id::$inner, $comp_name::COMP, None); + )* + } + } + )* + }; +} + +input_ids! { + InputId; + handed { + PrimaryButton { + Click, + Touch, + } + SecondaryButton { + Click, + Touch, + } + Select { + Click, + } + Menu { + Click, + } + Thumbstick { + Axes, + Click, + Touch, + } + Trigger { + Touch, + Click, + } + Grip { + Click, + Value, + Pose, + } + } +} diff --git a/xr_api/src/types.rs b/xr_api/src/types.rs index f246749..f3c7301 100644 --- a/xr_api/src/types.rs +++ b/xr_api/src/types.rs @@ -1,10 +1,9 @@ -use std::rc::Rc; - -use glam::{Quat, Vec3}; +use glam::{Quat, Vec2, Vec3}; use crate::api::Action; use crate::api_traits::{ActionInputTrait, HapticTrait, InputTrait}; use crate::error::Result; +use crate::path::UntypedActionPath; #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub struct ExtensionSet { @@ -18,44 +17,6 @@ pub struct SessionCreateInfo { pub struct Bindings {} -/// THIS IS NOT COMPLETE, im not sure how i am going to index actions currently. -#[derive(Clone, Copy, PartialEq)] -pub struct ActionPath { - pub device: DevicePath, - pub subpath: SubPath, -} - -#[derive(Clone, Copy, PartialEq)] -pub enum DevicePath { - LeftHand, - RightHand, - Head, - Gamepad, - Treadmill, -} - -#[derive(Clone, Copy, PartialEq)] -pub enum SubPath { - A, - B, - X, - Y, - Start, - Home, - End, - Select, - Joystick, - Trigger, - Squeeze, -} - -#[derive(Clone, Copy, PartialEq)] -pub enum Handedness { - Left, - Right, - None, -} - pub struct Haptic; pub struct Pose { @@ -66,37 +27,45 @@ pub struct Pose { pub trait ActionType: Sized { type Inner: ?Sized; - fn get(input: &dyn InputTrait, path: ActionPath) -> Result>; + fn get(input: &dyn InputTrait, name: &str, path: UntypedActionPath) -> Result>; } impl ActionType for Haptic { type Inner = dyn HapticTrait; - fn get(input: &dyn InputTrait, path: ActionPath) -> Result> { - input.get_haptics(path) + fn get(input: &dyn InputTrait, name: &str, path: UntypedActionPath) -> Result> { + input.create_action_haptics(name, path) } } impl ActionType for Pose { type Inner = dyn ActionInputTrait; - fn get(input: &dyn InputTrait, path: ActionPath) -> Result> { - input.get_pose(path) + fn get(input: &dyn InputTrait, name: &str, path: UntypedActionPath) -> Result> { + input.create_action_pose(name, path) } } impl ActionType for f32 { type Inner = dyn ActionInputTrait; - fn get(input: &dyn InputTrait, path: ActionPath) -> Result> { - input.get_float(path) + fn get(input: &dyn InputTrait, name: &str, path: UntypedActionPath) -> Result> { + input.create_action_float(name, path) } } impl ActionType for bool { type Inner = dyn ActionInputTrait; - fn get(input: &dyn InputTrait, path: ActionPath) -> Result> { - input.get_bool(path) + fn get(input: &dyn InputTrait, name: &str, path: UntypedActionPath) -> Result> { + input.create_action_bool(name, path) + } +} + +impl ActionType for Vec2 { + type Inner = dyn ActionInputTrait; + + fn get(input: &dyn InputTrait, name: &str, path: UntypedActionPath) -> Result> { + input.create_action_vec2(name, path) } }