refine watch grab & hand switch

This commit is contained in:
galister
2025-12-10 18:29:13 +09:00
parent 87806a84d2
commit 6294ccae1c
4 changed files with 70 additions and 62 deletions

View File

@@ -10,18 +10,12 @@ pub enum Positioning {
Floating, Floating,
/// Stays in place, recenters relative to anchor. Follows anchor during anchor grab. /// Stays in place, recenters relative to anchor. Follows anchor during anchor grab.
Anchored, Anchored,
/// Same as anchor but paused due to interaction
AnchoredPaused,
/// Stays in place, no recentering /// Stays in place, no recentering
Static, Static,
/// Following HMD /// Following HMD
FollowHead { lerp: f32 }, FollowHead { lerp: f32 },
/// Normally follows HMD, but paused due to interaction
FollowHeadPaused { lerp: f32 },
/// Following hand /// Following hand
FollowHand { hand: LeftRight, lerp: f32 }, FollowHand { hand: LeftRight, lerp: f32 },
/// Normally follows hand, but paused due to interaction
FollowHandPaused { hand: LeftRight, lerp: f32 },
} }
impl Positioning { impl Positioning {

View File

@@ -1,15 +1,18 @@
use std::f32::consts::PI; use std::f32::consts::PI;
use std::process::{Child, Command}; use std::process::{Child, Command};
use std::sync::Arc;
use std::{collections::VecDeque, time::Instant}; use std::{collections::VecDeque, time::Instant};
use glam::{Affine3A, Vec2, Vec3A, Vec3Swizzles}; use glam::{Affine3A, Vec2, Vec3A, Vec3Swizzles};
use idmap_derive::IntegerId; use idmap_derive::IntegerId;
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use wlx_common::common::LeftRight;
use wlx_common::windowing::{OverlayWindowState, Positioning}; use wlx_common::windowing::{OverlayWindowState, Positioning};
use crate::backend::task::OverlayTask; use crate::backend::task::OverlayTask;
use crate::overlays::anchor::ANCHOR_NAME; use crate::overlays::anchor::ANCHOR_NAME;
use crate::overlays::watch::WATCH_NAME;
use crate::state::{AppSession, AppState}; use crate::state::{AppSession, AppState};
use crate::subsystem::hid::WheelDelta; use crate::subsystem::hid::WheelDelta;
use crate::subsystem::input::KeyboardFocus; use crate::subsystem::input::KeyboardFocus;
@@ -232,6 +235,14 @@ impl Pointer {
interaction: InteractionState::default(), interaction: InteractionState::default(),
} }
} }
pub fn hand(&self) -> Option<LeftRight> {
match self.idx {
0 => Some(LeftRight::Left),
1 => Some(LeftRight::Right),
_ => None,
}
}
} }
#[derive(Clone, Copy, Default)] #[derive(Clone, Copy, Default)]
@@ -278,7 +289,7 @@ struct RayHit {
#[derive(Debug, Clone, Copy, Default)] #[derive(Debug, Clone, Copy, Default)]
pub struct GrabData { pub struct GrabData {
pub offset: Vec3A, pub offset: Affine3A,
pub grabbed_id: OverlayID, pub grabbed_id: OverlayID,
pub grab_anchor: bool, pub grab_anchor: bool,
} }
@@ -604,16 +615,13 @@ fn start_grab(
let grab_anchor = let grab_anchor =
!edit_mode && !app.anchor_grabbed && matches!(state.positioning, Positioning::Anchored); !edit_mode && !app.anchor_grabbed && matches!(state.positioning, Positioning::Anchored);
let relative_grab_point = if grab_anchor { let relative_grab_transform = if grab_anchor {
app.anchor.translation app.anchor
} else { } else {
state.transform.translation state.transform
}; };
let offset = pointer let offset = pointer.pose.inverse() * relative_grab_transform;
.pose
.inverse()
.transform_point3a(relative_grab_point);
app.anchor_grabbed = grab_anchor; app.anchor_grabbed = grab_anchor;
@@ -623,13 +631,6 @@ fn start_grab(
grab_anchor, grab_anchor,
}); });
state.positioning = match state.positioning {
Positioning::FollowHand { hand, lerp } => Positioning::FollowHandPaused { hand, lerp },
Positioning::FollowHead { lerp } => Positioning::FollowHeadPaused { lerp },
Positioning::Anchored if !grab_anchor => Positioning::AnchoredPaused,
x => x,
};
// Show anchor // Show anchor
app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify( app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify(
OverlaySelector::Name(ANCHOR_NAME.clone()), OverlaySelector::Name(ANCHOR_NAME.clone()),
@@ -664,22 +665,27 @@ where
}; };
let grab_anchor = grab_data.grab_anchor; let grab_anchor = grab_data.grab_anchor;
let Some(overlay_state) = overlay.config.active_state.as_mut() else {
return;
};
if pointer.now.grab { if pointer.now.grab {
let Some(overlay_state) = overlay.config.active_state.as_mut() else {
// overlay got toggled off while being grabbed. those dastardly users!
// just wait for them to release the grab
return;
};
if grab_anchor { if grab_anchor {
if pointer.now.click { if pointer.now.click {
pointer.interaction.mode = PointerMode::Special; pointer.interaction.mode = PointerMode::Special;
handle_scale(&mut app.anchor, pointer.now.scroll_y); handle_scale(&mut app.anchor, pointer.now.scroll_y);
} else if app.session.config.allow_sliding && pointer.now.scroll_y.is_finite() { } else if app.session.config.allow_sliding && pointer.now.scroll_y.is_finite() {
// single grab push/pull // single grab push/pull
grab_data.offset.z -= pointer.now.scroll_y * 0.05; grab_data.offset.translation.z -= pointer.now.scroll_y * 0.05;
} }
app.anchor.translation = pointer.pose.transform_point3a(grab_data.offset);
if !pointer.now.click_modifier_right { if !pointer.now.click_modifier_right {
app.anchor.translation =
pointer.pose.transform_point3a(grab_data.offset.translation);
realign(&mut app.anchor, &app.input_state.hmd); realign(&mut app.anchor, &app.input_state.hmd);
} else {
app.anchor = pointer.pose * grab_data.offset;
} }
} else { } else {
// single grab resize // single grab resize
@@ -688,12 +694,16 @@ where
handle_scale(&mut overlay_state.transform, pointer.now.scroll_y); handle_scale(&mut overlay_state.transform, pointer.now.scroll_y);
} else if app.session.config.allow_sliding && pointer.now.scroll_y.is_finite() { } else if app.session.config.allow_sliding && pointer.now.scroll_y.is_finite() {
// single grab push/pull // single grab push/pull
grab_data.offset.z -= pointer.now.scroll_y * 0.05; grab_data.offset.translation.z -= pointer.now.scroll_y * 0.05;
} }
overlay_state.transform.translation = pointer.pose.transform_point3a(grab_data.offset);
if !pointer.now.click_modifier_right { if !pointer.now.click_modifier_right {
overlay_state.transform.translation =
pointer.pose.transform_point3a(grab_data.offset.translation);
realign(&mut overlay_state.transform, &app.input_state.hmd); realign(&mut overlay_state.transform, &app.input_state.hmd);
} else {
overlay_state.transform = pointer.pose * grab_data.offset;
} }
overlay.config.pause_movement = true;
overlay.config.dirty = true; overlay.config.dirty = true;
} }
} else { } else {
@@ -703,16 +713,29 @@ where
app.anchor_grabbed = false; app.anchor_grabbed = false;
} else { } else {
// single grab released // single grab released
overlay_state.positioning = match overlay_state.positioning { if &*overlay.config.name == WATCH_NAME {
Positioning::FollowHandPaused { hand, lerp } => { // watch special: when dropped, follow the hand that wasn't grabbing
Positioning::FollowHand { hand, lerp } if let Some(overlay_state) = overlay.config.active_state.as_mut() {
overlay_state.positioning = match overlay_state.positioning {
Positioning::FollowHand { hand, lerp } => match pointer.hand() {
Some(LeftRight::Left) => Positioning::FollowHand {
hand: LeftRight::Right,
lerp,
},
Some(LeftRight::Right) => Positioning::FollowHand {
hand: LeftRight::Left,
lerp,
},
_ => Positioning::FollowHand { hand, lerp },
},
x => x,
};
} }
Positioning::FollowHeadPaused { lerp } => Positioning::FollowHead { lerp }, }
Positioning::AnchoredPaused => Positioning::Anchored, overlay.config.pause_movement = false;
x => x, if let Some(overlay_state) = overlay.config.active_state.as_mut() {
}; window::save_transform(overlay_state, app);
}
window::save_transform(overlay_state, app);
} }
// Hide anchor // Hide anchor

View File

@@ -131,24 +131,16 @@ fn key_to_pos(key: &str) -> Positioning {
const fn pos_to_key(pos: Positioning) -> &'static str { const fn pos_to_key(pos: Positioning) -> &'static str {
match pos { match pos {
Positioning::Static => "static", Positioning::Static => "static",
Positioning::Anchored | Positioning::AnchoredPaused => "anchored", Positioning::Anchored => "anchored",
Positioning::Floating => "floating", Positioning::Floating => "floating",
Positioning::FollowHead { .. } | Positioning::FollowHeadPaused { .. } => "hmd", Positioning::FollowHead { .. } => "hmd",
Positioning::FollowHand { Positioning::FollowHand {
hand: LeftRight::Left, hand: LeftRight::Left,
.. ..
}
| Positioning::FollowHandPaused {
hand: LeftRight::Left,
..
} => "hand_l", } => "hand_l",
Positioning::FollowHand { Positioning::FollowHand {
hand: LeftRight::Right, hand: LeftRight::Right,
.. ..
}
| Positioning::FollowHandPaused {
hand: LeftRight::Right,
..
} => "hand_r", } => "hand_r",
} }
} }

View File

@@ -79,6 +79,8 @@ pub struct OverlayWindowConfig {
pub dirty: bool, pub dirty: bool,
/// True if the window is showing the edit overlay /// True if the window is showing the edit overlay
pub editing: bool, pub editing: bool,
/// Used by grab to pause following of HMD or other devices
pub pause_movement: bool,
} }
impl OverlayWindowConfig { impl OverlayWindowConfig {
@@ -98,6 +100,7 @@ impl OverlayWindowConfig {
global: false, global: false,
dirty: true, dirty: true,
editing: false, editing: false,
pause_movement: false,
} }
} }
@@ -130,6 +133,10 @@ impl OverlayWindowConfig {
} }
pub fn auto_movement(&mut self, app: &mut AppState) { pub fn auto_movement(&mut self, app: &mut AppState) {
if self.pause_movement {
return;
}
let Some(state) = self.active_state.as_mut() else { let Some(state) = self.active_state.as_mut() else {
return; return;
}; };
@@ -184,13 +191,9 @@ impl OverlayWindowConfig {
.unwrap_or(self.default_state.transform); .unwrap_or(self.default_state.transform);
let parent_transform = match state.positioning { let parent_transform = match state.positioning {
Positioning::Floating Positioning::Floating | Positioning::FollowHead { .. } => app.input_state.hmd,
| Positioning::FollowHead { .. } Positioning::FollowHand { hand, .. } => app.input_state.pointers[hand as usize].pose,
| Positioning::FollowHeadPaused { .. } => app.input_state.hmd, Positioning::Anchored => app.anchor,
Positioning::FollowHand { hand, .. } | Positioning::FollowHandPaused { hand, .. } => {
app.input_state.pointers[hand as usize].pose
}
Positioning::Anchored | Positioning::AnchoredPaused => app.anchor,
Positioning::Static => return, Positioning::Static => return,
}; };
@@ -249,13 +252,9 @@ pub fn realign(transform: &mut Affine3A, hmd: &Affine3A) {
pub fn save_transform(state: &mut OverlayWindowState, app: &mut AppState) -> bool { pub fn save_transform(state: &mut OverlayWindowState, app: &mut AppState) -> bool {
let parent_transform = match state.positioning { let parent_transform = match state.positioning {
Positioning::Floating => snap_upright(app.input_state.hmd, Vec3A::Y), Positioning::Floating => snap_upright(app.input_state.hmd, Vec3A::Y),
Positioning::FollowHead { .. } | Positioning::FollowHeadPaused { .. } => { Positioning::FollowHead { .. } => app.input_state.hmd,
app.input_state.hmd Positioning::FollowHand { hand, .. } => app.input_state.pointers[hand as usize].pose,
} Positioning::Anchored => snap_upright(app.anchor, Vec3A::Y),
Positioning::FollowHand { hand, .. } | Positioning::FollowHandPaused { hand, .. } => {
app.input_state.pointers[hand as usize].pose
}
Positioning::Anchored | Positioning::AnchoredPaused => snap_upright(app.anchor, Vec3A::Y),
Positioning::Static => return false, Positioning::Static => return false,
}; };