diff --git a/webxr/Cargo.toml b/webxr/Cargo.toml index fefa2da..6ee86df 100644 --- a/webxr/Cargo.toml +++ b/webxr/Cargo.toml @@ -32,7 +32,7 @@ webxr-api = { path = "../webxr-api" } crossbeam-channel = "0.5" euclid = "0.22" log = "0.4.6" -openxr = { version = "0.18", optional = true } +openxr = { version = "0.19", optional = true } serde = { version = "1.0", optional = true } sparkle = "0.1" surfman = { version = "0.9", features = ["chains"] } diff --git a/webxr/openxr/input.rs b/webxr/openxr/input.rs index 83380c6..4ce5f68 100644 --- a/webxr/openxr/input.rs +++ b/webxr/openxr/input.rs @@ -1,4 +1,5 @@ use euclid::RigidTransform3D; +use log::debug; use openxr::d3d::D3D11; use openxr::{ self, Action, ActionSet, Binding, FrameState, Hand as HandEnum, HandJoint, HandTracker, @@ -19,6 +20,9 @@ use webxr_api::Viewer; use super::IDENTITY_POSE; +use crate::ext_string; +use crate::openxr::interaction_profiles::INTERACTION_PROFILES; + /// Number of frames to wait with the menu gesture before /// opening the menu. const MENU_GESTURE_SUSTAIN_THRESHOLD: u8 = 60; @@ -173,6 +177,7 @@ impl OpenXRInput { instance: &Instance, session: &Session, needs_hands: bool, + supported_interaction_profiles: Vec<&'static str>, ) -> (ActionSet, Self, Self) { let action_set = instance.create_action_set("hands", "Hands", 0).unwrap(); let right_hand = OpenXRInput::new( @@ -190,32 +195,33 @@ impl OpenXRInput { needs_hands, ); - let mut bindings = - right_hand.get_bindings(instance, "trigger/value", Some("squeeze/click")); - bindings.extend( - left_hand - .get_bindings(instance, "trigger/value", Some("squeeze/click")) - .into_iter(), - ); - let path_controller = instance - .string_to_path("/interaction_profiles/microsoft/motion_controller") - .unwrap(); - instance - .suggest_interaction_profile_bindings(path_controller, &bindings) - .unwrap(); + for profile in INTERACTION_PROFILES { + if let Some(extension_name) = profile.required_extension { + if !supported_interaction_profiles.contains(&ext_string!(extension_name)) { + continue; + } + } + let select = profile.standard_buttons[0]; + let squeeze = Option::from(profile.standard_buttons[1]).filter(|&s| !s.is_empty()); + let mut bindings = right_hand.get_bindings(instance, select, squeeze); + bindings.extend( + left_hand + .get_bindings(instance, select, squeeze) + .into_iter(), + ); + let path_controller = instance + .string_to_path(profile.path) + .expect(format!("Invalid interaction profile path: {}", profile.path).as_str()); + if let Err(_) = + instance.suggest_interaction_profile_bindings(path_controller, &bindings) + { + debug!( + "Interaction profile path not available for this runtime: {:?}", + profile.path + ); + } + } - let mut bindings = right_hand.get_bindings(instance, "select/click", None); - bindings.extend( - left_hand - .get_bindings(instance, "select/click", None) - .into_iter(), - ); - let path_controller = instance - .string_to_path("/interaction_profiles/khr/simple_controller") - .unwrap(); - instance - .suggest_interaction_profile_bindings(path_controller, &bindings) - .unwrap(); session.attach_action_sets(&[&action_set]).unwrap(); (action_set, right_hand, left_hand) @@ -230,22 +236,34 @@ impl OpenXRInput { let hand = hand_str(self.handedness); let path_aim_pose = instance .string_to_path(&format!("/user/hand/{}/input/aim/pose", hand)) - .unwrap(); + .expect(&format!( + "Failed to create path for /user/hand/{}/input/aim/pose", + hand + )); let binding_aim_pose = Binding::new(&self.action_aim_pose, path_aim_pose); let path_grip_pose = instance .string_to_path(&format!("/user/hand/{}/input/grip/pose", hand)) - .unwrap(); + .expect(&format!( + "Failed to create path for /user/hand/{}/input/grip/pose", + hand + )); let binding_grip_pose = Binding::new(&self.action_grip_pose, path_grip_pose); let path_click = instance .string_to_path(&format!("/user/hand/{}/input/{}", hand, select_name)) - .unwrap(); + .expect(&format!( + "Failed to create path for /user/hand/{}/input/{}", + hand, select_name + )); let binding_click = Binding::new(&self.action_click, path_click); let mut ret = vec![binding_aim_pose, binding_grip_pose, binding_click]; if let Some(squeeze_name) = squeeze_name { let path_squeeze = instance .string_to_path(&format!("/user/hand/{}/input/{}", hand, squeeze_name)) - .unwrap(); + .expect(&format!( + "Failed to create path for /user/hand/{}/input/{}", + hand, squeeze_name + )); let binding_squeeze = Binding::new(&self.action_squeeze, path_squeeze); ret.push(binding_squeeze); } @@ -349,9 +367,7 @@ impl OpenXRInput { id: self.id, target_ray_mode: TargetRayMode::TrackedPointer, supports_grip: true, - // XXXManishearth update with whatever we decide - // in https://github.com/immersive-web/webxr-input-profiles/issues/105 - profiles: vec!["generic-hand".into()], + profiles: vec![], hand_support, } } diff --git a/webxr/openxr/interaction_profiles.rs b/webxr/openxr/interaction_profiles.rs new file mode 100644 index 0000000..0e3041a --- /dev/null +++ b/webxr/openxr/interaction_profiles.rs @@ -0,0 +1,409 @@ +use openxr::{ + sys::{ + BD_CONTROLLER_INTERACTION_EXTENSION_NAME, EXT_HP_MIXED_REALITY_CONTROLLER_EXTENSION_NAME, + EXT_SAMSUNG_ODYSSEY_CONTROLLER_EXTENSION_NAME, FB_TOUCH_CONTROLLER_PRO_EXTENSION_NAME, + HTC_VIVE_COSMOS_CONTROLLER_INTERACTION_EXTENSION_NAME, + HTC_VIVE_FOCUS3_CONTROLLER_INTERACTION_EXTENSION_NAME, + META_TOUCH_CONTROLLER_PLUS_EXTENSION_NAME, ML_ML2_CONTROLLER_INTERACTION_EXTENSION_NAME, + }, + ExtensionSet, +}; + +#[macro_export] +macro_rules! ext_string { + ($ext_name:expr) => { + std::str::from_utf8($ext_name).unwrap() + }; +} + +#[derive(Clone, Copy, Debug)] +pub enum InteractionProfileType { + KhrSimpleController, + BytedancePicoNeo3Controller, + BytedancePico4Controller, + BytedancePicoG3Controller, + GoogleDaydreamController, + HpMixedRealityController, + HtcViveController, + HtcViveCosmosController, + HtcViveFocus3Controller, + MagicLeap2Controller, + MicrosoftMixedRealityMotionController, + OculusGoController, + OculusTouchController, + FacebookTouchControllerPro, + MetaTouchPlusController, + MetaTouchControllerRiftCv1, + MetaTouchControllerQuest1RiftS, + MetaTouchControllerQuest2, + SamsungOdysseyController, + ValveIndexController, +} + +#[derive(Clone, Copy, Debug)] +#[allow(unused)] +pub struct InteractionProfile<'a> { + pub profile_type: InteractionProfileType, + /// The interaction profile path + pub path: &'static str, + /// The OpenXR extension, if any, required to use this profile + pub required_extension: Option<&'a [u8]>, + /// Trigger, Grip, Touchpad, Thumbstick + pub standard_buttons: &'a [&'a str], + /// Touchpad X, Touchpad Y, Thumbstick X, Thumbstick Y + pub standard_axes: &'a [&'a str], + /// Any additional buttons on the left controller + pub left_buttons: &'a [&'a str], + /// Any additional buttons on the right controller + pub right_buttons: &'a [&'a str], + /// The corresponding WebXR Input Profile names + pub profiles: &'a [&'a str], +} + +pub static KHR_SIMPLE_CONTROLLER_PROFILE: InteractionProfile = InteractionProfile { + profile_type: InteractionProfileType::KhrSimpleController, + path: "/interaction_profiles/khr/simple_controller", + required_extension: None, + standard_buttons: &["select/click", "", "", ""], + standard_axes: &["", "", "", ""], + left_buttons: &[], + right_buttons: &[], + profiles: &["generic-trigger"], +}; + +pub static BYTEDANCE_PICO_NEO3_CONTROLLER_PROFILE: InteractionProfile = InteractionProfile { + profile_type: InteractionProfileType::BytedancePicoNeo3Controller, + path: "/interaction_profiles/bytedance/pico_neo3_controller", + required_extension: Some(BD_CONTROLLER_INTERACTION_EXTENSION_NAME), + standard_buttons: &["trigger/value", "squeeze/value", "", "thumbstick/click"], + standard_axes: &["", "", "thumbstick/x", "thumbstick/y"], + left_buttons: &["x/click", "y/click"], + right_buttons: &["a/click", "b/click"], + profiles: &["pico-neo3", "generic-trigger-squeeze-thumbstick"], +}; + +pub static BYTEDANCE_PICO_4_CONTROLLER_PROFILE: InteractionProfile = InteractionProfile { + profile_type: InteractionProfileType::BytedancePico4Controller, + path: "/interaction_profiles/bytedance/pico4_controller", + required_extension: Some(BD_CONTROLLER_INTERACTION_EXTENSION_NAME), + standard_buttons: &["trigger/value", "squeeze/value", "", "thumbstick/click"], + standard_axes: &["", "", "thumbstick/x", "thumbstick/y"], + left_buttons: &["x/click", "y/click"], + right_buttons: &["a/click", "b/click"], + profiles: &["pico-4", "generic-trigger-squeeze-thumbstick"], +}; + +pub static BYTEDANCE_PICO_G3_CONTROLLER_PROFILE: InteractionProfile = InteractionProfile { + profile_type: InteractionProfileType::BytedancePicoG3Controller, + path: "/interaction_profiles/bytedance/pico_g3_controller", + required_extension: Some(BD_CONTROLLER_INTERACTION_EXTENSION_NAME), + standard_buttons: &["trigger/value", "", "", "thumbstick/click"], + // Note: X/Y components not listed in the OpenXR spec currently due to vendor error. + // See + // It also uses the thumbstick path despite clearly being a touchpad, so + // move those values into the touchpad axes slots + standard_axes: &["thumbstick/x", "thumbstick/y", "", ""], + left_buttons: &[], + right_buttons: &[], + // Note: There is no corresponding WebXR Input profile for the Pico G3, + // but the controller seems identical to the G2, so use that instead. + profiles: &["pico-g2", "generic-trigger-touchpad"], +}; + +pub static GOOGLE_DAYDREAM_CONTROLLER_PROFILE: InteractionProfile = InteractionProfile { + profile_type: InteractionProfileType::GoogleDaydreamController, + path: "/interaction_profiles/google/daydream_controller", + required_extension: None, + standard_buttons: &["select/click", "", "trackpad/click", ""], + standard_axes: &["trackpad/x", "trackpad/y", "", ""], + left_buttons: &[], + right_buttons: &[], + profiles: &["google-daydream", "generic-touchpad"], +}; + +pub static HP_MIXED_REALITY_MOTION_CONTROLLER_PROFILE: InteractionProfile = InteractionProfile { + profile_type: InteractionProfileType::HpMixedRealityController, + path: "/interaction_profiles/hp/mixed_reality_controller", + required_extension: Some(EXT_HP_MIXED_REALITY_CONTROLLER_EXTENSION_NAME), + standard_buttons: &["trigger/value", "squeeze/value", "", "thumbstick/click"], + standard_axes: &["", "", "thumbstick/x", "thumbstick/y"], + left_buttons: &["x/click", "y/click"], + right_buttons: &["a/click", "b/click"], + profiles: &[ + "hp-mixed-reality", + "oculus-touch", + "generic-trigger-squeeze-thumbstick", + ], +}; + +pub static HTC_VIVE_CONTROLLER_PROFILE: InteractionProfile = InteractionProfile { + profile_type: InteractionProfileType::HtcViveController, + path: "/interaction_profiles/htc/vive_controller", + required_extension: None, + standard_buttons: &["trigger/value", "squeeze/click", "trackpad/click", ""], + standard_axes: &["trackpad/x", "trackpad/y", "", ""], + left_buttons: &[], + right_buttons: &[], + profiles: &["htc-vive", "generic-trigger-squeeze-touchpad"], +}; + +pub static HTC_VIVE_COSMOS_CONTROLLER_PROFILE: InteractionProfile = InteractionProfile { + profile_type: InteractionProfileType::HtcViveCosmosController, + path: "/interaction_profiles/htc/vive_cosmos_controller", + required_extension: Some(HTC_VIVE_COSMOS_CONTROLLER_INTERACTION_EXTENSION_NAME), + standard_buttons: &["trigger/value", "squeeze/click", "", "thumbstick/click"], + standard_axes: &["", "", "thumbstick/x", "thumbstick/y"], + left_buttons: &["x/click", "y/click"], + right_buttons: &["a/click", "b/click"], + profiles: &["htc-vive-cosmos", "generic-trigger-squeeze-thumbstick"], +}; + +pub static HTC_VIVE_FOCUS3_CONTROLLER_PROFILE: InteractionProfile = InteractionProfile { + profile_type: InteractionProfileType::HtcViveFocus3Controller, + path: "/interaction_profiles/htc/vive_focus3_controller", + required_extension: Some(HTC_VIVE_FOCUS3_CONTROLLER_INTERACTION_EXTENSION_NAME), + standard_buttons: &["trigger/value", "squeeze/value", "", "thumbstick/click"], + standard_axes: &["", "", "thumbstick/x", "thumbstick/y"], + left_buttons: &["x/click", "y/click"], + right_buttons: &["a/click", "b/click"], + profiles: &["htc-vive-focus-3", "generic-trigger-squeeze-thumbstick"], +}; + +pub static MAGIC_LEAP_2_CONTROLLER_PROFILE: InteractionProfile = InteractionProfile { + profile_type: InteractionProfileType::MagicLeap2Controller, + path: "/interaction_profiles/ml/ml2_controller", + required_extension: Some(ML_ML2_CONTROLLER_INTERACTION_EXTENSION_NAME), + standard_buttons: &["trigger/value", "", "trackpad/click", ""], + standard_axes: &["trackpad/x", "trackpad/y", "", ""], + left_buttons: &[], + right_buttons: &[], + // Note: There is no corresponding WebXR Input profile for the Magic Leap 2, + // but the controller seems mostly identical to the 1, so use that instead. + profiles: &["magicleap-one", "generic-trigger-squeeze-touchpad"], +}; + +pub static MICROSOFT_MIXED_REALITY_MOTION_CONTROLLER_PROFILE: InteractionProfile = + InteractionProfile { + profile_type: InteractionProfileType::MicrosoftMixedRealityMotionController, + path: "/interaction_profiles/microsoft/motion_controller", + required_extension: None, + standard_buttons: &[ + "trigger/value", + "squeeze/click", + "trackpad/click", + "thumbstick/click", + ], + standard_axes: &["trackpad/x", "trackpad/y", "thumbstick/x", "thumbstick/y"], + left_buttons: &[], + right_buttons: &[], + profiles: &[ + "microsoft-mixed-reality", + "generic-trigger-squeeze-touchpad-thumbstick", + ], + }; + +pub static OCULUS_GO_CONTROLLER_PROFILE: InteractionProfile = InteractionProfile { + profile_type: InteractionProfileType::OculusGoController, + path: "/interaction_profiles/oculus/go_controller", + required_extension: None, + standard_buttons: &["trigger/click", "", "trackpad/click", ""], + standard_axes: &["trackpad/x", "trackpad/y", "", ""], + left_buttons: &[], + right_buttons: &[], + profiles: &["oculus-go", "generic-trigger-touchpad"], +}; + +pub static OCULUS_TOUCH_CONTROLLER_PROFILE: InteractionProfile = InteractionProfile { + profile_type: InteractionProfileType::OculusTouchController, + path: "/interaction_profiles/oculus/touch_controller", + required_extension: None, + standard_buttons: &["trigger/value", "squeeze/value", "", "thumbstick/click"], + standard_axes: &["", "", "thumbstick/x", "thumbstick/y"], + left_buttons: &["x/click", "y/click"], + right_buttons: &["a/click", "b/click"], + profiles: &[ + "oculus-touch-v3", + "oculus-touch-v2", + "oculus-touch", + "generic-trigger-squeeze-thumbstick", + ], +}; + +pub static FACEBOOK_TOUCH_CONTROLLER_PRO_PROFILE: InteractionProfile = InteractionProfile { + profile_type: InteractionProfileType::FacebookTouchControllerPro, + path: "/interaction_profiles/facebook/touch_controller_pro", + required_extension: Some(FB_TOUCH_CONTROLLER_PRO_EXTENSION_NAME), + standard_buttons: &["trigger/value", "squeeze/value", "", "thumbstick/click"], + standard_axes: &["", "", "thumbstick/x", "thumbstick/y"], + left_buttons: &["x/click", "y/click"], + right_buttons: &["a/click", "b/click"], + profiles: &[ + "meta-quest-touch-pro", + "oculus-touch-v2", + "oculus-touch", + "generic-trigger-squeeze-thumbstick", + ], +}; + +pub static META_TOUCH_CONTROLLER_PLUS_PROFILE: InteractionProfile = InteractionProfile { + profile_type: InteractionProfileType::MetaTouchPlusController, + path: "/interaction_profiles/meta/touch_controller_plus", + required_extension: Some(META_TOUCH_CONTROLLER_PLUS_EXTENSION_NAME), + standard_buttons: &["trigger/value", "squeeze/value", "", "thumbstick/click"], + standard_axes: &["", "", "thumbstick/x", "thumbstick/y"], + left_buttons: &["x/click", "y/click"], + right_buttons: &["a/click", "b/click"], + profiles: &[ + "meta-quest-touch-plus", + "oculus-touch-v3", + "oculus-touch", + "generic-trigger-squeeze-thumbstick", + ], +}; + +pub static META_TOUCH_CONTROLLER_RIFT_CV1_PROFILE: InteractionProfile = InteractionProfile { + profile_type: InteractionProfileType::MetaTouchControllerRiftCv1, + path: "/interaction_profiles/meta/touch_controller_rift_cv1", + required_extension: None, + standard_buttons: &["trigger/value", "squeeze/value", "", "thumbstick/click"], + standard_axes: &["", "", "thumbstick/x", "thumbstick/y"], + left_buttons: &["x/click", "y/click"], + right_buttons: &["a/click", "b/click"], + profiles: &["oculus-touch", "generic-trigger-squeeze-thumbstick"], +}; + +pub static META_TOUCH_CONTROLLER_QUEST_1_RIFT_S_PROFILE: InteractionProfile = InteractionProfile { + profile_type: InteractionProfileType::MetaTouchControllerQuest1RiftS, + path: "/interaction_profiles/meta/touch_controller_quest_1_rift_s", + required_extension: None, + standard_buttons: &["trigger/value", "squeeze/value", "", "thumbstick/click"], + standard_axes: &["", "", "thumbstick/x", "thumbstick/y"], + left_buttons: &["x/click", "y/click"], + right_buttons: &["a/click", "b/click"], + profiles: &[ + "oculus-touch-v2", + "oculus-touch", + "generic-trigger-squeeze-thumbstick", + ], +}; + +pub static META_TOUCH_CONTROLLER_QUEST_2_PROFILE: InteractionProfile = InteractionProfile { + profile_type: InteractionProfileType::MetaTouchControllerQuest2, + path: "/interaction_profiles/meta/touch_controller_quest_2", + required_extension: None, + standard_buttons: &["trigger/value", "squeeze/value", "", "thumbstick/click"], + standard_axes: &["", "", "thumbstick/x", "thumbstick/y"], + left_buttons: &["x/click", "y/click"], + right_buttons: &["a/click", "b/click"], + profiles: &[ + "oculus-touch-v3", + "oculus-touch-v2", + "oculus-touch", + "generic-trigger-squeeze-thumbstick", + ], +}; + +pub static SAMSUNG_ODYSSEY_CONTROLLER_PROFILE: InteractionProfile = InteractionProfile { + profile_type: InteractionProfileType::SamsungOdysseyController, + path: "/interaction_profiles/samsung/odyssey_controller", + required_extension: Some(EXT_SAMSUNG_ODYSSEY_CONTROLLER_EXTENSION_NAME), + standard_buttons: &[ + "trigger/value", + "squeeze/click", + "trackpad/click", + "thumbstick/click", + ], + standard_axes: &["trackpad/x", "trackpad/y", "thumbstick/x", "thumbstick/y"], + left_buttons: &[], + right_buttons: &[], + profiles: &[ + "samsung-odyssey", + "microsoft-mixed-reality", + "generic-trigger-squeeze-touchpad-thumbstick", + ], +}; + +pub static VALVE_INDEX_CONTROLLER_PROFILE: InteractionProfile = InteractionProfile { + profile_type: InteractionProfileType::ValveIndexController, + path: "/interaction_profiles/valve/index_controller", + required_extension: None, + standard_buttons: &["trigger/value", "squeeze/value", "", "thumbstick/click"], + standard_axes: &["trackpad/x", "trackpad/y", "thumbstick/x", "thumbstick/y"], + left_buttons: &["a/click", "b/click"], + right_buttons: &["a/click", "b/click"], + profiles: &["valve-index", "generic-trigger-squeeze-touchpad-thumbstick"], +}; + +pub static INTERACTION_PROFILES: [InteractionProfile; 20] = [ + KHR_SIMPLE_CONTROLLER_PROFILE, + BYTEDANCE_PICO_NEO3_CONTROLLER_PROFILE, + BYTEDANCE_PICO_4_CONTROLLER_PROFILE, + BYTEDANCE_PICO_G3_CONTROLLER_PROFILE, + GOOGLE_DAYDREAM_CONTROLLER_PROFILE, + HP_MIXED_REALITY_MOTION_CONTROLLER_PROFILE, + HTC_VIVE_CONTROLLER_PROFILE, + HTC_VIVE_COSMOS_CONTROLLER_PROFILE, + HTC_VIVE_FOCUS3_CONTROLLER_PROFILE, + MAGIC_LEAP_2_CONTROLLER_PROFILE, + MICROSOFT_MIXED_REALITY_MOTION_CONTROLLER_PROFILE, + OCULUS_GO_CONTROLLER_PROFILE, + OCULUS_TOUCH_CONTROLLER_PROFILE, + FACEBOOK_TOUCH_CONTROLLER_PRO_PROFILE, + META_TOUCH_CONTROLLER_PLUS_PROFILE, + META_TOUCH_CONTROLLER_RIFT_CV1_PROFILE, + META_TOUCH_CONTROLLER_QUEST_1_RIFT_S_PROFILE, + META_TOUCH_CONTROLLER_QUEST_2_PROFILE, + SAMSUNG_ODYSSEY_CONTROLLER_PROFILE, + VALVE_INDEX_CONTROLLER_PROFILE, +]; + +pub fn get_profiles_from_path(path: String) -> &'static [&'static str] { + INTERACTION_PROFILES + .iter() + .find(|profile| profile.path == path) + .map_or(&[], |profile| profile.profiles) +} + +pub fn get_supported_interaction_profiles( + supported_extensions: &ExtensionSet, + enabled_extensions: &mut ExtensionSet, +) -> Vec<&'static str> { + let mut extensions = Vec::new(); + if supported_extensions.bd_controller_interaction { + extensions.push(ext_string!(BD_CONTROLLER_INTERACTION_EXTENSION_NAME)); + enabled_extensions.bd_controller_interaction = true; + } + if supported_extensions.ext_hp_mixed_reality_controller { + extensions.push(ext_string!(EXT_HP_MIXED_REALITY_CONTROLLER_EXTENSION_NAME)); + enabled_extensions.ext_hp_mixed_reality_controller = true; + } + if supported_extensions.ext_samsung_odyssey_controller { + extensions.push(ext_string!(EXT_SAMSUNG_ODYSSEY_CONTROLLER_EXTENSION_NAME)); + enabled_extensions.ext_samsung_odyssey_controller = true; + } + if supported_extensions.ml_ml2_controller_interaction { + extensions.push(ext_string!(ML_ML2_CONTROLLER_INTERACTION_EXTENSION_NAME)); + enabled_extensions.ml_ml2_controller_interaction = true; + } + if supported_extensions.htc_vive_cosmos_controller_interaction { + extensions.push(ext_string!( + HTC_VIVE_COSMOS_CONTROLLER_INTERACTION_EXTENSION_NAME + )); + enabled_extensions.htc_vive_cosmos_controller_interaction = true; + } + if supported_extensions.htc_vive_focus3_controller_interaction { + extensions.push(ext_string!( + HTC_VIVE_FOCUS3_CONTROLLER_INTERACTION_EXTENSION_NAME + )); + enabled_extensions.htc_vive_focus3_controller_interaction = true; + } + if supported_extensions.fb_touch_controller_pro { + extensions.push(ext_string!(FB_TOUCH_CONTROLLER_PRO_EXTENSION_NAME)); + enabled_extensions.fb_touch_controller_pro = true; + } + if supported_extensions.meta_touch_controller_plus { + extensions.push(ext_string!(META_TOUCH_CONTROLLER_PLUS_EXTENSION_NAME)); + enabled_extensions.meta_touch_controller_plus = true; + } + extensions +} diff --git a/webxr/openxr/mod.rs b/webxr/openxr/mod.rs index fca2718..f635c2a 100644 --- a/webxr/openxr/mod.rs +++ b/webxr/openxr/mod.rs @@ -8,15 +8,15 @@ use euclid::Rotation3D; use euclid::Size2D; use euclid::Transform3D; use euclid::Vector3D; +use interaction_profiles::{get_profiles_from_path, get_supported_interaction_profiles}; use log::{error, warn}; use openxr::d3d::{Requirements, SessionCreateInfoD3D11, D3D11}; -use openxr::Graphics; use openxr::{ self, ActionSet, ActiveActionSet, ApplicationInfo, CompositionLayerFlags, CompositionLayerProjection, Entry, EnvironmentBlendMode, ExtensionSet, Extent2Di, FormFactor, - Fovf, FrameState, FrameStream, FrameWaiter, Instance, Posef, Quaternionf, ReferenceSpaceType, - SecondaryEndInfo, Session, Space, Swapchain, SwapchainCreateFlags, SwapchainCreateInfo, - SwapchainUsageFlags, SystemId, Vector3f, ViewConfigurationType, + Fovf, FrameState, FrameStream, FrameWaiter, Graphics, Instance, Posef, Quaternionf, + ReferenceSpaceType, SecondaryEndInfo, Session, Space, Swapchain, SwapchainCreateFlags, + SwapchainCreateInfo, SwapchainUsageFlags, SystemId, Vector3f, Version, ViewConfigurationType, }; use sparkle::gl; use sparkle::gl::GLuint; @@ -79,6 +79,7 @@ use wio::com::ComPtr; mod input; use input::OpenXRInput; +mod interaction_profiles; const HEIGHT: f32 = 1.4; @@ -188,6 +189,7 @@ pub struct CreatedInstance { supports_secondary: bool, system: SystemId, supports_mutable_fov: bool, + supported_interaction_profiles: Vec<&'static str>, } pub fn create_instance( @@ -208,6 +210,7 @@ pub fn create_instance( application_version: 1, engine_name: "servo", engine_version: 1, + api_version: Version::new(1, 0, 36), }; let mut exts = ExtensionSet::default(); @@ -221,6 +224,8 @@ pub fn create_instance( exts.msft_first_person_observer = true; } + let supported_interaction_profiles = get_supported_interaction_profiles(&supported, &mut exts); + let instance = entry .create_instance(&app_info, &exts, &[]) .map_err(|e| format!("Entry::create_instance {:?}", e))?; @@ -247,6 +252,7 @@ pub fn create_instance( supports_secondary, system, supports_mutable_fov, + supported_interaction_profiles, }) } @@ -870,6 +876,7 @@ impl OpenXrDevice { supports_secondary, system, supports_mutable_fov, + supported_interaction_profiles, } = instance; let (init_tx, init_rx) = crossbeam_channel::unbounded(); @@ -1041,8 +1048,12 @@ impl OpenXrDevice { }); drop(data); - let (action_set, right_hand, left_hand) = - OpenXRInput::setup_inputs(&instance, &session, supports_hands); + let (action_set, right_hand, left_hand) = OpenXRInput::setup_inputs( + &instance, + &session, + supports_hands, + supported_interaction_profiles, + ); Ok(OpenXrDevice { instance, @@ -1115,6 +1126,33 @@ impl OpenXrDevice { self.events.callback(Event::SessionEnd); return false; } + Some(InteractionProfileChanged(_)) => { + let path = self.instance.string_to_path("/user/hand/right").unwrap(); + let profile_path = self.session.current_interaction_profile(path).unwrap(); + let profile = self.instance.path_to_string(profile_path); + + match profile { + Ok(profile) => { + let profiles = get_profiles_from_path(profile) + .iter() + .map(|s| s.to_string()) + .collect(); + + let mut new_left = self.left_hand.input_source(); + new_left.profiles.clone_from(&profiles); + self.events + .callback(Event::UpdateInput(new_left.id, new_left)); + + let mut new_right = self.right_hand.input_source(); + new_right.profiles.clone_from(&profiles); + self.events + .callback(Event::UpdateInput(new_right.id, new_right)); + } + Err(e) => { + error!("Failed to get interaction profile: {:?}", e); + } + } + } Some(_) => { // FIXME: Handle other events }