diff --git a/alvr/client_openxr/src/extra_extensions/multimodal_input.rs b/alvr/client_openxr/src/extra_extensions/multimodal_input.rs index ac55375208..4cc97f8c5a 100644 --- a/alvr/client_openxr/src/extra_extensions/multimodal_input.rs +++ b/alvr/client_openxr/src/extra_extensions/multimodal_input.rs @@ -14,20 +14,32 @@ pub const META_DETACHED_CONTROLLERS_EXTENSION_NAME: &str = "XR_META_detached_con static TYPE_SIMULTANEOUS_HANDS_AND_CONTROLLERS_TRACKING_RESUME_INFO_META: Lazy = Lazy::new(|| xr::StructureType::from_raw(1000532002)); +static TYPE_SIMULTANEOUS_HANDS_AND_CONTROLLERS_TRACKING_PAUSE_INFO_META: Lazy = + Lazy::new(|| xr::StructureType::from_raw(1000532003)); #[repr(C)] pub struct SimultaneousHandsAndControllersTrackingResumeInfoMETA { ty: xr::StructureType, next: *const c_void, } +#[repr(C)] +pub struct SimultaneousHandsAndControllersTrackingPauseInfoMETA { + ty: xr::StructureType, + next: *const c_void, +} pub type ResumeSimultaneousHandsAndControllersTrackingMETA = unsafe extern "system" fn( sys::Session, *const SimultaneousHandsAndControllersTrackingResumeInfoMETA, ) -> sys::Result; +pub type PauseSimultaneousHandsAndControllersTrackingMETA = + unsafe extern "system" fn( + sys::Session, + *const SimultaneousHandsAndControllersTrackingPauseInfoMETA, + ) -> sys::Result; -pub fn resume_simultaneous_hands_and_controllers_tracking( +pub fn resume_simultaneous_hands_and_controllers_tracking_meta( session: &xr::Session, ) -> xr::Result<()> { let resume_simultaneous_hands_and_controllers_tracking_meta = unsafe { @@ -48,13 +60,39 @@ pub fn resume_simultaneous_hands_and_controllers_tracking( ty: *TYPE_SIMULTANEOUS_HANDS_AND_CONTROLLERS_TRACKING_RESUME_INFO_META, next: ptr::null(), }; - unsafe { super::xr_res(resume_simultaneous_hands_and_controllers_tracking_meta( session.as_raw(), &resume_info, - ))?; + )) + } +} + +pub fn pause_simultaneous_hands_and_controllers_tracking_meta( + session: &xr::Session, +) -> xr::Result<()> { + let pause_simultaneous_hands_and_controllers_tracking_meta = unsafe { + let mut pause_simultaneous_hands_and_controllers_tracking_meta = None; + let _ = (session.instance().fp().get_instance_proc_addr)( + session.instance().as_raw(), + c"xrPauseSimultaneousHandsAndControllersTrackingMETA".as_ptr(), + &mut pause_simultaneous_hands_and_controllers_tracking_meta, + ); + + pause_simultaneous_hands_and_controllers_tracking_meta.map(|pfn| { + mem::transmute::(pfn) + }) } + .ok_or(sys::Result::ERROR_EXTENSION_NOT_PRESENT)?; - Ok(()) + let pause_info = SimultaneousHandsAndControllersTrackingPauseInfoMETA { + ty: *TYPE_SIMULTANEOUS_HANDS_AND_CONTROLLERS_TRACKING_PAUSE_INFO_META, + next: ptr::null(), + }; + unsafe { + super::xr_res(pause_simultaneous_hands_and_controllers_tracking_meta( + session.as_raw(), + &pause_info, + )) + } } diff --git a/alvr/client_openxr/src/interaction.rs b/alvr/client_openxr/src/interaction.rs index 3bdfe5b954..8bc194a249 100644 --- a/alvr/client_openxr/src/interaction.rs +++ b/alvr/client_openxr/src/interaction.rs @@ -5,10 +5,10 @@ use crate::{ FULL_BODY_JOINT_LEFT_FOOT_BALL_META, FULL_BODY_JOINT_LEFT_LOWER_LEG_META, FULL_BODY_JOINT_RIGHT_FOOT_BALL_META, FULL_BODY_JOINT_RIGHT_LOWER_LEG_META, }, - Platform, XrContext, + Platform, }; use alvr_common::{glam::Vec3, *}; -use alvr_packets::{ButtonEntry, ButtonValue, ViewParams}; +use alvr_packets::{ButtonEntry, ButtonValue, StreamConfig, ViewParams}; use alvr_session::{BodyTrackingSourcesConfig, FaceTrackingSourcesConfig}; use openxr as xr; use std::collections::HashMap; @@ -16,6 +16,28 @@ use xr::SpaceLocationFlags; const IPD_CHANGE_EPS: f32 = 0.001; +fn create_ext_object( + name: &str, + enabled: Option, + create_cb: impl FnOnce() -> xr::Result, +) -> Option { + enabled + .unwrap_or(false) + .then(|| match create_cb() { + Ok(obj) => Some(obj), + Err(xr::sys::Result::ERROR_FEATURE_UNSUPPORTED) => { + warn!("Cannot create unsupported {name}"); + None + } + Err(xr::sys::Result::ERROR_EXTENSION_NOT_PRESENT) => None, + Err(e) => { + warn!("Failed to create {name}: {e}"); + None + } + }) + .flatten() +} + pub enum ButtonAction { Binary(xr::Action), Scalar(xr::Action), @@ -43,223 +65,222 @@ pub struct BodySources { pub body_tracker_fb: Option<(BodyTrackerFB, usize)>, } +#[derive(Clone)] +pub struct InteractionSourcesConfig { + pub face_tracking: Option, + pub body_tracking: Option, + pub prefers_multimodal_input: bool, +} + +impl InteractionSourcesConfig { + pub fn new(config: &StreamConfig) -> Self { + Self { + face_tracking: config + .settings + .headset + .face_tracking + .as_option() + .map(|c| c.sources.clone()), + body_tracking: config + .settings + .headset + .body_tracking + .as_option() + .map(|c| c.sources.clone()), + prefers_multimodal_input: config + .settings + .headset + .controllers + .as_option() + .map(|c| c.multimodal_tracking) + .unwrap_or(false), + } + } +} + pub struct InteractionContext { + xr_session: xr::Session, + platform: Platform, pub action_set: xr::ActionSet, pub button_actions: HashMap, pub hands_interaction: [HandInteraction; 2], - pub uses_multimodal_hands: bool, + pub multimodal_hands_enabled: bool, pub face_sources: FaceSources, pub body_sources: BodySources, } -pub fn initialize_interaction( - xr_ctx: &XrContext, - platform: Platform, - prefer_multimodal_input: bool, - face_tracking_sources: Option, - body_tracking_sources: Option, -) -> InteractionContext { - // todo: check which permissions are needed for htc - #[cfg(target_os = "android")] - if let Some(config) = &face_tracking_sources { - if (config.combined_eye_gaze || config.eye_tracking_fb) - && matches!(platform, Platform::QuestPro) - { - alvr_system_info::try_get_permission("com.oculus.permission.EYE_TRACKING") - } - if config.combined_eye_gaze && platform.is_pico() { - alvr_system_info::try_get_permission("com.picovr.permission.EYE_TRACKING") - } - if config.face_tracking_fb && matches!(platform, Platform::QuestPro) { - alvr_system_info::try_get_permission("android.permission.RECORD_AUDIO"); - alvr_system_info::try_get_permission("com.oculus.permission.FACE_TRACKING") - } - } +impl InteractionContext { + pub fn new( + xr_session: xr::Session, + platform: Platform, + supports_multimodal: bool, + ) -> Self { + let xr_instance = xr_session.instance(); - #[cfg(target_os = "android")] - if let Some(config) = &body_tracking_sources { - if (config.body_tracking_fb.enabled()) - && platform.is_quest() - && platform != Platform::Quest1 - { - alvr_system_info::try_get_permission("com.oculus.permission.BODY_TRACKING") + let action_set = xr_instance + .create_action_set("alvr_interaction", "ALVR interaction", 0) + .unwrap(); + + let mut bindings = vec![]; + + fn binding<'a, T: xr::ActionTy>(action: &'a xr::Action, path: &str) -> xr::Binding<'a> { + xr::Binding::new(action, action.instance().string_to_path(path).unwrap()) } - } - let action_set = xr_ctx - .instance - .create_action_set("alvr_interaction", "ALVR interaction", 0) - .unwrap(); + let controllers_profile_path = match platform { + p if p.is_quest() => QUEST_CONTROLLER_PROFILE_PATH, // todo: create new controller profile for quest pro and 3 + Platform::PicoNeo3 => PICO_NEO3_CONTROLLER_PROFILE_PATH, + p if p.is_pico() => PICO4_CONTROLLER_PROFILE_PATH, + p if p.is_vive() => FOCUS3_CONTROLLER_PROFILE_PATH, + Platform::Yvr => YVR_CONTROLLER_PROFILE_PATH, + _ => QUEST_CONTROLLER_PROFILE_PATH, + }; + let controllers_profile_id = alvr_common::hash_string(controllers_profile_path); - let mut bindings = vec![]; + // Create actions: - fn binding<'a, T: xr::ActionTy>(action: &'a xr::Action, path: &str) -> xr::Binding<'a> { - xr::Binding::new(action, action.instance().string_to_path(path).unwrap()) - } + let mut button_actions = HashMap::new(); + for button_id in &CONTROLLER_PROFILE_INFO + .get(&controllers_profile_id) + .unwrap() + .button_set + { + let info = BUTTON_INFO.get(button_id).unwrap(); - let controllers_profile_path = match platform { - p if p.is_quest() => QUEST_CONTROLLER_PROFILE_PATH, // todo: create new controller profile for quest pro and 3 - Platform::PicoNeo3 => PICO_NEO3_CONTROLLER_PROFILE_PATH, - p if p.is_pico() => PICO4_CONTROLLER_PROFILE_PATH, - p if p.is_vive() => FOCUS3_CONTROLLER_PROFILE_PATH, - Platform::Yvr => YVR_CONTROLLER_PROFILE_PATH, - _ => QUEST_CONTROLLER_PROFILE_PATH, - }; - let controllers_profile_id = alvr_common::hash_string(controllers_profile_path); + let name = info.path[1..].replace('/', "_"); + let display_name = format!( + "{}{}", + name[0..1].to_uppercase(), + name[1..].replace('_', " ") + ); - // Create actions: + let action = match info.button_type { + ButtonType::Binary => ButtonAction::Binary( + action_set.create_action(&name, &display_name, &[]).unwrap(), + ), + ButtonType::Scalar => ButtonAction::Scalar( + action_set.create_action(&name, &display_name, &[]).unwrap(), + ), + }; + button_actions.insert(*button_id, action); + } - let mut button_actions = HashMap::new(); - for button_id in &CONTROLLER_PROFILE_INFO - .get(&controllers_profile_id) - .unwrap() - .button_set - { - let info = BUTTON_INFO.get(button_id).unwrap(); + let left_grip_action = action_set + .create_action("left_grip_pose", "Left grip pose", &[]) + .unwrap(); + let right_grip_action = action_set + .create_action("right_grip_pose", "Right grip pose", &[]) + .unwrap(); - let name = info.path[1..].replace('/', "_"); - let display_name = format!( - "{}{}", - name[0..1].to_uppercase(), - name[1..].replace('_', " ") - ); + let left_aim_action = action_set + .create_action("left_aim_pose", "Left aim pose", &[]) + .unwrap(); + let right_aim_action = action_set + .create_action("right_aim_pose", "Right aim pose", &[]) + .unwrap(); - let action = match info.button_type { - ButtonType::Binary => { - ButtonAction::Binary(action_set.create_action(&name, &display_name, &[]).unwrap()) - } - ButtonType::Scalar => { - ButtonAction::Scalar(action_set.create_action(&name, &display_name, &[]).unwrap()) - } - }; - button_actions.insert(*button_id, action); - } + let left_vibration_action = action_set + .create_action("left_hand_vibration", "Left hand vibration", &[]) + .unwrap(); + let right_vibration_action = action_set + .create_action("right_hand_vibration", "Right hand vibration", &[]) + .unwrap(); - let left_grip_action = action_set - .create_action("left_grip_pose", "Left grip pose", &[]) - .unwrap(); - let right_grip_action = action_set - .create_action("right_grip_pose", "Right grip pose", &[]) - .unwrap(); - - let left_aim_action = action_set - .create_action("left_aim_pose", "Left aim pose", &[]) - .unwrap(); - let right_aim_action = action_set - .create_action("right_aim_pose", "Right aim pose", &[]) - .unwrap(); - - let left_vibration_action = action_set - .create_action("left_hand_vibration", "Left hand vibration", &[]) - .unwrap(); - let right_vibration_action = action_set - .create_action("right_hand_vibration", "Right hand vibration", &[]) - .unwrap(); - - // Create action bindings: - - for (id, action) in &button_actions { - let path = &BUTTON_INFO.get(id).unwrap().path; - match action { - ButtonAction::Binary(action) => { - bindings.push(binding(action, path)); - } - ButtonAction::Scalar(action) => { - bindings.push(binding(action, path)); + // Create action bindings: + + for (id, action) in &button_actions { + let path = &BUTTON_INFO.get(id).unwrap().path; + match action { + ButtonAction::Binary(action) => { + bindings.push(binding(action, path)); + } + ButtonAction::Scalar(action) => { + bindings.push(binding(action, path)); + } } } - } - bindings.push(binding( - &left_grip_action, - "/user/hand/left/input/grip/pose", - )); - bindings.push(binding( - &right_grip_action, - "/user/hand/right/input/grip/pose", - )); - - bindings.push(binding(&left_aim_action, "/user/hand/left/input/aim/pose")); - bindings.push(binding( - &right_aim_action, - "/user/hand/right/input/aim/pose", - )); - - bindings.push(binding( - &left_vibration_action, - "/user/hand/left/output/haptic", - )); - bindings.push(binding( - &right_vibration_action, - "/user/hand/right/output/haptic", - )); - - // Note: We cannot enable multimodal if fb body tracking is active. It would result in a - // ERROR_RUNTIME_FAILURE crash. - let uses_multimodal_hands = prefer_multimodal_input - && !body_tracking_sources - .as_ref() - .map(|s| s.body_tracking_fb.enabled()) - .unwrap_or(false) - && extra_extensions::resume_simultaneous_hands_and_controllers_tracking(&xr_ctx.session) - .is_ok(); - - let left_detached_controller_pose_action; - let right_detached_controller_pose_action; - if uses_multimodal_hands { - // Note: when multimodal input is enabled, both controllers and hands will always be active. - // To be able to detect when controllers are actually held, we have to register detached - // controllers pose; the controller pose will be diverted to the detached controllers when - // they are not held. Currently the detached controllers pose is ignored - left_detached_controller_pose_action = action_set - .create_action::( - "left_detached_controller_pose", - "Left detached controller pose", - &[], - ) - .unwrap(); - right_detached_controller_pose_action = action_set - .create_action::( - "right_detached_controller_pose", - "Right detached controller pose", - &[], - ) - .unwrap(); + bindings.push(binding( + &left_grip_action, + "/user/hand/left/input/grip/pose", + )); + bindings.push(binding( + &right_grip_action, + "/user/hand/right/input/grip/pose", + )); + bindings.push(binding(&left_aim_action, "/user/hand/left/input/aim/pose")); bindings.push(binding( - &left_detached_controller_pose_action, - "/user/detached_controller_meta/left/input/grip/pose", + &right_aim_action, + "/user/hand/right/input/aim/pose", )); + bindings.push(binding( - &right_detached_controller_pose_action, - "/user/detached_controller_meta/right/input/grip/pose", + &left_vibration_action, + "/user/hand/left/output/haptic", + )); + bindings.push(binding( + &right_vibration_action, + "/user/hand/right/output/haptic", )); - } - // Apply bindings: - xr_ctx - .instance - .suggest_interaction_profile_bindings( - xr_ctx - .instance - .string_to_path(controllers_profile_path) - .unwrap(), - &bindings, - ) - .unwrap(); + let left_detached_controller_pose_action; + let right_detached_controller_pose_action; + if supports_multimodal { + // Note: when multimodal input is enabled, both controllers and hands will always be active. + // To be able to detect when controllers are actually held, we have to register detached + // controllers pose; the controller pose will be diverted to the detached controllers when + // they are not held. Currently the detached controllers pose is ignored + left_detached_controller_pose_action = action_set + .create_action::( + "left_detached_controller_pose", + "Left detached controller pose", + &[], + ) + .unwrap(); + right_detached_controller_pose_action = action_set + .create_action::( + "right_detached_controller_pose", + "Right detached controller pose", + &[], + ) + .unwrap(); + + bindings.push(binding( + &left_detached_controller_pose_action, + "/user/detached_controller_meta/left/input/grip/pose", + )); + bindings.push(binding( + &right_detached_controller_pose_action, + "/user/detached_controller_meta/right/input/grip/pose", + )); + } + + // Apply bindings: + xr_instance + .suggest_interaction_profile_bindings( + xr_instance + .string_to_path(controllers_profile_path) + .unwrap(), + &bindings, + ) + .unwrap(); + + let combined_eyes_source = if xr_instance.exts().ext_eye_gaze_interaction.is_some() + && !platform.is_quest() + && !platform.is_vive() + { + #[cfg(target_os = "android")] + if platform.is_pico() { + alvr_system_info::try_get_permission("com.picovr.permission.EYE_TRACKING") + } - let combined_eyes_source = face_tracking_sources - .as_ref() - .map(|s| s.combined_eye_gaze) - .unwrap_or(false) - .then(|| { let action = action_set .create_action("combined_eye_gaze", "Combined eye gaze", &[]) .unwrap(); - let res = xr_ctx.instance.suggest_interaction_profile_bindings( - xr_ctx - .instance + let res = xr_instance.suggest_interaction_profile_bindings( + xr_instance .string_to_path("/interaction_profiles/ext/eye_gaze_interaction") .unwrap(), &[binding(&action, "/user/eyes_ext/input/gaze_ext/pose")], @@ -269,136 +290,166 @@ pub fn initialize_interaction( } let space = action - .create_space(xr_ctx.session.clone(), xr::Path::NULL, xr::Posef::IDENTITY) + .create_space(xr_session.clone(), xr::Path::NULL, xr::Posef::IDENTITY) .unwrap(); Some((action, space)) - }) - .flatten(); - - xr_ctx.session.attach_action_sets(&[&action_set]).unwrap(); - - let left_grip_space = left_grip_action - .create_space(xr_ctx.session.clone(), xr::Path::NULL, xr::Posef::IDENTITY) - .unwrap(); - let right_grip_space = right_grip_action - .create_space(xr_ctx.session.clone(), xr::Path::NULL, xr::Posef::IDENTITY) - .unwrap(); - - let left_aim_space = left_aim_action - .create_space(xr_ctx.session.clone(), xr::Path::NULL, xr::Posef::IDENTITY) - .unwrap(); - let right_aim_space = right_aim_action - .create_space(xr_ctx.session.clone(), xr::Path::NULL, xr::Posef::IDENTITY) - .unwrap(); - - fn create_ext_object( - name: &str, - enabled: Option, - create_cb: impl FnOnce() -> xr::Result, - ) -> Option { - enabled - .unwrap_or(false) - .then(|| match create_cb() { - Ok(obj) => Some(obj), - Err(xr::sys::Result::ERROR_FEATURE_UNSUPPORTED) => { - warn!("Cannot create unsupported {name}"); - None - } - Err(xr::sys::Result::ERROR_EXTENSION_NOT_PRESENT) => None, - Err(e) => { - warn!("Failed to create {name}: {e}"); - None - } - }) - .flatten() - } + } else { + None + }; - let left_hand_tracker = create_ext_object("HandTracker (left)", Some(true), || { - xr_ctx.session.create_hand_tracker(xr::Hand::LEFT) - }); - let right_hand_tracker = create_ext_object("HandTracker (right)", Some(true), || { - xr_ctx.session.create_hand_tracker(xr::Hand::RIGHT) - }); - - let eye_tracker_fb = create_ext_object( - "EyeTrackerSocial", - face_tracking_sources.as_ref().map(|s| s.eye_tracking_fb), - || EyeTrackerSocial::new(&xr_ctx.session), - ); - - let face_tracker_fb = create_ext_object( - "FaceTracker2FB", - face_tracking_sources.as_ref().map(|s| s.face_tracking_fb), - || FaceTracker2FB::new(&xr_ctx.session, true, true), - ); - - let eye_tracker_htc = create_ext_object( - "FacialTrackerHTC (eyes)", - face_tracking_sources - .as_ref() - .map(|s| s.eye_expressions_htc), - || FacialTrackerHTC::new(&xr_ctx.session, xr::FacialTrackingTypeHTC::EYE_DEFAULT), - ); - - let lip_tracker_htc = create_ext_object( - "FacialTrackerHTC (lips)", - face_tracking_sources - .as_ref() - .map(|s| s.lip_expressions_htc), - || FacialTrackerHTC::new(&xr_ctx.session, xr::FacialTrackingTypeHTC::LIP_DEFAULT), - ); - - let body_tracker_fb = create_ext_object( - "BodyTrackerFB (full set)", - body_tracking_sources - .clone() - .and_then(|s| s.body_tracking_fb.into_option()) - .map(|c| c.full_body), - || BodyTrackerFB::new(&xr_ctx.session, *BODY_JOINT_SET_FULL_BODY_META), - ) - .map(|tracker| (tracker, FULL_BODY_JOINT_COUNT_META)) - .or_else(|| { - create_ext_object( - "BodyTrackerFB (default set)", - body_tracking_sources.map(|s| s.body_tracking_fb.enabled()), - || BodyTrackerFB::new(&xr_ctx.session, xr::BodyJointSetFB::DEFAULT), - ) - .map(|tracker| (tracker, xr::BodyJointFB::COUNT.into_raw() as usize)) - }); - - InteractionContext { - action_set, - button_actions, - hands_interaction: [ - HandInteraction { - controllers_profile_id, - grip_action: left_grip_action, - grip_space: left_grip_space, - aim_action: left_aim_action, - aim_space: left_aim_space, - vibration_action: left_vibration_action, - skeleton_tracker: left_hand_tracker, + xr_session.attach_action_sets(&[&action_set]).unwrap(); + + let left_grip_space = left_grip_action + .create_space(xr_session.clone(), xr::Path::NULL, xr::Posef::IDENTITY) + .unwrap(); + let right_grip_space = right_grip_action + .create_space(xr_session.clone(), xr::Path::NULL, xr::Posef::IDENTITY) + .unwrap(); + + let left_aim_space = left_aim_action + .create_space(xr_session.clone(), xr::Path::NULL, xr::Posef::IDENTITY) + .unwrap(); + let right_aim_space = right_aim_action + .create_space(xr_session.clone(), xr::Path::NULL, xr::Posef::IDENTITY) + .unwrap(); + + let left_hand_tracker = create_ext_object("HandTracker (left)", Some(true), || { + xr_session.create_hand_tracker(xr::Hand::LEFT) + }); + let right_hand_tracker = create_ext_object("HandTracker (right)", Some(true), || { + xr_session.create_hand_tracker(xr::Hand::RIGHT) + }); + + Self { + xr_session, + platform, + action_set, + button_actions, + hands_interaction: [ + HandInteraction { + controllers_profile_id, + grip_action: left_grip_action, + grip_space: left_grip_space, + aim_action: left_aim_action, + aim_space: left_aim_space, + vibration_action: left_vibration_action, + skeleton_tracker: left_hand_tracker, + }, + HandInteraction { + controllers_profile_id, + grip_action: right_grip_action, + grip_space: right_grip_space, + aim_action: right_aim_action, + aim_space: right_aim_space, + vibration_action: right_vibration_action, + skeleton_tracker: right_hand_tracker, + }, + ], + multimodal_hands_enabled: false, + face_sources: FaceSources { + combined_eyes_source, + eye_tracker_fb: None, + face_tracker_fb: None, + eye_tracker_htc: None, + lip_tracker_htc: None, }, - HandInteraction { - controllers_profile_id, - grip_action: right_grip_action, - grip_space: right_grip_space, - aim_action: right_aim_action, - aim_space: right_aim_space, - vibration_action: right_vibration_action, - skeleton_tracker: right_hand_tracker, + body_sources: BodySources { + body_tracker_fb: None, }, - ], - uses_multimodal_hands, - face_sources: FaceSources { - combined_eyes_source, - eye_tracker_fb, - face_tracker_fb, - eye_tracker_htc, - lip_tracker_htc, - }, - body_sources: BodySources { body_tracker_fb }, + } + } + + pub fn select_sources(&mut self, config: &InteractionSourcesConfig) { + // First of all, disable/delete all sources. This ensures there are no conflicts + extra_extensions::pause_simultaneous_hands_and_controllers_tracking_meta(&self.xr_session) + .ok(); + self.multimodal_hands_enabled = false; + self.face_sources.eye_tracker_fb = None; + self.face_sources.face_tracker_fb = None; + self.face_sources.eye_tracker_htc = None; + self.face_sources.lip_tracker_htc = None; + self.body_sources.body_tracker_fb = None; + + // todo: check which permissions are needed for htc + #[cfg(target_os = "android")] + if let Some(config) = &config.face_tracking { + if (config.eye_tracking_fb) && matches!(self.platform, Platform::QuestPro) { + alvr_system_info::try_get_permission("com.oculus.permission.EYE_TRACKING") + } + if config.face_tracking_fb && matches!(self.platform, Platform::QuestPro) { + alvr_system_info::try_get_permission("android.permission.RECORD_AUDIO"); + alvr_system_info::try_get_permission("com.oculus.permission.FACE_TRACKING") + } + } + + #[cfg(target_os = "android")] + if let Some(config) = &config.body_tracking { + if (config.body_tracking_fb.enabled()) + && self.platform.is_quest() + && self.platform != Platform::Quest1 + { + alvr_system_info::try_get_permission("com.oculus.permission.BODY_TRACKING") + } + } + + // Note: We cannot enable multimodal if fb body tracking is active. It would result in a + // ERROR_RUNTIME_FAILURE crash. + if config.body_tracking.is_none() + && config.prefers_multimodal_input + && extra_extensions::resume_simultaneous_hands_and_controllers_tracking_meta( + &self.xr_session, + ) + .is_ok() + { + self.multimodal_hands_enabled = true; + } + + self.face_sources.eye_tracker_fb = create_ext_object( + "EyeTrackerSocial", + config.face_tracking.as_ref().map(|s| s.eye_tracking_fb), + || EyeTrackerSocial::new(&self.xr_session), + ); + + self.face_sources.face_tracker_fb = create_ext_object( + "FaceTracker2FB", + config.face_tracking.as_ref().map(|s| s.face_tracking_fb), + || FaceTracker2FB::new(&self.xr_session, true, true), + ); + + self.face_sources.eye_tracker_htc = create_ext_object( + "FacialTrackerHTC (eyes)", + config.face_tracking.as_ref().map(|s| s.eye_expressions_htc), + || FacialTrackerHTC::new(&self.xr_session, xr::FacialTrackingTypeHTC::EYE_DEFAULT), + ); + + self.face_sources.lip_tracker_htc = create_ext_object( + "FacialTrackerHTC (lips)", + config.face_tracking.as_ref().map(|s| s.lip_expressions_htc), + || FacialTrackerHTC::new(&self.xr_session, xr::FacialTrackingTypeHTC::LIP_DEFAULT), + ); + + self.body_sources.body_tracker_fb = create_ext_object( + "BodyTrackerFB (full set)", + config + .body_tracking + .clone() + .and_then(|s| s.body_tracking_fb.into_option()) + .map(|c| c.full_body), + || BodyTrackerFB::new(&self.xr_session, *BODY_JOINT_SET_FULL_BODY_META), + ) + .map(|tracker| (tracker, FULL_BODY_JOINT_COUNT_META)) + .or_else(|| { + create_ext_object( + "BodyTrackerFB (default set)", + config + .body_tracking + .as_ref() + .map(|s| s.body_tracking_fb.enabled()), + || BodyTrackerFB::new(&self.xr_session, xr::BodyJointSetFB::DEFAULT), + ) + .map(|tracker| (tracker, xr::BodyJointFB::COUNT.into_raw() as usize)) + }); } } diff --git a/alvr/client_openxr/src/lib.rs b/alvr/client_openxr/src/lib.rs index 1697d13ccd..e4161c286f 100644 --- a/alvr/client_openxr/src/lib.rs +++ b/alvr/client_openxr/src/lib.rs @@ -13,12 +13,15 @@ use alvr_client_core::{ use alvr_common::{ error, glam::{Quat, UVec2, Vec3}, - info, Fov, Pose, HAND_LEFT_ID, + info, + parking_lot::RwLock, + Fov, Pose, HAND_LEFT_ID, }; use extra_extensions::{ META_BODY_TRACKING_FULL_BODY_EXTENSION_NAME, META_DETACHED_CONTROLLERS_EXTENSION_NAME, META_SIMULTANEOUS_HANDS_AND_CONTROLLERS_EXTENSION_NAME, }; +use interaction::{InteractionContext, InteractionSourcesConfig}; use lobby::Lobby; use openxr as xr; use passthrough::PassthroughLayer; @@ -86,13 +89,6 @@ fn to_xr_time(timestamp: Duration) -> xr::Time { xr::Time::from_nanos(timestamp.as_nanos() as _) } -#[derive(Clone)] -pub struct XrContext { - instance: xr::Instance, - system: xr::SystemId, - session: xr::Session, -} - fn default_view() -> xr::View { xr::View { pose: xr::Posef { @@ -194,7 +190,6 @@ pub fn entry_point() { let graphics_context = Rc::new(GraphicsContext::new_gl()); let mut last_lobby_message = String::new(); - let mut parsed_stream_config = None::; 'session_loop: loop { let xr_system = xr_instance @@ -212,12 +207,6 @@ pub fn entry_point() { .unwrap() }; - let xr_context = XrContext { - instance: xr_instance.clone(), - system: xr_system, - session: xr_session.clone(), - }; - let views_config = xr_instance .enumerate_view_configuration_views( xr_system, @@ -258,28 +247,29 @@ pub fn entry_point() { }; let core_context = Arc::new(ClientCoreContext::new(capabilities)); - let interaction_context = Arc::new(interaction::initialize_interaction( - &xr_context, + let interaction_context = Arc::new(RwLock::new(InteractionContext::new( + xr_session.clone(), platform, - parsed_stream_config - .as_ref() - .map(|c| c.prefers_multimodal_input) - .unwrap_or(false), - parsed_stream_config - .as_ref() - .and_then(|c| c.face_sources_config.clone()), - parsed_stream_config - .as_ref() - .and_then(|c| c.body_sources_config.clone()), - )); + exts.other + .contains(&META_SIMULTANEOUS_HANDS_AND_CONTROLLERS_EXTENSION_NAME.to_owned()), + ))); let mut lobby = Lobby::new( - &xr_context, + xr_session.clone(), Rc::clone(&graphics_context), Arc::clone(&interaction_context), default_view_resolution, &last_lobby_message, ); + let lobby_interaction_sources = InteractionSourcesConfig { + face_tracking: None, + body_tracking: None, + prefers_multimodal_input: true, + }; + interaction_context + .write() + .select_sources(&lobby_interaction_sources); + let mut session_running = false; let mut stream_context = None::; let mut passthrough_layer = None; @@ -360,40 +350,32 @@ pub fn entry_point() { lobby.update_hud_message(&message); } ClientCoreEvent::StreamingStarted(config) => { - let new_config = ParsedStreamConfig::new(&config); - - // combined_eye_gaze is a setting that needs to be enabled at session - // creation. Since HTC headsets don't support session reinitialization, skip - // all elements that need it, that is face and eye tracking. - if parsed_stream_config.as_ref() != Some(&new_config) && !platform.is_vive() - { - parsed_stream_config = Some(new_config); - - xr_session.request_exit().ok(); - } else { - let context = StreamContext::new( - Arc::clone(&core_context), - xr_context.clone(), - Rc::clone(&graphics_context), - Arc::clone(&interaction_context), - platform, - new_config.clone(), - ); - - if !context.uses_passthrough() { - passthrough_layer = None; - } - - stream_context = Some(context); - - parsed_stream_config = Some(new_config); + let config = ParsedStreamConfig::new(&config); + + let context = StreamContext::new( + Arc::clone(&core_context), + xr_session.clone(), + Rc::clone(&graphics_context), + Arc::clone(&interaction_context), + platform, + config, + ); + + if !context.uses_passthrough() { + passthrough_layer = None; } + + stream_context = Some(context); } ClientCoreEvent::StreamingStopped => { if passthrough_layer.is_none() { passthrough_layer = PassthroughLayer::new(&xr_session).ok(); } + interaction_context + .write() + .select_sources(&lobby_interaction_sources); + stream_context = None; } ClientCoreEvent::Haptics { @@ -402,11 +384,9 @@ pub fn entry_point() { frequency, amplitude, } => { - let action = if device_id == *HAND_LEFT_ID { - &interaction_context.hands_interaction[0].vibration_action - } else { - &interaction_context.hands_interaction[1].vibration_action - }; + let idx = if device_id == *HAND_LEFT_ID { 0 } else { 1 }; + let action = + &interaction_context.read().hands_interaction[idx].vibration_action; action .apply_feedback( diff --git a/alvr/client_openxr/src/lobby.rs b/alvr/client_openxr/src/lobby.rs index d15679d4d2..f9b11b1568 100644 --- a/alvr/client_openxr/src/lobby.rs +++ b/alvr/client_openxr/src/lobby.rs @@ -1,17 +1,16 @@ use crate::{ graphics::{self, ProjectionLayerAlphaConfig, ProjectionLayerBuilder}, interaction::{self, InteractionContext}, - XrContext, }; use alvr_client_core::graphics::{GraphicsContext, LobbyRenderer, RenderViewInput, SDR_FORMAT_GL}; -use alvr_common::{glam::UVec2, Pose}; +use alvr_common::{glam::UVec2, parking_lot::RwLock, Pose}; use openxr as xr; use std::{rc::Rc, sync::Arc}; // todo: add interaction? pub struct Lobby { xr_session: xr::Session, - interaction_ctx: Arc, + interaction_ctx: Arc>, reference_space: xr::Space, swapchains: [xr::Swapchain; 2], view_resolution: UVec2, @@ -21,13 +20,13 @@ pub struct Lobby { impl Lobby { pub fn new( - xr_ctx: &XrContext, + xr_session: xr::Session, gfx_ctx: Rc, - interaction_ctx: Arc, + interaction_ctx: Arc>, view_resolution: UVec2, initial_hud_message: &str, ) -> Self { - let reference_space_type = if xr_ctx.instance.exts().ext_local_floor.is_some() { + let reference_space_type = if xr_session.instance().exts().ext_local_floor.is_some() { xr::ReferenceSpaceType::LOCAL_FLOOR_EXT } else { // The Quest 1 doesn't support LOCAL_FLOOR_EXT, recentering is required for AppLab, but @@ -35,24 +34,11 @@ impl Lobby { xr::ReferenceSpaceType::STAGE }; - let reference_space = - interaction::get_reference_space(&xr_ctx.session, reference_space_type); + let reference_space = interaction::get_reference_space(&xr_session, reference_space_type); let swapchains = [ - graphics::create_swapchain( - &xr_ctx.session, - &gfx_ctx, - view_resolution, - SDR_FORMAT_GL, - None, - ), - graphics::create_swapchain( - &xr_ctx.session, - &gfx_ctx, - view_resolution, - SDR_FORMAT_GL, - None, - ), + graphics::create_swapchain(&xr_session, &gfx_ctx, view_resolution, SDR_FORMAT_GL, None), + graphics::create_swapchain(&xr_session, &gfx_ctx, view_resolution, SDR_FORMAT_GL, None), ]; let renderer = LobbyRenderer::new( @@ -76,7 +62,7 @@ impl Lobby { ); Self { - xr_session: xr_ctx.session.clone(), + xr_session, interaction_ctx, reference_space, swapchains, @@ -112,13 +98,13 @@ impl Lobby { }; self.xr_session - .sync_actions(&[(&self.interaction_ctx.action_set).into()]) + .sync_actions(&[(&self.interaction_ctx.read().action_set).into()]) .ok(); let left_hand_data = interaction::get_hand_data( &self.xr_session, &self.reference_space, predicted_display_time, - &self.interaction_ctx.hands_interaction[0], + &self.interaction_ctx.read().hands_interaction[0], &mut Pose::default(), &mut Pose::default(), ); @@ -126,13 +112,14 @@ impl Lobby { &self.xr_session, &self.reference_space, predicted_display_time, - &self.interaction_ctx.hands_interaction[1], + &self.interaction_ctx.read().hands_interaction[1], &mut Pose::default(), &mut Pose::default(), ); let body_skeleton_fb = self .interaction_ctx + .read() .body_sources .body_tracker_fb .as_ref() diff --git a/alvr/client_openxr/src/stream.rs b/alvr/client_openxr/src/stream.rs index c3ffd32c5e..0691f2a7b2 100644 --- a/alvr/client_openxr/src/stream.rs +++ b/alvr/client_openxr/src/stream.rs @@ -1,7 +1,6 @@ use crate::{ graphics::{self, ProjectionLayerAlphaConfig, ProjectionLayerBuilder}, - interaction::{self, InteractionContext}, - XrContext, + interaction::{self, InteractionContext, InteractionSourcesConfig}, }; use alvr_client_core::{ graphics::{GraphicsContext, StreamRenderer}, @@ -12,12 +11,13 @@ use alvr_common::{ anyhow::Result, error, glam::{UVec2, Vec2}, + parking_lot::RwLock, Pose, RelaxedAtomic, HAND_LEFT_ID, HAND_RIGHT_ID, HEAD_ID, }; use alvr_packets::{FaceData, StreamConfig, ViewParams}; use alvr_session::{ - BodyTrackingSourcesConfig, ClientsideFoveationConfig, ClientsideFoveationMode, CodecType, - FaceTrackingSourcesConfig, FoveatedEncodingConfig, MediacodecProperty, PassthroughMode, + ClientsideFoveationConfig, ClientsideFoveationMode, CodecType, FoveatedEncodingConfig, + MediacodecProperty, PassthroughMode, }; use openxr as xr; use std::{ @@ -30,7 +30,6 @@ use std::{ const DECODER_MAX_TIMEOUT_MULTIPLIER: f32 = 0.8; -#[derive(PartialEq, Clone)] pub struct ParsedStreamConfig { pub view_resolution: UVec2, pub refresh_rate_hint: f32, @@ -39,18 +38,16 @@ pub struct ParsedStreamConfig { pub passthrough: Option, pub foveated_encoding_config: Option, pub clientside_foveation_config: Option, - pub face_sources_config: Option, - pub body_sources_config: Option, - pub prefers_multimodal_input: bool, pub force_software_decoder: bool, pub max_buffering_frames: f32, pub buffering_history_weight: f32, pub decoder_options: Vec<(String, MediacodecProperty)>, + pub interaction_sources: InteractionSourcesConfig, } impl ParsedStreamConfig { - pub fn new(config: &StreamConfig) -> ParsedStreamConfig { - ParsedStreamConfig { + pub fn new(config: &StreamConfig) -> Self { + Self { view_resolution: config.negotiated_config.view_resolution, refresh_rate_hint: config.negotiated_config.refresh_rate_hint, encoding_gamma: config.negotiated_config.encoding_gamma, @@ -67,37 +64,19 @@ impl ParsedStreamConfig { .clientside_foveation .as_option() .cloned(), - face_sources_config: config - .settings - .headset - .face_tracking - .as_option() - .map(|c| c.sources.clone()), - body_sources_config: config - .settings - .headset - .body_tracking - .as_option() - .map(|c| c.sources.clone()), - prefers_multimodal_input: config - .settings - .headset - .controllers - .as_option() - .map(|c| c.multimodal_tracking) - .unwrap_or(false), force_software_decoder: config.settings.video.force_software_decoder, max_buffering_frames: config.settings.video.max_buffering_frames, buffering_history_weight: config.settings.video.buffering_history_weight, decoder_options: config.settings.video.mediacodec_extra_options.clone(), + interaction_sources: InteractionSourcesConfig::new(config), } } } pub struct StreamContext { core_context: Arc, - xr_context: XrContext, - interaction_context: Arc, + xr_session: xr::Session, + interaction_context: Arc>, reference_space: Arc, swapchains: [xr::Swapchain; 2], last_good_view_params: [ViewParams; 2], @@ -111,23 +90,28 @@ pub struct StreamContext { impl StreamContext { pub fn new( core_ctx: Arc, - xr_ctx: XrContext, + xr_session: xr::Session, gfx_ctx: Rc, - interaction_ctx: Arc, + interaction_ctx: Arc>, platform: Platform, config: ParsedStreamConfig, ) -> StreamContext { - if xr_ctx.instance.exts().fb_display_refresh_rate.is_some() { - xr_ctx - .session + interaction_ctx + .write() + .select_sources(&config.interaction_sources); + + let xr_exts = xr_session.instance().exts(); + + if xr_exts.fb_display_refresh_rate.is_some() { + xr_session .request_display_refresh_rate(config.refresh_rate_hint) .unwrap(); } let foveation_profile = if let Some(config) = &config.clientside_foveation_config { - if xr_ctx.instance.exts().fb_swapchain_update_state.is_some() - && xr_ctx.instance.exts().fb_foveation.is_some() - && xr_ctx.instance.exts().fb_foveation_configuration.is_some() + if xr_exts.fb_swapchain_update_state.is_some() + && xr_exts.fb_foveation.is_some() + && xr_exts.fb_foveation_configuration.is_some() { let level; let dynamic; @@ -142,8 +126,7 @@ impl StreamContext { } }; - xr_ctx - .session + xr_session .create_foveation_profile(Some(xr::FoveationLevelProfile { level: xr::FoveationLevelFB::from_raw(level as i32), vertical_offset: config.vertical_offset_deg, @@ -157,18 +140,18 @@ impl StreamContext { None }; - let format = graphics::swapchain_format(&gfx_ctx, &xr_ctx.session, config.enable_hdr); + let format = graphics::swapchain_format(&gfx_ctx, &xr_session, config.enable_hdr); let swapchains = [ graphics::create_swapchain( - &xr_ctx.session, + &xr_session, &gfx_ctx, config.view_resolution, format, foveation_profile.as_ref(), ), graphics::create_swapchain( - &xr_ctx.session, + &xr_session, &gfx_ctx, config.view_resolution, format, @@ -202,8 +185,7 @@ impl StreamContext { ); core_ctx.send_playspace( - xr_ctx - .session + xr_session .reference_space_bounds_rect(xr::ReferenceSpaceType::STAGE) .unwrap() .map(|a| Vec2::new(a.width, a.height)), @@ -211,23 +193,23 @@ impl StreamContext { core_ctx.send_active_interaction_profile( *HAND_LEFT_ID, - interaction_ctx.hands_interaction[0].controllers_profile_id, + interaction_ctx.read().hands_interaction[0].controllers_profile_id, ); core_ctx.send_active_interaction_profile( *HAND_RIGHT_ID, - interaction_ctx.hands_interaction[1].controllers_profile_id, + interaction_ctx.read().hands_interaction[1].controllers_profile_id, ); let input_thread_running = Arc::new(RelaxedAtomic::new(true)); let reference_space = Arc::new(interaction::get_reference_space( - &xr_ctx.session, + &xr_session, xr::ReferenceSpaceType::STAGE, )); let input_thread = thread::spawn({ let core_ctx = Arc::clone(&core_ctx); - let xr_ctx = xr_ctx.clone(); + let xr_session = xr_session.clone(); let interaction_ctx = Arc::clone(&interaction_ctx); let reference_space = Arc::clone(&reference_space); let refresh_rate = config.refresh_rate_hint; @@ -235,7 +217,7 @@ impl StreamContext { move || { stream_input_loop( &core_ctx, - xr_ctx, + xr_session, &interaction_ctx, Arc::clone(&reference_space), refresh_rate, @@ -246,7 +228,7 @@ impl StreamContext { StreamContext { core_context: core_ctx, - xr_context: xr_ctx, + xr_session, interaction_context: interaction_ctx, reference_space, swapchains, @@ -267,13 +249,12 @@ impl StreamContext { self.input_thread_running.set(false); self.reference_space = Arc::new(interaction::get_reference_space( - &self.xr_context.session, + &self.xr_session, xr::ReferenceSpaceType::STAGE, )); self.core_context.send_playspace( - self.xr_context - .session + self.xr_session .reference_space_bounds_rect(xr::ReferenceSpaceType::STAGE) .unwrap() .map(|a| Vec2::new(a.width, a.height)), @@ -287,7 +268,7 @@ impl StreamContext { self.input_thread = Some(thread::spawn({ let core_ctx = Arc::clone(&self.core_context); - let xr_ctx = self.xr_context.clone(); + let xr_session = self.xr_session.clone(); let interaction_ctx = Arc::clone(&self.interaction_context); let reference_space = Arc::clone(&self.reference_space); let refresh_rate = self.config.refresh_rate_hint; @@ -295,7 +276,7 @@ impl StreamContext { move || { stream_input_loop( &core_ctx, - xr_ctx, + xr_session, &interaction_ctx, Arc::clone(&reference_space), refresh_rate, @@ -388,7 +369,7 @@ impl StreamContext { self.swapchains[1].release_image().unwrap(); if !buffer_ptr.is_null() { - if let Some(xr_now) = crate::xr_runtime_now(&self.xr_context.instance) { + if let Some(xr_now) = crate::xr_runtime_now(&self.xr_session.instance()) { self.core_context.report_submit( timestamp, vsync_time.saturating_sub(Duration::from_nanos(xr_now.as_nanos() as u64)), @@ -447,8 +428,8 @@ impl Drop for StreamContext { fn stream_input_loop( core_ctx: &ClientCoreContext, - xr_ctx: XrContext, - interaction_ctx: &InteractionContext, + xr_session: xr::Session, + interaction_ctx: &RwLock, reference_space: Arc, refresh_rate: f32, running: Arc, @@ -460,27 +441,22 @@ fn stream_input_loop( let mut deadline = Instant::now(); let frame_interval = Duration::from_secs_f32(1.0 / refresh_rate); while running.value() { + let int_ctx = &*interaction_ctx.read(); // Streaming related inputs are updated here. Make sure every input poll is done in this // thread - if let Err(e) = xr_ctx - .session - .sync_actions(&[(&interaction_ctx.action_set).into()]) - { + if let Err(e) = xr_session.sync_actions(&[(&int_ctx.action_set).into()]) { error!("{e}"); return; } - let Some(xr_now) = crate::xr_runtime_now(&xr_ctx.instance) else { + let Some(xr_now) = crate::xr_runtime_now(&xr_session.instance()) else { error!("Cannot poll tracking: invalid time"); return; }; - let Some((head_motion, local_views)) = interaction::get_head_data( - &xr_ctx.session, - &reference_space, - xr_now, - &last_view_params, - ) else { + let Some((head_motion, local_views)) = + interaction::get_head_data(&xr_session, &reference_space, xr_now, &last_view_params) + else { continue; }; @@ -494,30 +470,30 @@ fn stream_input_loop( device_motions.push((*HEAD_ID, head_motion)); let (left_hand_motion, left_hand_skeleton) = crate::interaction::get_hand_data( - &xr_ctx.session, + &xr_session, &reference_space, xr_now, - &interaction_ctx.hands_interaction[0], + &int_ctx.hands_interaction[0], &mut last_controller_poses[0], &mut last_palm_poses[0], ); let (right_hand_motion, right_hand_skeleton) = crate::interaction::get_hand_data( - &xr_ctx.session, + &xr_session, &reference_space, xr_now, - &interaction_ctx.hands_interaction[1], + &int_ctx.hands_interaction[1], &mut last_controller_poses[1], &mut last_palm_poses[1], ); // Note: When multimodal input is enabled, we are sure that when free hands are used // (not holding controllers) the controller data is None. - if interaction_ctx.uses_multimodal_hands || left_hand_skeleton.is_none() { + if int_ctx.multimodal_hands_enabled || left_hand_skeleton.is_none() { if let Some(motion) = left_hand_motion { device_motions.push((*HAND_LEFT_ID, motion)); } } - if interaction_ctx.uses_multimodal_hands || right_hand_skeleton.is_none() { + if int_ctx.multimodal_hands_enabled || right_hand_skeleton.is_none() { if let Some(motion) = right_hand_motion { device_motions.push((*HAND_RIGHT_ID, motion)); } @@ -525,20 +501,17 @@ fn stream_input_loop( let face_data = FaceData { eye_gazes: interaction::get_eye_gazes( - &xr_ctx.session, - &interaction_ctx.face_sources, + &xr_session, + &int_ctx.face_sources, &reference_space, xr_now, ), - fb_face_expression: interaction::get_fb_face_expression( - &interaction_ctx.face_sources, - xr_now, - ), - htc_eye_expression: interaction::get_htc_eye_expression(&interaction_ctx.face_sources), - htc_lip_expression: interaction::get_htc_lip_expression(&interaction_ctx.face_sources), + fb_face_expression: interaction::get_fb_face_expression(&int_ctx.face_sources, xr_now), + htc_eye_expression: interaction::get_htc_eye_expression(&int_ctx.face_sources), + htc_lip_expression: interaction::get_htc_lip_expression(&int_ctx.face_sources), }; - if let Some((tracker, joint_count)) = &interaction_ctx.body_sources.body_tracker_fb { + if let Some((tracker, joint_count)) = &int_ctx.body_sources.body_tracker_fb { device_motions.append(&mut interaction::get_fb_body_tracking_points( &reference_space, xr_now, @@ -554,8 +527,7 @@ fn stream_input_loop( face_data, ); - let button_entries = - interaction::update_buttons(&xr_ctx.session, &interaction_ctx.button_actions); + let button_entries = interaction::update_buttons(&xr_session, &int_ctx.button_actions); if !button_entries.is_empty() { core_ctx.send_buttons(button_entries); } diff --git a/alvr/session/src/settings.rs b/alvr/session/src/settings.rs index 6c633b5075..7834194880 100644 --- a/alvr/session/src/settings.rs +++ b/alvr/session/src/settings.rs @@ -724,7 +724,6 @@ pub enum HeadsetEmulationMode { #[derive(SettingsSchema, Serialize, Deserialize, Clone, PartialEq)] pub struct FaceTrackingSourcesConfig { - pub combined_eye_gaze: bool, pub eye_tracking_fb: bool, pub face_tracking_fb: bool, pub eye_expressions_htc: bool, @@ -1628,7 +1627,6 @@ pub fn session_settings_default() -> SettingsDefault { content: FaceTrackingConfigDefault { gui_collapsed: true, sources: FaceTrackingSourcesConfigDefault { - combined_eye_gaze: true, eye_tracking_fb: true, face_tracking_fb: true, eye_expressions_htc: true,