refactor(input): use idl

This commit is contained in:
Nova
2024-02-26 04:56:48 -05:00
parent 47cbc2b8fc
commit 226554fadc
15 changed files with 752 additions and 815 deletions

View File

@@ -199,72 +199,6 @@ impl Deref for Field {
}
}
}
// impl FieldTrait for Field {
// fn spatial_ref(&self) -> &Spatial {
// match self {
// Field::Box(field) => field.spatial_ref(),
// Field::Cylinder(field) => field.spatial_ref(),
// Field::Sphere(field) => field.spatial_ref(),
// Field::Torus(field) => field.spatial_ref(),
// }
// }
// fn local_distance(&self, p: Vec3A) -> f32 {
// match self {
// Field::Box(field) => field.local_distance(p),
// Field::Cylinder(field) => field.local_distance(p),
// Field::Sphere(field) => field.local_distance(p),
// Field::Torus(field) => field.local_distance(p),
// }
// }
// fn local_normal(&self, p: Vec3A, r: f32) -> Vec3A {
// match self {
// Field::Box(field) => field.local_normal(p, r),
// Field::Cylinder(field) => field.local_normal(p, r),
// Field::Sphere(field) => field.local_normal(p, r),
// Field::Torus(field) => field.local_normal(p, r),
// }
// }
// fn local_closest_point(&self, p: Vec3A, r: f32) -> Vec3A {
// match self {
// Field::Box(field) => field.local_closest_point(p, r),
// Field::Cylinder(field) => field.local_closest_point(p, r),
// Field::Sphere(field) => field.local_closest_point(p, r),
// Field::Torus(field) => field.local_closest_point(p, r),
// }
// }
// fn distance(&self, reference_space: &Spatial, p: Vec3A) -> f32 {
// match self {
// Field::Box(field) => field.distance(reference_space, p),
// Field::Cylinder(field) => field.distance(reference_space, p),
// Field::Sphere(field) => field.distance(reference_space, p),
// Field::Torus(field) => field.distance(reference_space, p),
// }
// }
// fn normal(&self, reference_space: &Spatial, p: Vec3A, r: f32) -> Vec3A {
// match self {
// Field::Box(field) => field.normal(reference_space, p, r),
// Field::Cylinder(field) => field.normal(reference_space, p, r),
// Field::Sphere(field) => field.normal(reference_space, p, r),
// Field::Torus(field) => field.normal(reference_space, p, r),
// }
// }
// fn closest_point(&self, reference_space: &Spatial, p: Vec3A, r: f32) -> Vec3A {
// match self {
// Field::Box(field) => field.closest_point(reference_space, p, r),
// Field::Cylinder(field) => field.closest_point(reference_space, p, r),
// Field::Sphere(field) => field.closest_point(reference_space, p, r),
// Field::Torus(field) => field.closest_point(reference_space, p, r),
// }
// }
// fn ray_march(&self, ray: Ray) -> RayMarchResult {
// match self {
// Field::Box(field) => field.ray_march(ray),
// Field::Cylinder(field) => field.ray_march(ray),
// Field::Sphere(field) => field.ray_march(ray),
// Field::Torus(field) => field.ray_march(ray),
// }
// }
// }
create_interface!(FieldInterface, FieldInterfaceAspect, "/field");
pub struct FieldInterface;

View File

@@ -1,16 +1,57 @@
use super::{DistanceLink, Finger, Hand, InputDataTrait, Joint, Thumb};
use crate::nodes::fields::Field;
use crate::nodes::spatial::Spatial;
use glam::{vec3a, Mat4};
use stardust_xr::schemas::flat::{Hand as FlatHand, InputDataType, Joint};
use glam::{vec3a, Mat4, Quat};
use std::sync::Arc;
use super::{DistanceLink, InputSpecialization};
#[derive(Debug, Default)]
pub struct Hand {
pub base: FlatHand,
impl Default for Joint {
fn default() -> Self {
Joint {
position: [0.0; 3].into(),
rotation: Quat::IDENTITY.into(),
radius: 0.0,
distance: 0.0,
}
}
}
impl InputSpecialization for Hand {
impl Default for Finger {
fn default() -> Self {
Finger {
tip: Default::default(),
distal: Default::default(),
intermediate: Default::default(),
proximal: Default::default(),
metacarpal: Default::default(),
}
}
}
impl Default for Thumb {
fn default() -> Self {
Thumb {
tip: Default::default(),
distal: Default::default(),
proximal: Default::default(),
metacarpal: Default::default(),
}
}
}
impl Default for Hand {
fn default() -> Self {
Hand {
right: Default::default(),
thumb: Default::default(),
index: Default::default(),
middle: Default::default(),
ring: Default::default(),
little: Default::default(),
palm: Default::default(),
wrist: Default::default(),
elbow: Default::default(),
}
}
}
impl InputDataTrait for Hand {
fn compare_distance(&self, space: &Arc<Spatial>, field: &Field) -> f32 {
self.true_distance(space, field).abs()
}
@@ -18,34 +59,29 @@ impl InputSpecialization for Hand {
let mut min_distance = f32::MAX;
for tip in [
&self.base.thumb.tip.position,
&self.base.index.tip.position,
&self.base.middle.tip.position,
&self.base.ring.tip.position,
&self.base.little.tip.position,
&self.thumb.tip.position,
&self.index.tip.position,
&self.middle.tip.position,
&self.ring.tip.position,
&self.little.tip.position,
] {
min_distance = min_distance.min(field.distance(space, vec3a(tip.x, tip.y, tip.z)));
}
min_distance
}
fn serialize(
&self,
distance_link: &DistanceLink,
local_to_handler_matrix: Mat4,
) -> InputDataType {
let mut hand = self.base;
fn update_to(&mut self, distance_link: &DistanceLink, local_to_handler_matrix: Mat4) {
let mut joints: Vec<&mut Joint> = Vec::new();
joints.extend([&mut hand.palm, &mut hand.wrist]);
if let Some(elbow) = &mut hand.elbow {
joints.extend([&mut self.palm, &mut self.wrist]);
if let Some(elbow) = &mut self.elbow {
joints.push(elbow);
}
for finger in [
&mut hand.index,
&mut hand.middle,
&mut hand.ring,
&mut hand.little,
&mut self.index,
&mut self.middle,
&mut self.ring,
&mut self.little,
] {
joints.extend([
&mut finger.tip,
@@ -56,10 +92,10 @@ impl InputSpecialization for Hand {
]);
}
joints.extend([
&mut hand.thumb.tip,
&mut hand.thumb.distal,
&mut hand.thumb.proximal,
&mut hand.thumb.metacarpal,
&mut self.thumb.tip,
&mut self.thumb.distal,
&mut self.thumb.proximal,
&mut self.thumb.metacarpal,
]);
for joint in joints {
@@ -73,7 +109,5 @@ impl InputSpecialization for Hand {
.field
.distance(&distance_link.handler.spatial, position.into());
}
InputDataType::Hand(Box::new(hand))
}
}

View File

@@ -0,0 +1,83 @@
use super::{
input_handler_client, DistanceLink, InputHandlerAspect, INPUT_HANDLER_REGISTRY,
INPUT_METHOD_REGISTRY,
};
use crate::{
core::node_collections::LifeLinkedNodeMap,
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<AtomicBool>,
pub uid: String,
pub node: Weak<Node>,
pub spatial: Arc<Spatial>,
pub field: Arc<Field>,
pub(super) method_aliases: LifeLinkedNodeMap<usize>,
}
impl InputHandler {
pub fn add_to(node: &Arc<Node>, field: &Arc<Field>) -> Result<()> {
let handler = InputHandler {
enabled: node.enabled.clone(),
uid: node.uid.clone(),
node: Arc::downgrade(node),
spatial: node.get_aspect::<Spatial>().unwrap().clone(),
field: field.clone(),
method_aliases: LifeLinkedNodeMap::default(),
};
for method in INPUT_METHOD_REGISTRY.get_valid_contents() {
method.make_alias(&handler);
method.handle_new_handler(&handler);
}
let handler = INPUT_HANDLER_REGISTRY.add(handler);
node.add_aspect_raw(handler);
Ok(())
}
#[instrument(level = "debug", skip(self, distance_link))]
pub(super) fn send_input(
&self,
order: u32,
captured: bool,
distance_link: &DistanceLink,
datamap: Datamap,
) {
let Some(node) = self.node.upgrade() else {
return;
};
let Some(method_alias) = distance_link
.handler
.method_aliases
.get(&(Arc::as_ptr(&distance_link.method) as usize))
else {
return;
};
let _ = input_handler_client::input(
&node,
&method_alias,
&distance_link.serialize(order, captured, datamap),
);
}
}
impl Aspect for InputHandler {
const NAME: &'static str = "InputHandler";
}
impl InputHandlerAspect for InputHandler {}
impl PartialEq for InputHandler {
fn eq(&self, other: &Self) -> bool {
self.spatial == other.spatial
}
}
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);
}
}
}

211
src/nodes/input/method.rs Normal file
View File

@@ -0,0 +1,211 @@
use super::{
input_method_client, InputDataTrait, InputDataType, InputHandler, InputMethodAspect,
INPUT_HANDLER_REGISTRY, INPUT_METHOD_REGISTRY,
};
use crate::{
core::{client::Client, node_collections::LifeLinkedNodeMap, registry::Registry},
nodes::{
alias::{Alias, AliasInfo},
fields::{Field, FIELD_ALIAS_INFO},
spatial::Spatial,
Aspect, Node,
},
};
use color_eyre::eyre::Result;
use parking_lot::Mutex;
use portable_atomic::Ordering;
use stardust_xr::values::Datamap;
use std::sync::{Arc, Weak};
pub struct InputMethod {
pub node: Weak<Node>,
pub uid: String,
pub enabled: Mutex<bool>,
pub spatial: Arc<Spatial>,
pub data: Mutex<InputDataType>,
pub datamap: Mutex<Datamap>,
pub(super) captures: Registry<InputHandler>,
pub(super) handler_aliases: LifeLinkedNodeMap<String>,
pub(super) handler_order: Mutex<Option<Vec<Weak<InputHandler>>>>,
}
impl InputMethod {
pub fn add_to(
node: &Arc<Node>,
data: InputDataType,
datamap: Datamap,
) -> Result<Arc<InputMethod>> {
let method = InputMethod {
node: Arc::downgrade(node),
uid: node.uid.clone(),
enabled: Mutex::new(true),
spatial: node.get_aspect::<Spatial>().unwrap().clone(),
data: Mutex::new(data),
captures: Registry::new(),
datamap: Mutex::new(datamap),
handler_aliases: LifeLinkedNodeMap::default(),
handler_order: Mutex::new(None),
};
for handler in INPUT_HANDLER_REGISTRY.get_valid_contents() {
method.handle_new_handler(&handler);
method.make_alias(&handler);
}
let method = INPUT_METHOD_REGISTRY.add(method);
<InputMethod as InputMethodAspect>::add_node_members(node);
node.add_aspect_raw(method.clone());
Ok(method)
}
pub(super) fn make_alias(&self, handler: &InputHandler) {
let Some(method_node) = self.node.upgrade() else {
return;
};
let Some(handler_node) = handler.node.upgrade() else {
return;
};
let Some(client) = handler_node.get_client() else {
return;
};
let Ok(method_alias) = Alias::create(
&client,
handler_node.get_path(),
&self.uid,
&method_node,
AliasInfo {
server_signals: vec!["capture"],
..Default::default()
},
) else {
return;
};
method_alias.enabled.store(false, Ordering::Relaxed);
handler
.method_aliases
.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 true_distance(&self, to: &Field) -> f32 {
self.data.lock().true_distance(&self.spatial, to)
}
pub(super) 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!["get_transform"],
..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 _ = input_method_client::new_handler(&method_node, &handler.uid, &handler_node);
}
pub(super) 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 _ = input_method_client::drop_handler(&tx_node, &uid);
}
}
impl Aspect for InputMethod {
const NAME: &'static str = "InputMethod";
}
impl InputMethodAspect for InputMethod {
#[doc = "Set the spatial input component of this input method. You must keep the same input data type throughout the entire thing."]
fn set_input(
node: Arc<Node>,
_calling_client: Arc<Client>,
input: InputDataType,
) -> Result<()> {
let input_method = node.get_aspect::<InputMethod>()?;
*input_method.data.lock() = input;
Ok(())
}
#[doc = "Set the datmap of this input method"]
fn set_datamap(node: Arc<Node>, _calling_client: Arc<Client>, datamap: Datamap) -> Result<()> {
let input_method = node.get_aspect::<InputMethod>()?;
*input_method.datamap.lock() = datamap;
Ok(())
}
#[doc = "Manually set the order of handlers to propagate input to, or else let the server decide."]
fn set_handler_order(
node: Arc<Node>,
_calling_client: Arc<Client>,
handlers: Option<Vec<Arc<Node>>>,
) -> Result<()> {
let input_method = node.get_aspect::<InputMethod>()?;
let Some(handlers) = handlers else {
*input_method.handler_order.lock() = None;
return Ok(());
};
let handlers = handlers
.into_iter()
.filter_map(|p| p.get_aspect::<InputHandler>().ok())
.map(|i| Arc::downgrade(&i))
.collect::<Vec<_>>();
*input_method.handler_order.lock() = Some(handlers);
Ok(())
}
#[doc = "Have the input handler that this method reference came from capture the method for the next frame."]
fn capture(node: Arc<Node>, _calling_client: Arc<Client>, handler: Arc<Node>) -> Result<()> {
let input_method = node.get_aspect::<InputMethod>()?;
let input_handler = handler.get_aspect::<InputHandler>()?;
input_method.captures.add_raw(&input_handler);
// input_method_client::
// node.send_remote_signal("capture", message)
Ok(())
}
}
impl Drop for InputMethod {
fn drop(&mut self) {
INPUT_METHOD_REGISTRY.remove(self);
}
}

View File

@@ -1,255 +1,29 @@
pub mod hand;
pub mod pointer;
pub mod tip;
mod hand;
mod handler;
mod method;
mod pointer;
mod tip;
use self::hand::Hand;
use self::pointer::Pointer;
use self::tip::Tip;
pub use handler::*;
pub use method::*;
use super::{
alias::{Alias, AliasInfo},
fields::{find_field, Field, FIELD_ALIAS_INFO},
spatial::{parse_transform, Spatial},
Aspect, Message, Node,
};
use crate::core::{client::Client, node_collections::LifeLinkedNodeMap};
use super::fields::Field;
use super::spatial::Spatial;
use crate::create_interface;
use crate::nodes::alias::Alias;
use crate::{core::client::Client, nodes::Node};
use crate::{core::registry::Registry, nodes::spatial::Transform};
use color_eyre::eyre::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::InputDataType, flex::serialize};
use stardust_xr::{
schemas::{flat::InputData, flex::deserialize},
values::Datamap,
};
use std::ops::Deref;
use std::sync::atomic::Ordering;
use portable_atomic::Ordering;
use stardust_xr::values::Datamap;
use std::sync::{Arc, Weak};
use tracing::{debug_span, instrument};
static INPUT_METHOD_REGISTRY: Registry<InputMethod> = Registry::new();
static INPUT_HANDLER_REGISTRY: Registry<InputHandler> = Registry::new();
pub trait InputSpecialization: Send + Sync {
fn compare_distance(&self, space: &Arc<Spatial>, field: &Field) -> f32;
fn true_distance(&self, space: &Arc<Spatial>, field: &Field) -> f32;
fn serialize(
&self,
distance_link: &DistanceLink,
local_to_handler_matrix: Mat4,
) -> InputDataType;
}
pub enum InputType {
Pointer(Pointer),
Hand(Box<Hand>),
Tip(Tip),
}
impl Deref for InputType {
type Target = dyn InputSpecialization;
fn deref(&self) -> &Self::Target {
match self {
InputType::Pointer(p) => p,
InputType::Hand(h) => h.as_ref(),
InputType::Tip(t) => t,
}
}
}
pub struct InputMethod {
node: Weak<Node>,
uid: String,
pub enabled: Mutex<bool>,
pub spatial: Arc<Spatial>,
pub specialization: Mutex<InputType>,
captures: Registry<InputHandler>,
pub datamap: Mutex<Option<Datamap>>,
handler_aliases: LifeLinkedNodeMap<String>,
handler_order: OnceCell<Mutex<Vec<Weak<InputHandler>>>>,
}
impl InputMethod {
#[allow(dead_code)]
pub fn add_to(
node: &Arc<Node>,
specialization: InputType,
datamap: Option<Datamap>,
) -> Result<Arc<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),
uid: node.uid.clone(),
enabled: Mutex::new(true),
spatial: node.get_aspect::<Spatial>().unwrap().clone(),
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);
method.make_alias(&handler);
}
let method = INPUT_METHOD_REGISTRY.add(method);
node.add_aspect_raw(method.clone());
Ok(method)
}
fn get(node: &Node) -> Result<Arc<Self>> {
node.get_aspect::<Self>()
}
fn capture_flex(node: Arc<Node>, calling_client: Arc<Client>, message: Message) -> Result<()> {
let method = InputMethod::get(&node)?;
let handler = InputHandler::find(&calling_client, deserialize(message.as_ref())?)?;
method.captures.add_raw(&handler);
node.send_remote_signal("capture", message)
}
fn set_datamap_flex(
node: Arc<Node>,
_calling_client: Arc<Client>,
message: Message,
) -> Result<()> {
let method = InputMethod::get(&node)?;
method
.datamap
.lock()
.replace(Datamap::from_raw(message.data)?);
Ok(())
}
fn set_handlers_flex(
node: Arc<Node>,
calling_client: Arc<Client>,
message: Message,
) -> Result<()> {
let method = InputMethod::get(&node)?;
let handler_paths: Vec<&str> = deserialize(message.as_ref())?;
let handlers: Vec<Weak<InputHandler>> = 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 make_alias(&self, handler: &InputHandler) {
let Some(method_node) = self.node.upgrade() else {
return;
};
let Some(handler_node) = handler.node.upgrade() else {
return;
};
let Some(client) = handler_node.get_client() else {
return;
};
let Ok(method_alias) = Alias::create(
&client,
handler_node.get_path(),
&self.uid,
&method_node,
AliasInfo {
server_signals: vec!["capture"],
..Default::default()
},
) else {
return;
};
method_alias.enabled.store(false, Ordering::Relaxed);
handler
.method_aliases
.add(self as *const InputMethod as usize, &method_alias);
}
fn compare_distance(&self, to: &InputHandler) -> f32 {
let distance = self
.specialization
.lock()
.compare_distance(&self.spatial, &to.field);
if self.captures.contains(to) {
distance * 0.5
} else {
distance
}
}
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!["get_transform"],
..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 Aspect for InputMethod {
const NAME: &'static str = "InputMethod";
}
impl Drop for InputMethod {
fn drop(&mut self) {
INPUT_METHOD_REGISTRY.remove(self);
}
}
stardust_xr_server_codegen::codegen_input_protocol!();
pub struct DistanceLink {
distance: f32,
@@ -269,118 +43,98 @@ impl DistanceLink {
self.handler.send_input(order, captured, self, datamap);
}
#[instrument(level = "debug", skip(self))]
fn serialize(&self, order: u32, captured: bool, datamap: Datamap) -> Vec<u8> {
let input = self.method.specialization.lock().serialize(
fn serialize(&self, order: u32, captured: bool, datamap: Datamap) -> InputData {
let mut input = self.method.data.lock().clone();
input.update_to(
self,
Spatial::space_to_space_matrix(Some(&self.method.spatial), Some(&self.handler.spatial)),
);
let root = InputData {
InputData {
uid: self.method.uid.clone(),
input,
distance: self.method.true_distance(&self.handler.field),
datamap,
order,
captured,
};
root.serialize()
}
}
}
pub trait InputDataTrait {
fn compare_distance(&self, space: &Arc<Spatial>, field: &Field) -> f32;
fn true_distance(&self, space: &Arc<Spatial>, field: &Field) -> f32;
fn update_to(&mut self, distance_link: &DistanceLink, local_to_handler_matrix: Mat4);
}
impl InputDataTrait for InputDataType {
fn compare_distance(&self, space: &Arc<Spatial>, 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),
}
}
fn true_distance(&self, space: &Arc<Spatial>, field: &Field) -> f32 {
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),
}
}
}
pub struct InputHandler {
enabled: Arc<AtomicBool>,
uid: String,
node: Weak<Node>,
spatial: Arc<Spatial>,
field: Arc<Field>,
method_aliases: LifeLinkedNodeMap<usize>,
}
impl InputHandler {
pub fn add_to(node: &Arc<Node>, field: &Arc<Field>) -> Result<()> {
let handler = InputHandler {
enabled: node.enabled.clone(),
uid: node.uid.clone(),
node: Arc::downgrade(node),
spatial: node.get_aspect::<Spatial>().unwrap().clone(),
field: field.clone(),
method_aliases: LifeLinkedNodeMap::default(),
};
for method in INPUT_METHOD_REGISTRY.get_valid_contents() {
method.make_alias(&handler);
method.handle_new_handler(&handler);
}
let handler = INPUT_HANDLER_REGISTRY.add(handler);
node.add_aspect_raw(handler);
create_interface!(InputInterface, InputInterfaceAspect, "/input");
pub struct InputInterface;
impl InputInterfaceAspect for InputInterface {
#[doc = "Create an input method node"]
fn create_input_method(
_node: Arc<Node>,
calling_client: Arc<Client>,
name: String,
parent: Arc<Node>,
transform: Transform,
initial_data: InputDataType,
datamap: Datamap,
) -> Result<()> {
let parent = parent.get_aspect::<Spatial>()?;
let transform = transform.to_mat4(true, true, true);
let node = Node::create_parent_name(&calling_client, "/input/method", &name, true)
.add_to_scenegraph()?;
Spatial::add_to(&node, Some(parent.clone()), transform, false);
InputMethod::add_to(&node, initial_data, datamap)?;
Ok(())
}
fn find(client: &Client, path: &str) -> Result<Arc<Self>> {
client.get_node("Input Handler", path)?.get_aspect::<Self>()
}
#[instrument(level = "debug", skip(self, distance_link))]
fn send_input(
&self,
order: u32,
captured: bool,
distance_link: &DistanceLink,
datamap: Datamap,
) {
let Some(node) = self.node.upgrade() else {
return;
};
let _ = node.send_remote_signal("input", distance_link.serialize(order, captured, datamap));
}
}
impl Aspect for InputHandler {
const NAME: &'static str = "InputHandler";
}
impl PartialEq for InputHandler {
fn eq(&self, other: &Self) -> bool {
self.spatial == other.spatial
}
}
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<Client>) -> Result<()> {
let node = Node::create_path(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(|_| ())
}
pub fn create_input_handler_flex(
_node: Arc<Node>,
calling_client: Arc<Client>,
message: Message,
) -> Result<()> {
#[derive(Deserialize)]
struct CreateInputHandlerInfo<'a> {
name: &'a str,
parent_path: &'a str,
#[doc = "Create an input handler node"]
fn create_input_handler(
_node: Arc<Node>,
calling_client: Arc<Client>,
name: String,
parent: Arc<Node>,
transform: Transform,
field_path: &'a str,
}
let info: CreateInputHandlerInfo = deserialize(message.as_ref())?;
let parent = calling_client
.get_node("Spatial parent", info.parent_path)?
.get_aspect::<Spatial>()?;
let transform = parse_transform(info.transform, true, true, true);
let field = find_field(&calling_client, info.field_path)?;
field: Arc<Node>,
) -> Result<()> {
let parent = parent.get_aspect::<Spatial>()?;
let transform = transform.to_mat4(true, true, true);
let field = field.get_aspect::<Field>()?;
let node = Node::create_parent_name(&calling_client, "/input/handler", info.name, true)
.add_to_scenegraph()?;
Spatial::add_to(&node, Some(parent.clone()), transform, false);
InputHandler::add_to(&node, &field)?;
Ok(())
let node = Node::create_parent_name(&calling_client, "/input/handler", &name, true)
.add_to_scenegraph()?;
Spatial::add_to(&node, Some(parent.clone()), transform, false);
InputHandler::add_to(&node, &field)?;
Ok(())
}
}
#[tracing::instrument(level = "debug")]
pub fn process_input() {
// Iterate over all valid input methods
@@ -389,7 +143,6 @@ pub fn process_input() {
.get_valid_contents()
.into_iter()
.filter(|method| *method.enabled.lock())
.filter(|method| method.datamap.lock().is_some())
});
let handlers = INPUT_HANDLER_REGISTRY.get_valid_contents();
const LIMIT: usize = 50;
@@ -402,11 +155,10 @@ pub fn process_input() {
// Get all valid input handlers and convert them to DistanceLink objects
let distance_links: Vec<DistanceLink> = debug_span!("Generate distance links")
.in_scope(|| {
if let Some(handler_order) = method.handler_order.get() {
let handler_order = handler_order.lock();
if let Some(handler_order) = &*method.handler_order.lock() {
handler_order
.iter()
.filter_map(|h| h.upgrade())
.filter_map(Weak::upgrade)
.filter(|handler| handler.enabled.load(Ordering::Relaxed))
.map(|handler| DistanceLink::from(method.clone(), handler))
.collect()
@@ -445,11 +197,7 @@ pub fn process_input() {
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().unwrap(),
);
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

View File

@@ -1,38 +1,34 @@
use super::{DistanceLink, InputSpecialization};
use crate::core::client::Client;
use crate::nodes::fields::{Field, Ray, RayMarchResult};
use crate::nodes::input::{InputMethod, InputType};
use crate::nodes::spatial::{parse_transform, Spatial, Transform};
use crate::nodes::{Message, Node};
use glam::{vec3, Mat4};
use serde::Deserialize;
use stardust_xr::schemas::flat::{InputDataType, Pointer as FlatPointer};
use stardust_xr::schemas::flex::deserialize;
use stardust_xr::values::Datamap;
use super::{DistanceLink, InputDataTrait, Pointer};
use crate::nodes::{
fields::{Field, Ray, RayMarchResult},
spatial::Spatial,
};
use glam::{vec3, Mat4, Quat};
use std::sync::{Arc, Weak};
use std::sync::Arc;
#[derive(Default)]
pub struct Pointer;
// impl Default for Pointer {
// fn default() -> Self {
// Pointer {
// grab: Default::default(),
// select: Default::default(),
// }
// }
// }
impl Default for Pointer {
fn default() -> Self {
Pointer {
origin: [0.0; 3].into(),
orientation: Quat::IDENTITY.into(),
deepest_point: [0.0; 3].into(),
}
}
}
impl Pointer {
fn ray_march(&self, space: &Arc<Spatial>, field: &Field) -> RayMarchResult {
fn ray_march(&self, method_space: &Arc<Spatial>, field: &Field) -> RayMarchResult {
field.ray_march(Ray {
origin: vec3(0.0, 0.0, 0.0),
direction: vec3(0.0, 0.0, -1.0),
space: space.clone(),
space: Spatial::new(
Weak::new(),
Some(method_space.clone()),
Mat4::from_rotation_translation(self.orientation.into(), self.origin.into()),
),
})
}
}
impl InputSpecialization for Pointer {
impl InputDataTrait for Pointer {
fn compare_distance(&self, space: &Arc<Spatial>, field: &Field) -> f32 {
let ray_info = self.ray_march(space, field);
if ray_info.min_distance > 0.0 {
@@ -47,50 +43,20 @@ impl InputSpecialization for Pointer {
let ray_info = self.ray_march(space, field);
ray_info.min_distance
}
fn serialize(
&self,
distance_link: &DistanceLink,
local_to_handler_matrix: Mat4,
) -> InputDataType {
fn update_to(&mut self, distance_link: &DistanceLink, 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 direction = local_to_handler_matrix.transform_vector3(vec3(0.0, 0.0, -1.0));
let ray_march = self.ray_march(&distance_link.method.spatial, &distance_link.handler.field);
let direction = local_to_handler_matrix
.transform_vector3(vec3(0.0, 0.0, -1.0))
.normalize();
let deepest_point = (direction * ray_march.deepest_point_distance) + origin;
InputDataType::Pointer(FlatPointer {
origin: origin.into(),
orientation: orientation.into(),
deepest_point: deepest_point.into(),
})
self.origin = origin.into();
self.orientation = orientation.into();
self.deepest_point = deepest_point.into();
}
}
pub fn create_pointer_flex(
_node: Arc<Node>,
calling_client: Arc<Client>,
message: Message,
) -> color_eyre::eyre::Result<()> {
#[derive(Deserialize)]
struct CreatePointerInfo<'a> {
name: &'a str,
parent_path: &'a str,
transform: Transform,
datamap: Option<Vec<u8>>,
}
let info: CreatePointerInfo = deserialize(message.as_ref())?;
let node = Node::create_parent_name(&calling_client, "/input/method/pointer", info.name, true);
let parent = calling_client
.get_node("Spatial parent", info.parent_path)?
.get_aspect::<Spatial>()?;
let transform = parse_transform(info.transform, true, true, false);
let node = node.add_to_scenegraph()?;
Spatial::add_to(&node, Some(parent.clone()), transform, false);
InputMethod::add_to(
&node,
InputType::Pointer(Pointer),
info.datamap
.and_then(|datamap| Datamap::from_raw(datamap).ok()),
)?;
Ok(())
}

View File

@@ -1,82 +1,28 @@
use super::{DistanceLink, InputSpecialization};
use crate::core::client::Client;
use crate::nodes::fields::Field;
use crate::nodes::input::{InputMethod, InputType};
use crate::nodes::spatial::{parse_transform, Spatial, Transform};
use crate::nodes::{Message, Node};
use color_eyre::eyre::Result;
use glam::{vec3a, Mat4};
use serde::Deserialize;
use stardust_xr::schemas::flat::{InputDataType, Tip as FlatTip};
use stardust_xr::schemas::flex::deserialize;
use stardust_xr::values::Datamap;
use super::{DistanceLink, InputDataTrait, Tip};
use crate::nodes::{fields::Field, spatial::Spatial};
use glam::{Mat4, Quat};
use std::sync::Arc;
#[derive(Default)]
pub struct Tip {
pub radius: f32,
}
impl Tip {
fn set_radius(node: Arc<Node>, _calling_client: Arc<Client>, message: Message) -> Result<()> {
let input_method = node.get_aspect::<InputMethod>()?;
if let InputType::Tip(tip) = &mut *input_method.specialization.lock() {
tip.radius = deserialize(message.as_ref())?;
impl Default for Tip {
fn default() -> Self {
Tip {
origin: [0.0; 3].into(),
orientation: Quat::IDENTITY.into(),
}
Ok(())
}
}
impl InputSpecialization for Tip {
impl InputDataTrait for Tip {
fn compare_distance(&self, space: &Arc<Spatial>, field: &Field) -> f32 {
field.distance(space, vec3a(0.0, 0.0, 0.0)).abs()
field.distance(space, self.origin.into()).abs()
}
fn true_distance(&self, space: &Arc<Spatial>, field: &Field) -> f32 {
field.distance(space, vec3a(0.0, 0.0, 0.0))
field.distance(space, self.origin.into())
}
fn serialize(
&self,
_distance_link: &DistanceLink,
local_to_handler_matrix: Mat4,
) -> InputDataType {
fn update_to(&mut self, _distance_link: &DistanceLink, 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();
InputDataType::Tip(FlatTip {
origin: origin.into(),
orientation: orientation.into(),
radius: self.radius,
})
self.origin = origin.into();
self.orientation = orientation.into();
}
}
pub fn create_tip_flex(
_node: Arc<Node>,
calling_client: Arc<Client>,
message: Message,
) -> Result<()> {
#[derive(Deserialize)]
struct CreateTipInfo<'a> {
name: &'a str,
parent_path: &'a str,
transform: Transform,
radius: f32,
datamap: Option<Vec<u8>>,
}
let info: CreateTipInfo = deserialize(message.as_ref())?;
let node = Node::create_parent_name(&calling_client, "/input/method/tip", info.name, true);
let parent = calling_client
.get_node("Spatial parent", info.parent_path)?
.get_aspect::<Spatial>()?;
let transform = parse_transform(info.transform, true, true, false);
let node = node.add_to_scenegraph()?;
Spatial::add_to(&node, Some(parent.clone()), transform, false);
InputMethod::add_to(
&node,
InputType::Tip(Tip {
radius: info.radius,
}),
info.datamap
.and_then(|datamap| Datamap::from_raw(datamap).ok()),
)?;
node.add_local_signal("set_radius", Tip::set_radius);
Ok(())
}

View File

@@ -1,7 +1,7 @@
use crate::{
core::client::INTERNAL_CLIENT,
nodes::{
input::{pointer::Pointer, InputMethod, InputType},
input::{InputDataType, InputMethod, Pointer},
spatial::Spatial,
Node,
},
@@ -9,11 +9,16 @@ use crate::{
use color_eyre::eyre::Result;
use glam::Mat4;
use nanoid::nanoid;
use serde::Serialize;
use stardust_xr::{schemas::flex::flexbuffers, values::Datamap};
use serde::{Deserialize, Serialize};
use stardust_xr::values::Datamap;
use std::sync::Arc;
use stereokit::StereoKitMultiThread;
#[derive(Default, Deserialize, Serialize)]
pub struct EyeDatamap {
eye: u32,
}
#[derive(Debug, Clone, Serialize)]
pub struct KeyboardEvent {
pub keyboard: String,
@@ -31,8 +36,12 @@ impl EyePointer {
let node = Node::create_parent_name(&INTERNAL_CLIENT, "", &nanoid!(), false)
.add_to_scenegraph()?;
let spatial = Spatial::add_to(&node, None, Mat4::IDENTITY, false);
let pointer =
InputMethod::add_to(&node, InputType::Pointer(Pointer::default()), None).unwrap();
let pointer = InputMethod::add_to(
&node,
InputDataType::Pointer(Pointer::default()),
Datamap::from_typed(EyeDatamap::default())?,
)
.unwrap();
Ok(EyePointer { spatial, pointer })
}
@@ -45,11 +54,7 @@ impl EyePointer {
));
{
// Set pointer input datamap
let mut fbb = flexbuffers::Builder::default();
let mut map = fbb.start_map();
map.push("eye", 2);
map.end_map();
*self.pointer.datamap.lock() = Datamap::from_raw(fbb.take_buffer()).ok();
*self.pointer.datamap.lock() = Datamap::from_typed(EyeDatamap { eye: 2 }).unwrap();
}
}
}

View File

@@ -5,7 +5,7 @@ use crate::{
mask_matches, pulse_receiver_client, PulseSender, KEYMAPS, PULSE_RECEIVER_REGISTRY,
},
fields::{Field, Ray},
input::{pointer::Pointer, InputMethod, InputType},
input::{InputDataType, InputMethod, Pointer},
spatial::Spatial,
Node,
},
@@ -61,8 +61,11 @@ impl MousePointer {
let node = Node::create_parent_name(&INTERNAL_CLIENT, "", &nanoid!(), false)
.add_to_scenegraph()?;
let spatial = Spatial::add_to(&node, None, Mat4::IDENTITY, false);
let pointer =
InputMethod::add_to(&node, InputType::Pointer(Pointer::default()), None).unwrap();
let pointer = InputMethod::add_to(
&node,
InputDataType::Pointer(Pointer::default()),
Datamap::from_typed(MouseEvent::default())?,
)?;
KEYMAPS.lock().insert(
"flatscreen".to_string(),
@@ -129,7 +132,7 @@ impl MousePointer {
};
self.mouse_datamap.scroll_continuous = vec2(0.0, mouse.scroll_change / 120.0);
self.mouse_datamap.scroll_discrete = vec2(0.0, mouse.scroll_change / 120.0);
*self.pointer.datamap.lock() = Datamap::from_typed(&self.mouse_datamap).ok();
*self.pointer.datamap.lock() = Datamap::from_typed(&self.mouse_datamap).unwrap();
}
self.send_keyboard_input(sk);
}

View File

@@ -1,7 +1,7 @@
use crate::{
core::client::INTERNAL_CLIENT,
nodes::{
input::{tip::Tip, InputMethod, InputType},
input::{InputDataType, InputMethod, Tip},
spatial::Spatial,
Node,
},
@@ -45,8 +45,12 @@ impl SkController {
.add_to_scenegraph()?;
Spatial::add_to(&_node, None, Mat4::IDENTITY, false);
let model = sk.model_create_mem("cursor.glb", include_bytes!("cursor.glb"), None)?;
let tip = InputType::Tip(Tip::default());
let input = InputMethod::add_to(&_node, tip, None)?;
let tip = InputDataType::Tip(Tip::default());
let input = InputMethod::add_to(
&_node,
tip,
Datamap::from_typed(ControllerDatamap::default())?,
)?;
Ok(SkController {
_node,
input,
@@ -74,6 +78,6 @@ impl SkController {
self.datamap.select = controller.trigger;
self.datamap.grab = controller.grip;
self.datamap.scroll = controller.stick;
*self.input.datamap.lock() = Datamap::from_typed(&self.datamap).ok();
*self.input.datamap.lock() = Datamap::from_typed(&self.datamap).unwrap();
}
}

View File

@@ -1,19 +1,15 @@
use crate::{
core::client::INTERNAL_CLIENT,
nodes::{
input::{hand::Hand, InputMethod, InputType},
spatial::Spatial,
Node,
},
use crate::core::client::INTERNAL_CLIENT;
use crate::nodes::input::InputDataType;
use crate::nodes::{
input::{Hand, InputMethod, Joint},
spatial::Spatial,
Node,
};
use color_eyre::eyre::Result;
use glam::Mat4;
use nanoid::nanoid;
use serde::{Deserialize, Serialize};
use stardust_xr::{
schemas::flat::{Hand as FlatHand, Joint},
values::Datamap,
};
use stardust_xr::values::Datamap;
use std::sync::Arc;
use stereokit::{ButtonState, HandJoint, Handed, StereoKitMultiThread};
@@ -43,13 +39,12 @@ impl SkHand {
let _node = Node::create_parent_name(&INTERNAL_CLIENT, "", &nanoid!(), false)
.add_to_scenegraph()?;
Spatial::add_to(&_node, None, Mat4::IDENTITY, false);
let hand = InputType::Hand(Box::new(Hand {
base: FlatHand {
right: handed == Handed::Right,
..Default::default()
},
}));
let input = InputMethod::add_to(&_node, hand, None)?;
let hand = InputDataType::Hand(Hand {
right: handed == Handed::Right,
..Default::default()
});
let datamap = Datamap::from_typed(HandDatamap::default())?;
let input = InputMethod::add_to(&_node, hand, datamap)?;
Ok(SkHand {
_node,
input,
@@ -59,7 +54,7 @@ impl SkHand {
}
pub fn update(&mut self, controller_enabled: bool, sk: &impl StereoKitMultiThread) {
let sk_hand = sk.input_hand(self.handed);
if let InputType::Hand(hand) = &mut *self.input.specialization.lock() {
if let InputDataType::Hand(hand) = &mut *self.input.data.lock() {
let controller_active = controller_enabled
&& sk
.input_controller(self.handed)
@@ -69,16 +64,16 @@ impl SkHand {
!controller_active && sk_hand.tracked_state.contains(ButtonState::ACTIVE);
sk.input_hand_visible(self.handed, *self.input.enabled.lock());
if *self.input.enabled.lock() {
hand.base.thumb.tip = convert_joint(sk_hand.fingers[0][4]);
hand.base.thumb.distal = convert_joint(sk_hand.fingers[0][3]);
hand.base.thumb.proximal = convert_joint(sk_hand.fingers[0][2]);
hand.base.thumb.metacarpal = convert_joint(sk_hand.fingers[0][1]);
hand.thumb.tip = convert_joint(sk_hand.fingers[0][4]);
hand.thumb.distal = convert_joint(sk_hand.fingers[0][3]);
hand.thumb.proximal = convert_joint(sk_hand.fingers[0][2]);
hand.thumb.metacarpal = convert_joint(sk_hand.fingers[0][1]);
for (finger, sk_finger) in [
(&mut hand.base.index, sk_hand.fingers[1]),
(&mut hand.base.middle, sk_hand.fingers[2]),
(&mut hand.base.ring, sk_hand.fingers[3]),
(&mut hand.base.little, sk_hand.fingers[4]),
(&mut hand.index, sk_hand.fingers[1]),
(&mut hand.middle, sk_hand.fingers[2]),
(&mut hand.ring, sk_hand.fingers[3]),
(&mut hand.little, sk_hand.fingers[4]),
] {
finger.tip = convert_joint(sk_finger[4]);
finger.distal = convert_joint(sk_finger[3]);
@@ -87,21 +82,21 @@ impl SkHand {
finger.metacarpal = convert_joint(sk_finger[0]);
}
hand.base.palm.position = sk_hand.palm.position.into();
hand.base.palm.rotation = sk_hand.palm.orientation.into();
hand.base.palm.radius =
hand.palm.position = sk_hand.palm.position.into();
hand.palm.rotation = sk_hand.palm.orientation.into();
hand.palm.radius =
(sk_hand.fingers[2][0].radius + sk_hand.fingers[2][1].radius) * 0.5;
hand.base.wrist.position = sk_hand.wrist.position.into();
hand.base.wrist.rotation = sk_hand.wrist.orientation.into();
hand.base.wrist.radius =
hand.wrist.position = sk_hand.wrist.position.into();
hand.wrist.rotation = sk_hand.wrist.orientation.into();
hand.wrist.radius =
(sk_hand.fingers[0][0].radius + sk_hand.fingers[4][0].radius) * 0.5;
hand.base.elbow = None;
hand.elbow = None;
}
}
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).ok();
*self.input.datamap.lock() = Datamap::from_typed(&self.datamap).unwrap();
}
}