From 9b336535f7af6c2b08301949b4c6727f124e0c31 Mon Sep 17 00:00:00 2001 From: galister <22305755+galister@users.noreply.github.com> Date: Sun, 26 Jul 2026 01:39:45 +0900 Subject: [PATCH] input: InputState: should_disable_lerp (#593) Co-authored-by: Aleksander --- wayvr/src/backend/input.rs | 17 +++++++++++-- wayvr/src/backend/openxr/input.rs | 38 +++++++++++++++++++++------- wayvr/src/backend/wayvr/mod.rs | 3 +-- wayvr/src/overlays/screen/backend.rs | 2 +- wayvr/src/overlays/wayvr.rs | 4 +-- 5 files changed, 48 insertions(+), 16 deletions(-) diff --git a/wayvr/src/backend/input.rs b/wayvr/src/backend/input.rs index f4cbcfa8..48626692 100644 --- a/wayvr/src/backend/input.rs +++ b/wayvr/src/backend/input.rs @@ -1,7 +1,7 @@ use std::f32::consts::PI; use std::process::{Child, Command}; use std::sync::Arc; -use std::time::Instant; +use std::time::{Duration, Instant}; use glam::{Affine3A, Vec2, Vec3A, Vec3Swizzles}; @@ -58,7 +58,7 @@ pub enum TrackedDeviceRole { Tracker, } -#[derive(Debug, Clone, Copy, EnumIs)] +#[derive(Debug, Eq, PartialEq, Clone, Copy, EnumIs)] pub enum FocusPickState { None, /// aiming to pick the focus @@ -75,6 +75,7 @@ pub struct InputState { pub handsfree_state: PointerState, pub picking_focus: FocusPickState, processes: Vec, + disable_lerp_until: Instant, } impl InputState { @@ -87,6 +88,18 @@ impl InputState { processes: Vec::new(), handsfree_state: PointerState::default(), picking_focus: FocusPickState::None, + disable_lerp_until: Instant::now(), + } + } + + pub fn should_disable_lerp(&self) -> bool { + self.disable_lerp_until > Instant::now() + } + + pub fn stop_picking(&mut self) { + if !self.picking_focus.is_none() { + self.picking_focus = FocusPickState::None; + self.disable_lerp_until = Instant::now() + Duration::from_millis(250); } } diff --git a/wayvr/src/backend/openxr/input.rs b/wayvr/src/backend/openxr/input.rs index 7860de5c..d7039497 100644 --- a/wayvr/src/backend/openxr/input.rs +++ b/wayvr/src/backend/openxr/input.rs @@ -251,12 +251,14 @@ impl OpenXrInputSource { ptr1.now = PointerState::default(); ptr1.tracked = false; } else { + let should_disable_lerp = app.input_state.should_disable_lerp(); for i in 0..2 { let pointer = &mut app.input_state.pointers[i]; - self.pointers[i].update(pointer, xr, &app.session)?; + self.pointers[i].update(pointer, xr, &app.session, !should_disable_lerp)?; any_tracked |= pointer.tracked; } } + if !any_tracked { self.handsfree_pointer.update_handsfree( &mut app.input_state.pointers[0], @@ -392,7 +394,11 @@ impl OpenXrPointer { } HandsfreePointer::EyeTracking | HandsfreePointer::EyeTrackingOnly => { // more aggressive smoothing for eye - self.pointer_load_pose(pointer, xr, session.config.pointer_lerp_factor * 0.5)?; + self.pointer_load_pose( + pointer, + xr, + Some(session.config.pointer_lerp_factor * 0.5), + )?; } } @@ -426,9 +432,18 @@ impl OpenXrPointer { pointer: &mut Pointer, xr: &XrState, session: &AppSession, + do_lerp: bool, ) -> anyhow::Result<()> { pointer.handsfree = false; - self.pointer_load_pose(pointer, xr, session.config.pointer_lerp_factor)?; + self.pointer_load_pose( + pointer, + xr, + if do_lerp { + Some(session.config.pointer_lerp_factor) + } else { + None + }, + )?; self.pointer_load_actions(pointer, xr)?; Ok(()) @@ -438,7 +453,7 @@ impl OpenXrPointer { &mut self, pointer: &mut Pointer, xr: &XrState, - lerp_factor: f32, + lerp_factor: Option, ) -> anyhow::Result<()> { let location = self.space.locate(&xr.stage, xr.predicted_display_time)?; if location @@ -453,12 +468,17 @@ impl OpenXrPointer { transmute::(location.pose.position), ) }; - let lerp_factor = (1.0 / (xr.fps / 100.0) * lerp_factor).clamp(0.1, 1.0); + pointer.raw_pose = Affine3A::from_rotation_translation(new_quat, new_pos); - pointer.pose = Affine3A::from_rotation_translation( - cur_quat.lerp(new_quat, lerp_factor), - cur_pos.lerp(new_pos.into(), lerp_factor).into(), - ); + if let Some(lerp_factor) = lerp_factor { + let lerp_factor = (1.0 / (xr.fps / 100.0) * lerp_factor).clamp(0.1, 1.0); + pointer.pose = Affine3A::from_rotation_translation( + cur_quat.lerp(new_quat, lerp_factor), + cur_pos.lerp(new_pos.into(), lerp_factor).into(), + ); + } else { + pointer.pose = pointer.raw_pose; // no lerp + } pointer.tracked = true; } else { pointer.tracked = false; diff --git a/wayvr/src/backend/wayvr/mod.rs b/wayvr/src/backend/wayvr/mod.rs index a46b69c7..e41df3c8 100644 --- a/wayvr/src/backend/wayvr/mod.rs +++ b/wayvr/src/backend/wayvr/mod.rs @@ -946,8 +946,7 @@ impl WvrServerState { ))); tasks.enqueue_at( TaskType::Global(Box::new(|app| { - app.input_state.picking_focus = - super::input::FocusPickState::None; + app.input_state.stop_picking(); let _ = std::mem::take(&mut app.input_state.handsfree_state); })), diff --git a/wayvr/src/overlays/screen/backend.rs b/wayvr/src/overlays/screen/backend.rs index d00186de..323bd930 100644 --- a/wayvr/src/overlays/screen/backend.rs +++ b/wayvr/src/overlays/screen/backend.rs @@ -304,7 +304,7 @@ impl OverlayBackend for ScreenBackend { let pick_now = app.input_state.picking_focus.is_picking(); if pick_now { - app.input_state.picking_focus = input::FocusPickState::None; + app.input_state.stop_picking(); app.hid_provider .set_input_focus(app.wvr_server.as_mut(), InputFocus::PhysicalScreen); } diff --git a/wayvr/src/overlays/wayvr.rs b/wayvr/src/overlays/wayvr.rs index fe35df75..90f5ad25 100644 --- a/wayvr/src/overlays/wayvr.rs +++ b/wayvr/src/overlays/wayvr.rs @@ -847,7 +847,7 @@ impl OverlayBackend for WvrWindowBackend { let pick_now = app.input_state.picking_focus.is_picking(); if pick_now { - app.input_state.picking_focus = input::FocusPickState::None; + app.input_state.stop_picking(); app.hid_provider .set_input_focus(app.wvr_server.as_mut(), InputFocus::WayVR); } @@ -870,7 +870,7 @@ impl OverlayBackend for WvrWindowBackend { let pick_now = app.input_state.picking_focus.is_picking(); if pick_now { - app.input_state.picking_focus = input::FocusPickState::None; + app.input_state.stop_picking(); app.hid_provider .set_input_focus(app.wvr_server.as_mut(), InputFocus::WayVR); }