From c325cb8595d3c4bf38a3dd28205eb2310a426fe4 Mon Sep 17 00:00:00 2001 From: Nova Date: Sat, 11 Jun 2022 15:34:45 -0400 Subject: [PATCH] feat: field --- src/nodes/core.rs | 3 + src/nodes/field.rs | 140 +++++++++++++++++++++++++++++++++++++++++++++ src/nodes/mod.rs | 1 + 3 files changed, 144 insertions(+) create mode 100644 src/nodes/field.rs diff --git a/src/nodes/core.rs b/src/nodes/core.rs index 660031b..113b9ee 100644 --- a/src/nodes/core.rs +++ b/src/nodes/core.rs @@ -1,3 +1,4 @@ +use super::field::Field; use crate::core::client::Client; use crate::nodes::spatial::Spatial; use anyhow::{anyhow, Result}; @@ -16,6 +17,7 @@ pub struct Node<'a> { destroyable: bool, pub spatial: Option>, + pub field: Option>>, } impl<'a> Node<'a> { @@ -44,6 +46,7 @@ impl<'a> Node<'a> { local_methods: HashMap::new(), destroyable, spatial: None, + field: None, }; node.add_local_signal("destroy", Node::destroy_flex); node diff --git a/src/nodes/field.rs b/src/nodes/field.rs new file mode 100644 index 0000000..5b697cd --- /dev/null +++ b/src/nodes/field.rs @@ -0,0 +1,140 @@ +use super::core::Node; +use super::spatial::Spatial; +use anyhow::{anyhow, ensure, Result}; +use glam::{vec2, vec3a, Vec3, Vec3A}; +use libstardustxr::flex_to_vec3; +use libstardustxr::fusion::flex::FlexBuffable; +use rccell::RcCell; +use std::boxed::Box; +use std::rc::Rc; + +pub trait Field { + fn local_distance(&self, p: Vec3A) -> f32; + fn local_normal(&self, p: Vec3A, r: f32) -> Vec3A { + let d = self.local_distance(p); + let e = vec2(r, 0_f32); + + let n = vec3a(d, d, d) + - vec3a( + self.local_distance(vec3a(e.x, e.y, e.y)), + self.local_distance(vec3a(e.y, e.x, e.y)), + self.local_distance(vec3a(e.y, e.y, e.x)), + ); + + n.normalize() + } + fn local_closest_point(&self, p: Vec3A, r: f32) -> Vec3A { + p - (self.local_normal(p, r) * self.local_distance(p)) + } + + fn distance(&self, local_space: &Spatial, reference_space: &Spatial, p: Vec3A) -> f32 { + let reference_to_local_space = + Spatial::space_to_space_matrix(Some(reference_space), Some(local_space)); + let local_p = reference_to_local_space.transform_point3a(p); + self.local_distance(local_p) + } + fn normal(&self, local_space: &Spatial, reference_space: &Spatial, p: Vec3A, r: f32) -> Vec3A { + let reference_to_local_space = + Spatial::space_to_space_matrix(Some(reference_space), Some(local_space)); + let local_p = reference_to_local_space.transform_point3a(p); + reference_to_local_space + .inverse() + .transform_vector3a(self.local_normal(local_p, r)) + } + fn closest_point( + &self, + local_space: &Spatial, + reference_space: &Spatial, + p: Vec3A, + r: f32, + ) -> Vec3A { + let reference_to_local_space = + Spatial::space_to_space_matrix(Some(reference_space), Some(local_space)); + let local_p = reference_to_local_space.transform_point3a(p); + reference_to_local_space + .inverse() + .transform_point3a(self.local_closest_point(local_p, r)) + } + + fn add_field_methods(&self, node: &RcCell) { + node.borrow_mut() + .add_local_method("distance", |node, calling_client, data| { + let root = flexbuffers::Reader::get_root(data)?; + let flex_vec = root.get_vector()?; + let reference_space_path = flex_vec.idx(0).as_str(); + let reference_space = calling_client + .get_scenegraph() + .get_node(reference_space_path) + .ok_or_else(|| anyhow!("Reference space node does not exist"))? + .borrow() + .spatial + .as_ref() + .ok_or_else(|| anyhow!("Reference space node does not have a spatial"))? + .clone(); + let point = + flex_to_vec3!(flex_vec.idx(1)).ok_or_else(|| anyhow!("Point is invalid"))?; + + let field = node + .field + .as_ref() + .ok_or_else(|| anyhow!("Node does not have a field!"))?; + let spatial = field.spatial_ref(); + let distance = field.distance(spatial, reference_space.as_ref(), point.into()); + Ok(FlexBuffable::from(distance).build_singleton()) + }); + node.borrow_mut() + .add_local_method("normal", |node, calling_client, data| { + let root = flexbuffers::Reader::get_root(data)?; + let flex_vec = root.get_vector()?; + let reference_space_path = flex_vec.idx(0).as_str(); + let reference_space = calling_client + .get_scenegraph() + .get_node(reference_space_path) + .ok_or_else(|| anyhow!("Reference space node does not exist"))? + .borrow() + .spatial + .as_ref() + .ok_or_else(|| anyhow!("Reference space node does not have a spatial"))? + .clone(); + let point = + flex_to_vec3!(flex_vec.idx(1)).ok_or_else(|| anyhow!("Point is invalid"))?; + + let field = node + .field + .as_ref() + .ok_or_else(|| anyhow!("Node does not have a field!"))?; + let spatial = field.spatial_ref(); + let normal = + field.normal(spatial, reference_space.as_ref(), point.into(), 0.001_f32); + Ok(FlexBuffable::from(mint::Vector3::from(normal)).build_singleton()) + }); + node.borrow_mut() + .add_local_method("closest_point", |node, calling_client, data| { + let root = flexbuffers::Reader::get_root(data)?; + let flex_vec = root.get_vector()?; + let reference_space_path = flex_vec.idx(0).as_str(); + let reference_space = calling_client + .get_scenegraph() + .get_node(reference_space_path) + .ok_or_else(|| anyhow!("Reference space node does not exist"))? + .borrow() + .spatial + .as_ref() + .ok_or_else(|| anyhow!("Reference space node does not have a spatial"))? + .clone(); + let point = + flex_to_vec3!(flex_vec.idx(1)).ok_or_else(|| anyhow!("Point is invalid"))?; + + let field = node + .field + .as_ref() + .ok_or_else(|| anyhow!("Node does not have a field!"))?; + let spatial = field.spatial_ref(); + let closest_point = + field.closest_point(spatial, reference_space.as_ref(), point.into(), 0.001_f32); + Ok(FlexBuffable::from(mint::Vector3::from(closest_point)).build_singleton()) + }); + } + + fn spatial_ref(&self) -> &Spatial; +} diff --git a/src/nodes/mod.rs b/src/nodes/mod.rs index e2d5055..dd6a454 100644 --- a/src/nodes/mod.rs +++ b/src/nodes/mod.rs @@ -1,2 +1,3 @@ pub mod core; +pub mod field; pub mod spatial;