feat: spatial bounds

This commit is contained in:
Nova
2023-06-11 00:38:05 -04:00
parent 0e61d51072
commit b12b171b53
6 changed files with 88 additions and 5 deletions

View File

@@ -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(),

View File

@@ -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<Model> = 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,

View File

@@ -41,7 +41,7 @@ pub fn make_alias(client: &Arc<Client>) -> Result<Arc<Node>> {
"hmd",
&HMD,
AliasInfo {
server_signals: vec!["get_transform"],
server_signals: vec!["get_bounds", "get_transform"],
..Default::default()
},
)

View File

@@ -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",

View File

@@ -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<Spatial> = Registry::new();
@@ -28,6 +29,7 @@ pub struct Spatial {
pub(super) transform: Mutex<Mat4>,
zone: Mutex<Weak<Zone>>,
children: Registry<Spatial>,
pub(super) bounding_box_calc: OnceLock<fn(&Node) -> 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<Client>,
data: &[u8],
) -> Result<Vec<u8>> {
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<Client>,

View File

@@ -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()
},
)