diff --git a/Cargo.lock b/Cargo.lock index 77e81a9..e45238e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2031,8 +2031,8 @@ checksum = "2f2b15926089e5526bb2dd738a2eb0e59034356e06eb71e1cd912358c0e62c4d" [[package]] name = "stardust-xr" -version = "0.44.0" -source = "git+https://github.com/StardustXR/core.git#c587446076b92d8a945f4d0e2a872c09f80cc575" +version = "0.45.0" +source = "git+https://github.com/StardustXR/core.git#21120b8924d2a450f4dd92a022d4df40b7e3885d" dependencies = [ "cluFlock", "color-rs", @@ -2052,7 +2052,7 @@ dependencies = [ [[package]] name = "stardust-xr-schemas" version = "1.5.3" -source = "git+https://github.com/StardustXR/core.git#c587446076b92d8a945f4d0e2a872c09f80cc575" +source = "git+https://github.com/StardustXR/core.git#21120b8924d2a450f4dd92a022d4df40b7e3885d" dependencies = [ "flatbuffers", "flexbuffers", diff --git a/src/main.rs b/src/main.rs index 758d957..b6bead9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -170,8 +170,8 @@ fn main() { .unwrap(); let mut hands = (!cli_args.flatscreen) .then(|| { - let left = SkHand::new(Handed::Left).ok(); - let right = SkHand::new(Handed::Right).ok(); + let left = SkHand::new(Handed::Left, &sk).ok(); + let right = SkHand::new(Handed::Right, &sk).ok(); left.zip(right) }) .flatten(); diff --git a/src/nodes/input/hand.rs b/src/nodes/input/hand.rs index 0c8f9dc..db7875e 100644 --- a/src/nodes/input/hand.rs +++ b/src/nodes/input/hand.rs @@ -1,4 +1,4 @@ -use super::{DistanceLink, Finger, Hand, InputDataTrait, Joint, Thumb}; +use super::{Finger, Hand, InputDataTrait, InputLink, Joint, Thumb}; use crate::nodes::fields::Field; use crate::nodes::spatial::Spatial; use glam::{vec3a, Mat4, Quat}; @@ -52,10 +52,7 @@ impl Default for Hand { } impl InputDataTrait for Hand { - fn compare_distance(&self, space: &Arc, field: &Field) -> f32 { - self.true_distance(space, field).abs() - } - fn true_distance(&self, space: &Arc, field: &Field) -> f32 { + fn distance(&self, space: &Arc, field: &Field) -> f32 { let mut min_distance = f32::MAX; for tip in [ @@ -70,7 +67,7 @@ impl InputDataTrait for Hand { min_distance } - fn update_to(&mut self, distance_link: &DistanceLink, local_to_handler_matrix: Mat4) { + fn update_to(&mut self, input_link: &InputLink, local_to_handler_matrix: Mat4) { let mut joints: Vec<&mut Joint> = Vec::new(); joints.extend([&mut self.palm, &mut self.wrist]); @@ -104,10 +101,10 @@ impl InputDataTrait for Hand { let (_, rotation, position) = joint_matrix.to_scale_rotation_translation(); joint.position = position.into(); joint.rotation = rotation.into(); - joint.distance = distance_link + joint.distance = input_link .handler .field - .distance(&distance_link.handler.spatial, position.into()); + .distance(&input_link.handler.spatial, position.into()); } } } diff --git a/src/nodes/input/handler.rs b/src/nodes/input/handler.rs index 2ca0f97..1d68302 100644 --- a/src/nodes/input/handler.rs +++ b/src/nodes/input/handler.rs @@ -1,5 +1,5 @@ use super::{ - input_handler_client, DistanceLink, InputHandlerAspect, INPUT_HANDLER_REGISTRY, + input_handler_client, InputHandlerAspect, InputLink, INPUT_HANDLER_REGISTRY, INPUT_METHOD_REGISTRY, }; use crate::{ @@ -7,13 +7,11 @@ use crate::{ nodes::{fields::Field, spatial::Spatial, Aspect, Node}, }; use color_eyre::eyre::Result; -use portable_atomic::AtomicBool; use stardust_xr::values::Datamap; use std::sync::{Arc, Weak}; use tracing::instrument; pub struct InputHandler { - pub enabled: Arc, pub uid: String, pub node: Weak, pub spatial: Arc, @@ -23,7 +21,6 @@ pub struct InputHandler { impl InputHandler { pub fn add_to(node: &Arc, field: &Arc) -> Result<()> { let handler = InputHandler { - enabled: node.enabled.clone(), uid: node.uid.clone(), node: Arc::downgrade(node), spatial: node.get_aspect::().unwrap().clone(), @@ -39,28 +36,28 @@ impl InputHandler { Ok(()) } - #[instrument(level = "debug", skip(self, distance_link))] + #[instrument(level = "debug", skip(self, input_link))] pub(super) fn send_input( &self, order: u32, captured: bool, - distance_link: &DistanceLink, + input_link: &InputLink, datamap: Datamap, ) { let Some(node) = self.node.upgrade() else { return; }; - let Some(method_alias) = distance_link + let Some(method_alias) = input_link .handler .method_aliases - .get(&(Arc::as_ptr(&distance_link.method) as usize)) + .get(&(Arc::as_ptr(&input_link.method) as usize)) else { return; }; let _ = input_handler_client::input( &node, &method_alias, - &distance_link.serialize(order, captured, datamap), + &input_link.serialize(order, captured, datamap), ); } } diff --git a/src/nodes/input/method.rs b/src/nodes/input/method.rs index 0d716c1..8743e02 100644 --- a/src/nodes/input/method.rs +++ b/src/nodes/input/method.rs @@ -25,9 +25,9 @@ pub struct InputMethod { pub data: Mutex, pub datamap: Mutex, - pub(super) captures: Registry, + pub captures: Registry, pub(super) handler_aliases: LifeLinkedNodeMap, - pub(super) handler_order: Mutex>>>, + pub(super) handler_order: Mutex>>, } impl InputMethod { pub fn add_to( @@ -44,7 +44,7 @@ impl InputMethod { captures: Registry::new(), datamap: Mutex::new(datamap), handler_aliases: LifeLinkedNodeMap::default(), - handler_order: Mutex::new(None), + handler_order: Mutex::new(Vec::new()), }; for handler in INPUT_HANDLER_REGISTRY.get_valid_contents() { method.handle_new_handler(&handler); @@ -84,16 +84,12 @@ impl InputMethod { .add(self as *const InputMethod as usize, &method_alias); } - pub fn compare_distance(&self, to: &InputHandler) -> f32 { - let distance = self.data.lock().compare_distance(&self.spatial, &to.field); - if self.captures.contains(to) { - distance * 0.5 - } else { - distance - } + pub fn distance(&self, to: &Field) -> f32 { + self.data.lock().distance(&self.spatial, to) } - pub fn true_distance(&self, to: &Field) -> f32 { - self.data.lock().true_distance(&self.spatial, to) + + pub fn set_handler_order<'a>(&self, handlers: impl Iterator>) { + *self.handler_order.lock() = handlers.map(Arc::downgrade).collect(); } pub(super) fn handle_new_handler(&self, handler: &InputHandler) { @@ -137,7 +133,7 @@ impl InputMethod { .add(handler.uid.clone() + "-field", &rx_field_alias); } - let _ = input_method_client::new_handler(&method_node, &handler.uid, &handler_node); + let _ = input_method_client::create_handler(&method_node, &handler.uid, &handler_node); } pub(super) fn handle_drop_handler(&self, handler: &InputHandler) { let uid = handler.uid.as_str(); @@ -147,7 +143,7 @@ impl InputMethod { return; }; - let _ = input_method_client::drop_handler(&tx_node, &uid); + let _ = input_method_client::destroy_handler(&tx_node, &uid); } } impl Aspect for InputMethod { @@ -176,25 +172,25 @@ impl InputMethodAspect for InputMethod { fn set_handler_order( node: Arc, _calling_client: Arc, - handlers: Option>>, + handlers: Vec>, ) -> Result<()> { let input_method = node.get_aspect::()?; - let Some(handlers) = handlers else { - *input_method.handler_order.lock() = None; - return Ok(()); - }; let handlers = handlers .into_iter() .filter_map(|p| p.get_aspect::().ok()) .map(|i| Arc::downgrade(&i)) .collect::>(); - *input_method.handler_order.lock() = Some(handlers); + *input_method.handler_order.lock() = handlers; Ok(()) } #[doc = "Have the input handler that this method reference came from capture the method for the next frame."] - fn capture(node: Arc, _calling_client: Arc, handler: Arc) -> Result<()> { + fn request_capture( + node: Arc, + _calling_client: Arc, + handler: Arc, + ) -> Result<()> { let input_method = node.get_aspect::()?; let input_handler = handler.get_aspect::()?; diff --git a/src/nodes/input/mod.rs b/src/nodes/input/mod.rs index c72c3f4..f91f2a6 100644 --- a/src/nodes/input/mod.rs +++ b/src/nodes/input/mod.rs @@ -21,22 +21,17 @@ use std::sync::{Arc, Weak}; use tracing::{debug_span, instrument}; static INPUT_METHOD_REGISTRY: Registry = Registry::new(); -static INPUT_HANDLER_REGISTRY: Registry = Registry::new(); +pub static INPUT_HANDLER_REGISTRY: Registry = Registry::new(); stardust_xr_server_codegen::codegen_input_protocol!(); -pub struct DistanceLink { - distance: f32, +pub struct InputLink { method: Arc, handler: Arc, } -impl DistanceLink { +impl InputLink { fn from(method: Arc, handler: Arc) -> Self { - DistanceLink { - distance: method.compare_distance(&handler), - method, - handler, - } + InputLink { method, handler } } fn send_input(&self, order: u32, captured: bool, datamap: Datamap) { @@ -53,7 +48,7 @@ impl DistanceLink { InputData { uid: self.method.uid.clone(), input, - distance: self.method.true_distance(&self.handler.field), + distance: self.method.distance(&self.handler.field), datamap, order, captured, @@ -61,32 +56,23 @@ impl DistanceLink { } } pub trait InputDataTrait { - fn compare_distance(&self, space: &Arc, field: &Field) -> f32; - fn true_distance(&self, space: &Arc, field: &Field) -> f32; - fn update_to(&mut self, distance_link: &DistanceLink, local_to_handler_matrix: Mat4); + fn distance(&self, space: &Arc, field: &Field) -> f32; + fn update_to(&mut self, input_link: &InputLink, local_to_handler_matrix: Mat4); } impl InputDataTrait for InputDataType { - fn compare_distance(&self, space: &Arc, field: &Field) -> f32 { + fn distance(&self, space: &Arc, field: &Field) -> f32 { match self { - InputDataType::Pointer(i) => i.compare_distance(space, field), - InputDataType::Hand(i) => i.compare_distance(space, field), - InputDataType::Tip(i) => i.compare_distance(space, field), + InputDataType::Pointer(i) => i.distance(space, field), + InputDataType::Hand(i) => i.distance(space, field), + InputDataType::Tip(i) => i.distance(space, field), } } - fn true_distance(&self, space: &Arc, field: &Field) -> f32 { + fn update_to(&mut self, input_link: &InputLink, local_to_handler_matrix: Mat4) { match self { - InputDataType::Pointer(i) => i.true_distance(space, field), - InputDataType::Hand(i) => i.true_distance(space, field), - InputDataType::Tip(i) => i.true_distance(space, field), - } - } - - fn update_to(&mut self, distance_link: &DistanceLink, local_to_handler_matrix: Mat4) { - match self { - InputDataType::Pointer(i) => i.update_to(distance_link, local_to_handler_matrix), - InputDataType::Hand(i) => i.update_to(distance_link, local_to_handler_matrix), - InputDataType::Tip(i) => i.update_to(distance_link, local_to_handler_matrix), + InputDataType::Pointer(i) => i.update_to(input_link, local_to_handler_matrix), + InputDataType::Hand(i) => i.update_to(input_link, local_to_handler_matrix), + InputDataType::Tip(i) => i.update_to(input_link, local_to_handler_matrix), } } } @@ -144,66 +130,42 @@ pub fn process_input() { .into_iter() .filter(|method| *method.enabled.lock()) }); - let handlers = INPUT_HANDLER_REGISTRY.get_valid_contents(); - const LIMIT: usize = 50; + // const LIMIT: usize = 50; for method in methods { for alias in method.node.upgrade().unwrap().aliases.get_valid_contents() { alias.enabled.store(false, Ordering::Release); } debug_span!("Process input method").in_scope(|| { - // Get all valid input handlers and convert them to DistanceLink objects - let distance_links: Vec = debug_span!("Generate distance links") - .in_scope(|| { - if let Some(handler_order) = &*method.handler_order.lock() { - handler_order - .iter() - .filter_map(Weak::upgrade) - .filter(|handler| handler.enabled.load(Ordering::Relaxed)) - .map(|handler| DistanceLink::from(method.clone(), handler)) - .collect() - } else { - let mut distance_links: Vec<_> = handlers - .iter() - .filter(|handler| handler.enabled.load(Ordering::Relaxed)) - .map(|handler| { - debug_span!("Create distance link").in_scope(|| { - DistanceLink::from(method.clone(), handler.clone()) - }) - }) - .collect(); + // Get all valid input handlers and convert them to InputLink objects + let input_links: Vec = debug_span!("Generate input links").in_scope(|| { + method + .handler_order + .lock() + .iter() + .filter_map(Weak::upgrade) + .filter(|handler| { + let Some(node) = handler.node.upgrade() else { + return false; + }; + node.enabled() + }) + .map(|handler| InputLink::from(method.clone(), handler)) + .collect() + }); - // Sort the distance links by their distance in ascending order - debug_span!("Sort distance links").in_scope(|| { - distance_links.sort_unstable_by(|a, b| { - a.distance.abs().partial_cmp(&b.distance.abs()).unwrap() - }); - }); - - distance_links.truncate(LIMIT); - distance_links - } - }); - - let captures = method.captures.take_valid_contents(); + method.captures.clear(); // Iterate over the distance links and send input to them - for (i, distance_link) in distance_links.into_iter().enumerate() { - if let Some(method_alias) = distance_link + for (i, input_link) in input_links.into_iter().enumerate() { + if let Some(method_alias) = input_link .handler .method_aliases - .get(&(Arc::as_ptr(&distance_link.method) as usize)) + .get(&(Arc::as_ptr(&input_link.method) as usize)) .and_then(|a| a.get_aspect::().ok()) { method_alias.enabled.store(true, Ordering::Release); } - let captured = captures.contains(&distance_link.handler); - distance_link.send_input(i as u32, captured, method.datamap.lock().clone()); - - // If the current distance link is in the list of captured input handlers, - // break out of the loop to avoid sending input to the remaining distance links - if captured { - break; - } + input_link.send_input(i as u32, true, method.datamap.lock().clone()); } }); } diff --git a/src/nodes/input/pointer.rs b/src/nodes/input/pointer.rs index 1c7f7d7..c0fdc13 100644 --- a/src/nodes/input/pointer.rs +++ b/src/nodes/input/pointer.rs @@ -1,4 +1,4 @@ -use super::{DistanceLink, InputDataTrait, Pointer}; +use super::{InputDataTrait, InputLink, Pointer}; use crate::nodes::{ fields::{Field, Ray, RayMarchResult}, spatial::Spatial, @@ -29,27 +29,17 @@ impl Pointer { } } impl InputDataTrait for Pointer { - fn compare_distance(&self, space: &Arc, field: &Field) -> f32 { - let ray_info = self.ray_march(space, field); - if ray_info.min_distance > 0.0 { - ray_info.deepest_point_distance + 1000.0 - } else { - ray_info - .deepest_point_distance - .hypot(0.001 / ray_info.min_distance) - } - } - fn true_distance(&self, space: &Arc, field: &Field) -> f32 { + fn distance(&self, space: &Arc, field: &Field) -> f32 { let ray_info = self.ray_march(space, field); ray_info.min_distance } - fn update_to(&mut self, distance_link: &DistanceLink, mut local_to_handler_matrix: Mat4) { + fn update_to(&mut self, input_link: &InputLink, mut local_to_handler_matrix: Mat4) { local_to_handler_matrix = Mat4::from_rotation_translation(self.orientation.into(), self.origin.into()) * local_to_handler_matrix; let (_, orientation, origin) = local_to_handler_matrix.to_scale_rotation_translation(); - let ray_march = self.ray_march(&distance_link.method.spatial, &distance_link.handler.field); + let ray_march = self.ray_march(&input_link.method.spatial, &input_link.handler.field); let direction = local_to_handler_matrix .transform_vector3(vec3(0.0, 0.0, -1.0)) .normalize(); diff --git a/src/nodes/input/tip.rs b/src/nodes/input/tip.rs index edb102b..ce48fc0 100644 --- a/src/nodes/input/tip.rs +++ b/src/nodes/input/tip.rs @@ -1,4 +1,4 @@ -use super::{DistanceLink, InputDataTrait, Tip}; +use super::{InputDataTrait, InputLink, Tip}; use crate::nodes::{fields::Field, spatial::Spatial}; use glam::{Mat4, Quat}; use std::sync::Arc; @@ -12,13 +12,10 @@ impl Default for Tip { } } impl InputDataTrait for Tip { - fn compare_distance(&self, space: &Arc, field: &Field) -> f32 { - field.distance(space, self.origin.into()).abs() - } - fn true_distance(&self, space: &Arc, field: &Field) -> f32 { + fn distance(&self, space: &Arc, field: &Field) -> f32 { field.distance(space, self.origin.into()) } - fn update_to(&mut self, _distance_link: &DistanceLink, mut local_to_handler_matrix: Mat4) { + fn update_to(&mut self, _input_link: &InputLink, mut local_to_handler_matrix: Mat4) { local_to_handler_matrix *= Mat4::from_rotation_translation(self.orientation.into(), self.origin.into()); let (_, orientation, origin) = local_to_handler_matrix.to_scale_rotation_translation(); diff --git a/src/nodes/mod.rs b/src/nodes/mod.rs index 7b67846..196be96 100644 --- a/src/nodes/mod.rs +++ b/src/nodes/mod.rs @@ -55,7 +55,7 @@ pub type Method = fn(Arc, Arc, Message, MethodResponseSender); stardust_xr_server_codegen::codegen_node_protocol!(); pub struct Node { - pub enabled: Arc, + enabled: Arc, pub(super) uid: String, path: String, client: Weak, @@ -113,6 +113,9 @@ impl Node { .scenegraph .add_node(self)) } + pub fn enabled(&self) -> bool { + self.enabled.load(Ordering::Relaxed) + } pub fn destroy(&self) { if let Some(client) = self.get_client() { client.scenegraph.remove_node(self.get_path()); diff --git a/src/nodes/root.rs b/src/nodes/root.rs index f935ae0..1ccb8d9 100644 --- a/src/nodes/root.rs +++ b/src/nodes/root.rs @@ -74,7 +74,7 @@ impl Root { message: Message, ) -> Result<()> { let prefixes: Vec = deserialize(message.as_ref())?; - info!(?client, ?prefixes, "Set base prefixes"); + info!(?calling_client, ?prefixes, "Set base prefixes"); *calling_client.base_resource_prefixes.lock() = prefixes; Ok(()) } diff --git a/src/objects/input/eye_pointer.rs b/src/objects/input/eye_pointer.rs index 2382e1c..cec98e5 100644 --- a/src/objects/input/eye_pointer.rs +++ b/src/objects/input/eye_pointer.rs @@ -1,13 +1,14 @@ use crate::{ core::client::INTERNAL_CLIENT, nodes::{ - input::{InputDataType, InputMethod, Pointer}, + fields::Ray, + input::{InputDataType, InputMethod, Pointer, INPUT_HANDLER_REGISTRY}, spatial::Spatial, Node, }, }; use color_eyre::eyre::Result; -use glam::Mat4; +use glam::{vec3, Mat4}; use nanoid::nanoid; use serde::{Deserialize, Serialize}; use stardust_xr::values::Datamap; @@ -56,5 +57,47 @@ impl EyePointer { // Set pointer input datamap *self.pointer.datamap.lock() = Datamap::from_typed(EyeDatamap { eye: 2 }).unwrap(); } + + // send input to all the input handlers that are the closest to the ray as possible + let rx = INPUT_HANDLER_REGISTRY + .get_valid_contents() + .into_iter() + // filter out all the disabled handlers + .filter(|handler| { + let Some(node) = handler.node.upgrade() else { + return false; + }; + node.enabled() + }) + // ray march to all the enabled handlers' fields + .map(|handler| { + let result = handler.field.ray_march(Ray { + origin: vec3(0.0, 0.0, 0.0), + direction: vec3(0.0, 0.0, -1.0), + space: self.spatial.clone(), + }); + (vec![handler], result) + }) + // make sure the field isn't at the pointer origin and that it's being hit + .filter(|(_, result)| result.deepest_point_distance > 0.01 && result.min_distance < 0.0) + // .inspect(|(_, result)| { + // dbg!(result); + // }) + // now collect all handlers that are same distance if they're the closest + .reduce(|(mut handlers_a, result_a), (handlers_b, result_b)| { + if (result_a.deepest_point_distance - result_b.deepest_point_distance).abs() < 0.001 + { + // distance is basically the same + handlers_a.extend(handlers_b); + (handlers_a, result_a) + } else if result_a.deepest_point_distance < result_b.deepest_point_distance { + (handlers_a, result_a) + } else { + (handlers_b, result_b) + } + }) + .map(|(rx, _)| rx) + .unwrap_or_default(); + self.pointer.set_handler_order(rx.iter()); } } diff --git a/src/objects/input/mouse_pointer.rs b/src/objects/input/mouse_pointer.rs index 1f0a5c7..003ad7f 100644 --- a/src/objects/input/mouse_pointer.rs +++ b/src/objects/input/mouse_pointer.rs @@ -5,7 +5,7 @@ use crate::{ mask_matches, pulse_receiver_client, PulseSender, KEYMAPS, PULSE_RECEIVER_REGISTRY, }, fields::{Field, Ray}, - input::{InputDataType, InputMethod, Pointer}, + input::{InputDataType, InputHandler, InputMethod, Pointer, INPUT_HANDLER_REGISTRY}, spatial::Spatial, Node, }, @@ -52,6 +52,7 @@ pub struct MousePointer { node: Arc, spatial: Arc, pointer: Arc, + capture: Option>, mouse_datamap: MouseEvent, keyboard_datamap: KeyboardEvent, keyboard_sender: Arc, @@ -84,6 +85,7 @@ impl MousePointer { node, spatial, pointer, + capture: None, mouse_datamap: Default::default(), keyboard_datamap: Default::default(), keyboard_sender, @@ -134,9 +136,106 @@ impl MousePointer { self.mouse_datamap.scroll_discrete = vec2(0.0, mouse.scroll_change / 120.0); *self.pointer.datamap.lock() = Datamap::from_typed(&self.mouse_datamap).unwrap(); } + self.target_pointer_input(); self.send_keyboard_input(sk); } + fn target_pointer_input(&mut self) { + // remove the capture when it's removed from captures list + if let Some(capture) = &self.capture { + if !self + .pointer + .captures + .get_valid_contents() + .contains(&capture) + { + self.capture.take(); + } + } + // add the capture that's the closest if we don't have one + if self.capture.is_none() { + if let Some(new_capture) = self + .pointer + .captures + .get_valid_contents() + .into_iter() + .map(|h| { + ( + h.clone(), + h.field.ray_march(Ray { + origin: vec3(0.0, 0.0, 0.0), + direction: vec3(0.0, 0.0, -1.0), + space: self.spatial.clone(), + }), + ) + }) + .reduce(|(handlers_a, result_a), (handlers_b, result_b)| { + if result_a.min_distance < result_b.min_distance { + (handlers_a, result_a) + } else { + (handlers_b, result_b) + } + }) + .map(|(rx, _)| rx) + { + self.capture = Some(new_capture.clone()); + } + } + + // make sure that if something is captured only send input to it + if let Some(capture) = &self.capture { + self.pointer.set_handler_order([capture].into_iter()); + return; + } + + // send input to all the input handlers that are the closest to the ray as possible + self.pointer.set_handler_order( + INPUT_HANDLER_REGISTRY + .get_valid_contents() + .into_iter() + // filter out all the disabled handlers + .filter(|handler| { + let Some(node) = handler.node.upgrade() else { + return false; + }; + node.enabled() + }) + // ray march to all the enabled handlers' fields + .map(|handler| { + let result = handler.field.ray_march(Ray { + origin: vec3(0.0, 0.0, 0.0), + direction: vec3(0.0, 0.0, -1.0), + space: self.spatial.clone(), + }); + (vec![handler], result) + }) + // make sure the field isn't at the pointer origin and that it's being hit + .filter(|(_, result)| { + result.deepest_point_distance > 0.01 && result.min_distance < 0.0 + }) + // .inspect(|(_, result)| { + // dbg!(result); + // }) + // now collect all handlers that are same distance if they're the closest + .reduce(|(mut handlers_a, result_a), (handlers_b, result_b)| { + if (result_a.deepest_point_distance - result_b.deepest_point_distance).abs() + < 0.001 + { + // distance is basically the same + handlers_a.extend(handlers_b); + (handlers_a, result_a) + } else if result_a.deepest_point_distance < result_b.deepest_point_distance { + (handlers_a, result_a) + } else { + (handlers_b, result_b) + } + }) + .map(|(rx, _)| rx) + .unwrap_or_default() + .iter(), + ); + } + fn send_keyboard_input(&mut self, sk: &impl StereoKitMultiThread) { let rx = PULSE_RECEIVER_REGISTRY .get_valid_contents() diff --git a/src/objects/input/sk_controller.rs b/src/objects/input/sk_controller.rs index 4259284..740e809 100644 --- a/src/objects/input/sk_controller.rs +++ b/src/objects/input/sk_controller.rs @@ -1,7 +1,7 @@ use crate::{ core::client::INTERNAL_CLIENT, nodes::{ - input::{InputDataType, InputMethod, Tip}, + input::{InputDataType, InputHandler, InputMethod, Tip, INPUT_HANDLER_REGISTRY}, spatial::Spatial, Node, }, @@ -26,8 +26,9 @@ struct ControllerDatamap { pub struct SkController { _node: Arc, input: Arc, - model: Model, handed: Handed, + model: Model, + capture: Option>, datamap: ControllerDatamap, } impl SkController { @@ -56,6 +57,7 @@ impl SkController { input, handed, model, + capture: None, datamap: Default::default(), }) } @@ -79,5 +81,85 @@ impl SkController { self.datamap.grab = controller.grip; self.datamap.scroll = controller.stick; *self.input.datamap.lock() = Datamap::from_typed(&self.datamap).unwrap(); + + // remove the capture when it's removed from captures list + if let Some(capture) = &self.capture { + if !self.input.captures.get_valid_contents().contains(&capture) { + self.capture.take(); + } + } + // add the capture that's the closest if we don't have one + if self.capture.is_none() { + self.capture = self + .input + .captures + .get_valid_contents() + .into_iter() + .map(|handler| { + ( + handler.clone(), + handler + .field + .distance(&self.input.spatial, [0.0; 3].into()) + .abs(), + ) + }) + .reduce(|(handlers_a, distance_a), (handlers_b, distance_b)| { + if distance_a < distance_b { + (handlers_a, distance_a) + } else { + (handlers_b, distance_b) + } + }) + .map(|(rx, _)| rx); + } + + // make sure that if something is captured only send input to it + if let Some(capture) = &self.capture { + self.input.set_handler_order([capture].into_iter()); + return; + } + + // send input to all the input handlers that are the closest to the ray as possible + self.input.set_handler_order( + INPUT_HANDLER_REGISTRY + .get_valid_contents() + .into_iter() + // filter out all the disabled handlers + .filter(|handler| { + let Some(node) = handler.node.upgrade() else { + return false; + }; + node.enabled() + }) + // get the unsigned distance to the handler's field (unsigned so giant fields won't always eat input) + .map(|handler| { + ( + vec![handler.clone()], + handler + .field + .distance(&self.input.spatial, [0.0; 3].into()) + .abs(), + ) + }) + // .inspect(|(_, result)| { + // dbg!(result); + // }) + // now collect all handlers that are same distance if they're the closest + .reduce(|(mut handlers_a, distance_a), (handlers_b, distance_b)| { + if (distance_a - distance_b).abs() < 0.001 { + // distance is basically the same (within 1mm) + handlers_a.extend(handlers_b); + (handlers_a, distance_a) + } else if distance_a < distance_b { + (handlers_a, distance_a) + } else { + (handlers_b, distance_b) + } + }) + .map(|(rx, _)| rx) + .unwrap_or_default() + .iter(), + ); } } diff --git a/src/objects/input/sk_hand.rs b/src/objects/input/sk_hand.rs index 0049344..90f91af 100644 --- a/src/objects/input/sk_hand.rs +++ b/src/objects/input/sk_hand.rs @@ -1,5 +1,6 @@ use crate::core::client::INTERNAL_CLIENT; -use crate::nodes::input::InputDataType; +use crate::nodes::fields::Field; +use crate::nodes::input::{InputDataType, InputHandler, INPUT_HANDLER_REGISTRY}; use crate::nodes::{ input::{Hand, InputMethod, Joint}, spatial::Spatial, @@ -10,8 +11,9 @@ use glam::Mat4; use nanoid::nanoid; use serde::{Deserialize, Serialize}; use stardust_xr::values::Datamap; +use std::f32::INFINITY; use std::sync::Arc; -use stereokit::{ButtonState, HandJoint, Handed, StereoKitMultiThread}; +use stereokit::{ButtonState, Color128, HandJoint, Handed, Material, StereoKitMultiThread}; fn convert_joint(joint: HandJoint) -> Joint { Joint { @@ -30,12 +32,14 @@ struct HandDatamap { pub struct SkHand { _node: Arc, - input: Arc, handed: Handed, + material: Material, + input: Arc, + capture: Option>, datamap: HandDatamap, } impl SkHand { - pub fn new(handed: Handed) -> Result { + pub fn new(handed: Handed, sk: &impl StereoKitMultiThread) -> Result { let _node = Node::create_parent_name(&INTERNAL_CLIENT, "", &nanoid!(), false) .add_to_scenegraph()?; Spatial::add_to(&_node, None, Mat4::IDENTITY, false); @@ -45,10 +49,15 @@ impl SkHand { }); let datamap = Datamap::from_typed(HandDatamap::default())?; let input = InputMethod::add_to(&_node, hand, datamap)?; + + let material = sk.material_copy(Material::HAND); + sk.input_hand_material(handed, Material(material.0)); Ok(SkHand { _node, - input, handed, + material, + input, + capture: None, datamap: Default::default(), }) } @@ -98,5 +107,94 @@ impl SkHand { self.datamap.pinch_strength = sk_hand.pinch_activation; self.datamap.grab_strength = sk_hand.grip_activation; *self.input.datamap.lock() = Datamap::from_typed(&self.datamap).unwrap(); + + // remove the capture when it's removed from captures list + if let Some(capture) = &self.capture { + if !self.input.captures.get_valid_contents().contains(&capture) { + self.capture.take(); + sk.material_set_color(&self.material, "color", Color128::new_rgb(1.0, 1.0, 1.0)); + } + } + // add the capture that's the closest if we don't have one + if self.capture.is_none() { + self.capture = self + .input + .captures + .get_valid_contents() + .into_iter() + .map(|handler| (handler.clone(), self.compare_distance(&handler.field).abs())) + .reduce(|(handlers_a, distance_a), (handlers_b, distance_b)| { + if distance_a < distance_b { + (handlers_a, distance_a) + } else { + (handlers_b, distance_b) + } + }) + .map(|(rx, _)| rx); + if self.capture.is_some() { + sk.material_set_color(&self.material, "color", Color128::new_rgb(0.0, 1.0, 0.75)); + } + } + + // make sure that if something is captured only send input to it + if let Some(capture) = &self.capture { + self.input.set_handler_order([capture].into_iter()); + return; + } + + // send input to all the input handlers that are the closest to the ray as possible + self.input.set_handler_order( + INPUT_HANDLER_REGISTRY + .get_valid_contents() + .into_iter() + // filter out all the disabled handlers + .filter(|handler| { + let Some(node) = handler.node.upgrade() else { + return false; + }; + node.enabled() + }) + // get the unsigned distance to the handler's field (unsigned so giant fields won't always eat input) + .map(|handler| { + ( + vec![handler.clone()], + self.compare_distance(&handler.field).abs(), + ) + }) + // .inspect(|(_, result)| { + // dbg!(result); + // }) + // now collect all handlers that are same distance if they're the closest + .reduce(|(mut handlers_a, distance_a), (handlers_b, distance_b)| { + if (distance_a - distance_b).abs() < 0.001 { + // distance is basically the same (within 1mm) + handlers_a.extend(handlers_b); + (handlers_a, distance_a) + } else if distance_a < distance_b { + (handlers_a, distance_a) + } else { + (handlers_b, distance_b) + } + }) + .map(|(rx, _)| rx) + .unwrap_or_default() + .iter(), + ); + } + + fn compare_distance(&self, field: &Field) -> f32 { + let InputDataType::Hand(hand) = &*self.input.data.lock() else { + return INFINITY; + }; + let spatial = &self.input.spatial; + let thumb_tip_distance = field.distance(spatial, hand.thumb.tip.position.into()); + let index_tip_distance = field.distance(spatial, hand.index.tip.position.into()); + let middle_tip_distance = field.distance(spatial, hand.middle.tip.position.into()); + let ring_tip_distance = field.distance(spatial, hand.ring.tip.position.into()); + + (thumb_tip_distance * 0.3) + + (index_tip_distance * 0.4) + + (middle_tip_distance * 0.15) + + (ring_tip_distance * 0.15) } }