diff --git a/src/core/client.rs b/src/core/client.rs index 4715a62..7d4b3a9 100644 --- a/src/core/client.rs +++ b/src/core/client.rs @@ -1,6 +1,7 @@ use super::scenegraph::Scenegraph; use crate::nodes::data; use crate::nodes::field; +use crate::nodes::input; use crate::nodes::item; use crate::nodes::root; use crate::nodes::spatial; @@ -31,6 +32,7 @@ impl Client { field::create_interface(&client); data::create_interface(&client); item::create_interface(&client); + input::create_interface(&client); client } pub fn dispatch(&self) -> Result<(), std::io::Error> { diff --git a/src/nodes/input.rs b/src/nodes/input.rs index 1d901a3..c695407 100644 --- a/src/nodes/input.rs +++ b/src/nodes/input.rs @@ -1,16 +1,13 @@ -use super::core::{Alias, Node}; +use super::core::Node; use super::field::Field; use super::spatial::{get_spatial_parent_flex, get_transform_pose_flex, Spatial}; use crate::core::client::Client; -use crate::core::nodelist::LifeLinkedNodeList; +use crate::core::eventloop::FRAME; use crate::core::registry::Registry; use anyhow::{anyhow, ensure, Result}; -use glam::{vec3a, Mat4}; use lazy_static::lazy_static; -use libstardustxr::flex::flexbuffer_from_vector_arguments; -use libstardustxr::{flex_to_quat, flex_to_vec3}; -use parking_lot::Mutex; use std::ops::Deref; +use std::sync::atomic::Ordering; use std::sync::{Arc, Weak}; lazy_static! { @@ -18,16 +15,13 @@ lazy_static! { static ref INPUT_HANDLER_REGISTRY: Registry = Default::default(); } -pub struct InputMethod { - spatial: Weak, - specialization: InputType, -} -trait InputMethodSpecialization { +pub trait InputSpecializationTrait { fn distance(&self, space: &Spatial, field: &Field) -> f32; + fn serialize(&self, space: &Spatial, distance: f32) -> Vec; } -enum InputType {} -impl Deref for InputType { - type Target = dyn InputMethodSpecialization; +enum InputSpecialization {} +impl Deref for InputSpecialization { + type Target = dyn InputSpecializationTrait; fn deref(&self) -> &Self::Target { todo!() // match self { @@ -35,13 +29,177 @@ impl Deref for InputType { // } } } + +pub struct InputMethod { + spatial: Arc, + specialization: InputSpecialization, +} +impl InputMethod { + fn add_to(node: &Arc, specialization: InputSpecialization) -> Result<()> { + ensure!( + node.spatial.get().is_some(), + "Internal: Node does not have a spatial attached!" + ); + + let method = InputMethod { + spatial: node.spatial.get().unwrap().clone(), + specialization, + }; + let method = INPUT_METHOD_REGISTRY.add(method); + let _ = node.input_method.set(method); + Ok(()) + } + fn distance(&self, to: &InputHandler) -> Option { + to.field + .upgrade() + .map(|field| self.specialization.distance(&self.spatial, &field)) + } +} +impl Drop for InputMethod { + fn drop(&mut self) { + INPUT_METHOD_REGISTRY.remove(self); + } +} + +struct DistanceLink { + distance: f32, + method: Weak, + handler: Weak, +} +impl DistanceLink { + fn from(method: &Arc, handler: &Arc) -> Option { + Some(DistanceLink { + distance: method.distance(handler)?, + method: Arc::downgrade(method), + handler: Arc::downgrade(handler), + }) + } + fn serialize(&self) -> Option> { + self.method.upgrade().and_then(|method| { + self.handler.upgrade().map(|handler| { + method + .specialization + .serialize(&handler.spatial.upgrade().unwrap(), self.distance) + }) + }) + } +} + pub struct InputHandler { + node: Weak, spatial: Weak, field: Weak, } +impl InputHandler { + pub fn add_to(node: &Arc, field: &Arc) -> Result<()> { + ensure!( + node.spatial.get().is_some(), + "Internal: Node does not have a spatial attached!" + ); + + let handler = InputHandler { + node: Arc::downgrade(node), + spatial: Arc::downgrade(node.spatial.get().unwrap()), + field: Arc::downgrade(field), + }; + let handler = INPUT_HANDLER_REGISTRY.add(handler); + let _ = node.input_handler.set(handler); + Ok(()) + } + + fn send_input( + &self, + old_frame: u64, + distance_link: DistanceLink, + distance_links: Vec, + ) { + if old_frame < FRAME.load(Ordering::Relaxed) { + return; + } + + if let Some(data) = distance_link.serialize() { + let _ = self.node.upgrade().unwrap().execute_remote_method( + "input", + &data, + Box::new(move |data| { + let capture = flexbuffers::Reader::get_root(data) + .and_then(|data| data.get_bool()) + .unwrap_or(false); + if !distance_links.is_empty() && !capture { + InputHandler::next_input(old_frame, distance_links); + } + }), + ); + } else { + InputHandler::next_input(old_frame, distance_links); + } + } + + 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 { + fn drop(&mut self) { + INPUT_HANDLER_REGISTRY.remove(self); + } +} pub fn create_interface(client: &Arc) { let node = Node::create(client, "", "data", false); - // node.add_local_signal("createInputHandler", create_input_handler_flex); + node.add_local_signal("createInputHandler", create_input_handler_flex); node.add_to_scenegraph(); } + +pub fn create_input_handler_flex( + _node: &Node, + calling_client: Arc, + data: &[u8], +) -> Result<()> { + let root = flexbuffers::Reader::get_root(data)?; + let flex_vec = root.get_vector()?; + let node = Node::create( + &calling_client, + "/input/handler", + flex_vec.idx(0).get_str()?, + true, + ); + let parent = get_spatial_parent_flex(&calling_client, flex_vec.idx(1).get_str()?)?; + let transform = get_transform_pose_flex(&flex_vec.idx(2), &flex_vec.idx(3))?; + let field = calling_client + .scenegraph + .get_node(flex_vec.idx(4).as_str()) + .ok_or_else(|| anyhow!("Field not found"))? + .field + .get() + .ok_or_else(|| anyhow!("Field node is not a field"))? + .clone(); + + let node = node.add_to_scenegraph(); + Spatial::add_to(&node, Some(parent), transform)?; + InputHandler::add_to(&node, &field)?; + 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; + } + distance_links + .sort_unstable_by(|a, b| a.distance.partial_cmp(&b.distance).unwrap().reverse()); + InputHandler::next_input(FRAME.load(Ordering::Relaxed), distance_links); + } +}