diff --git a/src/nodes/alias.rs b/src/nodes/alias.rs index 9bfad57..df2e5c2 100644 --- a/src/nodes/alias.rs +++ b/src/nodes/alias.rs @@ -5,9 +5,9 @@ use std::sync::{Arc, Weak}; #[derive(Debug, Default, Clone)] pub struct AliasInfo { - pub(super) local_signals: Vec<&'static str>, - pub(super) local_methods: Vec<&'static str>, - pub(super) remote_signals: Vec<&'static str>, + pub(super) server_signals: Vec<&'static str>, + pub(super) server_methods: Vec<&'static str>, + pub(super) client_signals: Vec<&'static str>, } #[allow(dead_code)] diff --git a/src/nodes/data.rs b/src/nodes/data.rs index 792c985..42cc7f1 100644 --- a/src/nodes/data.rs +++ b/src/nodes/data.rs @@ -5,7 +5,7 @@ use super::{Alias, Node}; use crate::core::client::Client; use crate::core::node_collections::LifeLinkedNodeMap; use crate::core::registry::Registry; -use crate::nodes::fields::find_field; +use crate::nodes::fields::{find_field, FIELD_ALIAS_INFO}; use crate::nodes::spatial::find_spatial_parent; use color_eyre::eyre::{ensure, eyre, Result}; use glam::vec3a; @@ -90,7 +90,7 @@ impl PulseSender { receiver.uid.as_str(), &rx_node, AliasInfo { - local_methods: vec!["sendData", "getTransform"], + server_methods: vec!["sendData", "getTransform"], ..Default::default() }, ); @@ -104,10 +104,7 @@ impl PulseSender { rx_alias.get_path(), "field", &rx_field_node, - AliasInfo { - local_methods: vec!["sendData", "getTransform"], - ..Default::default() - }, + FIELD_ALIAS_INFO.clone(), ); if let Ok(rx_field_alias) = rx_field_alias { self.aliases @@ -143,9 +140,10 @@ impl PulseSender { let _ = tx_node.send_remote_signal("new_receiver", &data); } - fn handle_drop_receiver(&self, uid: String) { - self.aliases.remove(&uid); - self.aliases.remove(&(uid.clone() + "-field")); + fn handle_drop_receiver(&self, receiver: &PulseReceiver) { + let uid = receiver.uid.as_str(); + self.aliases.remove(uid); + self.aliases.remove(&(uid.to_string() + "-field")); let Some(tx_node) = self.node.upgrade() else {return}; let Ok(data) = serialize(&uid) else {return}; let _ = tx_node.send_remote_signal("drop_receiver", &data); @@ -217,7 +215,7 @@ impl Drop for PulseReceiver { fn drop(&mut self) { PULSE_RECEIVER_REGISTRY.remove(self); for sender in PULSE_SENDER_REGISTRY.get_valid_contents() { - sender.handle_drop_receiver(self.uid.clone()); + sender.handle_drop_receiver(self); } } } diff --git a/src/nodes/fields/mod.rs b/src/nodes/fields/mod.rs index 019854b..2da55ca 100644 --- a/src/nodes/fields/mod.rs +++ b/src/nodes/fields/mod.rs @@ -8,6 +8,7 @@ use self::r#box::{create_box_field_flex, BoxField}; use self::sphere::{create_sphere_field_flex, SphereField}; use self::torus::{create_torus_field_flex, TorusField}; +use super::alias::AliasInfo; use super::spatial::Spatial; use super::Node; use crate::core::client::Client; @@ -15,12 +16,18 @@ use crate::nodes::spatial::find_reference_space; use color_eyre::eyre::Result; use glam::{vec2, vec3a, Vec3, Vec3A}; use mint::Vector3; +use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; use stardust_xr::schemas::flex::{deserialize, serialize}; use std::ops::Deref; use std::sync::Arc; +pub static FIELD_ALIAS_INFO: Lazy = Lazy::new(|| AliasInfo { + server_methods: vec!["distance", "normal", "closest_point", "ray_march"], + ..Default::default() +}); + pub trait FieldTrait { fn add_field_methods(&self, node: &Arc) { node.add_local_method("distance", field_distance_flex); diff --git a/src/nodes/hmd.rs b/src/nodes/hmd.rs index 4384abf..d7fd1a5 100644 --- a/src/nodes/hmd.rs +++ b/src/nodes/hmd.rs @@ -41,7 +41,7 @@ pub fn make_alias(client: &Arc) -> Result> { "hmd", &HMD, AliasInfo { - local_signals: vec!["get_transform"], + server_signals: vec!["get_transform"], ..Default::default() }, ) diff --git a/src/nodes/input/mod.rs b/src/nodes/input/mod.rs index e716888..6bce80d 100644 --- a/src/nodes/input/mod.rs +++ b/src/nodes/input/mod.rs @@ -8,19 +8,23 @@ use self::tip::Tip; use super::{ alias::{Alias, AliasInfo}, - fields::{find_field, Field}, + fields::{find_field, Field, FIELD_ALIAS_INFO}, spatial::{find_spatial_parent, parse_transform, Spatial}, Node, }; -use crate::core::client::Client; +use crate::core::{client::Client, node_collections::LifeLinkedNodeMap}; use crate::core::{node_collections::LifeLinkedNodeList, registry::Registry}; use color_eyre::eyre::{ensure, Result}; use glam::Mat4; +use once_cell::sync::OnceCell; use parking_lot::Mutex; use portable_atomic::AtomicBool; use serde::Deserialize; -use stardust_xr::schemas::flat::{Datamap, InputDataType}; use stardust_xr::schemas::{flat::InputData, flex::deserialize}; +use stardust_xr::schemas::{ + flat::{Datamap, InputDataType}, + flex::serialize, +}; use stardust_xr::values::Transform; use std::ops::Deref; use std::sync::atomic::Ordering; @@ -63,6 +67,8 @@ pub struct InputMethod { pub specialization: Mutex, captures: Registry, pub datamap: Mutex>, + handler_aliases: LifeLinkedNodeMap, + handler_order: OnceCell>>>, } impl InputMethod { #[allow(dead_code)] @@ -78,6 +84,7 @@ impl InputMethod { node.add_local_signal("capture", InputMethod::capture_flex); node.add_local_signal("set_datamap", InputMethod::set_datamap_flex); + node.add_local_signal("set_handlers", InputMethod::set_handlers_flex); let method = InputMethod { node: Arc::downgrade(node), @@ -87,26 +94,48 @@ impl InputMethod { specialization: Mutex::new(specialization), captures: Registry::new(), datamap: Mutex::new(datamap), + handler_aliases: LifeLinkedNodeMap::default(), + handler_order: OnceCell::new(), }; + for handler in INPUT_HANDLER_REGISTRY.get_valid_contents() { + method.handle_new_handler(&handler); + } let method = INPUT_METHOD_REGISTRY.add(method); let _ = node.input_method.set(method.clone()); Ok(method) } + fn get(node: &Node) -> Result> { + node.get_aspect("Input Method", "input method", |n| &n.input_method) + .cloned() + } fn capture_flex(node: &Node, calling_client: Arc, data: &[u8]) -> Result<()> { - let method = node.get_aspect("Input Method", "input method", |n| &n.input_method)?; - let handler_node = calling_client.get_node("Input Handler", deserialize(data)?)?; - let handler = - handler_node.get_aspect("Input Handler", "input handler", |n| &n.input_handler)?; + let method = InputMethod::get(node)?; + let handler = InputHandler::find(&calling_client, deserialize(data)?)?; method.captures.add_raw(&handler); - Ok(()) + node.send_remote_signal("capture", data) } fn set_datamap_flex(node: &Node, _calling_client: Arc, data: &[u8]) -> Result<()> { - let method = node.get_aspect("Input Method", "input method", |n| &n.input_method)?; + let method = InputMethod::get(node)?; method.datamap.lock().replace(Datamap::new(data.to_vec())?); Ok(()) } + fn set_handlers_flex(node: &Node, calling_client: Arc, data: &[u8]) -> Result<()> { + let method = InputMethod::get(node)?; + let handler_paths: Vec<&str> = deserialize(data)?; + let handlers: Vec> = handler_paths + .into_iter() + .filter_map(|p| InputHandler::find(&calling_client, p).ok()) + .map(|h| Arc::downgrade(&h)) + .collect(); + + *method + .handler_order + .get_or_init(|| Mutex::new(Vec::new())) + .lock() = handlers; + Ok(()) + } fn compare_distance(&self, to: &Field) -> f32 { self.specialization @@ -116,6 +145,49 @@ impl InputMethod { fn true_distance(&self, to: &Field) -> f32 { self.specialization.lock().true_distance(&self.spatial, to) } + + fn handle_new_handler(&self, handler: &InputHandler) { + let Some(method_node) = self.node.upgrade() else { return }; + let Some(method_client) = method_node.get_client() else { return }; + let Some(handler_node) = handler.node.upgrade() else { return }; + // Receiver itself + let Ok(handler_alias) = Alias::create( + &method_client, + method_node.get_path(), + handler.uid.as_str(), + &handler_node, + AliasInfo { + server_methods: vec!["getTransform"], + ..Default::default() + }, + ) else {return}; + self.handler_aliases + .add(handler.uid.clone(), &handler_alias); + + if let Some(handler_field_node) = handler.field.spatial_ref().node.upgrade() { + // Handler's field + let Ok(rx_field_alias) = Alias::create( + &method_client, + handler_alias.get_path(), + "field", + &handler_field_node, + FIELD_ALIAS_INFO.clone(), + ) else {return}; + self.handler_aliases + .add(handler.uid.clone() + "-field", &rx_field_alias); + } + + let Ok(data) = serialize(&handler.uid) else {return}; + let _ = method_node.send_remote_signal("handler_created", &data); + } + fn handle_drop_handler(&self, handler: &InputHandler) { + let uid = handler.uid.as_str(); + self.handler_aliases.remove(uid); + self.handler_aliases.remove(&(uid.to_string() + "-field")); + let Some(tx_node) = self.node.upgrade() else {return}; + let Ok(data) = serialize(&uid) else {return}; + let _ = tx_node.send_remote_signal("handler_destroyed", &data); + } } impl Drop for InputMethod { fn drop(&mut self) { @@ -137,7 +209,7 @@ impl DistanceLink { &method.uid, &method.node.upgrade()?, AliasInfo { - local_signals: vec!["capture"], + server_signals: vec!["capture"], ..Default::default() }, ) @@ -173,6 +245,7 @@ impl DistanceLink { pub struct InputHandler { enabled: Arc, + uid: String, node: Weak, spatial: Arc, field: Arc, @@ -187,15 +260,26 @@ impl InputHandler { let handler = InputHandler { enabled: node.enabled.clone(), + uid: node.uid.clone(), node: Arc::downgrade(node), spatial: node.spatial.get().unwrap().clone(), field: field.clone(), method_aliases: LifeLinkedNodeList::default(), }; + for method in INPUT_METHOD_REGISTRY.get_valid_contents() { + method.handle_new_handler(&handler); + } let handler = INPUT_HANDLER_REGISTRY.add(handler); let _ = node.input_handler.set(handler); Ok(()) } + fn find(client: &Client, path: &str) -> Result> { + InputHandler::get(&*client.get_node("Input Handler", path)?) + } + fn get(node: &Node) -> Result> { + node.get_aspect("Input Handler", "input handler", |n| &n.input_handler) + .cloned() + } #[instrument(level = "debug", skip(self, distance_link))] fn send_input(&self, order: u32, distance_link: &DistanceLink, datamap: Datamap) { @@ -211,12 +295,16 @@ impl PartialEq for InputHandler { impl Drop for InputHandler { fn drop(&mut self) { INPUT_HANDLER_REGISTRY.remove(self); + for method in INPUT_METHOD_REGISTRY.get_valid_contents() { + method.handle_drop_handler(self); + } } } pub fn create_interface(client: &Arc) -> Result<()> { let node = Node::create(client, "", "input", false); node.add_local_signal("create_input_handler", create_input_handler_flex); + node.add_local_signal("create_input_method_pointer", pointer::create_pointer_flex); node.add_local_signal("create_input_method_tip", tip::create_tip_flex); node.add_to_scenegraph().map(|_| ()) } @@ -261,25 +349,39 @@ pub fn process_input() { for method in methods { debug_span!("Process input method").in_scope(|| { // Get all valid input handlers and convert them to DistanceLink objects - let mut distance_links: Vec = debug_span!("Generate distance links") + let distance_links: Vec = debug_span!("Generate distance links") .in_scope(|| { - handlers - .iter() - .filter(|handler| handler.enabled.load(Ordering::Relaxed)) - .filter_map(|handler| DistanceLink::from(method.clone(), handler.clone())) - .collect() - }); + if let Some(handler_order) = method.handler_order.get() { + let handler_order = handler_order.lock(); + handler_order + .iter() + .filter_map(|h| h.upgrade()) + .filter(|handler| handler.enabled.load(Ordering::Relaxed)) + .filter_map(|handler| DistanceLink::from(method.clone(), handler)) + .collect() + } else { + let mut distance_links: Vec<_> = handlers + .iter() + .filter(|handler| handler.enabled.load(Ordering::Relaxed)) + .filter_map(|handler| { + DistanceLink::from(method.clone(), handler.clone()) + }) + .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() + // 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 + } }); - }); let captures = method.captures.take_valid_contents(); // Iterate over the distance links and send input to them - for (i, distance_link) in distance_links.iter().enumerate() { + for (i, distance_link) in distance_links.into_iter().enumerate() { distance_link.send_input(i as u32, method.datamap.lock().clone().unwrap()); // If the current distance link is in the list of captured input handlers, diff --git a/src/nodes/input/pointer.rs b/src/nodes/input/pointer.rs index dd05c67..1bb7621 100644 --- a/src/nodes/input/pointer.rs +++ b/src/nodes/input/pointer.rs @@ -1,12 +1,18 @@ use super::{DistanceLink, InputSpecialization}; +use crate::core::client::Client; use crate::nodes::fields::{Field, Ray, RayMarchResult}; -use crate::nodes::spatial::Spatial; +use crate::nodes::input::{InputMethod, InputType}; +use crate::nodes::spatial::{find_spatial_parent, parse_transform, Spatial}; +use crate::nodes::Node; use glam::{vec3, Mat4}; -use stardust_xr::schemas::flat::{InputDataType, Pointer as FlatPointer}; +use serde::Deserialize; +use stardust_xr::schemas::flat::{Datamap, InputDataType, Pointer as FlatPointer}; +use stardust_xr::schemas::flex::deserialize; +use stardust_xr::values::Transform; use std::sync::Arc; #[derive(Default)] -pub struct Pointer {} +pub struct Pointer; // impl Default for Pointer { // fn default() -> Self { // Pointer { @@ -53,3 +59,30 @@ impl InputSpecialization for Pointer { }) } } + +pub fn create_pointer_flex( + _node: &Node, + calling_client: Arc, + data: &[u8], +) -> color_eyre::eyre::Result<()> { + #[derive(Deserialize)] + struct CreatePointerInfo<'a> { + name: &'a str, + parent_path: &'a str, + transform: Transform, + datamap: Option>, + } + let info: CreatePointerInfo = deserialize(data)?; + let node = Node::create(&calling_client, "/input/method/pointer", info.name, true); + let parent = find_spatial_parent(&calling_client, info.parent_path)?; + let transform = parse_transform(info.transform, true, true, false); + + let node = node.add_to_scenegraph()?; + Spatial::add_to(&node, Some(parent), transform, false)?; + InputMethod::add_to( + &node, + InputType::Pointer(Pointer), + info.datamap.and_then(|datamap| Datamap::new(datamap).ok()), + )?; + Ok(()) +} diff --git a/src/nodes/items/mod.rs b/src/nodes/items/mod.rs index e73c62f..9564fc6 100644 --- a/src/nodes/items/mod.rs +++ b/src/nodes/items/mod.rs @@ -131,17 +131,17 @@ impl Item { name, &self.node.upgrade().unwrap(), AliasInfo { - local_signals: [ + server_signals: [ &self.type_info.aliased_local_signals, ITEM_ALIAS_LOCAL_SIGNALS.as_slice(), ] .concat(), - local_methods: [ + server_methods: [ &self.type_info.aliased_local_methods, ITEM_ALIAS_LOCAL_METHODS.as_slice(), ] .concat(), - remote_signals: [ + client_signals: [ &self.type_info.aliased_remote_signals, ITEM_ALIAS_REMOTE_SIGNALS.as_slice(), ] @@ -336,7 +336,7 @@ impl ItemAcceptor { &self.uid, acceptor_node, AliasInfo { - local_signals: vec!["capture"], + server_signals: vec!["capture"], ..Default::default() }, )?; diff --git a/src/nodes/mod.rs b/src/nodes/mod.rs index 274aaff..d2a2662 100644 --- a/src/nodes/mod.rs +++ b/src/nodes/mod.rs @@ -183,7 +183,7 @@ impl Node { data: &[u8], ) -> Result<(), ScenegraphError> { if let Some(alias) = self.alias.get() { - if !alias.info.local_signals.iter().any(|e| e == &method) { + if !alias.info.server_signals.iter().any(|e| e == &method) { return Err(ScenegraphError::SignalNotFound); } alias @@ -208,7 +208,7 @@ impl Node { data: &[u8], ) -> Result, ScenegraphError> { if let Some(alias) = self.alias.get() { - if !alias.info.local_methods.iter().any(|e| e == &method) { + if !alias.info.server_methods.iter().any(|e| e == &method) { return Err(ScenegraphError::MethodNotFound); } alias @@ -234,7 +234,7 @@ impl Node { self.aliases .get_valid_contents() .iter() - .filter(|alias| alias.info.remote_signals.iter().any(|e| e == &method)) + .filter(|alias| alias.info.client_signals.iter().any(|e| e == &method)) .filter_map(|alias| alias.node.upgrade()) .for_each(|node| { let _ = node.send_remote_signal(method, data); diff --git a/src/nodes/spatial/zone.rs b/src/nodes/spatial/zone.rs index 3faf906..2dfdabe 100644 --- a/src/nodes/spatial/zone.rs +++ b/src/nodes/spatial/zone.rs @@ -114,12 +114,12 @@ impl Zone { &zoneable.uid, &zoneable.node.upgrade().unwrap(), AliasInfo { - local_signals: vec![ + server_signals: vec![ "set_transform", "set_spatial_parent", "set_spatial_parent_in_place", ], - local_methods: vec!["get_transform"], + server_methods: vec!["get_transform"], ..Default::default() }, )