From d4b7c3f61a18e48d2fb23c3eb7622066312262ae Mon Sep 17 00:00:00 2001 From: Nova Date: Mon, 5 Feb 2024 05:09:48 -0500 Subject: [PATCH] refactor: use typemap for aspects! --- Cargo.lock | 19 ++++ Cargo.toml | 1 + src/core/client.rs | 5 +- src/core/client_state.rs | 4 +- src/core/destroy_queue.rs | 16 +++- src/core/registry.rs | 17 +++- src/core/scenegraph.rs | 3 +- src/nodes/alias.rs | 7 +- src/nodes/audio.rs | 24 +++-- src/nodes/data.rs | 40 ++++----- src/nodes/drawable/lines.rs | 48 +++++----- src/nodes/drawable/mod.rs | 28 ++---- src/nodes/drawable/model.rs | 89 +++++++++---------- src/nodes/drawable/text.rs | 34 +++----- src/nodes/fields/box.rs | 25 ++---- src/nodes/fields/cylinder.rs | 22 ++--- src/nodes/fields/mod.rs | 129 ++++++++++++++++++++------- src/nodes/fields/sphere.rs | 22 ++--- src/nodes/fields/torus.rs | 39 ++------- src/nodes/hmd.rs | 7 +- src/nodes/input/mod.rs | 47 +++++----- src/nodes/input/pointer.rs | 8 +- src/nodes/input/tip.rs | 11 ++- src/nodes/items/camera.rs | 36 ++++---- src/nodes/items/environment.rs | 20 +++-- src/nodes/items/mod.rs | 35 +++++--- src/nodes/items/panel.rs | 14 ++- src/nodes/mod.rs | 108 ++++++++++------------- src/nodes/root.rs | 2 +- src/nodes/spatial/mod.rs | 136 ++++++++++------------------- src/nodes/spatial/zone.rs | 33 ++++--- src/objects/input/eye_pointer.rs | 2 +- src/objects/input/mouse_pointer.rs | 6 +- src/objects/input/sk_controller.rs | 2 +- src/objects/input/sk_hand.rs | 2 +- src/objects/play_space.rs | 5 +- 36 files changed, 518 insertions(+), 528 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 756a9da..66a93e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -786,6 +786,24 @@ dependencies = [ "pin-utils", ] +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "fxtypemap" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c11c87c936dd5cbf3389179749cf020d886f32cc577fc214a7a65eaac5c569db" +dependencies = [ + "fxhash", +] + [[package]] name = "generator" version = "0.7.5" @@ -2108,6 +2126,7 @@ dependencies = [ "console-subscriber", "ctrlc", "directories", + "fxtypemap", "glam 0.23.0", "global_counter", "input-event-codes", diff --git a/Cargo.toml b/Cargo.toml index ecfa40b..0cce1f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,6 +70,7 @@ nix = "0.27.1" wayland-scanner = "0.31.0" wayland-backend = "0.3.2" cluFlock = "1.2.7" +fxtypemap = "0.2.0" [dependencies.smithay] # git = "https://github.com/technobaboo/smithay.git" # Until we get stereokit to understand OES samplers and external textures diff --git a/src/core/client.rs b/src/core/client.rs index 91de2f7..5bbe52c 100644 --- a/src/core/client.rs +++ b/src/core/client.rs @@ -1,5 +1,6 @@ use super::{ client_state::{ClientState, CLIENT_STATES}, + destroy_queue, scenegraph::Scenegraph, }; use crate::{ @@ -209,7 +210,9 @@ impl Client { if let Some(flush_join_handle) = self.flush_join_handle.get() { flush_join_handle.abort(); } - CLIENTS.remove(self); + if let Some(client) = CLIENTS.remove(self) { + destroy_queue::add(client); + } } } impl Drop for Client { diff --git a/src/core/client_state.rs b/src/core/client_state.rs index 36bfc56..124115f 100644 --- a/src/core/client_state.rs +++ b/src/core/client_state.rs @@ -49,7 +49,7 @@ impl ClientState { } fn spatial_transform(client: &Client, path: &str) -> Option { let node = client.scenegraph.get_node(path)?; - let spatial = node.spatial.get()?; + let spatial = node.get_aspect::().ok()?; Some(spatial.global_transform()) } @@ -81,7 +81,7 @@ impl ClientState { let node = Node::create_parent_name(client, "/spatial/anchor", k, true) .add_to_scenegraph() .unwrap(); - Spatial::add_to(&node, None, *v, false).unwrap(); + Spatial::add_to(&node, None, *v, false); k.clone() }) }) diff --git a/src/core/destroy_queue.rs b/src/core/destroy_queue.rs index 20038d5..8a1a863 100644 --- a/src/core/destroy_queue.rs +++ b/src/core/destroy_queue.rs @@ -1,12 +1,22 @@ +use once_cell::sync::Lazy; use parking_lot::Mutex; use std::any::Any; +use tokio::sync::mpsc::{self, unbounded_channel}; -static MAIN_DESTROY_QUEUE: Mutex>> = Mutex::new(Vec::new()); +static MAIN_DESTROY_QUEUE: Lazy<( + mpsc::UnboundedSender>, + Mutex>>, +)> = Lazy::new(|| { + let (tx, rx) = unbounded_channel(); + (tx, Mutex::new(rx)) +}); pub fn add(thing: T) { - MAIN_DESTROY_QUEUE.lock().push(Box::new(thing)); + MAIN_DESTROY_QUEUE.0.send(Box::new(thing)).unwrap(); } pub fn clear() { - MAIN_DESTROY_QUEUE.lock().clear(); + while let Ok(thing) = MAIN_DESTROY_QUEUE.1.lock().try_recv() { + drop(thing) + } } diff --git a/src/core/registry.rs b/src/core/registry.rs index 23c3da7..7916cc0 100644 --- a/src/core/registry.rs +++ b/src/core/registry.rs @@ -5,6 +5,7 @@ use rustc_hash::FxHashMap; use std::ptr; use std::sync::{Arc, Weak}; +#[derive(Debug)] pub struct Registry(Mutex>>>); impl Registry { @@ -56,7 +57,9 @@ impl Registry { } pub fn is_empty(&self) -> bool { let registry = self.0.lock(); - let Some(registry) = &*registry else {return true}; + let Some(registry) = &*registry else { + return true; + }; if registry.is_empty() { return true; } @@ -68,6 +71,11 @@ impl Clone for Registry { Self(Mutex::new(self.0.lock().clone())) } } +impl Default for Registry { + fn default() -> Self { + Self::new() + } +} pub struct OwnedRegistry(Mutex>>>); @@ -98,9 +106,12 @@ impl OwnedRegistry { self.lock() .contains_key(&(ptr::addr_of!(*t) as *const () as usize)) } - pub fn remove(&self, t: &T) { + pub fn remove(&self, t: &T) -> Option> + where + T: Sized, + { self.lock() - .remove(&(ptr::addr_of!(*t) as *const () as usize)); + .remove(&(ptr::addr_of!(*t) as *const () as usize)) } pub fn clear(&self) { self.lock().clear(); diff --git a/src/core/scenegraph.rs b/src/core/scenegraph.rs index 11e1cbe..587c5dd 100644 --- a/src/core/scenegraph.rs +++ b/src/core/scenegraph.rs @@ -1,3 +1,4 @@ +use crate::nodes::alias::Alias; use crate::nodes::Node; use crate::{core::client::Client, nodes::Message}; use color_eyre::eyre::Result; @@ -39,7 +40,7 @@ impl Scenegraph { pub fn get_node(&self, path: &str) -> Option> { let mut node = self.nodes.lock().get(path)?.clone(); - while let Some(alias) = node.alias.get() { + while let Ok(alias) = node.get_aspect::() { if alias.enabled.load(Ordering::Acquire) { node = alias.original.upgrade()?; } else { diff --git a/src/nodes/alias.rs b/src/nodes/alias.rs index b07505b..1856892 100644 --- a/src/nodes/alias.rs +++ b/src/nodes/alias.rs @@ -1,4 +1,4 @@ -use super::Node; +use super::{Aspect, Node}; use crate::core::client::Client; use color_eyre::eyre::{ensure, Result}; use portable_atomic::AtomicBool; @@ -43,7 +43,10 @@ impl Alias { info, }; let alias = original.aliases.add(alias); - let _ = node.alias.set(alias); + node.add_aspect_raw(alias); Ok(node) } } +impl Aspect for Alias { + const NAME: &'static str = "Alias"; +} diff --git a/src/nodes/audio.rs b/src/nodes/audio.rs index c20f96f..8d017a7 100644 --- a/src/nodes/audio.rs +++ b/src/nodes/audio.rs @@ -1,12 +1,11 @@ -use super::spatial::get_spatial; -use super::Node; +use super::{Aspect, Node}; use crate::core::client::Client; use crate::core::destroy_queue; use crate::core::registry::Registry; use crate::core::resource::get_resource_file; use crate::create_interface; use crate::nodes::spatial::{Spatial, Transform}; -use color_eyre::eyre::{ensure, eyre, Result}; +use color_eyre::eyre::{eyre, Result}; use glam::{vec3, Vec4Swizzles}; use once_cell::sync::OnceCell; use parking_lot::Mutex; @@ -33,10 +32,6 @@ pub struct Sound { } impl Sound { pub fn add_to(node: &Arc, resource_id: ResourceID) -> Result> { - ensure!( - node.spatial.get().is_some(), - "Internal: Node does not have a spatial attached!" - ); let pending_audio_path = get_resource_file( &resource_id, &*node.get_client().ok_or_else(|| eyre!("Client not found"))?, @@ -44,7 +39,7 @@ impl Sound { ) .ok_or_else(|| eyre!("Resource not found"))?; let sound = Sound { - space: node.spatial.get().unwrap().clone(), + space: node.get_aspect::().unwrap().clone(), volume: 1.0, pending_audio_path, sk_sound: OnceCell::new(), @@ -53,7 +48,7 @@ impl Sound { play: Mutex::new(None), }; let sound_arc = SOUND_REGISTRY.add(sound); - let _ = node.sound.set(sound_arc.clone()); + node.add_aspect_raw(sound_arc.clone()); ::add_node_members(node); Ok(sound_arc) } @@ -79,14 +74,17 @@ impl Sound { } } } +impl Aspect for Sound { + const NAME: &'static str = "Sound"; +} impl SoundAspect for Sound { fn play(node: Arc, _calling_client: Arc) -> Result<()> { - let sound = node.sound.get().unwrap(); + let sound = node.get_aspect::().unwrap(); sound.play.lock().replace(()); Ok(()) } fn stop(node: Arc, _calling_client: Arc) -> Result<()> { - let sound = node.sound.get().unwrap(); + let sound = node.get_aspect::().unwrap(); sound.stop.lock().replace(()); Ok(()) } @@ -120,10 +118,10 @@ impl AudioInterfaceAspect for AudioInterface { ) -> Result<()> { let node = Node::create_parent_name(&calling_client, Self::CREATE_SOUND_PARENT_PATH, &name, true); - let parent = get_spatial(&parent, "Spatial parent")?; + let parent = parent.get_aspect::()?; let transform = transform.to_mat4(true, true, true); let node = node.add_to_scenegraph()?; - Spatial::add_to(&node, Some(parent), transform, false)?; + Spatial::add_to(&node, Some(parent.clone()), transform, false); Sound::add_to(&node, resource)?; Ok(()) } diff --git a/src/nodes/data.rs b/src/nodes/data.rs index 7c775d3..7a21e2c 100644 --- a/src/nodes/data.rs +++ b/src/nodes/data.rs @@ -1,7 +1,7 @@ use super::alias::AliasInfo; -use super::fields::get_field; -use super::spatial::{get_spatial, parse_transform, Spatial}; -use super::{Alias, Node}; +use super::fields::Field; +use super::spatial::{parse_transform, Spatial}; +use super::{Alias, Aspect, Node}; use crate::core::client::Client; use crate::core::node_collections::LifeLinkedNodeMap; use crate::core::registry::Registry; @@ -62,11 +62,6 @@ pub struct PulseSender { } impl PulseSender { pub fn add_to(node: &Arc, mask: Datamap) -> Result> { - ensure!( - node.spatial.get().is_some(), - "Internal: Node does not have a spatial attached!" - ); - let sender = PulseSender { node: Arc::downgrade(node), mask, @@ -75,7 +70,7 @@ impl PulseSender { // ::add_node_members(node); let sender = PULSE_SENDER_REGISTRY.add(sender); - let _ = node.pulse_sender.set(sender.clone()); + node.add_aspect_raw(sender.clone()); for receiver in PULSE_RECEIVER_REGISTRY.get_valid_contents() { sender.handle_new_receiver(&receiver); } @@ -114,7 +109,7 @@ impl PulseSender { &tx_client, rx_alias.get_path(), "field", - &rx_node.pulse_receiver.get().unwrap().field_node, + &rx_node.get_aspect::().unwrap().field_node, FIELD_ALIAS_INFO.clone(), ) else { return; @@ -136,6 +131,9 @@ impl PulseSender { let _ = pulse_sender_client::drop_receiver(&tx_node, uid); } } +impl Aspect for PulseSender { + const NAME: &'static str = "PulseSender"; +} impl PulseSenderAspect for PulseSender {} impl Drop for PulseSender { fn drop(&mut self) { @@ -155,11 +153,6 @@ impl PulseReceiver { field_node: Arc, mask: Datamap, ) -> Result> { - ensure!( - node.spatial.get().is_some(), - "Internal: Node does not have a spatial attached!" - ); - let receiver = PulseReceiver { uid: nanoid!(), node: Arc::downgrade(node), @@ -169,13 +162,16 @@ impl PulseReceiver { let receiver = PULSE_RECEIVER_REGISTRY.add(receiver); ::add_node_members(node); - let _ = node.pulse_receiver.set(receiver.clone()); + node.add_aspect_raw(receiver.clone()); for sender in PULSE_SENDER_REGISTRY.get_valid_contents() { sender.handle_new_receiver(&receiver); } Ok(receiver) } } +impl Aspect for PulseReceiver { + const NAME: &'static str = "PulseReceiver"; +} impl PulseReceiverAspect for PulseReceiver { fn send_data( node: Arc, @@ -183,7 +179,7 @@ impl PulseReceiverAspect for PulseReceiver { sender: Arc, data: Datamap, ) -> Result<()> { - let this_receiver = node.pulse_receiver.get().unwrap(); + let this_receiver = node.get_aspect::().unwrap(); ensure!( mask_matches(&this_receiver.mask, &data), @@ -221,11 +217,11 @@ impl DataInterfaceAspect for DataInterface { &name, true, ); - let parent = get_spatial(&parent, "Spatial parent")?; + let parent = parent.get_aspect::()?; let transform = transform.to_mat4(true, true, false); let node = node.add_to_scenegraph()?; - Spatial::add_to(&node, Some(parent), transform, false)?; + Spatial::add_to(&node, Some(parent.clone()), transform, false); PulseSender::add_to(&node, mask)?; Ok(()) } @@ -246,12 +242,12 @@ impl DataInterfaceAspect for DataInterface { &name, true, ); - let parent = get_spatial(&parent, "Spatial parent")?; + let parent = parent.get_aspect::()?; let transform = parse_transform(transform, true, true, false); - get_field(&field)?; + let _ = field.get_aspect::()?; let node = node.add_to_scenegraph()?; - Spatial::add_to(&node, Some(parent), transform, false)?; + Spatial::add_to(&node, Some(parent.clone()), transform, false); PulseReceiver::add_to(&node, field, mask)?; Ok(()) } diff --git a/src/nodes/drawable/lines.rs b/src/nodes/drawable/lines.rs index ce3f0d6..38a7196 100644 --- a/src/nodes/drawable/lines.rs +++ b/src/nodes/drawable/lines.rs @@ -1,9 +1,9 @@ -use super::{Drawable, Line, LinesAspect}; +use super::{Line, LinesAspect}; use crate::{ core::{client::Client, registry::Registry}, - nodes::{spatial::Spatial, Node}, + nodes::{spatial::Spatial, Aspect, Node}, }; -use color_eyre::eyre::{bail, ensure, Result}; +use color_eyre::eyre::Result; use glam::Vec3A; use parking_lot::Mutex; use portable_atomic::{AtomicBool, Ordering}; @@ -20,31 +20,29 @@ pub struct Lines { } impl Lines { pub fn add_to(node: &Arc, lines: Vec) -> Result> { - ensure!( - node.drawable.get().is_none(), - "Internal: Node already has a drawable attached!" - ); - - let _ = node.spatial.get().unwrap().bounding_box_calc.set(|node| { - let mut bounds = Bounds::default(); - let Some(Drawable::Lines(lines)) = node.drawable.get() else { - return bounds; - }; - for line in &*lines.data.lock() { - for point in &line.points { - bounds = bounds_grow_to_fit_pt(bounds, point.point); + let _ = node + .get_aspect::() + .unwrap() + .bounding_box_calc + .set(|node| { + let mut bounds = Bounds::default(); + if let Ok(lines) = node.get_aspect::() { + for line in &*lines.data.lock() { + for point in &line.points { + bounds = bounds_grow_to_fit_pt(bounds, point.point); + } + } } - } - bounds - }); + bounds + }); let lines = LINES_REGISTRY.add(Lines { enabled: node.enabled.clone(), - space: node.get_aspect("Lines", "spatial", |n| &n.spatial)?.clone(), + space: node.get_aspect::()?.clone(), data: Mutex::new(lines), }); ::add_node_members(node); - let _ = node.drawable.set(Drawable::Lines(lines.clone())); + node.add_aspect_raw(lines.clone()); Ok(lines) } @@ -94,12 +92,12 @@ impl Lines { } } } +impl Aspect for Lines { + const NAME: &'static str = "Lines"; +} impl LinesAspect for Lines { fn set_lines(node: Arc, _calling_client: Arc, lines: Vec) -> Result<()> { - let Some(Drawable::Lines(lines_aspect)) = node.drawable.get() else { - bail!("Not a drawable??") - }; - + let lines_aspect = node.get_aspect::()?; *lines_aspect.data.lock() = lines; Ok(()) } diff --git a/src/nodes/drawable/mod.rs b/src/nodes/drawable/mod.rs index e90eb1e..3841f9a 100644 --- a/src/nodes/drawable/mod.rs +++ b/src/nodes/drawable/mod.rs @@ -3,14 +3,9 @@ pub mod model; pub mod shaders; pub mod text; -use self::{ - lines::Lines, - model::{Model, ModelPart}, - text::Text, -}; - +use self::{lines::Lines, model::Model, text::Text}; use super::{ - spatial::{get_spatial, Spatial, Transform}, + spatial::{Spatial, Transform}, Node, }; use crate::{ @@ -23,13 +18,6 @@ use stardust_xr::values::ResourceID; use std::{ffi::OsStr, path::PathBuf, sync::Arc}; use stereokit::StereoKitDraw; -pub enum Drawable { - Lines(Arc), - Model(Arc), - ModelPart(Arc), - Text(Arc), -} - // #[instrument(level = "debug", skip(sk))] pub fn draw(sk: &impl StereoKitDraw) { lines::draw_all(sk); @@ -87,11 +75,11 @@ impl DrawableInterfaceAspect for DrawableInterface { ) -> Result<()> { let node = Node::create_parent_name(&calling_client, Self::CREATE_LINES_PARENT_PATH, &name, true); - let parent = get_spatial(&parent, "Spatial parent")?; + let parent = parent.get_aspect::()?; let transform = transform.to_mat4(true, true, true); let node = node.add_to_scenegraph()?; - Spatial::add_to(&node, Some(parent), transform, false)?; + Spatial::add_to(&node, Some(parent.clone()), transform, false); Lines::add_to(&node, lines)?; Ok(()) } @@ -107,10 +95,10 @@ impl DrawableInterfaceAspect for DrawableInterface { ) -> Result<()> { let node = Node::create_parent_name(&calling_client, Self::LOAD_MODEL_PARENT_PATH, &name, true); - let parent = get_spatial(&parent, "Spatial parent")?; + let parent = parent.get_aspect::()?; let transform = transform.to_mat4(true, true, true); let node = node.add_to_scenegraph()?; - Spatial::add_to(&node, Some(parent), transform, false)?; + Spatial::add_to(&node, Some(parent.clone()), transform, false); Model::add_to(&node, model)?; Ok(()) } @@ -127,11 +115,11 @@ impl DrawableInterfaceAspect for DrawableInterface { ) -> Result<()> { let node = Node::create_parent_name(&calling_client, Self::CREATE_TEXT_PARENT_PATH, &name, true); - let parent = get_spatial(&parent, "Spatial parent")?; + let parent = parent.get_aspect::()?; let transform = transform.to_mat4(true, true, true); let node = node.add_to_scenegraph()?; - Spatial::add_to(&node, Some(parent), transform, false)?; + Spatial::add_to(&node, Some(parent.clone()), transform, false); Text::add_to(&node, text, style)?; Ok(()) } diff --git a/src/nodes/drawable/model.rs b/src/nodes/drawable/model.rs index 9c746c3..855e377 100644 --- a/src/nodes/drawable/model.rs +++ b/src/nodes/drawable/model.rs @@ -4,10 +4,10 @@ use crate::core::destroy_queue; use crate::core::node_collections::LifeLinkedNodeMap; use crate::core::registry::Registry; use crate::core::resource::get_resource_file; -use crate::nodes::drawable::Drawable; use crate::nodes::spatial::Spatial; +use crate::nodes::Aspect; use crate::SK_MULTITHREAD; -use color_eyre::eyre::{bail, ensure, eyre, Result}; +use color_eyre::eyre::{eyre, Result}; use once_cell::sync::OnceCell; use parking_lot::Mutex; use portable_atomic::{AtomicBool, Ordering}; @@ -133,10 +133,7 @@ impl ModelPart { .and_then(|id| model.parts.get(&id)); let parent_part = parent_node .as_ref() - .and_then(|node| match node.drawable.get() { - Some(Drawable::ModelPart(model_part)) => Some(model_part), - _ => None, - }); + .and_then(|node| node.get_aspect::().ok()); let stardust_model_part = model.space.node()?; let client = stardust_model_part.get_client()?; @@ -149,34 +146,37 @@ impl ModelPart { false, )); let spatial_parent = parent_node - .and_then(|n| n.spatial.get().cloned()) + .and_then(|n| n.get_aspect::().ok()) .unwrap_or_else(|| model.space.clone()); let space = Spatial::add_to( &node, Some(spatial_parent), sk.model_node_get_transform_local(sk_model, id), false, - ) - .ok()?; + ); - let _ = node.spatial.get().unwrap().bounding_box_calc.set(|node| { - let Some(Drawable::ModelPart(model_part)) = node.drawable.get() else { - return Bounds::default(); - }; - let Some(sk) = SK_MULTITHREAD.get() else { - return Bounds::default(); - }; - let Some(model) = model_part.model.upgrade() else { - return Bounds::default(); - }; - let Some(sk_model) = model.sk_model.get() else { - return Bounds::default(); - }; - let Some(sk_mesh) = sk.model_node_get_mesh(sk_model, model_part.id) else { - return Bounds::default(); - }; - sk.mesh_get_bounds(sk_mesh) - }); + let _ = node + .get_aspect::() + .unwrap() + .bounding_box_calc + .set(|node| { + let Ok(model_part) = node.get_aspect::() else { + return Bounds::default(); + }; + let Some(sk) = SK_MULTITHREAD.get() else { + return Bounds::default(); + }; + let Some(model) = model_part.model.upgrade() else { + return Bounds::default(); + }; + let Some(sk_model) = model.sk_model.get() else { + return Bounds::default(); + }; + let Some(sk_mesh) = sk.model_node_get_mesh(sk_model, model_part.id) else { + return Bounds::default(); + }; + sk.mesh_get_bounds(sk_mesh) + }); let model_part = Arc::new(ModelPart { id, @@ -187,7 +187,7 @@ impl ModelPart { pending_material_replacement: Mutex::new(None), }); ::add_node_members(&node); - let _ = node.drawable.set(Drawable::ModelPart(model_part.clone())); + node.add_aspect_raw(model_part.clone()); model.parts.add(id, &node); Some(model_part) } @@ -232,12 +232,13 @@ impl ModelPart { ); } } +impl Aspect for ModelPart { + const NAME: &'static str = "ModelPart"; +} impl ModelPartAspect for ModelPart { #[doc = "Set this model part's material to one that cuts a hole in the world. Often used for overlays/passthrough where you want to show the background through an object."] fn apply_holdout_material(node: Arc, _calling_client: Arc) -> Result<()> { - let Some(Drawable::ModelPart(model_part)) = node.drawable.get() else { - bail!("Not a drawable??") - }; + let model_part = node.get_aspect::()?; model_part.replace_material(HOLDOUT_MATERIAL.get().unwrap().clone()); Ok(()) } @@ -249,10 +250,7 @@ impl ModelPartAspect for ModelPart { parameter_name: String, value: MaterialParameter, ) -> Result<()> { - let Some(Drawable::ModelPart(model_part)) = node.drawable.get() else { - bail!("Not a drawable??") - }; - + let model_part = node.get_aspect::()?; model_part .pending_material_parameters .lock() @@ -275,15 +273,6 @@ unsafe impl Sync for Model {} impl Model { pub fn add_to(node: &Arc, resource_id: ResourceID) -> Result> { - ensure!( - node.spatial.get().is_some(), - "Internal: Node does not have a spatial attached!" - ); - ensure!( - node.drawable.get().is_none(), - "Internal: Node already has a drawable attached!" - ); - let pending_model_path = get_resource_file( &resource_id, &*node.get_client().ok_or_else(|| eyre!("Client not found"))?, @@ -294,7 +283,7 @@ impl Model { let model = Arc::new_cyclic(|self_ref| Model { self_ref: self_ref.clone(), enabled: node.enabled.clone(), - space: node.spatial.get().unwrap().clone(), + space: node.get_aspect::().unwrap().clone(), _resource_id: resource_id, sk_model: OnceCell::new(), parts: LifeLinkedNodeMap::default(), @@ -307,7 +296,7 @@ impl Model { ); ModelPart::create_for_model(sk, &model.self_ref.upgrade().unwrap(), &sk_model); let _ = model.sk_model.set(sk_model); - let _ = node.drawable.set(Drawable::Model(model.clone())); + node.add_aspect_raw(model.clone()); Ok(model) } @@ -316,10 +305,9 @@ impl Model { return; }; for model_node_node in self.parts.nodes() { - let Some(Drawable::ModelPart(model_node)) = model_node_node.drawable.get() else { - continue; + if let Ok(model_node) = model_node_node.get_aspect::() { + model_node.update(sk); }; - model_node.update(sk); } sk.model_draw( @@ -330,6 +318,9 @@ impl Model { ); } } +impl Aspect for Model { + const NAME: &'static str = "Model"; +} impl ModelAspect for Model {} impl Drop for Model { fn drop(&mut self) { diff --git a/src/nodes/drawable/text.rs b/src/nodes/drawable/text.rs index df8adeb..e50dc10 100644 --- a/src/nodes/drawable/text.rs +++ b/src/nodes/drawable/text.rs @@ -1,8 +1,8 @@ use crate::{ core::{client::Client, destroy_queue, registry::Registry, resource::get_resource_file}, - nodes::{drawable::Drawable, spatial::Spatial, Node}, + nodes::{spatial::Spatial, Aspect, Node}, }; -use color_eyre::eyre::{bail, ensure, eyre, Result}; +use color_eyre::eyre::{eyre, Result}; use glam::{vec3, Mat4, Vec2}; use once_cell::sync::OnceCell; use parking_lot::Mutex; @@ -42,19 +42,10 @@ pub struct Text { } impl Text { pub fn add_to(node: &Arc, text: String, style: TextStyle) -> Result> { - ensure!( - node.spatial.get().is_some(), - "Internal: Node does not have a spatial attached!" - ); - ensure!( - node.drawable.get().is_none(), - "Internal: Node already has a drawable attached!" - ); - let client = node.get_client().ok_or_else(|| eyre!("Client not found"))?; let text = TEXT_REGISTRY.add(Text { enabled: node.enabled.clone(), - space: node.spatial.get().unwrap().clone(), + space: node.get_aspect::().unwrap().clone(), font_path: style.font.as_ref().and_then(|res| { get_resource_file(&res, &client, &[OsStr::new("ttf"), OsStr::new("otf")]) }), @@ -64,7 +55,7 @@ impl Text { data: Mutex::new(style), }); ::add_node_members(node); - let _ = node.drawable.set(Drawable::Text(text.clone())); + node.add_aspect_raw(text.clone()); Ok(text) } @@ -122,26 +113,23 @@ impl Text { } } } +impl Aspect for Text { + const NAME: &'static str = "Text"; +} impl TextAspect for Text { fn set_character_height( node: Arc, _calling_client: Arc, height: f32, ) -> Result<()> { - let Some(Drawable::Text(text)) = node.drawable.get() else { - bail!("Not a drawable??") - }; - - text.data.lock().character_height = height; + let this_text = node.get_aspect::()?; + this_text.data.lock().character_height = height; Ok(()) } fn set_text(node: Arc, _calling_client: Arc, text: String) -> Result<()> { - let Some(Drawable::Text(text_aspect)) = node.drawable.get() else { - bail!("Not a drawable??") - }; - - *text_aspect.text.lock() = text; + let this_text = node.get_aspect::()?; + *this_text.text.lock() = text; Ok(()) } } diff --git a/src/nodes/fields/box.rs b/src/nodes/fields/box.rs index 0e36ad9..6190288 100644 --- a/src/nodes/fields/box.rs +++ b/src/nodes/fields/box.rs @@ -1,8 +1,8 @@ -use super::{get_field, BoxFieldAspect, Field, FieldTrait, Node}; -use crate::core::client::Client; +use super::{BoxFieldAspect, FieldTrait, Node}; use crate::nodes::fields::FieldAspect; use crate::nodes::spatial::Spatial; -use color_eyre::eyre::{ensure, Result}; +use crate::{core::client::Client, nodes::fields::Field}; +use color_eyre::eyre::Result; use glam::{vec3, vec3a, Vec3, Vec3A}; use mint::Vector3; use parking_lot::Mutex; @@ -14,24 +14,14 @@ pub struct BoxField { } impl BoxField { - pub fn add_to(node: &Arc, size: Vector3) -> Result> { - ensure!( - node.spatial.get().is_some(), - "Internal: Node does not have a spatial attached!" - ); - ensure!( - node.field.get().is_none(), - "Internal: Node already has a field attached!" - ); + pub fn add_to(node: &Arc, size: Vector3) { let box_field = BoxField { - space: node.spatial.get().unwrap().clone(), + space: node.get_aspect::().unwrap().clone(), size: Mutex::new(size.into()), }; ::add_node_members(node); ::add_node_members(node); - let field = Arc::new(Field::Box(box_field)); - let _ = node.field.set(field.clone()); - Ok(field) + node.add_aspect(Field::Box(box_field)); } pub fn set_size(&self, size: Vector3) { @@ -60,7 +50,8 @@ impl BoxFieldAspect for BoxField { _calling_client: Arc, size: mint::Vector3, ) -> Result<()> { - let Field::Box(this_field) = &*get_field(&node)? else { + let this_field = node.get_aspect::()?; + let Field::Box(this_field) = &*this_field else { return Ok(()); }; this_field.set_size(size.into()); diff --git a/src/nodes/fields/cylinder.rs b/src/nodes/fields/cylinder.rs index 2b48200..de44fc5 100644 --- a/src/nodes/fields/cylinder.rs +++ b/src/nodes/fields/cylinder.rs @@ -1,8 +1,8 @@ -use super::{get_field, CylinderFieldAspect, Field, FieldTrait, Node}; +use super::{CylinderFieldAspect, Field, FieldTrait, Node}; use crate::core::client::Client; use crate::nodes::fields::FieldAspect; use crate::nodes::spatial::Spatial; -use color_eyre::eyre::{ensure, Result}; +use color_eyre::eyre::Result; use glam::{swizzles::*, vec2, Vec3A}; use portable_atomic::AtomicF32; @@ -16,24 +16,15 @@ pub struct CylinderField { } impl CylinderField { - pub fn add_to(node: &Arc, length: f32, radius: f32) -> Result<()> { - ensure!( - node.spatial.get().is_some(), - "Internal: Node does not have a spatial attached!" - ); - ensure!( - node.field.get().is_none(), - "Internal: Node already has a field attached!" - ); + pub fn add_to(node: &Arc, length: f32, radius: f32) { let cylinder_field = CylinderField { - space: node.spatial.get().unwrap().clone(), + space: node.get_aspect::().unwrap().clone(), length: AtomicF32::new(length.abs()), radius: AtomicF32::new(radius.abs()), }; ::add_node_members(node); ::add_node_members(node); - let _ = node.field.set(Arc::new(Field::Cylinder(cylinder_field))); - Ok(()) + node.add_aspect(Field::Cylinder(cylinder_field)); } pub fn set_size(&self, length: f32, radius: f32) { @@ -60,7 +51,8 @@ impl CylinderFieldAspect for CylinderField { length: f32, radius: f32, ) -> Result<()> { - let Field::Cylinder(this_field) = &*get_field(&node)? else { + let this_field = node.get_aspect::()?; + let Field::Cylinder(this_field) = &*this_field else { return Ok(()); }; this_field.set_size(length, radius); diff --git a/src/nodes/fields/mod.rs b/src/nodes/fields/mod.rs index 10b5f91..f82206e 100644 --- a/src/nodes/fields/mod.rs +++ b/src/nodes/fields/mod.rs @@ -9,8 +9,8 @@ use self::sphere::SphereField; use self::torus::TorusField; use super::alias::AliasInfo; -use super::spatial::{get_spatial, Spatial}; -use super::Node; +use super::spatial::Spatial; +use super::{Aspect, Node}; use crate::core::client::Client; use crate::create_interface; use crate::nodes::spatial::Transform; @@ -30,7 +30,7 @@ pub static FIELD_ALIAS_INFO: Lazy = Lazy::new(|| AliasInfo { stardust_xr_server_codegen::codegen_field_protocol!(); -pub trait FieldTrait { +pub trait FieldTrait: Send + Sync + 'static { fn spatial_ref(&self) -> &Spatial; fn local_distance(&self, p: Vec3A) -> f32; @@ -114,9 +114,9 @@ impl FieldAspect for Fi { space: Arc, point: mint::Vector3, ) -> Result { - let reference_space = get_spatial(&space, "Reference space")?; - let this_field = node.field.get().unwrap(); - Ok(this_field.distance(reference_space.as_ref(), point.into())) + let reference_space = space.get_aspect::()?; + let this_field = node.get_aspect::()?; + Ok((*this_field).distance(reference_space.as_ref(), point.into())) } async fn normal( @@ -125,8 +125,8 @@ impl FieldAspect for Fi { space: Arc, point: mint::Vector3, ) -> Result> { - let reference_space = get_spatial(&space, "Reference space")?; - let this_field = node.field.get().unwrap(); + let reference_space = space.get_aspect::()?; + let this_field = node.get_aspect::()?; Ok(this_field .normal(reference_space.as_ref(), point.into(), 0.001) .into()) @@ -138,8 +138,8 @@ impl FieldAspect for Fi { space: Arc, point: mint::Vector3, ) -> Result> { - let reference_space = get_spatial(&space, "Reference space")?; - let this_field = node.field.get().unwrap(); + let reference_space = space.get_aspect::()?; + let this_field = node.get_aspect::()?; Ok(this_field .closest_point(reference_space.as_ref(), point.into(), 0.001) .into()) @@ -152,12 +152,12 @@ impl FieldAspect for Fi { ray_origin: mint::Vector3, ray_direction: mint::Vector3, ) -> Result { - let reference_space = get_spatial(&space, "Reference space")?; - let this_field = node.field.get().unwrap(); + let reference_space = space.get_aspect::()?; + let this_field = node.get_aspect::()?; Ok(this_field.ray_march(Ray { origin: ray_origin.into(), direction: ray_direction.into(), - space: reference_space, + space: reference_space.clone(), })) } } @@ -183,6 +183,9 @@ pub enum Field { Sphere(SphereField), Torus(TorusField), } +impl Aspect for Field { + const NAME: &'static str = "Field"; +} impl Deref for Field { type Target = dyn FieldTrait; fn deref(&self) -> &Self::Target { @@ -194,6 +197,72 @@ 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; @@ -207,7 +276,7 @@ impl FieldInterfaceAspect for FieldInterface { size: mint::Vector3, ) -> Result<()> { let transform = transform.to_mat4(true, true, false); - let parent = get_spatial(&parent, "Spatial parent")?; + let parent = parent.get_aspect::()?; let node = Node::create_parent_name( &calling_client, Self::CREATE_BOX_FIELD_PARENT_PATH, @@ -215,8 +284,8 @@ impl FieldInterfaceAspect for FieldInterface { true, ) .add_to_scenegraph()?; - Spatial::add_to(&node, Some(parent), transform, false)?; - BoxField::add_to(&node, size)?; + Spatial::add_to(&node, Some(parent.clone()), transform, false); + BoxField::add_to(&node, size); Ok(()) } @@ -230,7 +299,7 @@ impl FieldInterfaceAspect for FieldInterface { radius: f32, ) -> Result<()> { let transform = transform.to_mat4(true, true, false); - let parent = get_spatial(&parent, "Spatial parent")?; + let parent = parent.get_aspect::()?; let node = Node::create_parent_name( &calling_client, Self::CREATE_CYLINDER_FIELD_PARENT_PATH, @@ -238,8 +307,8 @@ impl FieldInterfaceAspect for FieldInterface { true, ) .add_to_scenegraph()?; - Spatial::add_to(&node, Some(parent), transform, false)?; - CylinderField::add_to(&node, length, radius)?; + Spatial::add_to(&node, Some(parent.clone()), transform, false); + CylinderField::add_to(&node, length, radius); Ok(()) } @@ -251,7 +320,7 @@ impl FieldInterfaceAspect for FieldInterface { position: mint::Vector3, radius: f32, ) -> Result<()> { - let parent = get_spatial(&parent, "Spatial parent")?; + let parent = parent.get_aspect::()?; let node = Node::create_parent_name( &calling_client, Self::CREATE_SPHERE_FIELD_PARENT_PATH, @@ -261,11 +330,11 @@ impl FieldInterfaceAspect for FieldInterface { .add_to_scenegraph()?; Spatial::add_to( &node, - Some(parent), + Some(parent.clone()), Mat4::from_translation(position.into()), false, - )?; - SphereField::add_to(&node, radius)?; + ); + SphereField::add_to(&node, radius); Ok(()) } @@ -279,7 +348,7 @@ impl FieldInterfaceAspect for FieldInterface { radius_b: f32, ) -> Result<()> { let transform = transform.to_mat4(true, true, false); - let parent = get_spatial(&parent, "Spatial parent")?; + let parent = parent.get_aspect::()?; let node = Node::create_parent_name( &calling_client, Self::CREATE_TORUS_FIELD_PARENT_PATH, @@ -287,18 +356,12 @@ impl FieldInterfaceAspect for FieldInterface { true, ) .add_to_scenegraph()?; - Spatial::add_to(&node, Some(parent), transform, false)?; - TorusField::add_to(&node, radius_a, radius_b)?; + Spatial::add_to(&node, Some(parent.clone()), transform, false); + TorusField::add_to(&node, radius_a, radius_b); Ok(()) } } pub fn find_field(client: &Client, path: &str) -> Result> { - client - .get_node("Field", path)? - .get_aspect("Field", "info", |n| &n.field) - .cloned() -} -pub fn get_field(node: &Node) -> Result> { - node.get_aspect("Field", "info", |n| &n.field).cloned() + client.get_node("Field", path)?.get_aspect::() } diff --git a/src/nodes/fields/sphere.rs b/src/nodes/fields/sphere.rs index ea35799..c198489 100644 --- a/src/nodes/fields/sphere.rs +++ b/src/nodes/fields/sphere.rs @@ -1,8 +1,8 @@ -use super::{get_field, Field, FieldTrait, Node, SphereFieldAspect}; +use super::{Field, FieldTrait, Node, SphereFieldAspect}; use crate::core::client::Client; use crate::nodes::fields::FieldAspect; use crate::nodes::spatial::Spatial; -use color_eyre::eyre::{ensure, Result}; +use color_eyre::eyre::Result; use glam::Vec3A; use portable_atomic::AtomicF32; use std::sync::atomic::Ordering; @@ -14,23 +14,14 @@ pub struct SphereField { } impl SphereField { - pub fn add_to(node: &Arc, radius: f32) -> Result<()> { - ensure!( - node.spatial.get().is_some(), - "Internal: Node does not have a spatial attached!" - ); - ensure!( - node.field.get().is_none(), - "Internal: Node already has a field attached!" - ); + pub fn add_to(node: &Arc, radius: f32) { let sphere_field = SphereField { - space: node.spatial.get().unwrap().clone(), + space: node.get_aspect::().unwrap().clone(), radius: AtomicF32::new(radius), }; ::add_node_members(node); ::add_node_members(node); - let _ = node.field.set(Arc::new(Field::Sphere(sphere_field))); - Ok(()) + node.add_aspect(Field::Sphere(sphere_field)); } pub fn set_radius(&self, radius: f32) { @@ -54,7 +45,8 @@ impl FieldTrait for SphereField { } impl SphereFieldAspect for SphereField { fn set_radius(node: Arc, _calling_client: Arc, radius: f32) -> Result<()> { - let Field::Sphere(this_field) = &*get_field(&node)? else { + let this_field = node.get_aspect::()?; + let Field::Sphere(this_field) = &*this_field else { return Ok(()); }; this_field.set_radius(radius); diff --git a/src/nodes/fields/torus.rs b/src/nodes/fields/torus.rs index a82e1d2..902c764 100644 --- a/src/nodes/fields/torus.rs +++ b/src/nodes/fields/torus.rs @@ -1,12 +1,10 @@ -use super::{get_field, Field, FieldTrait, Node, TorusFieldAspect}; +use super::{Field, FieldTrait, Node, TorusFieldAspect}; use crate::core::client::Client; use crate::nodes::fields::FieldAspect; use crate::nodes::spatial::Spatial; -use crate::nodes::Message; -use color_eyre::eyre::{ensure, Result}; +use color_eyre::eyre::Result; use glam::{swizzles::*, vec2, Vec3A}; use portable_atomic::AtomicF32; -use stardust_xr::schemas::flex::deserialize; use std::sync::atomic::Ordering; use std::sync::Arc; @@ -18,45 +16,21 @@ pub struct TorusField { } impl TorusField { - pub fn add_to(node: &Arc, radius_a: f32, radius_b: f32) -> Result<()> { - ensure!( - node.spatial.get().is_some(), - "Internal: Node does not have a spatial attached!" - ); - ensure!( - node.field.get().is_none(), - "Internal: Node already has a field attached!" - ); + pub fn add_to(node: &Arc, radius_a: f32, radius_b: f32) { let torus_field = TorusField { - space: node.spatial.get().unwrap().clone(), + space: node.get_aspect::().unwrap().clone(), radius_a: AtomicF32::new(radius_a.abs()), radius_b: AtomicF32::new(radius_b.abs()), }; ::add_node_members(node); ::add_node_members(node); - node.add_local_signal("set_size", TorusField::set_size_flex); - let _ = node.field.set(Arc::new(Field::Torus(torus_field))); - Ok(()) + node.add_aspect(Field::Torus(torus_field)); } pub fn set_size(&self, radius_a: f32, radius_b: f32) { self.radius_a.store(radius_a.abs(), Ordering::Relaxed); self.radius_b.store(radius_b.abs(), Ordering::Relaxed); } - - pub fn set_size_flex( - node: Arc, - _calling_client: Arc, - message: Message, - ) -> Result<()> { - let Field::Torus(torus_field) = node.field.get().unwrap().as_ref() else { - return Ok(()); - }; - let (radius_a, radius_b) = deserialize(message.as_ref())?; - torus_field.set_size(radius_a, radius_b); - - Ok(()) - } } impl FieldTrait for TorusField { fn local_distance(&self, p: Vec3A) -> f32 { @@ -76,7 +50,8 @@ impl TorusFieldAspect for TorusField { radius_a: f32, radius_b: f32, ) -> Result<()> { - let Field::Torus(this_field) = &*get_field(&node)? else { + let this_field = node.get_aspect::()?; + let Field::Torus(this_field) = &*this_field else { return Ok(()); }; this_field.set_size(radius_a, radius_b); diff --git a/src/nodes/hmd.rs b/src/nodes/hmd.rs index 55bf7d8..7d4f153 100644 --- a/src/nodes/hmd.rs +++ b/src/nodes/hmd.rs @@ -14,16 +14,13 @@ lazy_static::lazy_static! { fn create() -> Arc { let node = Arc::new(Node::create_parent_name(&INTERNAL_CLIENT, "", "hmd", false)); - Spatial::add_to(&node, None, Mat4::IDENTITY, false).expect("Unable to make spatial for HMD"); + Spatial::add_to(&node, None, Mat4::IDENTITY, false); node } pub fn frame(sk: &impl StereoKitMultiThread) { - let spatial = HMD - .spatial - .get() - .expect("Unable to get spatial to update HMD"); + let spatial = HMD.get_aspect::().unwrap(); let hmd_pose = sk.input_head(); *spatial.transform.lock() = Mat4::from_scale_rotation_translation( vec3(1.0, 1.0, 1.0), diff --git a/src/nodes/input/mod.rs b/src/nodes/input/mod.rs index be3a6ff..5ae4a89 100644 --- a/src/nodes/input/mod.rs +++ b/src/nodes/input/mod.rs @@ -9,12 +9,12 @@ use self::tip::Tip; use super::{ alias::{Alias, AliasInfo}, fields::{find_field, Field, FIELD_ALIAS_INFO}, - spatial::{find_spatial_parent, parse_transform, Spatial}, - Message, Node, + spatial::{parse_transform, Spatial}, + Aspect, Message, Node, }; use crate::core::{client::Client, node_collections::LifeLinkedNodeMap}; use crate::{core::registry::Registry, nodes::spatial::Transform}; -use color_eyre::eyre::{ensure, Result}; +use color_eyre::eyre::Result; use glam::Mat4; use once_cell::sync::OnceCell; use parking_lot::Mutex; @@ -76,11 +76,6 @@ impl InputMethod { specialization: InputType, datamap: Option, ) -> Result> { - ensure!( - node.spatial.get().is_some(), - "Internal: Node does not have a spatial attached!" - ); - 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); @@ -89,7 +84,7 @@ impl InputMethod { node: Arc::downgrade(node), uid: node.uid.clone(), enabled: Mutex::new(true), - spatial: node.spatial.get().unwrap().clone(), + spatial: node.get_aspect::().unwrap().clone(), specialization: Mutex::new(specialization), captures: Registry::new(), datamap: Mutex::new(datamap), @@ -101,12 +96,11 @@ impl InputMethod { method.make_alias(&handler); } let method = INPUT_METHOD_REGISTRY.add(method); - let _ = node.input_method.set(method.clone()); + node.add_aspect_raw(method.clone()); Ok(method) } fn get(node: &Node) -> Result> { - node.get_aspect("Input Method", "input method", |n| &n.input_method) - .cloned() + node.get_aspect::() } fn capture_flex(node: Arc, calling_client: Arc, message: Message) -> Result<()> { @@ -242,6 +236,9 @@ impl InputMethod { 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); @@ -294,16 +291,11 @@ pub struct InputHandler { } 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 { enabled: node.enabled.clone(), uid: node.uid.clone(), node: Arc::downgrade(node), - spatial: node.spatial.get().unwrap().clone(), + spatial: node.get_aspect::().unwrap().clone(), field: field.clone(), method_aliases: LifeLinkedNodeMap::default(), }; @@ -312,15 +304,11 @@ impl InputHandler { method.handle_new_handler(&handler); } let handler = INPUT_HANDLER_REGISTRY.add(handler); - let _ = node.input_handler.set(handler); + node.add_aspect_raw(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() + client.get_node("Input Handler", path)?.get_aspect::() } #[instrument(level = "debug", skip(self, distance_link))] @@ -337,6 +325,9 @@ impl InputHandler { 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 @@ -372,13 +363,15 @@ pub fn create_input_handler_flex( field_path: &'a str, } let info: CreateInputHandlerInfo = deserialize(message.as_ref())?; - let parent = find_spatial_parent(&calling_client, info.parent_path)?; + let parent = calling_client + .get_node("Spatial parent", info.parent_path)? + .get_aspect::()?; let transform = parse_transform(info.transform, true, true, true); let field = find_field(&calling_client, info.field_path)?; let node = Node::create_parent_name(&calling_client, "/input/handler", info.name, true) .add_to_scenegraph()?; - Spatial::add_to(&node, Some(parent), transform, false)?; + Spatial::add_to(&node, Some(parent.clone()), transform, false); InputHandler::add_to(&node, &field)?; Ok(()) } @@ -441,7 +434,7 @@ pub fn process_input() { .handler .method_aliases .get(&(Arc::as_ptr(&distance_link.method) as usize)) - .and_then(|a| a.alias.get().cloned()) + .and_then(|a| a.get_aspect::().ok()) { method_alias.enabled.store(true, Ordering::Release); } diff --git a/src/nodes/input/pointer.rs b/src/nodes/input/pointer.rs index 334a1e5..2effe14 100644 --- a/src/nodes/input/pointer.rs +++ b/src/nodes/input/pointer.rs @@ -2,7 +2,7 @@ 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::{find_spatial_parent, parse_transform, Spatial, Transform}; +use crate::nodes::spatial::{parse_transform, Spatial, Transform}; use crate::nodes::{Message, Node}; use glam::{vec3, Mat4}; use serde::Deserialize; @@ -79,11 +79,13 @@ pub fn create_pointer_flex( } let info: CreatePointerInfo = deserialize(message.as_ref())?; let node = Node::create_parent_name(&calling_client, "/input/method/pointer", info.name, true); - let parent = find_spatial_parent(&calling_client, info.parent_path)?; + let parent = calling_client + .get_node("Spatial parent", info.parent_path)? + .get_aspect::()?; let transform = parse_transform(info.transform, true, true, false); let node = node.add_to_scenegraph()?; - Spatial::add_to(&node, Some(parent), transform, false)?; + Spatial::add_to(&node, Some(parent.clone()), transform, false); InputMethod::add_to( &node, InputType::Pointer(Pointer), diff --git a/src/nodes/input/tip.rs b/src/nodes/input/tip.rs index cdcf418..5545374 100644 --- a/src/nodes/input/tip.rs +++ b/src/nodes/input/tip.rs @@ -2,7 +2,7 @@ use super::{DistanceLink, InputSpecialization}; use crate::core::client::Client; use crate::nodes::fields::Field; use crate::nodes::input::{InputMethod, InputType}; -use crate::nodes::spatial::{find_spatial_parent, parse_transform, Spatial, Transform}; +use crate::nodes::spatial::{parse_transform, Spatial, Transform}; use crate::nodes::{Message, Node}; use color_eyre::eyre::Result; use glam::{vec3a, Mat4}; @@ -19,7 +19,8 @@ pub struct Tip { } impl Tip { fn set_radius(node: Arc, _calling_client: Arc, message: Message) -> Result<()> { - if let InputType::Tip(tip) = &mut *node.input_method.get().unwrap().specialization.lock() { + let input_method = node.get_aspect::()?; + if let InputType::Tip(tip) = &mut *input_method.specialization.lock() { tip.radius = deserialize(message.as_ref())?; } Ok(()) @@ -61,11 +62,13 @@ pub fn create_tip_flex( } let info: CreateTipInfo = deserialize(message.as_ref())?; let node = Node::create_parent_name(&calling_client, "/input/method/tip", info.name, true); - let parent = find_spatial_parent(&calling_client, info.parent_path)?; + let parent = calling_client + .get_node("Spatial parent", info.parent_path)? + .get_aspect::()?; let transform = parse_transform(info.transform, true, true, false); let node = node.add_to_scenegraph()?; - Spatial::add_to(&node, Some(parent), transform, false)?; + Spatial::add_to(&node, Some(parent.clone()), transform, false); InputMethod::add_to( &node, InputType::Tip(Tip { diff --git a/src/nodes/items/camera.rs b/src/nodes/items/camera.rs index bbd969f..5a6f4bb 100644 --- a/src/nodes/items/camera.rs +++ b/src/nodes/items/camera.rs @@ -6,9 +6,9 @@ use crate::{ scenegraph::MethodResponseSender, }, nodes::{ - drawable::{model::ModelPart, shaders::UNLIT_SHADER_BYTES, Drawable}, + drawable::{model::ModelPart, shaders::UNLIT_SHADER_BYTES}, items::TypeInfo, - spatial::{find_spatial_parent, parse_transform, Spatial, Transform}, + spatial::{parse_transform, Spatial, Transform}, Message, Node, }, }; @@ -58,7 +58,7 @@ impl CameraItem { nanoid!(), &ITEM_TYPE_INFO_CAMERA, ItemType::Camera(CameraItem { - space: node.spatial.get().unwrap().clone(), + space: node.get_aspect::().unwrap().clone(), frame_info: Mutex::new(FrameInfo { proj_matrix, px_size, @@ -83,7 +83,8 @@ impl CameraItem { response: MethodResponseSender, ) { response.wrap_sync(move || { - let ItemType::Camera(_camera) = &node.item.get().unwrap().specialization else { + let ItemType::Camera(_camera) = &node.get_aspect::().unwrap().specialization + else { return Err(eyre!("Wrong item type?")); }; Ok(serialize(())?.into()) @@ -95,18 +96,14 @@ impl CameraItem { calling_client: Arc, message: Message, ) -> Result<()> { - let ItemType::Camera(camera) = &node.item.get().unwrap().specialization else { + let ItemType::Camera(camera) = &node.get_aspect::().unwrap().specialization else { bail!("Wrong item type?") }; let model_part_node = calling_client.get_node("Model part", deserialize(&message.data).unwrap())?; - let Drawable::ModelPart(model_part) = - model_part_node.get_aspect("Model part", "model part", |n| &n.drawable)? - else { - bail!("Drawable is not a model node") - }; - camera.applied_to.add_raw(model_part); - camera.apply_to.add_raw(model_part); + let model_part = model_part_node.get_aspect::()?; + camera.applied_to.add_raw(&model_part); + camera.apply_to.add_raw(&model_part); Ok(()) } @@ -178,16 +175,19 @@ pub(super) fn create_camera_item_flex( } let info: CreateCameraItemInfo = deserialize(message.as_ref())?; let parent_name = format!("/item/{}/item", ITEM_TYPE_INFO_CAMERA.type_name); - let space = find_spatial_parent(&calling_client, info.parent_path)?; + let space = calling_client + .get_node("Spatial parent", info.parent_path)? + .get_aspect::()?; let transform = parse_transform(info.transform, true, true, false); let node = Node::create_parent_name(&INTERNAL_CLIENT, &parent_name, info.name, false) .add_to_scenegraph()?; - Spatial::add_to(&node, None, transform * space.global_transform(), false)?; + Spatial::add_to(&node, None, transform * space.global_transform(), false); CameraItem::add_to(&node, info.proj_matrix.into(), info.px_size); - node.item - .get() - .unwrap() - .make_alias_named(&calling_client, &parent_name, info.name)?; + node.get_aspect::().unwrap().make_alias_named( + &calling_client, + &parent_name, + info.name, + )?; Ok(()) } diff --git a/src/nodes/items/environment.rs b/src/nodes/items/environment.rs index b7df92a..753c1cd 100644 --- a/src/nodes/items/environment.rs +++ b/src/nodes/items/environment.rs @@ -7,7 +7,7 @@ use crate::{ }, nodes::{ items::TypeInfo, - spatial::{find_spatial_parent, parse_transform, Spatial, Transform}, + spatial::{parse_transform, Spatial, Transform}, Message, Node, }, }; @@ -51,7 +51,8 @@ impl EnvironmentItem { response: MethodResponseSender, ) { response.wrap_sync(move || { - let ItemType::Environment(environment_item) = &node.item.get().unwrap().specialization + let ItemType::Environment(environment_item) = + &node.get_aspect::().unwrap().specialization else { return Err(eyre!("Wrong item type?")); }; @@ -78,16 +79,19 @@ pub(super) fn create_environment_item_flex( } let info: CreateEnvironmentItemInfo = deserialize(message.as_ref())?; let parent_name = format!("/item/{}/item", ITEM_TYPE_INFO_ENVIRONMENT.type_name); - let space = find_spatial_parent(&calling_client, info.parent_path)?; + let space = calling_client + .get_node("Spatial parent", info.parent_path)? + .get_aspect::()?; let transform = parse_transform(info.transform, true, true, false); let node = Node::create_parent_name(&INTERNAL_CLIENT, &parent_name, info.name, false) .add_to_scenegraph()?; - Spatial::add_to(&node, None, transform * space.global_transform(), false)?; + Spatial::add_to(&node, None, transform * space.global_transform(), false); EnvironmentItem::add_to(&node, info.item_data); - node.item - .get() - .unwrap() - .make_alias_named(&calling_client, &parent_name, info.name)?; + node.get_aspect::().unwrap().make_alias_named( + &calling_client, + &parent_name, + info.name, + )?; Ok(()) } diff --git a/src/nodes/items/mod.rs b/src/nodes/items/mod.rs index 2a26517..2259324 100644 --- a/src/nodes/items/mod.rs +++ b/src/nodes/items/mod.rs @@ -6,8 +6,8 @@ use self::camera::CameraItem; use self::environment::{EnvironmentItem, ITEM_TYPE_INFO_ENVIRONMENT}; use self::panel::{PanelItemTrait, ITEM_TYPE_INFO_PANEL}; use super::fields::Field; -use super::spatial::{find_spatial_parent, parse_transform, Spatial}; -use super::{Alias, Message, Node}; +use super::spatial::{parse_transform, Spatial}; +use super::{Alias, Aspect, Message, Node}; use crate::core::client::Client; use crate::core::node_collections::LifeLinkedNodeMap; use crate::core::registry::Registry; @@ -108,7 +108,7 @@ impl Item { if let Some(ui) = type_info.ui.lock().upgrade() { ui.handle_create_item(&item); } - let _ = node.item.set(item.clone()); + node.add_aspect_raw(item.clone()); // if let Some(auto_acceptor) = node.get_client().and_then(|client| { // client @@ -161,12 +161,15 @@ impl Item { _calling_client: Arc, _message: Message, ) -> Result<()> { - let item = node.get_aspect("Item", "item", |n| &n.item)?; - release(item); + let item = node.get_aspect::()?; + release(&item); Ok(()) } } +impl Aspect for Item { + const NAME: &'static str = "Item"; +} impl Drop for Item { fn drop(&mut self) { self.type_info.items.remove(self); @@ -224,7 +227,7 @@ impl ItemUI { acceptor_field_aliases: Default::default(), }); *type_info.ui.lock() = Arc::downgrade(&ui); - let _ = node.item_ui.set(ui.clone()); + node.add_aspect_raw(ui.clone()); for item in type_info.items.get_valid_contents() { ui.handle_create_item(&item); @@ -314,6 +317,9 @@ impl ItemUI { self.acceptor_field_aliases.remove(&acceptor.uid); } } +impl Aspect for ItemUI { + const NAME: &'static str = "Item"; +} impl Drop for ItemUI { fn drop(&mut self) { *self.type_info.ui.lock() = Weak::new(); @@ -342,7 +348,7 @@ impl ItemAcceptor { if let Some(ui) = type_info.ui.lock().upgrade() { ui.handle_create_acceptor(&acceptor); } - let _ = node.item_acceptor.set(acceptor); + node.add_aspect_raw(acceptor); } fn capture_flex(node: Arc, calling_client: Arc, message: Message) -> Result<()> { @@ -350,11 +356,11 @@ impl ItemAcceptor { return Ok(()); } - let acceptor = node.item_acceptor.get().unwrap(); + let acceptor = node.get_aspect::().unwrap(); let item_path: &str = deserialize(message.as_ref())?; let item_node = calling_client.get_node("Item", item_path)?; - let item = item_node.get_aspect("Item", "item", |n| &n.item)?; - capture(item, acceptor); + let item = item_node.get_aspect::()?; + capture(&item, &acceptor); Ok(()) } @@ -413,6 +419,9 @@ impl ItemAcceptor { let _ = node.send_remote_signal("release", message); } } +impl Aspect for ItemAcceptor { + const NAME: &'static str = "ItemAcceptor"; +} impl Drop for ItemAcceptor { fn drop(&mut self) { self.type_info.acceptors.remove(self); @@ -477,7 +486,9 @@ fn create_item_acceptor_flex( item_type: &'a str, } let info: CreateItemAcceptorInfo = deserialize(message.as_ref())?; - let space = find_spatial_parent(&calling_client, info.parent_path)?; + let space = calling_client + .get_node("Reference space", info.parent_path)? + .get_aspect::()?; let transform = parse_transform(info.transform, true, true, false); let field = find_field(&calling_client, info.field_path)?; let type_info = type_info(info.item_type)?; @@ -489,7 +500,7 @@ fn create_item_acceptor_flex( true, ) .add_to_scenegraph()?; - Spatial::add_to(&node, Some(space), transform, false)?; + Spatial::add_to(&node, Some(space.clone()), transform, false); ItemAcceptor::add_to(&node, type_info, field); Ok(()) } diff --git a/src/nodes/items/panel.rs b/src/nodes/items/panel.rs index fd91fbf..aaaecc3 100644 --- a/src/nodes/items/panel.rs +++ b/src/nodes/items/panel.rs @@ -4,13 +4,13 @@ use crate::{ registry::Registry, }, nodes::{ - drawable::{model::ModelPart, Drawable}, + drawable::model::ModelPart, items::{Item, ItemType, TypeInfo}, spatial::Spatial, Message, Node, }, }; -use color_eyre::eyre::{bail, eyre, Result}; +use color_eyre::eyre::{eyre, Result}; use glam::Mat4; use lazy_static::lazy_static; use mint::Vector2; @@ -203,7 +203,7 @@ pub trait Backend: Send + Sync + 'static { } pub fn panel_item_from_node(node: &Node) -> Option> { - let ItemType::Panel(panel_item) = &node.item.get()?.specialization else { + let ItemType::Panel(panel_item) = &node.get_aspect::().ok()?.specialization else { return None; }; Some(panel_item.clone()) @@ -231,7 +231,7 @@ impl PanelItem { let node = Node::create_parent_name(&INTERNAL_CLIENT, "/item/panel/item", &uid, true) .add_to_scenegraph() .unwrap(); - let spatial = Spatial::add_to(&node, None, Mat4::IDENTITY, false).unwrap(); + let spatial = Spatial::add_to(&node, None, Mat4::IDENTITY, false); if let Some(startup_settings) = &startup_settings { spatial.set_local_transform(startup_settings.root); } @@ -402,12 +402,10 @@ impl PanelItem { .scenegraph .get_node(info.model_node_path) .ok_or_else(|| eyre!("Model node not found"))?; - let Some(Drawable::ModelPart(model_part)) = model_node.drawable.get() else { - bail!("Node is not a model") - }; + let model_part = model_node.get_aspect::()?; debug!(?info, "Apply surface material"); - panel_item.apply_surface_material(info.surface, model_part); + panel_item.apply_surface_material(info.surface, &model_part); Ok(()) } diff --git a/src/nodes/mod.rs b/src/nodes/mod.rs index 661214a..7b67846 100644 --- a/src/nodes/mod.rs +++ b/src/nodes/mod.rs @@ -11,7 +11,6 @@ pub mod spatial; use color_eyre::eyre::{eyre, Result}; use nanoid::nanoid; -use once_cell::sync::OnceCell; use parking_lot::Mutex; use portable_atomic::{AtomicBool, Ordering}; use rustc_hash::FxHashMap; @@ -19,6 +18,7 @@ use serde::{de::DeserializeOwned, Serialize}; use stardust_xr::messenger::MessageSenderHandle; use stardust_xr::scenegraph::ScenegraphError; use stardust_xr::schemas::flex::{deserialize, serialize}; +use std::any::{Any, TypeId}; use std::fmt::Debug; use std::os::fd::OwnedFd; use std::sync::{Arc, Weak}; @@ -29,14 +29,6 @@ use crate::core::registry::Registry; use crate::core::scenegraph::MethodResponseSender; use self::alias::Alias; -use self::audio::Sound; -use self::data::{PulseReceiver, PulseSender}; -use self::drawable::Drawable; -use self::fields::Field; -use self::input::{InputHandler, InputMethod}; -use self::items::{Item, ItemAcceptor, ItemUI}; -use self::spatial::zone::Zone; -use self::spatial::Spatial; #[derive(Default)] pub struct Message { @@ -71,33 +63,9 @@ pub struct Node { // trailing_slash_pos: usize, local_signals: Mutex>, local_methods: Mutex>, - destroyable: bool, - - pub alias: OnceCell>, aliases: Registry, - - pub spatial: OnceCell>, - pub field: OnceCell>, - pub zone: OnceCell>, - - // Data - pub pulse_sender: OnceCell>, - pub pulse_receiver: OnceCell>, - - // Drawable - pub drawable: OnceCell, - - // Input - pub input_method: OnceCell>, - pub input_handler: OnceCell>, - - // Item - pub item: OnceCell>, - pub item_acceptor: OnceCell>, - pub item_ui: OnceCell>, - - // Sound - pub sound: OnceCell>, + aspects: Aspects, + destroyable: bool, } impl Node { pub fn get_client(&self) -> Option> { @@ -131,23 +99,9 @@ impl Node { // trailing_slash_pos: parent.len(), local_signals: Default::default(), local_methods: Default::default(), + aliases: Default::default(), + aspects: Default::default(), destroyable, - - alias: OnceCell::new(), - aliases: Registry::new(), - - spatial: OnceCell::new(), - field: OnceCell::new(), - zone: OnceCell::new(), - pulse_sender: OnceCell::new(), - pulse_receiver: OnceCell::new(), - drawable: OnceCell::new(), - input_method: OnceCell::new(), - input_handler: OnceCell::new(), - item: OnceCell::new(), - item_acceptor: OnceCell::new(), - item_ui: OnceCell::new(), - sound: OnceCell::new(), }; ::add_node_members(&node); node @@ -186,13 +140,14 @@ impl Node { self.local_methods.lock().insert(name.to_string(), method); } - pub fn get_aspect(&self, node_name: &str, aspect_type: &str, aspect_fn: F) -> Result<&T> - where - F: FnOnce(&Node) -> &OnceCell, - { - aspect_fn(self) - .get() - .ok_or_else(|| eyre!("{} is not a {} node", node_name, aspect_type)) + pub fn add_aspect(&self, aspect: A) -> Arc { + self.aspects.add(aspect) + } + pub fn add_aspect_raw(&self, aspect: Arc) { + self.aspects.add_raw(aspect) + } + pub fn get_aspect(&self) -> Result> { + self.aspects.get() } pub fn send_local_signal( @@ -201,7 +156,7 @@ impl Node { method: &str, message: Message, ) -> Result<(), ScenegraphError> { - if let Some(alias) = self.alias.get() { + if let Ok(alias) = self.get_aspect::() { if !alias.info.server_signals.iter().any(|e| e == &method) { return Err(ScenegraphError::SignalNotFound); } @@ -229,7 +184,7 @@ impl Node { message: Message, response: MethodResponseSender, ) { - if let Some(alias) = self.alias.get() { + if let Ok(alias) = self.get_aspect::() { if !alias.info.server_methods.iter().any(|e| e == &method) { response.send(Err(ScenegraphError::MethodNotFound)); return; @@ -327,3 +282,36 @@ impl Drop for Node { // Debug breakpoint } } + +pub trait Aspect: Any + Send + Sync + 'static { + const NAME: &'static str; +} + +#[derive(Default)] +struct Aspects(Mutex>>); +impl Aspects { + fn add(&self, t: A) -> Arc { + let aspect = Arc::new(t); + self.add_raw(aspect.clone()); + aspect + } + fn add_raw(&self, aspect: Arc) { + self.0.lock().insert(Self::type_key::(), aspect); + } + fn get(&self) -> Result> { + self.0 + .lock() + .get(&Self::type_key::()) + .and_then(|a| Arc::downcast(a.clone()).ok()) + .ok_or(eyre!("Couldn't get aspect {}", A::NAME.to_lowercase())) + } + + fn type_key() -> TypeId { + TypeId::of::() + } +} +impl Drop for Aspects { + fn drop(&mut self) { + self.0.lock().clear() + } +} diff --git a/src/nodes/root.rs b/src/nodes/root.rs index 2421095..c2a9b5a 100644 --- a/src/nodes/root.rs +++ b/src/nodes/root.rs @@ -90,7 +90,7 @@ impl Root { } pub fn set_transform(&self, transform: Mat4) { - let spatial = self.node.spatial.get().unwrap(); + let spatial = self.node.get_aspect::().unwrap(); spatial.set_spatial_parent(None).unwrap(); spatial.set_local_transform(transform); } diff --git a/src/nodes/spatial/mod.rs b/src/nodes/spatial/mod.rs index c3c057c..f7c6205 100644 --- a/src/nodes/spatial/mod.rs +++ b/src/nodes/spatial/mod.rs @@ -1,12 +1,12 @@ pub mod zone; use self::zone::Zone; -use super::fields::get_field; -use super::Node; +use super::fields::Field; +use super::{Aspect, Node}; use crate::core::client::Client; use crate::core::registry::Registry; use crate::create_interface; -use color_eyre::eyre::{ensure, eyre, Result}; +use color_eyre::eyre::{eyre, Result}; use glam::{vec3a, Mat4, Quat}; use mint::Vector3; use nanoid::nanoid; @@ -42,7 +42,6 @@ static ZONEABLE_REGISTRY: Registry = Registry::new(); pub struct Spatial { uid: String, pub(super) node: Weak, - self_ref: Weak, parent: Mutex>>, old_parent: Mutex>>, pub(super) transform: Mutex, @@ -53,10 +52,9 @@ pub struct Spatial { impl Spatial { pub fn new(node: Weak, parent: Option>, transform: Mat4) -> Arc { - Arc::new_cyclic(|self_ref| Spatial { + Arc::new(Spatial { uid: nanoid!(), node, - self_ref: self_ref.clone(), parent: Mutex::new(parent), old_parent: Mutex::new(None), transform: Mutex::new(transform), @@ -70,11 +68,7 @@ impl Spatial { parent: Option>, transform: Mat4, zoneable: bool, - ) -> Result> { - ensure!( - node.spatial.get().is_none(), - "Internal: Node already has a Spatial aspect!" - ); + ) -> Arc { let spatial = Spatial::new(Arc::downgrade(node), parent.clone(), transform); ::add_node_members(node); if zoneable { @@ -83,8 +77,9 @@ impl Spatial { if let Some(parent) = parent { parent.children.add_raw(&spatial); } - let _ = node.spatial.set(spatial.clone()); - Ok(spatial) + ::add_node_members(node); + node.add_aspect_raw(spatial.clone()); + spatial } pub fn node(&self) -> Option> { @@ -190,23 +185,21 @@ impl Spatial { fn get_parent(&self) -> Option> { self.parent.lock().clone() } - fn set_parent(&self, new_parent: Option>) { + fn set_parent(self: &Arc, new_parent: Option<&Arc>) { if let Some(parent) = self.get_parent() { - parent.children.remove(self); + parent.children.remove(&self); } if let Some(new_parent) = &new_parent { - new_parent - .children - .add_raw(&self.self_ref.upgrade().unwrap()); + new_parent.children.add_raw(self); } - *self.parent.lock() = new_parent; + *self.parent.lock() = new_parent.cloned(); } - pub fn set_spatial_parent(&self, parent: Option>) -> Result<()> { + pub fn set_spatial_parent(self: &Arc, parent: Option<&Arc>) -> Result<()> { let is_ancestor = parent .as_ref() - .map(|parent| self.is_ancestor_of(parent.clone())) + .map(|parent| self.is_ancestor_of((*parent).clone())) .unwrap_or(false); if is_ancestor { return Err(eyre!("Setting spatial parent would cause a loop")); @@ -215,18 +208,21 @@ impl Spatial { Ok(()) } - pub fn set_spatial_parent_in_place(&self, parent: Option>) -> Result<()> { + pub fn set_spatial_parent_in_place( + self: &Arc, + parent: Option<&Arc>, + ) -> Result<()> { let is_ancestor = parent .as_ref() - .map(|parent| self.is_ancestor_of(parent.clone())) + .map(|parent| self.is_ancestor_of((*parent).clone())) .unwrap_or(false); if is_ancestor { return Err(eyre!("Setting spatial parent would cause a loop")); } self.set_local_transform(Spatial::space_to_space_matrix( - Some(self), - parent.as_deref(), + Some(&self), + parent.map(AsRef::as_ref), )); self.set_parent(parent); @@ -242,15 +238,15 @@ impl Spatial { .unwrap_or(f32::MAX) } } +impl Aspect for Spatial { + const NAME: &'static str = "Spatial"; +} impl SpatialAspect for Spatial { async fn get_local_bounding_box( node: Arc, _calling_client: Arc, ) -> Result { - let this_spatial = node - .spatial - .get() - .ok_or_else(|| eyre!("Node doesn't have a spatial?"))?; + let this_spatial = node.get_aspect::()?; let bounds = this_spatial.get_bounding_box(); Ok(BoundingBox { @@ -264,11 +260,8 @@ impl SpatialAspect for Spatial { _calling_client: Arc, relative_to: Arc, ) -> Result { - let this_spatial = node - .spatial - .get() - .ok_or_else(|| eyre!("Node doesn't have a spatial?"))?; - let relative_spatial = get_spatial(&relative_to, "Relative node")?; + let this_spatial = node.get_aspect::()?; + let relative_spatial = relative_to.get_aspect::()?; let center = Spatial::space_to_space_matrix(Some(&this_spatial), Some(&relative_spatial)) .transform_point3([0.0; 3].into()); let bounds = Bounds { @@ -295,11 +288,8 @@ impl SpatialAspect for Spatial { _calling_client: Arc, relative_to: Arc, ) -> Result { - let this_spatial = node - .spatial - .get() - .ok_or_else(|| eyre!("Node doesn't have a spatial?"))?; - let relative_spatial = get_spatial(&relative_to, "Relative node")?; + let this_spatial = node.get_aspect::()?; + let relative_spatial = relative_to.get_aspect::()?; let (scale, rotation, position) = Spatial::space_to_space_matrix( Some(this_spatial.as_ref()), @@ -319,10 +309,7 @@ impl SpatialAspect for Spatial { _calling_client: Arc, transform: Transform, ) -> Result<()> { - let this_spatial = node - .spatial - .get() - .ok_or_else(|| eyre!("Node doesn't have a spatial?"))?; + let this_spatial = node.get_aspect::()?; this_spatial.set_local_transform_components(None, transform); Ok(()) } @@ -332,11 +319,8 @@ impl SpatialAspect for Spatial { relative_to: Arc, transform: Transform, ) -> Result<()> { - let this_spatial = node - .spatial - .get() - .ok_or_else(|| eyre!("Node doesn't have a spatial?"))?; - let relative_spatial = get_spatial(&relative_to, "Relative node")?; + let this_spatial = node.get_aspect::()?; + let relative_spatial = relative_to.get_aspect::()?; this_spatial.set_local_transform_components(Some(&relative_spatial), transform); Ok(()) @@ -347,13 +331,10 @@ impl SpatialAspect for Spatial { _calling_client: Arc, parent: Arc, ) -> Result<()> { - let this_spatial = node - .spatial - .get() - .ok_or_else(|| eyre!("Node doesn't have a spatial?"))?; - let parent = get_spatial(&parent, "Parent")?; + let this_spatial = node.get_aspect::()?; + let parent = parent.get_aspect::()?; - this_spatial.set_spatial_parent(Some(parent))?; + this_spatial.set_spatial_parent(Some(&parent))?; Ok(()) } @@ -362,23 +343,20 @@ impl SpatialAspect for Spatial { _calling_client: Arc, parent: Arc, ) -> Result<()> { - let this_spatial = node - .spatial - .get() - .ok_or_else(|| eyre!("Node doesn't have a spatial?"))?; - let parent = get_spatial(&parent, "Parent")?; + let this_spatial = node.get_aspect::()?; + let parent = parent.get_aspect::()?; - this_spatial.set_spatial_parent_in_place(Some(parent))?; + this_spatial.set_spatial_parent_in_place(Some(&parent))?; Ok(()) } fn set_zoneable(node: Arc, _calling_client: Arc, zoneable: bool) -> Result<()> { - let spatial = node.spatial.get().unwrap(); + let spatial = node.get_aspect::()?; if zoneable { - ZONEABLE_REGISTRY.add_raw(spatial); + ZONEABLE_REGISTRY.add_raw(&spatial); } else { - ZONEABLE_REGISTRY.remove(spatial); - zone::release(spatial); + ZONEABLE_REGISTRY.remove(&spatial); + zone::release(&spatial); } Ok(()) } @@ -400,8 +378,8 @@ impl Debug for Spatial { } impl Drop for Spatial { fn drop(&mut self) { + zone::release_drop(self); ZONEABLE_REGISTRY.remove(self); - zone::release(self); } } @@ -422,24 +400,6 @@ pub fn parse_transform(transform: Transform, position: bool, rotation: bool, sca Mat4::from_scale_rotation_translation(scale.into(), rotation.into(), position.into()) } -pub fn find_spatial( - calling_client: &Arc, - node_name: &'static str, - node_path: &str, -) -> Result> { - calling_client - .get_node(node_name, node_path)? - .get_aspect(node_name, "spatial", |n| &n.spatial) - .cloned() -} -pub fn find_spatial_parent(calling_client: &Arc, node_path: &str) -> Result> { - find_spatial(calling_client, "Spatial parent", node_path) -} -pub fn get_spatial(node: &Arc, node_name: &str) -> Result> { - node.get_aspect(node_name, "spatial", |n| &n.spatial) - .cloned() -} - pub struct SpatialInterface; impl SpatialInterfaceAspect for SpatialInterface { fn create_spatial( @@ -450,11 +410,11 @@ impl SpatialInterfaceAspect for SpatialInterface { transform: Transform, zoneable: bool, ) -> Result<()> { - let parent = get_spatial(&parent, "Spatial parent")?; + let parent = parent.get_aspect::()?; let transform = parse_transform(transform, true, true, true); let node = Node::create_parent_name(&calling_client, "/spatial/spatial", &name, true) .add_to_scenegraph()?; - Spatial::add_to(&node, Some(parent), transform, zoneable)?; + Spatial::add_to(&node, Some(parent.clone()), transform, zoneable); Ok(()) } fn create_zone( @@ -465,13 +425,13 @@ impl SpatialInterfaceAspect for SpatialInterface { transform: Transform, field: Arc, ) -> Result<()> { - let parent = get_spatial(&parent, "Spatial parent")?; + let parent = parent.get_aspect::()?; let transform = parse_transform(transform, true, true, false); - let field = get_field(&field)?; + let field = field.get_aspect::()?; let node = Node::create_parent_name(&calling_client, "/spatial/zone", &name, true) .add_to_scenegraph()?; - let space = Spatial::add_to(&node, Some(parent), transform, false)?; + let space = Spatial::add_to(&node, Some(parent.clone()), transform, false); Zone::add_to(&node, space, &field); Ok(()) } diff --git a/src/nodes/spatial/zone.rs b/src/nodes/spatial/zone.rs index 422275c..c53fe46 100644 --- a/src/nodes/spatial/zone.rs +++ b/src/nodes/spatial/zone.rs @@ -1,10 +1,10 @@ -use super::{get_spatial, Spatial, ZoneAspect, ZONEABLE_REGISTRY}; +use super::{Spatial, ZoneAspect, ZONEABLE_REGISTRY}; use crate::{ core::{client::Client, registry::Registry}, nodes::{ alias::{Alias, AliasInfo}, fields::Field, - Node, + Aspect, Node, }, }; use glam::vec3a; @@ -30,8 +30,8 @@ pub fn capture(spatial: &Arc, zone: &Arc) { let _ = super::zone_client::capture(&node, &spatial.uid); } } -pub fn release(spatial: &Spatial) { - let _ = spatial.set_spatial_parent_in_place(spatial.old_parent.lock().take()); +pub fn release(spatial: &Arc) { + let _ = spatial.set_spatial_parent_in_place(spatial.old_parent.lock().take().as_ref()); let mut spatial_zone = spatial.zone.lock(); if let Some(spatial_zone) = spatial_zone.upgrade() { let Some(node) = spatial_zone.spatial.node.upgrade() else { @@ -42,6 +42,16 @@ pub fn release(spatial: &Spatial) { } *spatial_zone = Weak::new(); } +pub(super) fn release_drop(spatial: &Spatial) { + let spatial_zone = spatial.zone.lock(); + if let Some(spatial_zone) = spatial_zone.upgrade() { + let Some(node) = spatial_zone.spatial.node.upgrade() else { + return; + }; + spatial_zone.captured.remove(spatial); + let _ = super::zone_client::release(&node, &spatial.uid); + } +} pub struct Zone { spatial: Arc, @@ -58,13 +68,16 @@ impl Zone { captured: Registry::new(), }); ::add_node_members(node); - let _ = node.zone.set(zone.clone()); + node.add_aspect_raw(zone.clone()); zone } } +impl Aspect for Zone { + const NAME: &'static str = "Zone"; +} impl ZoneAspect for Zone { fn update(node: Arc, _calling_client: Arc) -> color_eyre::eyre::Result<()> { - let zone = node.zone.get().unwrap(); + let zone = node.get_aspect::()?; let Some(field) = zone.field.upgrade() else { return Err(color_eyre::eyre::eyre!("Zone's field has been destroyed")); }; @@ -137,9 +150,9 @@ impl ZoneAspect for Zone { _calling_client: Arc, spatial: Arc, ) -> color_eyre::eyre::Result<()> { - let zone = node.zone.get().unwrap(); - let spatial = get_spatial(&spatial, "Spatial")?; - capture(&spatial, zone); + let zone = node.get_aspect::()?; + let spatial = spatial.get_aspect()?; + capture(&spatial, &zone); Ok(()) } @@ -148,7 +161,7 @@ impl ZoneAspect for Zone { _calling_client: Arc, spatial: Arc, ) -> color_eyre::eyre::Result<()> { - let spatial = get_spatial(&spatial, "Spatial")?; + let spatial = spatial.get_aspect()?; release(&spatial); Ok(()) } diff --git a/src/objects/input/eye_pointer.rs b/src/objects/input/eye_pointer.rs index 0da135b..81d9cf9 100644 --- a/src/objects/input/eye_pointer.rs +++ b/src/objects/input/eye_pointer.rs @@ -30,7 +30,7 @@ impl EyePointer { pub fn new() -> Result { let node = Node::create_parent_name(&INTERNAL_CLIENT, "", &nanoid!(), false) .add_to_scenegraph()?; - let spatial = Spatial::add_to(&node, None, Mat4::IDENTITY, false).unwrap(); + let spatial = Spatial::add_to(&node, None, Mat4::IDENTITY, false); let pointer = InputMethod::add_to(&node, InputType::Pointer(Pointer::default()), None).unwrap(); diff --git a/src/objects/input/mouse_pointer.rs b/src/objects/input/mouse_pointer.rs index 7cdc912..a8b7ea1 100644 --- a/src/objects/input/mouse_pointer.rs +++ b/src/objects/input/mouse_pointer.rs @@ -4,7 +4,7 @@ use crate::{ data::{ mask_matches, pulse_receiver_client, PulseSender, KEYMAPS, PULSE_RECEIVER_REGISTRY, }, - fields::Ray, + fields::{Field, Ray}, input::{pointer::Pointer, InputMethod, InputType}, spatial::Spatial, Node, @@ -60,7 +60,7 @@ impl MousePointer { pub fn new() -> Result { let node = Node::create_parent_name(&INTERNAL_CLIENT, "", &nanoid!(), false) .add_to_scenegraph()?; - let spatial = Spatial::add_to(&node, None, Mat4::IDENTITY, false).unwrap(); + let spatial = Spatial::add_to(&node, None, Mat4::IDENTITY, false); let pointer = InputMethod::add_to(&node, InputType::Pointer(Pointer::default()), None).unwrap(); @@ -140,7 +140,7 @@ impl MousePointer { .into_iter() .filter(|rx| mask_matches(&rx.mask, &self.keyboard_sender.mask)) .map(|rx| { - let result = rx.field_node.field.get().unwrap().ray_march(Ray { + let result = rx.field_node.get_aspect::().unwrap().ray_march(Ray { origin: vec3(0.0, 0.0, 0.0), direction: vec3(0.0, 0.0, -1.0), space: self.spatial.clone(), diff --git a/src/objects/input/sk_controller.rs b/src/objects/input/sk_controller.rs index e6a691f..fb70b62 100644 --- a/src/objects/input/sk_controller.rs +++ b/src/objects/input/sk_controller.rs @@ -43,7 +43,7 @@ impl SkController { false, ) .add_to_scenegraph()?; - Spatial::add_to(&_node, None, Mat4::IDENTITY, false)?; + 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)?; diff --git a/src/objects/input/sk_hand.rs b/src/objects/input/sk_hand.rs index 4f3ddda..627704d 100644 --- a/src/objects/input/sk_hand.rs +++ b/src/objects/input/sk_hand.rs @@ -42,7 +42,7 @@ impl SkHand { pub fn new(handed: Handed) -> Result { let _node = Node::create_parent_name(&INTERNAL_CLIENT, "", &nanoid!(), false) .add_to_scenegraph()?; - Spatial::add_to(&_node, None, Mat4::IDENTITY, false)?; + Spatial::add_to(&_node, None, Mat4::IDENTITY, false); let hand = InputType::Hand(Box::new(Hand { base: FlatHand { right: handed == Handed::Right, diff --git a/src/objects/play_space.rs b/src/objects/play_space.rs index dfff8d5..ca7b521 100644 --- a/src/objects/play_space.rs +++ b/src/objects/play_space.rs @@ -42,8 +42,9 @@ impl PlaySpace { pub fn new() -> Result { let node = Node::create_parent_name(&INTERNAL_CLIENT, "", &nanoid!(), false) .add_to_scenegraph()?; - let spatial = Spatial::add_to(&node, None, Mat4::IDENTITY, false)?; - let field = BoxField::add_to(&node, [0.0; 3].into())?; + let spatial = Spatial::add_to(&node, None, Mat4::IDENTITY, false); + BoxField::add_to(&node, [0.0; 3].into()); + let field = node.get_aspect::()?.clone(); let pulse_rx = PulseReceiver::add_to( &node,