openvr haptics

This commit is contained in:
galister
2024-02-04 03:16:30 +01:00
parent b5d970e5fd
commit d7c8b49efa
7 changed files with 76 additions and 23 deletions

View File

@@ -135,6 +135,7 @@ pub struct InteractionState {
pub hovered_id: Option<usize>, pub hovered_id: Option<usize>,
pub release_actions: VecDeque<Box<dyn Fn()>>, pub release_actions: VecDeque<Box<dyn Fn()>>,
pub next_push: Instant, pub next_push: Instant,
pub haptics: Option<f32>,
} }
impl Default for InteractionState { impl Default for InteractionState {
@@ -146,6 +147,7 @@ impl Default for InteractionState {
hovered_id: None, hovered_id: None,
release_actions: VecDeque::new(), release_actions: VecDeque::new(),
next_push: Instant::now(), next_push: Instant::now(),
haptics: None,
} }
} }
} }
@@ -193,8 +195,14 @@ pub struct PointerHit {
pub dist: f32, pub dist: f32,
} }
pub struct Haptics {
pub intensity: f32,
pub duration: f32,
pub frequency: f32,
}
pub trait InteractionHandler { pub trait InteractionHandler {
fn on_hover(&mut self, app: &mut AppState, hit: &PointerHit); fn on_hover(&mut self, app: &mut AppState, hit: &PointerHit) -> Option<Haptics>;
fn on_left(&mut self, app: &mut AppState, pointer: usize); fn on_left(&mut self, app: &mut AppState, pointer: usize);
fn on_pointer(&mut self, app: &mut AppState, hit: &PointerHit, pressed: bool); fn on_pointer(&mut self, app: &mut AppState, hit: &PointerHit, pressed: bool);
fn on_scroll(&mut self, app: &mut AppState, hit: &PointerHit, delta: f32); fn on_scroll(&mut self, app: &mut AppState, hit: &PointerHit, delta: f32);
@@ -204,7 +212,9 @@ pub struct DummyInteractionHandler;
impl InteractionHandler for DummyInteractionHandler { impl InteractionHandler for DummyInteractionHandler {
fn on_left(&mut self, _app: &mut AppState, _pointer: usize) {} 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<Haptics> {
None
}
fn on_pointer(&mut self, _app: &mut AppState, _hit: &PointerHit, _pressed: bool) {} fn on_pointer(&mut self, _app: &mut AppState, _hit: &PointerHit, _pressed: bool) {}
fn on_scroll(&mut self, _app: &mut AppState, _hit: &PointerHit, _delta: f32) {} fn on_scroll(&mut self, _app: &mut AppState, _hit: &PointerHit, _delta: f32) {}
} }
@@ -231,7 +241,10 @@ pub enum PointerMode {
Middle, Middle,
} }
pub fn interact<O>(overlays: &mut OverlayContainer<O>, app: &mut AppState) -> [f32; 2] pub fn interact<O>(
overlays: &mut OverlayContainer<O>,
app: &mut AppState,
) -> [(f32, Option<Haptics>); 2]
where where
O: Default, O: Default,
{ {
@@ -241,7 +254,11 @@ where
] ]
} }
fn interact_hand<O>(idx: usize, overlays: &mut OverlayContainer<O>, app: &mut AppState) -> f32 fn interact_hand<O>(
idx: usize,
overlays: &mut OverlayContainer<O>,
app: &mut AppState,
) -> (f32, Option<Haptics>)
where where
O: Default, O: Default,
{ {
@@ -254,7 +271,7 @@ where
log::warn!("Grabbed overlay {} does not exist", grab_data.grabbed_id); log::warn!("Grabbed overlay {} does not exist", grab_data.grabbed_id);
pointer.interaction.grabbed = None; pointer.interaction.grabbed = None;
} }
return 0.1; return (0.1, None);
} }
let Some(mut hit) = pointer.get_nearest_hit(overlays) else { let Some(mut hit) = pointer.get_nearest_hit(overlays) else {
@@ -276,7 +293,7 @@ where
clicked.backend.on_pointer(app, &hit, false); 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 { if let Some(hovered_id) = pointer.interaction.hovered_id {
@@ -292,7 +309,7 @@ where
} }
let Some(hovered) = overlays.mut_by_id(hit.overlay) else { let Some(hovered) = overlays.mut_by_id(hit.overlay) else {
log::warn!("Hit overlay {} does not exist", hit.overlay); 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); pointer.interaction.hovered_id = Some(hit.overlay);
@@ -312,10 +329,17 @@ where
if pointer.now.grab && !pointer.before.grab && hovered.state.grabbable { if pointer.now.grab && !pointer.before.grab && hovered.state.grabbable {
pointer.start_grab(hovered); 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]; pointer = &mut app.input_state.pointers[idx];
if pointer.now.scroll.abs() > 0.1 { if pointer.now.scroll.abs() > 0.1 {
@@ -336,7 +360,7 @@ where
hovered.backend.on_pointer(app, &hit, false); hovered.backend.on_pointer(app, &hit, false);
} }
} }
hit.dist (hit.dist, haptics)
} }
impl Pointer { impl Pointer {

View File

@@ -1,4 +1,4 @@
use std::{array, io::Write, path::Path}; use std::{array, io::Write, path::Path, time::Duration};
use ovr_overlay::{ use ovr_overlay::{
input::{ActionHandle, ActionSetHandle, ActiveActionSet, InputManager, InputValueHandle}, input::{ActionHandle, ActionSetHandle, ActiveActionSet, InputManager, InputValueHandle},
@@ -11,7 +11,7 @@ use ovr_overlay::{
}; };
use crate::{ use crate::{
backend::input::{TrackedDevice, TrackedDeviceRole}, backend::input::{Haptics, TrackedDevice, TrackedDeviceRole},
state::AppState, 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( pub fn update(
&mut self, &mut self,
input: &mut InputManager, input: &mut InputManager,

View File

@@ -162,8 +162,8 @@ pub fn openvr_run(running: Arc<AtomicBool>) -> Result<(), BackendError> {
space_mover.update(&mut chaperone_mgr, &mut overlays, &state); space_mover.update(&mut chaperone_mgr, &mut overlays, &state);
let pointer_lengths = interact(&mut overlays, &mut state); let lengths_haptics = interact(&mut overlays, &mut state);
for (idx, len) in pointer_lengths.iter().enumerate() { for (idx, (len, haptics)) in lengths_haptics.iter().enumerate() {
lines.draw_from( lines.draw_from(
pointer_lines[idx], pointer_lines[idx],
state.input_state.pointers[idx].pose, state.input_state.pointers[idx].pose,
@@ -171,6 +171,9 @@ pub fn openvr_run(running: Arc<AtomicBool>) -> Result<(), BackendError> {
state.input_state.pointers[idx].interaction.mode as usize + 1, state.input_state.pointers[idx].interaction.mode as usize + 1,
&state.input_state.hmd, &state.input_state.hmd,
); );
if let Some(haptics) = haptics {
input_source.haptics(&mut input_mngr, idx, haptics)
}
} }
lines.update(&mut overlay_mngr, &mut state); lines.update(&mut overlay_mngr, &mut state);

View File

@@ -223,8 +223,8 @@ pub fn openxr_run(running: Arc<AtomicBool>) -> Result<(), BackendError> {
.iter_mut() .iter_mut()
.for_each(|o| o.state.auto_movement(&mut app_state)); .for_each(|o| o.state.auto_movement(&mut app_state));
let pointer_lengths = interact(&mut overlays, &mut app_state); let lengths_haptics = interact(&mut overlays, &mut app_state);
for (idx, len) in pointer_lengths.iter().enumerate() { for (idx, (len, haptics)) in lengths_haptics.iter().enumerate() {
lines.draw_from( lines.draw_from(
pointer_lines[idx], pointer_lines[idx],
app_state.input_state.pointers[idx].pose, app_state.input_state.pointers[idx].pose,
@@ -232,6 +232,9 @@ pub fn openxr_run(running: Arc<AtomicBool>) -> Result<(), BackendError> {
app_state.input_state.pointers[idx].interaction.mode as usize + 1, app_state.input_state.pointers[idx].interaction.mode as usize + 1,
&app_state.input_state.hmd, &app_state.input_state.hmd,
); );
if let Some(haptics) = haptics {
//TODO!
}
} }
let mut layers = vec![]; let mut layers = vec![];

View File

@@ -11,7 +11,7 @@ use vulkano::image::view::ImageView;
use crate::state::AppState; use crate::state::AppState;
use super::input::{DummyInteractionHandler, InteractionHandler, PointerHit}; use super::input::{DummyInteractionHandler, Haptics, InteractionHandler, PointerHit};
static AUTO_INCREMENT: AtomicUsize = AtomicUsize::new(0); 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) { fn on_left(&mut self, app: &mut AppState, pointer: usize) {
self.interaction.on_left(app, pointer); self.interaction.on_left(app, pointer);
} }
fn on_hover(&mut self, app: &mut AppState, hit: &PointerHit) { fn on_hover(&mut self, app: &mut AppState, hit: &PointerHit) -> Option<Haptics> {
self.interaction.on_hover(app, hit); self.interaction.on_hover(app, hit)
} }
fn on_scroll(&mut self, app: &mut AppState, hit: &PointerHit, delta: f32) { fn on_scroll(&mut self, app: &mut AppState, hit: &PointerHit, delta: f32) {
self.interaction.on_scroll(app, hit, delta); self.interaction.on_scroll(app, hit, delta);

View File

@@ -9,7 +9,7 @@ use vulkano::{
use crate::{ use crate::{
backend::{ backend::{
input::{InteractionHandler, PointerHit, PointerMode}, input::{Haptics, InteractionHandler, PointerHit, PointerMode},
overlay::{OverlayBackend, OverlayRenderer}, overlay::{OverlayBackend, OverlayRenderer},
}, },
graphics::{WlxCommandBuffer, WlxGraphics, WlxPass, WlxPipeline, WlxPipelineLegacy}, graphics::{WlxCommandBuffer, WlxGraphics, WlxPass, WlxPipeline, WlxPipelineLegacy},
@@ -351,12 +351,22 @@ impl<D, S> InteractionHandler for Canvas<D, S> {
fn on_left(&mut self, _app: &mut AppState, pointer: usize) { fn on_left(&mut self, _app: &mut AppState, pointer: usize) {
self.hover_controls[pointer] = None; 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<Haptics> {
let old = self.hover_controls[hit.pointer];
if let Some(i) = self.interactive_get_idx(hit.uv) { if let Some(i) = self.interactive_get_idx(hit.uv) {
self.hover_controls[hit.pointer] = Some(i); self.hover_controls[hit.pointer] = Some(i);
} else { } else {
self.hover_controls[hit.pointer] = None; 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) { fn on_pointer(&mut self, app: &mut AppState, hit: &PointerHit, pressed: bool) {
let idx = if pressed { let idx = if pressed {

View File

@@ -26,7 +26,7 @@ use glam::{vec2, vec3a, Affine2, Quat, Vec2, Vec3};
use crate::{ use crate::{
backend::{ backend::{
input::{InteractionHandler, PointerHit, PointerMode}, input::{Haptics, InteractionHandler, PointerHit, PointerMode},
overlay::{OverlayData, OverlayRenderer, OverlayState, SplitOverlayBackend}, overlay::{OverlayData, OverlayRenderer, OverlayState, SplitOverlayBackend},
}, },
config::def_pw_tokens, config::def_pw_tokens,
@@ -73,13 +73,14 @@ impl ScreenInteractionHandler {
} }
impl InteractionHandler for 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<Haptics> {
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
log::trace!("Hover: {:?}", hit.uv); log::trace!("Hover: {:?}", hit.uv);
if self.next_move < Instant::now() { if self.next_move < Instant::now() {
let pos = self.mouse_transform.transform_point2(hit.uv); let pos = self.mouse_transform.transform_point2(hit.uv);
app.hid_provider.mouse_move(pos); app.hid_provider.mouse_move(pos);
} }
None
} }
fn on_pointer(&mut self, app: &mut AppState, hit: &PointerHit, pressed: bool) { fn on_pointer(&mut self, app: &mut AppState, hit: &PointerHit, pressed: bool) {
let btn = match hit.mode { let btn = match hit.mode {