diff --git a/src/backend/openvr/playspace.rs b/src/backend/openvr/playspace.rs index b792042..fc4ce79 100644 --- a/src/backend/openvr/playspace.rs +++ b/src/backend/openvr/playspace.rs @@ -200,6 +200,15 @@ impl PlayspaceMover { set_working_copy(&self.universe, chaperone_mgr, &mat); chaperone_mgr.commit_working_copy(EChaperoneConfigFile::EChaperoneConfigFile_Live); + + if self.drag.is_some() { + log::info!("Space drag interrupted by fix floor"); + self.drag = None; + } + if self.rotate.is_some() { + log::info!("Space rotate interrupted by fix floor"); + self.rotate = None; + } } pub fn playspace_changed( diff --git a/src/backend/openxr/input.rs b/src/backend/openxr/input.rs index fd75d4b..d27d0ba 100644 --- a/src/backend/openxr/input.rs +++ b/src/backend/openxr/input.rs @@ -141,6 +141,7 @@ pub(super) struct OpenXrHandSource { action_alt_click: CustomClickAction, action_show_hide: CustomClickAction, action_space_drag: CustomClickAction, + action_space_rotate: CustomClickAction, action_modifier_right: CustomClickAction, action_modifier_middle: CustomClickAction, action_move_mouse: CustomClickAction, @@ -271,6 +272,11 @@ impl OpenXrHand { .action_space_drag .state(pointer.before.space_drag, xr, session)?; + pointer.now.space_rotate = + self.source + .action_space_rotate + .state(pointer.before.space_rotate, xr, session)?; + Ok(()) } } @@ -303,6 +309,7 @@ impl OpenXrHandSource { action_alt_click: CustomClickAction::new(action_set, "alt_click", side)?, action_show_hide: CustomClickAction::new(action_set, "show_hide", side)?, action_space_drag: CustomClickAction::new(action_set, "space_drag", side)?, + action_space_rotate: CustomClickAction::new(action_set, "space_rotate", side)?, action_modifier_right: CustomClickAction::new( action_set, "click_modifier_right", @@ -456,6 +463,14 @@ fn suggest_bindings(instance: &xr::Instance, hands: &[&OpenXrHandSource; 2]) -> instance ); + add_custom!( + profile.space_rotate, + &hands[0].action_space_rotate, + &hands[1].action_space_rotate, + bindings, + instance + ); + add_custom!( profile.click_modifier_right, &hands[0].action_modifier_right, @@ -509,6 +524,7 @@ struct OpenXrActionConfProfile { alt_click: Option, show_hide: Option, space_drag: Option, + space_rotate: Option, click_modifier_right: Option, click_modifier_middle: Option, move_mouse: Option, diff --git a/src/backend/openxr/playspace.rs b/src/backend/openxr/playspace.rs index fb39c52..fe50f35 100644 --- a/src/backend/openxr/playspace.rs +++ b/src/backend/openxr/playspace.rs @@ -37,6 +37,7 @@ enum ApiImpl { pub(super) struct PlayspaceMover { last_transform: Affine3A, drag: Option>, + rotate: Option>, libmonado: Library, mnd_root: *mut c_void, @@ -75,6 +76,7 @@ impl PlayspaceMover { Ok(Self { last_transform: Affine3A::IDENTITY, drag: None, + rotate: None, libmonado, mnd_root: root, @@ -84,6 +86,61 @@ impl PlayspaceMover { } pub fn update(&mut self, overlays: &mut OverlayContainer, state: &AppState) { + if let Some(mut data) = self.rotate.take() { + let pointer = &state.input_state.pointers[data.hand]; + if !pointer.now.space_rotate { + self.last_transform = data.pose; + log::info!("End space rotate"); + return; + } + + let new_hand = + Quat::from_affine3(&(data.pose * state.input_state.pointers[data.hand].raw_pose)); + + let dq = new_hand * data.hand_pose.conjugate(); + let rel_y = f32::atan2( + 2.0 * (dq.y * dq.w + dq.x * dq.z), + (2.0 * (dq.w * dq.w + dq.x * dq.x)) - 1.0, + ); + + let mut space_transform = Affine3A::from_rotation_y(rel_y); + let offset = (space_transform.transform_vector3a(state.input_state.hmd.translation) + - state.input_state.hmd.translation) + * -1.0; + let mut overlay_transform = Affine3A::from_rotation_y(-rel_y); + + overlay_transform.translation = offset; + space_transform.translation = offset; + + overlays.iter_mut().for_each(|overlay| { + if overlay.state.grabbable { + overlay.state.dirty = true; + overlay.state.transform.translation = + overlay_transform.transform_point3a(overlay.state.transform.translation); + } + }); + + data.pose *= space_transform; + data.hand_pose = new_hand; + + self.apply_offset(data.pose); + self.rotate = Some(data); + } else { + for (i, pointer) in state.input_state.pointers.iter().enumerate() { + if pointer.now.space_rotate { + let hand_pose = Quat::from_affine3(&self.last_transform); + self.rotate = Some(MoverData { + pose: self.last_transform, + hand: i, + hand_pose, + }); + self.drag = None; + log::info!("Start space rotate"); + return; + } + } + } + if let Some(mut data) = self.drag.take() { let pointer = &state.input_state.pointers[data.hand]; if !pointer.now.space_drag { @@ -137,8 +194,12 @@ impl PlayspaceMover { pub fn reset_offset(&mut self) { if self.drag.is_some() { - log::info!("Cannot reset offset while dragging."); - return; + log::info!("Space drag interrupted by manual reset"); + self.drag = None; + } + if self.rotate.is_some() { + log::info!("Space rotate interrupted by manual reset"); + self.rotate = None; } self.last_transform = Affine3A::IDENTITY; @@ -147,8 +208,12 @@ impl PlayspaceMover { pub fn fix_floor(&mut self, input: &InputState) { if self.drag.is_some() { - log::info!("Cannot fix floor while dragging."); - return; + log::info!("Space drag interrupted by fix floor"); + self.drag = None; + } + if self.rotate.is_some() { + log::info!("Space rotate interrupted by fix floor"); + self.rotate = None; } let y1 = input.pointers[0].pose.translation.y;