From c8206b14dd98ff07afb630ca56724fd84bebd749 Mon Sep 17 00:00:00 2001 From: Nova Date: Sun, 11 Jun 2023 00:38:05 -0400 Subject: [PATCH] feat: spatial bounds --- src/nodes/drawable/lines.rs | 12 ++++++- src/nodes/drawable/model.rs | 13 +++++++- src/nodes/hmd.rs | 2 +- src/nodes/items/mod.rs | 1 + src/nodes/spatial/mod.rs | 63 ++++++++++++++++++++++++++++++++++++- src/nodes/spatial/zone.rs | 2 +- 6 files changed, 88 insertions(+), 5 deletions(-) diff --git a/src/nodes/drawable/lines.rs b/src/nodes/drawable/lines.rs index 69d1b6c..83284d6 100644 --- a/src/nodes/drawable/lines.rs +++ b/src/nodes/drawable/lines.rs @@ -14,7 +14,7 @@ use prisma::{Flatten, Lerp, Rgba}; use serde::Deserialize; use stardust_xr::{schemas::flex::deserialize, values::Transform}; use std::{collections::VecDeque, sync::Arc}; -use stereokit::{Color128, LinePoint as SkLinePoint, StereoKitDraw}; +use stereokit::{bounds_grow_to_fit_pt, Bounds, Color128, LinePoint as SkLinePoint, StereoKitDraw}; use super::Drawable; @@ -45,6 +45,16 @@ impl Lines { "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 point in &lines.data.lock().points { + bounds = bounds_grow_to_fit_pt(bounds, point.point); + } + + bounds + }); + let lines = LINES_REGISTRY.add(Lines { enabled: node.enabled.clone(), space: node.get_aspect("Lines", "spatial", |n| &n.spatial)?.clone(), diff --git a/src/nodes/drawable/model.rs b/src/nodes/drawable/model.rs index b8d498c..bb5d798 100644 --- a/src/nodes/drawable/model.rs +++ b/src/nodes/drawable/model.rs @@ -22,7 +22,8 @@ use std::path::PathBuf; use std::sync::{Arc, Weak}; use stereokit::named_colors::WHITE; use stereokit::{ - Color128, Material, Model as SKModel, RenderLayer, Shader, StereoKitDraw, StereoKitMultiThread, + Bounds, Color128, Material, Model as SKModel, RenderLayer, Shader, StereoKitDraw, + StereoKitMultiThread, }; static MODEL_REGISTRY: Registry = Registry::new(); @@ -182,6 +183,16 @@ impl ModelPart { 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 model_part = Arc::new(ModelPart { id, path: part_path, diff --git a/src/nodes/hmd.rs b/src/nodes/hmd.rs index d7fd1a5..54a390a 100644 --- a/src/nodes/hmd.rs +++ b/src/nodes/hmd.rs @@ -41,7 +41,7 @@ pub fn make_alias(client: &Arc) -> Result> { "hmd", &HMD, AliasInfo { - server_signals: vec!["get_transform"], + server_signals: vec!["get_bounds", "get_transform"], ..Default::default() }, ) diff --git a/src/nodes/items/mod.rs b/src/nodes/items/mod.rs index 9564fc6..8b5a86e 100644 --- a/src/nodes/items/mod.rs +++ b/src/nodes/items/mod.rs @@ -25,6 +25,7 @@ use std::sync::{Arc, Weak}; lazy_static! { static ref ITEM_ALIAS_LOCAL_SIGNALS: Vec<&'static str> = vec![ + "get_bounds", "get_transform", "set_transform", "set_spatial_parent", diff --git a/src/nodes/spatial/mod.rs b/src/nodes/spatial/mod.rs index bbb67a9..4409464 100644 --- a/src/nodes/spatial/mod.rs +++ b/src/nodes/spatial/mod.rs @@ -14,7 +14,8 @@ use stardust_xr::schemas::flex::{deserialize, serialize}; use stardust_xr::values::Transform; use std::fmt::Debug; use std::ptr; -use std::sync::{Arc, Weak}; +use std::sync::{Arc, OnceLock, Weak}; +use stereokit::{bounds_grow_to_fit_box, Bounds}; use tracing::instrument; static ZONEABLE_REGISTRY: Registry = Registry::new(); @@ -28,6 +29,7 @@ pub struct Spatial { pub(super) transform: Mutex, zone: Mutex>, children: Registry, + pub(super) bounding_box_calc: OnceLock Bounds>, } impl Spatial { @@ -41,6 +43,7 @@ impl Spatial { transform: Mutex::new(transform), zone: Mutex::new(Weak::new()), children: Registry::new(), + bounding_box_calc: OnceLock::default(), }) } pub fn add_to( @@ -54,6 +57,7 @@ impl Spatial { "Internal: Node already has a Spatial aspect!" ); let spatial = Spatial::new(Arc::downgrade(node), parent, transform); + node.add_local_method("get_bounding_box", Spatial::get_bounding_box_flex); node.add_local_method("get_transform", Spatial::get_transform_flex); node.add_local_signal("set_transform", Spatial::set_transform_flex); node.add_local_signal("set_spatial_parent", Spatial::set_spatial_parent_flex); @@ -83,6 +87,25 @@ impl Spatial { world_to_space_matrix * space_to_world_matrix } + // the output bounds are probably way bigger than they need to be + #[instrument(level = "debug")] + pub fn get_bounding_box(&self) -> Bounds { + let Some(node) = self.node() else {return Bounds::default()}; + let mut bounds = self + .bounding_box_calc + .get() + .map(|b| (b)(&node)) + .unwrap_or_default(); + for child in self.children.get_valid_contents() { + bounds = bounds_grow_to_fit_box( + bounds, + child.get_bounding_box(), + Some(child.local_transform()), + ); + } + bounds + } + #[instrument(level = "debug", skip_all)] pub fn local_transform(&self) -> Mat4 { *self.transform.lock() @@ -206,6 +229,44 @@ impl Spatial { Ok(()) } + pub fn get_bounding_box_flex( + node: &Node, + calling_client: Arc, + data: &[u8], + ) -> Result> { + let this_spatial = node + .spatial + .get() + .ok_or_else(|| eyre!("Node doesn't have a spatial?"))?; + let relative_spatial_path: Option<&str> = deserialize(data)?; + let bounds = if let Some(relative_spatial_path) = relative_spatial_path { + let relative_spatial = find_reference_space(&calling_client, relative_spatial_path)?; + let center = + Spatial::space_to_space_matrix(Some(&this_spatial), Some(&relative_spatial)) + .transform_point3([0.0; 3].into()); + let bounds: Bounds = Bounds { + center, + dimensions: [0.0; 3].into(), + }; + bounds_grow_to_fit_box( + bounds, + this_spatial.get_bounding_box(), + Some(Spatial::space_to_space_matrix( + Some(&this_spatial), + Some(&relative_spatial), + )), + ) + } else { + this_spatial.get_bounding_box() + }; + + serialize(( + mint::Vector3::from(bounds.center), + mint::Vector3::from(bounds.dimensions), + )) + .map_err(|e| e.into()) + } + pub fn get_transform_flex( node: &Node, calling_client: Arc, diff --git a/src/nodes/spatial/zone.rs b/src/nodes/spatial/zone.rs index 2dfdabe..ea2400a 100644 --- a/src/nodes/spatial/zone.rs +++ b/src/nodes/spatial/zone.rs @@ -119,7 +119,7 @@ impl Zone { "set_spatial_parent", "set_spatial_parent_in_place", ], - server_methods: vec!["get_transform"], + server_methods: vec!["get_bounds", "get_transform"], ..Default::default() }, )