From d7c8b49efa7ec248f53ac109daeddc95006bf6b9 Mon Sep 17 00:00:00 2001 From: galister <22305755+galister@users.noreply.github.com> Date: Sun, 4 Feb 2024 03:16:30 +0100 Subject: [PATCH] openvr haptics --- src/backend/input.rs | 44 ++++++++++++++++++++++++++++--------- src/backend/openvr/input.rs | 16 ++++++++++++-- src/backend/openvr/mod.rs | 7 ++++-- src/backend/openxr/mod.rs | 7 ++++-- src/backend/overlay.rs | 6 ++--- src/gui/mod.rs | 14 ++++++++++-- src/overlays/screen.rs | 5 +++-- 7 files changed, 76 insertions(+), 23 deletions(-) diff --git a/src/backend/input.rs b/src/backend/input.rs index 1d3bf5d..3fa4266 100644 --- a/src/backend/input.rs +++ b/src/backend/input.rs @@ -135,6 +135,7 @@ pub struct InteractionState { pub hovered_id: Option, pub release_actions: VecDeque>, pub next_push: Instant, + pub haptics: Option, } impl Default for InteractionState { @@ -146,6 +147,7 @@ impl Default for InteractionState { hovered_id: None, release_actions: VecDeque::new(), next_push: Instant::now(), + haptics: None, } } } @@ -193,8 +195,14 @@ pub struct PointerHit { pub dist: f32, } +pub struct Haptics { + pub intensity: f32, + pub duration: f32, + pub frequency: f32, +} + pub trait InteractionHandler { - fn on_hover(&mut self, app: &mut AppState, hit: &PointerHit); + fn on_hover(&mut self, app: &mut AppState, hit: &PointerHit) -> Option; fn on_left(&mut self, app: &mut AppState, pointer: usize); fn on_pointer(&mut self, app: &mut AppState, hit: &PointerHit, pressed: bool); fn on_scroll(&mut self, app: &mut AppState, hit: &PointerHit, delta: f32); @@ -204,7 +212,9 @@ pub struct DummyInteractionHandler; impl InteractionHandler for DummyInteractionHandler { fn on_left(&mut self, _app: &mut AppState, _pointer: usize) {} - fn on_hover(&mut self, _app: &mut AppState, _hit: &PointerHit) {} + fn on_hover(&mut self, _app: &mut AppState, _hit: &PointerHit) -> Option { + None + } fn on_pointer(&mut self, _app: &mut AppState, _hit: &PointerHit, _pressed: bool) {} fn on_scroll(&mut self, _app: &mut AppState, _hit: &PointerHit, _delta: f32) {} } @@ -231,7 +241,10 @@ pub enum PointerMode { Middle, } -pub fn interact(overlays: &mut OverlayContainer, app: &mut AppState) -> [f32; 2] +pub fn interact( + overlays: &mut OverlayContainer, + app: &mut AppState, +) -> [(f32, Option); 2] where O: Default, { @@ -241,7 +254,11 @@ where ] } -fn interact_hand(idx: usize, overlays: &mut OverlayContainer, app: &mut AppState) -> f32 +fn interact_hand( + idx: usize, + overlays: &mut OverlayContainer, + app: &mut AppState, +) -> (f32, Option) where O: Default, { @@ -254,7 +271,7 @@ where log::warn!("Grabbed overlay {} does not exist", grab_data.grabbed_id); pointer.interaction.grabbed = None; } - return 0.1; + return (0.1, None); } let Some(mut hit) = pointer.get_nearest_hit(overlays) else { @@ -276,7 +293,7 @@ where clicked.backend.on_pointer(app, &hit, false); } } - return 0.0; // no hit + return (0.0, None); // no hit }; if let Some(hovered_id) = pointer.interaction.hovered_id { @@ -292,7 +309,7 @@ where } let Some(hovered) = overlays.mut_by_id(hit.overlay) else { log::warn!("Hit overlay {} does not exist", hit.overlay); - return 0.0; // no hit + return (0.0, None); // no hit }; pointer.interaction.hovered_id = Some(hit.overlay); @@ -312,10 +329,17 @@ where if pointer.now.grab && !pointer.before.grab && hovered.state.grabbable { pointer.start_grab(hovered); - return hit.dist; + return ( + hit.dist, + Some(Haptics { + intensity: 0.25, + duration: 0.1, + frequency: 0.1, + }), + ); } - hovered.backend.on_hover(app, &hit); + let haptics = hovered.backend.on_hover(app, &hit); pointer = &mut app.input_state.pointers[idx]; if pointer.now.scroll.abs() > 0.1 { @@ -336,7 +360,7 @@ where hovered.backend.on_pointer(app, &hit, false); } } - hit.dist + (hit.dist, haptics) } impl Pointer { diff --git a/src/backend/openvr/input.rs b/src/backend/openvr/input.rs index 73f4c66..4ac313c 100644 --- a/src/backend/openvr/input.rs +++ b/src/backend/openvr/input.rs @@ -1,4 +1,4 @@ -use std::{array, io::Write, path::Path}; +use std::{array, io::Write, path::Path, time::Duration}; use ovr_overlay::{ input::{ActionHandle, ActionSetHandle, ActiveActionSet, InputManager, InputValueHandle}, @@ -11,7 +11,7 @@ use ovr_overlay::{ }; use crate::{ - backend::input::{TrackedDevice, TrackedDeviceRole}, + backend::input::{Haptics, TrackedDevice, TrackedDeviceRole}, state::AppState, }; @@ -119,6 +119,18 @@ impl OpenVrInputSource { }) } + pub fn haptics(&mut self, input: &mut InputManager, hand: usize, haptics: &Haptics) { + let hnd = self.hands[hand].haptics_hnd; + let _ = input.trigger_haptic_vibration_action( + hnd, + 0.0, + Duration::from_secs_f32(haptics.duration), + haptics.frequency, + haptics.intensity, + INPUT_ANY, + ); + } + pub fn update( &mut self, input: &mut InputManager, diff --git a/src/backend/openvr/mod.rs b/src/backend/openvr/mod.rs index d777d9a..8514344 100644 --- a/src/backend/openvr/mod.rs +++ b/src/backend/openvr/mod.rs @@ -162,8 +162,8 @@ pub fn openvr_run(running: Arc) -> Result<(), BackendError> { space_mover.update(&mut chaperone_mgr, &mut overlays, &state); - let pointer_lengths = interact(&mut overlays, &mut state); - for (idx, len) in pointer_lengths.iter().enumerate() { + let lengths_haptics = interact(&mut overlays, &mut state); + for (idx, (len, haptics)) in lengths_haptics.iter().enumerate() { lines.draw_from( pointer_lines[idx], state.input_state.pointers[idx].pose, @@ -171,6 +171,9 @@ pub fn openvr_run(running: Arc) -> Result<(), BackendError> { state.input_state.pointers[idx].interaction.mode as usize + 1, &state.input_state.hmd, ); + if let Some(haptics) = haptics { + input_source.haptics(&mut input_mngr, idx, haptics) + } } lines.update(&mut overlay_mngr, &mut state); diff --git a/src/backend/openxr/mod.rs b/src/backend/openxr/mod.rs index fff0a75..fe2e0b1 100644 --- a/src/backend/openxr/mod.rs +++ b/src/backend/openxr/mod.rs @@ -223,8 +223,8 @@ pub fn openxr_run(running: Arc) -> Result<(), BackendError> { .iter_mut() .for_each(|o| o.state.auto_movement(&mut app_state)); - let pointer_lengths = interact(&mut overlays, &mut app_state); - for (idx, len) in pointer_lengths.iter().enumerate() { + let lengths_haptics = interact(&mut overlays, &mut app_state); + for (idx, (len, haptics)) in lengths_haptics.iter().enumerate() { lines.draw_from( pointer_lines[idx], app_state.input_state.pointers[idx].pose, @@ -232,6 +232,9 @@ pub fn openxr_run(running: Arc) -> Result<(), BackendError> { app_state.input_state.pointers[idx].interaction.mode as usize + 1, &app_state.input_state.hmd, ); + if let Some(haptics) = haptics { + //TODO! + } } let mut layers = vec![]; diff --git a/src/backend/overlay.rs b/src/backend/overlay.rs index d0e691f..c0ea5a7 100644 --- a/src/backend/overlay.rs +++ b/src/backend/overlay.rs @@ -11,7 +11,7 @@ use vulkano::image::view::ImageView; use crate::state::AppState; -use super::input::{DummyInteractionHandler, InteractionHandler, PointerHit}; +use super::input::{DummyInteractionHandler, Haptics, InteractionHandler, PointerHit}; static AUTO_INCREMENT: AtomicUsize = AtomicUsize::new(0); @@ -259,8 +259,8 @@ impl InteractionHandler for SplitOverlayBackend { fn on_left(&mut self, app: &mut AppState, pointer: usize) { self.interaction.on_left(app, pointer); } - fn on_hover(&mut self, app: &mut AppState, hit: &PointerHit) { - self.interaction.on_hover(app, hit); + fn on_hover(&mut self, app: &mut AppState, hit: &PointerHit) -> Option { + self.interaction.on_hover(app, hit) } fn on_scroll(&mut self, app: &mut AppState, hit: &PointerHit, delta: f32) { self.interaction.on_scroll(app, hit, delta); diff --git a/src/gui/mod.rs b/src/gui/mod.rs index 1cf9aeb..c79df26 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -9,7 +9,7 @@ use vulkano::{ use crate::{ backend::{ - input::{InteractionHandler, PointerHit, PointerMode}, + input::{Haptics, InteractionHandler, PointerHit, PointerMode}, overlay::{OverlayBackend, OverlayRenderer}, }, graphics::{WlxCommandBuffer, WlxGraphics, WlxPass, WlxPipeline, WlxPipelineLegacy}, @@ -351,12 +351,22 @@ impl InteractionHandler for Canvas { fn on_left(&mut self, _app: &mut AppState, pointer: usize) { self.hover_controls[pointer] = None; } - fn on_hover(&mut self, _app: &mut AppState, hit: &PointerHit) { + fn on_hover(&mut self, _app: &mut AppState, hit: &PointerHit) -> Option { + let old = self.hover_controls[hit.pointer]; if let Some(i) = self.interactive_get_idx(hit.uv) { self.hover_controls[hit.pointer] = Some(i); } else { self.hover_controls[hit.pointer] = None; } + if old != self.hover_controls[hit.pointer] { + Some(Haptics { + intensity: 0.1, + duration: 0.01, + frequency: 5.0, + }) + } else { + None + } } fn on_pointer(&mut self, app: &mut AppState, hit: &PointerHit, pressed: bool) { let idx = if pressed { diff --git a/src/overlays/screen.rs b/src/overlays/screen.rs index 3b62178..cda6edc 100644 --- a/src/overlays/screen.rs +++ b/src/overlays/screen.rs @@ -26,7 +26,7 @@ use glam::{vec2, vec3a, Affine2, Quat, Vec2, Vec3}; use crate::{ backend::{ - input::{InteractionHandler, PointerHit, PointerMode}, + input::{Haptics, InteractionHandler, PointerHit, PointerMode}, overlay::{OverlayData, OverlayRenderer, OverlayState, SplitOverlayBackend}, }, config::def_pw_tokens, @@ -73,13 +73,14 @@ impl ScreenInteractionHandler { } impl InteractionHandler for ScreenInteractionHandler { - fn on_hover(&mut self, app: &mut AppState, hit: &PointerHit) { + fn on_hover(&mut self, app: &mut AppState, hit: &PointerHit) -> Option { #[cfg(debug_assertions)] log::trace!("Hover: {:?}", hit.uv); if self.next_move < Instant::now() { let pos = self.mouse_transform.transform_point2(hit.uv); app.hid_provider.mouse_move(pos); } + None } fn on_pointer(&mut self, app: &mut AppState, hit: &PointerHit, pressed: bool) { let btn = match hit.mode {