From 4d897f4fcddb7c6a7806a4a6100002946799b513 Mon Sep 17 00:00:00 2001 From: Nova Date: Tue, 13 Sep 2022 16:40:59 -0400 Subject: [PATCH] feat(input): mouse pointer --- src/main.rs | 11 +- src/nodes/{input.rs => input/mod.rs} | 193 +++++++++--------- .../{input_pointer.rs => input/pointer.rs} | 42 ++-- src/nodes/mod.rs | 1 - src/nodes/spatial.rs | 7 + src/objects/input/mod.rs | 1 + src/objects/input/mouse_pointer.rs | 34 +++ src/objects/mod.rs | 1 + 8 files changed, 171 insertions(+), 119 deletions(-) rename src/nodes/{input.rs => input/mod.rs} (54%) rename src/nodes/{input_pointer.rs => input/pointer.rs} (63%) create mode 100644 src/objects/input/mod.rs create mode 100644 src/objects/input/mouse_pointer.rs create mode 100644 src/objects/mod.rs diff --git a/src/main.rs b/src/main.rs index 72e8536..cddf69d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,12 @@ mod core; mod nodes; +mod objects; mod wayland; use crate::core::destroy_queue; -use crate::nodes::hmd; use crate::nodes::model::{MODELS_TO_DROP, MODEL_REGISTRY}; +use crate::nodes::{hmd, input}; +use crate::objects::input::mouse_pointer::MousePointer; use crate::wayland::Wayland; use self::core::eventloop::EventLoop; @@ -46,6 +48,8 @@ fn main() -> Result<()> { .expect("StereoKit failed to initialize"); println!("Init StereoKit"); + let mouse_pointer = cli_args.flatscreen.then(MousePointer::new); + if cli_args.flatscreen { unsafe { stereokit::sys::input_hand_visible(stereokit::sys::handed__handed_left, false as i32); @@ -74,6 +78,11 @@ fn main() -> Result<()> { } MODELS_TO_DROP.lock().clear(); + if let Some(mouse_pointer) = &mouse_pointer { + mouse_pointer.update(&stereokit); + } + input::process_input(); + wayland.make_context_current(); }, || { diff --git a/src/nodes/input.rs b/src/nodes/input/mod.rs similarity index 54% rename from src/nodes/input.rs rename to src/nodes/input/mod.rs index 6579a44..7336bff 100644 --- a/src/nodes/input.rs +++ b/src/nodes/input/mod.rs @@ -1,3 +1,6 @@ +pub mod pointer; + +use self::pointer::Pointer; use super::core::Node; use super::field::Field; use super::spatial::{get_spatial_parent_flex, get_transform_pose_flex, Spatial}; @@ -7,6 +10,7 @@ use crate::core::registry::Registry; use anyhow::{anyhow, ensure, Result}; use glam::Mat4; use libstardustxr::schemas::input::{InputData, InputDataArgs, InputDataRaw}; +use nanoid::nanoid; use std::ops::Deref; use std::sync::atomic::Ordering; use std::sync::{Arc, Weak}; @@ -14,7 +18,7 @@ use std::sync::{Arc, Weak}; static INPUT_METHOD_REGISTRY: Registry = Registry::new(); static INPUT_HANDLER_REGISTRY: Registry = Registry::new(); -pub trait InputSpecializationTrait { +pub trait InputSpecialization: Send + Sync { fn distance(&self, space: &Arc, field: &Field) -> f32; fn serialize( &self, @@ -27,25 +31,36 @@ pub trait InputSpecializationTrait { ); fn serialize_datamap(&self) -> Vec; } -enum InputSpecialization {} -impl Deref for InputSpecialization { - type Target = dyn InputSpecializationTrait; +pub enum InputType { + Pointer(Pointer), +} +impl Deref for InputType { + type Target = dyn InputSpecialization; fn deref(&self) -> &Self::Target { - todo!() - // match self { - // Field::Box(field) => field, - // } + match self { + InputType::Pointer(p) => p, + } } } pub struct InputMethod { - uid: String, + pub uid: String, pub spatial: Arc, - specialization: InputSpecialization, + pub specialization: InputType, + pub captures: Registry, } impl InputMethod { + pub fn new(spatial: Arc, specialization: InputType) -> Arc { + let method = InputMethod { + uid: nanoid!(), + spatial, + specialization, + captures: Registry::new(), + }; + INPUT_METHOD_REGISTRY.add(method) + } #[allow(dead_code)] - fn add_to(node: &Arc, specialization: InputSpecialization) -> Result<()> { + pub fn add_to(node: &Arc, specialization: InputType) -> Result<()> { ensure!( node.spatial.get().is_some(), "Internal: Node does not have a spatial attached!" @@ -55,6 +70,7 @@ impl InputMethod { uid: node.uid.clone(), spatial: node.spatial.get().unwrap().clone(), specialization, + captures: Registry::new(), }; let method = INPUT_METHOD_REGISTRY.add(method); let _ = node.input_method.set(method); @@ -65,6 +81,9 @@ impl InputMethod { .upgrade() .map(|field| self.specialization.distance(&self.spatial, &field)) } + fn serialize_datamap(&self) -> Vec { + self.specialization.serialize_datamap() + } } impl Drop for InputMethod { fn drop(&mut self) { @@ -74,51 +93,44 @@ impl Drop for InputMethod { pub struct DistanceLink { pub distance: f32, - pub method: Weak, - pub handler: Weak, + pub method: Arc, + pub handler: Arc, } impl DistanceLink { - fn from(method: &Arc, handler: &Arc) -> Option { + fn from(method: Arc, handler: Arc) -> Option { Some(DistanceLink { - distance: method.distance(handler)?, - method: Arc::downgrade(method), - handler: Arc::downgrade(handler), + distance: method.distance(&handler)?, + method, + handler, }) } - fn serialize(&self) -> Option> { - self.method.upgrade().and_then(|method| { - self.handler.upgrade().map(|handler| { - let mut fbb = flatbuffers::FlatBufferBuilder::with_capacity(1024); - let uid = Some(fbb.create_string(&method.uid)); - let datamap = Some(fbb.create_vector(&self.serialize_datamap())); - let (input_type, input_data) = method.specialization.serialize( - &mut fbb, - self, - Spatial::space_to_space_matrix(Some(&method.spatial), Some(&handler.spatial)), - ); - - let root = InputData::create( - &mut fbb, - &InputDataArgs { - uid, - input_type, - input: Some(input_data), - distance: self.distance, - datamap, - }, - ); - fbb.finish(root, None); - Vec::from(fbb.finished_data()) - }) - }) + fn send_input(&self, frame: u64, datamap: &[u8]) { + self.handler.send_input(frame, self, datamap); } - fn serialize_datamap(&self) -> Vec { - if let Some(method) = self.method.upgrade() { - method.specialization.serialize_datamap() - } else { - Default::default() - } + fn serialize(&self, datamap: &[u8]) -> Vec { + let mut fbb = flatbuffers::FlatBufferBuilder::with_capacity(1024); + let uid = Some(fbb.create_string(&self.method.uid)); + let datamap = Some(fbb.create_vector(datamap)); + + let (input_type, input_data) = self.method.specialization.serialize( + &mut fbb, + self, + Spatial::space_to_space_matrix(Some(&self.method.spatial), Some(&self.handler.spatial)), + ); + + let root = InputData::create( + &mut fbb, + &InputDataArgs { + uid, + input_type, + input: Some(input_data), + distance: self.distance, + datamap, + }, + ); + fbb.finish(root, None); + Vec::from(fbb.finished_data()) } } @@ -144,43 +156,30 @@ impl InputHandler { Ok(()) } - fn send_input( - &self, - old_frame: u64, - distance_link: DistanceLink, - distance_links: Vec, - ) { - if old_frame < FRAME.load(Ordering::Relaxed) { - return; - } + fn send_input(&self, frame: u64, distance_link: &DistanceLink, datamap: &[u8]) { + let data = distance_link.serialize(datamap); + let node = self.node.upgrade().unwrap(); + let method = Arc::downgrade(&distance_link.method); + let handler = Arc::downgrade(&distance_link.handler); - match distance_link.serialize() { - None => InputHandler::next_input(old_frame, distance_links), - Some(data) => { - let node = self.node.upgrade().unwrap(); + tokio::spawn(async move { + let data = node.execute_remote_method("input", data).await; + if frame == FRAME.load(Ordering::Relaxed) { + if let Ok(data) = data { + let capture = flexbuffers::Reader::get_root(data.as_slice()) + .and_then(|data| data.get_bool()) + .unwrap_or(false); - tokio::spawn(async move { - let data = node.execute_remote_method("input", data).await; - if let Ok(data) = data { - let capture = flexbuffers::Reader::get_root(data.as_slice()) - .and_then(|data| data.get_bool()) - .unwrap_or(false); - if !distance_links.is_empty() && !capture { - InputHandler::next_input(old_frame, distance_links); + if let Some(method) = method.upgrade() { + if let Some(handler) = handler.upgrade() { + if capture { + method.captures.add_raw(&handler); + } } } - }); + } } - } - } - - fn next_input(old_frame: u64, distance_links: Vec) { - let mut distance_links = distance_links; - if let Some(distance_link) = distance_links.pop() { - if let Some(handler) = distance_link.handler.upgrade() { - handler.send_input(old_frame, distance_link, distance_links); - } - } + }); } } impl Drop for InputHandler { @@ -190,7 +189,7 @@ impl Drop for InputHandler { } pub fn create_interface(client: &Arc) { - let node = Node::create(client, "", "data", false); + let node = Node::create(client, "", "input", false); node.add_local_signal("createInputHandler", create_input_handler_flex); node.add_to_scenegraph(); } @@ -225,20 +224,28 @@ pub fn create_input_handler_flex( Ok(()) } -#[allow(dead_code)] pub fn process_input() { for method in INPUT_METHOD_REGISTRY.get_valid_contents() { - let mut distance_links: Vec = Default::default(); - for handler in INPUT_HANDLER_REGISTRY.get_valid_contents() { - if let Some(distance_link) = DistanceLink::from(&method, &handler) { - distance_links.push(distance_link); - } - } - if distance_links.is_empty() { - continue; - } + let mut distance_links: Vec = INPUT_HANDLER_REGISTRY + .get_valid_contents() + .into_iter() + .filter_map(|handler| DistanceLink::from(method.clone(), handler)) + .collect(); distance_links .sort_unstable_by(|a, b| a.distance.partial_cmp(&b.distance).unwrap().reverse()); - InputHandler::next_input(FRAME.load(Ordering::Relaxed), distance_links); + + let datamap = method.serialize_datamap(); + let frame = FRAME.load(Ordering::Relaxed); + let captures = method.captures.get_valid_contents(); + for distance_link in distance_links { + distance_link.send_input(frame, &datamap); + if captures + .iter() + .any(|c| Arc::ptr_eq(c, &distance_link.handler)) + { + break; + } + } + method.captures.clear(); } } diff --git a/src/nodes/input_pointer.rs b/src/nodes/input/pointer.rs similarity index 63% rename from src/nodes/input_pointer.rs rename to src/nodes/input/pointer.rs index 7cb85c9..fdcef56 100644 --- a/src/nodes/input_pointer.rs +++ b/src/nodes/input/pointer.rs @@ -1,13 +1,14 @@ -use super::field::{ray_march, Field, Ray, RayMarchResult}; -use super::input::{DistanceLink, InputSpecializationTrait}; -use super::spatial::Spatial; -use glam::{vec3, vec3a, Mat4}; -use libstardustxr::schemas::common; +use glam::{vec3, Mat4}; use libstardustxr::schemas::input::InputDataRaw; use libstardustxr::schemas::input_pointer; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; +use crate::nodes::field::{ray_march, Field, Ray, RayMarchResult}; +use crate::nodes::spatial::Spatial; + +use super::{DistanceLink, InputSpecialization}; + #[derive(Default)] pub struct Pointer { grab: AtomicBool, @@ -34,7 +35,7 @@ impl Pointer { } } -impl InputSpecializationTrait for Pointer { +impl InputSpecialization for Pointer { fn distance(&self, space: &Arc, field: &Field) -> f32 { self.ray_march(space, field).distance } @@ -47,31 +48,24 @@ impl InputSpecializationTrait for Pointer { InputDataRaw, flatbuffers::WIPOffset, ) { - let origin = local_to_handler_matrix.transform_point3a(vec3a(0_f32, 0_f32, 0_f32)); - let direction = local_to_handler_matrix.transform_vector3a(vec3a(0_f32, 0_f32, 1_f32)); + let (_, orientation, origin) = local_to_handler_matrix.to_scale_rotation_translation(); + let direction = local_to_handler_matrix.transform_vector3(vec3(0_f32, 0_f32, 1_f32)); let ray_march = self.ray_march( - &distance_link.method.upgrade().unwrap().spatial, - &distance_link - .handler - .upgrade() - .unwrap() - .field - .upgrade() - .unwrap(), + &distance_link.method.spatial, + &distance_link.handler.field.upgrade().unwrap(), ); let deepest_point = (direction * ray_march.deepest_point_distance) + origin; + let origin: mint::Vector3 = origin.into(); + let orientation: mint::Quaternion = orientation.into(); + let deepest_point: mint::Vector3 = deepest_point.into(); + let pointer = input_pointer::Pointer::create( fbb, &input_pointer::PointerArgs { - origin: Some(&common::Vec3::new(origin.x, origin.y, origin.z)), - direction: Some(&common::Vec3::new(direction.x, direction.y, direction.z)), - tilt: 0_f32, - deepest_point: Some(&common::Vec3::new( - deepest_point.x, - deepest_point.y, - deepest_point.z, - )), + origin: Some(&origin.into()), + orientation: Some(&orientation.into()), + deepest_point: Some(&deepest_point.into()), }, ); (InputDataRaw::Pointer, pointer.as_union_value()) diff --git a/src/nodes/mod.rs b/src/nodes/mod.rs index 8eaae3b..d27a014 100644 --- a/src/nodes/mod.rs +++ b/src/nodes/mod.rs @@ -3,7 +3,6 @@ pub mod data; pub mod field; pub mod hmd; pub mod input; -pub mod input_pointer; pub mod item; pub mod model; pub mod root; diff --git a/src/nodes/spatial.rs b/src/nodes/spatial.rs index 6da8ba2..b7926c3 100644 --- a/src/nodes/spatial.rs +++ b/src/nodes/spatial.rs @@ -16,6 +16,13 @@ pub struct Spatial { } impl Spatial { + pub fn new(node: Weak, parent: Option>, transform: Mat4) -> Arc { + Arc::new(Spatial { + node, + parent: Mutex::new(parent), + transform: Mutex::new(transform), + }) + } pub fn add_to( node: &Arc, parent: Option>, diff --git a/src/objects/input/mod.rs b/src/objects/input/mod.rs new file mode 100644 index 0000000..b9be815 --- /dev/null +++ b/src/objects/input/mod.rs @@ -0,0 +1 @@ +pub mod mouse_pointer; diff --git a/src/objects/input/mouse_pointer.rs b/src/objects/input/mouse_pointer.rs new file mode 100644 index 0000000..6aedc55 --- /dev/null +++ b/src/objects/input/mouse_pointer.rs @@ -0,0 +1,34 @@ +use crate::nodes::{ + input::{pointer::Pointer, InputMethod, InputType}, + spatial::Spatial, +}; +use glam::{vec3, Mat4}; +use std::sync::{Arc, Weak}; +use stereokit::{input::Ray, StereoKit}; + +pub struct MousePointer { + pointer: Arc, +} +impl MousePointer { + pub fn new() -> Self { + MousePointer { + pointer: InputMethod::new( + Spatial::new(Weak::new(), None, Mat4::IDENTITY), + InputType::Pointer(Pointer::default()), + ), + } + } + pub fn update(&self, sk: &StereoKit) { + if let Some(ray) = Ray::from_mouse(sk.input_mouse()) { + self.pointer.spatial.set_local_transform_components( + None, + Some(ray.pos.into()), + Some(glam::Quat::from_rotation_arc( + vec3(0.0, 0.0, 1.0), + ray.dir.into(), + )), + None, + ); + } + } +} diff --git a/src/objects/mod.rs b/src/objects/mod.rs new file mode 100644 index 0000000..7839bc5 --- /dev/null +++ b/src/objects/mod.rs @@ -0,0 +1 @@ +pub mod input;