diff --git a/src/nodes/data.rs b/src/nodes/data.rs index e5e9de5..6559507 100644 --- a/src/nodes/data.rs +++ b/src/nodes/data.rs @@ -253,7 +253,7 @@ pub fn create_pulse_sender_flex( let parent = find_spatial_parent(&calling_client, info.parent_path)?; let transform = parse_transform(info.transform, true, true, false)?; let node = node.add_to_scenegraph(); - Spatial::add_to(&node, Some(parent), transform)?; + Spatial::add_to(&node, Some(parent), transform, false)?; PulseSender::add_to(&node)?; Ok(()) } @@ -287,7 +287,7 @@ pub fn create_pulse_receiver_flex( let field = find_field(&calling_client, info.field_path)?; let node = node.add_to_scenegraph(); - Spatial::add_to(&node, Some(parent), transform)?; + Spatial::add_to(&node, Some(parent), transform, false)?; PulseReceiver::add_to(&node, field)?; Ok(()) } diff --git a/src/nodes/drawable/model.rs b/src/nodes/drawable/model.rs index 6f8e7b2..e03da50 100644 --- a/src/nodes/drawable/model.rs +++ b/src/nodes/drawable/model.rs @@ -178,7 +178,7 @@ pub fn create_flex(_node: &Node, calling_client: Arc, data: &[u8]) -> Re let parent = find_spatial_parent(&calling_client, info.parent_path)?; let transform = parse_transform(info.transform, true, true, true)?; let node = node.add_to_scenegraph(); - Spatial::add_to(&node, Some(parent), transform)?; + Spatial::add_to(&node, Some(parent), transform, false)?; Model::add_to(&node, info.resource)?; Ok(()) } diff --git a/src/nodes/drawable/text.rs b/src/nodes/drawable/text.rs index 462edac..6344912 100644 --- a/src/nodes/drawable/text.rs +++ b/src/nodes/drawable/text.rs @@ -195,7 +195,7 @@ pub fn create_flex(_node: &Node, calling_client: Arc, data: &[u8]) -> Re let color = Rgba::from_slice(&info.color); let node = node.add_to_scenegraph(); - Spatial::add_to(&node, Some(parent), transform)?; + Spatial::add_to(&node, Some(parent), transform, false)?; Text::add_to( &node, info.font_resource, diff --git a/src/nodes/fields/box.rs b/src/nodes/fields/box.rs index cae7a48..1455319 100644 --- a/src/nodes/fields/box.rs +++ b/src/nodes/fields/box.rs @@ -76,7 +76,7 @@ pub fn create_box_field_flex(_node: &Node, calling_client: Arc, data: &[ let parent = find_spatial_parent(&calling_client, info.parent_path)?; let transform = parse_transform(info.transform, true, true, false)?; let node = node.add_to_scenegraph(); - Spatial::add_to(&node, Some(parent), transform)?; + Spatial::add_to(&node, Some(parent), transform, false)?; BoxField::add_to(&node, info.size)?; Ok(()) } diff --git a/src/nodes/fields/cylinder.rs b/src/nodes/fields/cylinder.rs index d1befb2..d41d68c 100644 --- a/src/nodes/fields/cylinder.rs +++ b/src/nodes/fields/cylinder.rs @@ -83,7 +83,7 @@ pub fn create_cylinder_field_flex( let parent = find_spatial_parent(&calling_client, info.parent_path)?; let transform = parse_transform(info.transform, true, true, false)?; let node = node.add_to_scenegraph(); - Spatial::add_to(&node, Some(parent), transform)?; + Spatial::add_to(&node, Some(parent), transform, false)?; CylinderField::add_to(&node, dbg!(info.length), dbg!(info.radius))?; Ok(()) } diff --git a/src/nodes/fields/sphere.rs b/src/nodes/fields/sphere.rs index 815e28f..5e972db 100644 --- a/src/nodes/fields/sphere.rs +++ b/src/nodes/fields/sphere.rs @@ -84,7 +84,7 @@ pub fn create_sphere_field_flex( .into(), ); let node = node.add_to_scenegraph(); - Spatial::add_to(&node, Some(parent), transform)?; + Spatial::add_to(&node, Some(parent), transform, false)?; SphereField::add_to(&node, info.radius)?; Ok(()) } diff --git a/src/nodes/hmd.rs b/src/nodes/hmd.rs index b75468a..9efb6f0 100644 --- a/src/nodes/hmd.rs +++ b/src/nodes/hmd.rs @@ -13,7 +13,7 @@ lazy_static::lazy_static! { fn create() -> Arc { let node = Arc::new(Node::create(&INTERNAL_CLIENT, "", "hmd", false)); - Spatial::add_to(&node, None, Mat4::IDENTITY).unwrap(); + Spatial::add_to(&node, None, Mat4::IDENTITY, false).unwrap(); node } diff --git a/src/nodes/input/mod.rs b/src/nodes/input/mod.rs index 3b682c6..b9e6334 100644 --- a/src/nodes/input/mod.rs +++ b/src/nodes/input/mod.rs @@ -233,7 +233,7 @@ pub fn create_input_handler_flex( let field = find_field(&calling_client, info.field_path)?; let node = Node::create(&calling_client, "/input/handler", info.name, true).add_to_scenegraph(); - Spatial::add_to(&node, Some(parent), transform)?; + Spatial::add_to(&node, Some(parent), transform, false)?; InputHandler::add_to(&node, &field)?; Ok(()) } diff --git a/src/nodes/input/tip.rs b/src/nodes/input/tip.rs index ac5cf87..0bcefdf 100644 --- a/src/nodes/input/tip.rs +++ b/src/nodes/input/tip.rs @@ -57,7 +57,7 @@ pub fn create_tip_flex(_node: &Node, calling_client: Arc, data: &[u8]) - let transform = parse_transform(info.transform, true, true, false)?; let node = node.add_to_scenegraph(); - Spatial::add_to(&node, Some(parent), transform)?; + Spatial::add_to(&node, Some(parent), transform, false)?; InputMethod::add_to( &node, InputType::Tip(Tip { diff --git a/src/nodes/items/environment.rs b/src/nodes/items/environment.rs index 7cce788..839e8e2 100644 --- a/src/nodes/items/environment.rs +++ b/src/nodes/items/environment.rs @@ -59,7 +59,7 @@ pub(super) fn create_environment_item_flex( let space = find_spatial_parent(&calling_client, info.parent_path)?; let transform = parse_transform(info.transform, true, true, false)?; let node = node.add_to_scenegraph(); - Spatial::add_to(&node, None, transform * space.global_transform())?; + Spatial::add_to(&node, None, transform * space.global_transform(), false)?; EnvironmentItem::add_to(&node, info.item_data); node.item .get() diff --git a/src/nodes/items/mod.rs b/src/nodes/items/mod.rs index 393a5c0..ce82e68 100644 --- a/src/nodes/items/mod.rs +++ b/src/nodes/items/mod.rs @@ -359,7 +359,7 @@ pub(self) fn create_item_acceptor_flex( let field = find_field(&calling_client, info.field_path)?; let node = Node::create(&INTERNAL_CLIENT, &parent_name, info.name, true).add_to_scenegraph(); - Spatial::add_to(&node, Some(space), transform)?; + Spatial::add_to(&node, Some(space), transform, false)?; ItemAcceptor::add_to(&node, type_info, Arc::downgrade(&field)); node.item .get() diff --git a/src/nodes/mod.rs b/src/nodes/mod.rs index 38cd5fa..b6e6082 100644 --- a/src/nodes/mod.rs +++ b/src/nodes/mod.rs @@ -33,6 +33,7 @@ use self::drawable::text::Text; use self::fields::Field; use self::input::{InputHandler, InputMethod}; use self::items::{Item, ItemAcceptor, ItemUI}; +use self::spatial::zone::Zone; use self::spatial::Spatial; use self::startup::StartupSettings; @@ -52,6 +53,7 @@ pub struct Node { pub spatial: OnceCell>, pub field: OnceCell>, + pub zone: OnceCell>, // Data pub pulse_sender: OnceCell>, @@ -108,6 +110,7 @@ impl Node { spatial: OnceCell::new(), field: OnceCell::new(), + zone: OnceCell::new(), pulse_sender: OnceCell::new(), pulse_receiver: OnceCell::new(), model: OnceCell::new(), diff --git a/src/nodes/root.rs b/src/nodes/root.rs index 63c2328..31e91db 100644 --- a/src/nodes/root.rs +++ b/src/nodes/root.rs @@ -23,7 +23,7 @@ impl Root { node.add_local_signal("subscribeLogicStep", Root::subscribe_logic_step); node.add_local_signal("setBasePrefixes", Root::set_base_prefixes); let node = node.add_to_scenegraph(); - let _ = Spatial::add_to(&node, None, Mat4::IDENTITY); + let _ = Spatial::add_to(&node, None, Mat4::IDENTITY, false); ROOT_REGISTRY.add(Root { node, diff --git a/src/nodes/spatial.rs b/src/nodes/spatial/mod.rs similarity index 85% rename from src/nodes/spatial.rs rename to src/nodes/spatial/mod.rs index 9d274cf..8dbe8ab 100644 --- a/src/nodes/spatial.rs +++ b/src/nodes/spatial/mod.rs @@ -1,43 +1,59 @@ +pub mod zone; + +use self::zone::{create_zone_flex, Zone}; use super::Node; use crate::core::client::Client; +use crate::core::registry::Registry; use anyhow::{anyhow, ensure, Result}; -use glam::{Mat4, Quat}; +use glam::{vec3a, Mat4, Quat}; use mint::Vector3; +use nanoid::nanoid; use parking_lot::Mutex; - use serde::Deserialize; use stardust_xr::schemas::flex::{deserialize, serialize}; use stardust_xr::values::Transform; use std::ptr; use std::sync::{Arc, Weak}; +static ZONEABLE_REGISTRY: Registry = Registry::new(); + pub struct Spatial { + pub(self) uid: String, pub(super) node: Weak, parent: Mutex>>, + pub(self) old_parent: Mutex>>, pub(super) transform: Mutex, + pub(self) zone: Mutex>, } impl Spatial { pub fn new(node: Weak, parent: Option>, transform: Mat4) -> Arc { Arc::new(Spatial { + uid: nanoid!(), node, parent: Mutex::new(parent), + old_parent: Mutex::new(None), transform: Mutex::new(transform), + zone: Mutex::new(Weak::new()), }) } pub fn add_to( node: &Arc, parent: Option>, transform: Mat4, + zoneable: bool, ) -> Result> { ensure!( node.spatial.get().is_none(), "Internal: Node already has a Spatial aspect!" ); let spatial = Spatial { + uid: nanoid!(), node: Arc::downgrade(node), parent: Mutex::new(parent), + old_parent: Mutex::new(None), transform: Mutex::new(transform), + zone: Mutex::new(Weak::new()), }; node.add_local_method("getTransform", Spatial::get_transform_flex); node.add_local_signal("setTransform", Spatial::set_transform_flex); @@ -46,7 +62,11 @@ impl Spatial { "setSpatialParentInPlace", Spatial::set_spatial_parent_in_place_flex, ); + node.add_local_signal("setZoneable", Spatial::set_zoneable); let spatial_arc = Arc::new(spatial); + if zoneable { + ZONEABLE_REGISTRY.add_raw(&spatial_arc); + } let _ = node.spatial.set(spatial_arc.clone()); Ok(spatial_arc) } @@ -217,6 +237,32 @@ impl Spatial { .set_spatial_parent_in_place(Some(&parent))?; Ok(()) } + pub fn set_zoneable(node: &Node, _calling_client: Arc, data: &[u8]) -> Result<()> { + let zoneable: bool = deserialize(data)?; + let spatial = node.spatial.get().unwrap(); + if zoneable { + ZONEABLE_REGISTRY.add_raw(spatial); + } else { + ZONEABLE_REGISTRY.remove(spatial); + zone::release(spatial); + } + Ok(()) + } + + pub(self) fn zone_distance(&self) -> f32 { + self.zone + .lock() + .upgrade() + .and_then(|zone| zone.field.upgrade()) + .map(|field| field.distance(self, vec3a(0.0, 0.0, 0.0))) + .unwrap_or(f32::MAX) + } +} +impl Drop for Spatial { + fn drop(&mut self) { + ZONEABLE_REGISTRY.remove(self); + zone::release(self); + } } pub fn parse_transform( @@ -271,6 +317,7 @@ pub fn find_reference_space( pub fn create_interface(client: &Arc) { let node = Node::create(client, "", "spatial", false); node.add_local_signal("createSpatial", create_spatial_flex); + node.add_local_signal("createZone", create_zone_flex); node.add_to_scenegraph(); } @@ -280,12 +327,13 @@ pub fn create_spatial_flex(_node: &Node, calling_client: Arc, data: &[u8 name: &'a str, parent_path: &'a str, transform: Transform, + zoneable: bool, } let info: CreateSpatialInfo = deserialize(data)?; let node = Node::create(&calling_client, "/spatial/spatial", info.name, true); let parent = find_spatial_parent(&calling_client, info.parent_path)?; let transform = parse_transform(info.transform, true, true, true)?; let node = node.add_to_scenegraph(); - Spatial::add_to(&node, Some(parent), transform)?; + Spatial::add_to(&node, Some(parent), transform, info.zoneable)?; Ok(()) } diff --git a/src/nodes/spatial/zone.rs b/src/nodes/spatial/zone.rs new file mode 100644 index 0000000..2b14ea9 --- /dev/null +++ b/src/nodes/spatial/zone.rs @@ -0,0 +1,176 @@ +use super::{find_spatial, Spatial, ZONEABLE_REGISTRY}; +use crate::{ + core::{client::Client, registry::Registry}, + nodes::{ + alias::{Alias, AliasInfo}, + fields::{find_field, Field}, + spatial::{find_spatial_parent, parse_transform}, + Node, + }, +}; +use anyhow::Result; +use glam::vec3a; +use parking_lot::Mutex; +use rustc_hash::FxHashMap; +use serde::Deserialize; +use stardust_xr::{ + schemas::flex::{deserialize, serialize}, + values::Transform, +}; +use std::sync::{Arc, Weak}; + +pub fn capture(spatial: &Arc, zone: &Arc) { + let old_distance = spatial.zone_distance(); + let new_distance = zone + .field + .upgrade() + .map(|field| field.distance(spatial, vec3a(0.0, 0.0, 0.0))) + .unwrap_or(f32::MAX); + if new_distance.abs() < old_distance.abs() { + release(spatial); + *spatial.old_parent.lock() = spatial.parent.lock().clone(); + *spatial.zone.lock() = Arc::downgrade(zone); + zone.captured.add_raw(spatial); + let node = zone.spatial.node.upgrade().unwrap(); + let _ = node.send_remote_signal("capture", &serialize(&spatial.uid).unwrap()); + } +} +pub fn release(spatial: &Spatial) { + 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 node = spatial_zone.spatial.node.upgrade().unwrap(); + spatial_zone.captured.remove(spatial); + let _ = node.send_remote_signal("release", &serialize(&spatial.uid).unwrap()); + } + *spatial_zone = Weak::new(); +} + +pub struct Zone { + spatial: Arc, + pub field: Weak, + zoneables: Mutex>>, + captured: Registry, +} +impl Zone { + pub fn add_to(node: &Arc, spatial: Arc, field: &Arc) -> Arc { + let zone = Arc::new(Zone { + spatial, + field: Arc::downgrade(field), + zoneables: Mutex::new(FxHashMap::default()), + captured: Registry::new(), + }); + node.add_local_signal("capture", Zone::capture_flex); + node.add_local_signal("release", Zone::release_flex); + node.add_local_signal("update", Zone::update); + let _ = node.zone.set(zone.clone()); + zone + } + fn capture_flex(node: &Node, calling_client: Arc, data: &[u8]) -> Result<()> { + let zone = node.zone.get().unwrap(); + let capture_uid: &str = deserialize(data)?; + let capture_path = node.path.clone() + "/" + capture_uid; + let spatial = find_spatial(&calling_client, "Spatial", &capture_path)?; + capture(&spatial, zone); + Ok(()) + } + fn release_flex(node: &Node, calling_client: Arc, data: &[u8]) -> Result<()> { + let capture_uid: &str = deserialize(data)?; + let capture_path = node.path.clone() + "/" + capture_uid; + let spatial = find_spatial(&calling_client, "Spatial", &capture_path)?; + release(&spatial); + Ok(()) + } + fn update(node: &Node, _calling_client: Arc, _data: &[u8]) -> Result<()> { + let zone = node.zone.get().unwrap(); + if let Some(field) = zone.field.upgrade() { + if let Some((zone_client, zone_node)) = zone + .spatial + .node + .upgrade() + .and_then(|n| n.get_client().zip(Some(n))) + { + let mut old_zoneables = zone.zoneables.lock(); + for (_uid, zoneable) in old_zoneables.iter() { + zoneable.destroy(); + } + let captured = zone.captured.get_valid_contents(); + let zoneables = ZONEABLE_REGISTRY + .get_valid_contents() + .into_iter() + .filter(|zoneable| zoneable.node.upgrade().is_some()) + .filter(|zoneable| { + if captured + .iter() + .any(|captured| Arc::ptr_eq(captured, zoneable)) + { + return true; + } + let spatial_zone_distance = zoneable.zone_distance(); + let self_zone_distance = field.distance(zoneable, vec3a(0.0, 0.0, 0.0)); + self_zone_distance < 0.0 && spatial_zone_distance > self_zone_distance + }) + .map(|zoneable| { + let alias = Alias::new( + &zone_client, + zone_node.get_path(), + &zoneable.uid, + &zoneable.node.upgrade().unwrap(), + AliasInfo { + local_signals: vec![ + "setTransform", + "setSpatialParent", + "setSpatialParentInPlace", + ], + local_methods: vec!["getTransform"], + ..Default::default() + }, + ); + (zoneable.uid.clone(), alias) + }) + .collect::>>(); + + for entered_uid in zoneables.keys().filter(|k| !old_zoneables.contains_key(*k)) { + node.send_remote_signal("enter", &serialize(entered_uid)?)?; + } + for left_uid in old_zoneables.keys().filter(|k| !zoneables.contains_key(*k)) { + node.send_remote_signal("leave", &serialize(left_uid)?)?; + } + + *old_zoneables = zoneables; + + Ok(()) + } else { + Err(anyhow::anyhow!("No client on node?")) + } + } else { + Err(anyhow::anyhow!("Zone's field has been destroyed")) + } + } +} +impl Drop for Zone { + fn drop(&mut self) { + for captured in self.captured.get_valid_contents() { + release(&captured); + } + } +} + +pub fn create_zone_flex(_node: &Node, calling_client: Arc, data: &[u8]) -> Result<()> { + #[derive(Deserialize)] + struct CreateZoneInfo<'a> { + name: &'a str, + parent_path: &'a str, + transform: Transform, + field_path: &'a str, + } + let info: CreateZoneInfo = deserialize(data)?; + let parent = find_spatial_parent(&calling_client, info.parent_path)?; + let transform = parse_transform(info.transform, true, true, false)?; + let field = find_field(&calling_client, info.field_path)?; + + let node = Node::create(&calling_client, "/spatial/zone", info.name, true).add_to_scenegraph(); + let space = Spatial::add_to(&node, Some(parent), transform, false)?; + Zone::add_to(&node, space, &field); + Ok(()) +} diff --git a/src/wayland/panel_item.rs b/src/wayland/panel_item.rs index 980892f..c33d1ea 100644 --- a/src/wayland/panel_item.rs +++ b/src/wayland/panel_item.rs @@ -66,7 +66,7 @@ impl PanelItem { &nanoid!(), true, )); - Spatial::add_to(&node, None, Mat4::IDENTITY).unwrap(); + Spatial::add_to(&node, None, Mat4::IDENTITY, false).unwrap(); let specialization = ItemType::Panel(PanelItem { node: Arc::downgrade(&node),