refactor(spatial): general improvements and efficiency

This commit is contained in:
Nova
2025-09-21 04:17:45 -07:00
parent b542dc1b23
commit 25b0760913
3 changed files with 53 additions and 68 deletions

View File

@@ -126,12 +126,7 @@ impl Node {
pub fn enabled(&self) -> bool { pub fn enabled(&self) -> bool {
self.enabled.load(Ordering::Relaxed) self.enabled.load(Ordering::Relaxed)
&& if let Ok(spatial) = self.get_aspect::<Spatial>() { && if let Ok(spatial) = self.get_aspect::<Spatial>() {
spatial spatial.visible()
.global_transform()
.to_scale_rotation_translation()
.0
.length_squared()
> 0.0
} else { } else {
true true
} }

View File

@@ -16,10 +16,9 @@ use bevy::render::primitives::Aabb;
use color_eyre::eyre::OptionExt; use color_eyre::eyre::OptionExt;
use glam::{Mat4, Quat, Vec3, vec3a}; use glam::{Mat4, Quat, Vec3, vec3a};
use mint::Vector3; use mint::Vector3;
use parking_lot::Mutex; use parking_lot::{Mutex, RwLock};
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use std::fmt::Debug; use std::fmt::Debug;
use std::sync::atomic::Ordering;
use std::sync::{Arc, OnceLock, Weak}; use std::sync::{Arc, OnceLock, Weak};
use std::{f32, ptr}; use std::{f32, ptr};
@@ -44,7 +43,7 @@ fn spawn_spatial_nodes(mut cmds: Commands) {
for spatial in SPATIAL_REGISTRY for spatial in SPATIAL_REGISTRY
.get_valid_contents() .get_valid_contents()
.into_iter() .into_iter()
.filter(|v| v.entity.lock().is_none()) .filter(|v| v.entity.read().is_none())
{ {
let entity = cmds let entity = cmds
.spawn((SpatialNode(Arc::downgrade(&spatial)), Name::new("Spatial"))) .spawn((SpatialNode(Arc::downgrade(&spatial)), Name::new("Spatial")))
@@ -64,7 +63,7 @@ fn update_spatial_node_parenting(
let Some(parent_entity) = spatial let Some(parent_entity) = spatial
.get_parent() .get_parent()
.map(|v| v.entity.lock().as_ref().map(|v| v.0)) .map(|v| v.entity.read().as_ref().map(|v| v.0))
else { else {
continue; continue;
}; };
@@ -95,26 +94,11 @@ fn update_spatial_nodes(mut query: Query<(&mut BevyTransform, &SpatialNode, &mut
let Some(spatial) = spatial_node.0.upgrade() else { let Some(spatial) = spatial_node.0.upgrade() else {
return; return;
}; };
if spatial // Set visibility based on node enabled state
.node() if spatial.node().is_some_and(|v| v.enabled()) {
.is_some_and(|v| !v.enabled.load(Ordering::Relaxed)) *vis = Visibility::Inherited;
{ } else {
if !matches!(*vis, Visibility::Hidden) { *vis = Visibility::Hidden;
*vis = Visibility::Hidden;
}
return;
}
let mat4 =
debug_span!("getting global transform").in_scope(|| spatial.global_transform());
let (scale, _, _) = mat4.to_scale_rotation_translation();
match (*vis, scale == Vec3::ZERO) {
(Visibility::Inherited | Visibility::Visible, true) => {
*vis = Visibility::Hidden;
}
(Visibility::Hidden, false) => {
*vis = Visibility::Inherited;
}
_ => {}
} }
*transform = BevyTransform::from_matrix(spatial.local_transform()); *transform = BevyTransform::from_matrix(spatial.local_transform());
}); });
@@ -160,12 +144,11 @@ static ZONEABLE_REGISTRY: Registry<Spatial> = Registry::new();
pub struct Spatial { pub struct Spatial {
pub node: Weak<Node>, pub node: Weak<Node>,
entity: Mutex<Option<EntityHandle>>, entity: RwLock<Option<EntityHandle>>,
parent: Mutex<Option<Arc<Spatial>>>, parent: RwLock<Option<Arc<Spatial>>>,
old_parent: Mutex<Option<Arc<Spatial>>>, old_parent: RwLock<Option<Arc<Spatial>>>,
transform: Mutex<Mat4>, transform: RwLock<Mat4>,
cached_global_transform: Mutex<Option<Mat4>>, zone: RwLock<Weak<Zone>>,
zone: Mutex<Weak<Zone>>,
children: Registry<Spatial>, children: Registry<Spatial>,
pub bounding_box_calc: OnceLock<fn(&Node) -> Aabb>, pub bounding_box_calc: OnceLock<fn(&Node) -> Aabb>,
} }
@@ -174,18 +157,17 @@ impl Spatial {
pub fn new(node: Weak<Node>, parent: Option<Arc<Spatial>>, transform: Mat4) -> Arc<Self> { pub fn new(node: Weak<Node>, parent: Option<Arc<Spatial>>, transform: Mat4) -> Arc<Self> {
SPATIAL_REGISTRY.add(Spatial { SPATIAL_REGISTRY.add(Spatial {
node, node,
entity: Mutex::new(None), entity: RwLock::new(None),
parent: Mutex::new(parent), parent: RwLock::new(parent),
old_parent: Mutex::new(None), old_parent: RwLock::new(None),
transform: Mutex::new(transform), transform: RwLock::new(transform),
cached_global_transform: Mutex::new(None), zone: RwLock::new(Weak::new()),
zone: Mutex::new(Weak::new()),
children: Registry::new(), children: Registry::new(),
bounding_box_calc: OnceLock::default(), bounding_box_calc: OnceLock::default(),
}) })
} }
pub fn set_entity(&self, entity: Entity) { pub fn set_entity(&self, entity: Entity) {
self.entity.lock().replace(entity.into()); self.entity.write().replace(entity.into());
} }
pub fn add_to( pub fn add_to(
node: &Arc<Node>, node: &Arc<Node>,
@@ -239,28 +221,37 @@ impl Spatial {
bounds bounds
} }
fn clear_cached_global_transform(&self) {
self.cached_global_transform.lock().take();
for child in self.children.get_valid_contents() {
child.clear_cached_global_transform();
}
}
pub fn local_transform(&self) -> Mat4 { pub fn local_transform(&self) -> Mat4 {
*self.transform.lock() *self.transform.read()
}
/// Check if this node or any ancestor has zero scale (for visibility culling)
pub fn visible(&self) -> bool {
// Check parent chain
if let Some(parent) = self.get_parent()
&& !parent.visible()
{
return false;
}
// Check our own scale by looking at matrix column lengths
let mat = self.local_transform();
let x_scale = mat.x_axis.length_squared();
let y_scale = mat.y_axis.length_squared();
let z_scale = mat.z_axis.length_squared();
!(x_scale == 0.0 || y_scale == 0.0 || z_scale == 0.0)
} }
pub fn global_transform(&self) -> Mat4 { pub fn global_transform(&self) -> Mat4 {
*self.cached_global_transform.lock().get_or_insert_with(|| { let parent_transform = self
let parent_transform = self .get_parent()
.get_parent() .as_deref()
.as_deref() .map(Self::global_transform)
.map(Self::global_transform) .unwrap_or_default();
.unwrap_or_default(); parent_transform * self.local_transform()
parent_transform * self.local_transform()
})
} }
pub fn set_local_transform(&self, transform: Mat4) { pub fn set_local_transform(&self, transform: Mat4) {
self.clear_cached_global_transform(); *self.transform.write() = transform;
*self.transform.lock() = transform;
} }
pub fn set_local_transform_components( pub fn set_local_transform_components(
&self, &self,
@@ -321,16 +312,15 @@ impl Spatial {
} }
fn get_parent(&self) -> Option<Arc<Spatial>> { fn get_parent(&self) -> Option<Arc<Spatial>> {
self.parent.lock().clone() self.parent.read().clone()
} }
fn set_parent(self: &Arc<Self>, new_parent: &Arc<Spatial>) { fn set_parent(self: &Arc<Self>, new_parent: &Arc<Spatial>) {
if let Some(parent) = self.get_parent() { if let Some(parent) = self.get_parent() {
parent.children.remove(self); parent.children.remove(self);
} }
new_parent.children.add_raw(self); new_parent.children.add_raw(self);
self.clear_cached_global_transform();
*self.parent.lock() = Some(new_parent.clone()); *self.parent.write() = Some(new_parent.clone());
} }
pub fn set_spatial_parent(self: &Arc<Self>, parent: &Arc<Spatial>) -> Result<()> { pub fn set_spatial_parent(self: &Arc<Self>, parent: &Arc<Spatial>) -> Result<()> {
@@ -354,7 +344,7 @@ impl Spatial {
pub(self) fn zone_distance(&self) -> f32 { pub(self) fn zone_distance(&self) -> f32 {
self.zone self.zone
.lock() .read()
.upgrade() .upgrade()
.map(|zone| zone.field.clone()) .map(|zone| zone.field.clone())
.map(|field| field.distance(self, vec3a(0.0, 0.0, 0.0))) .map(|field| field.distance(self, vec3a(0.0, 0.0, 0.0)))

View File

@@ -21,8 +21,8 @@ pub fn capture(spatial: &Arc<Spatial>, zone: &Arc<Zone>) {
} }
release(spatial); release(spatial);
*spatial.old_parent.lock() = spatial.get_parent(); *spatial.old_parent.write() = spatial.get_parent();
*spatial.zone.lock() = Arc::downgrade(zone); *spatial.zone.write() = Arc::downgrade(zone);
let Some(zone_node) = zone.spatial.node.upgrade() else { let Some(zone_node) = zone.spatial.node.upgrade() else {
return; return;
}; };
@@ -45,11 +45,11 @@ pub fn release(spatial: &Spatial) {
}; };
let spatial = spatial_node.get_aspect::<Spatial>().unwrap(); let spatial = spatial_node.get_aspect::<Spatial>().unwrap();
let Some(old_parent) = spatial.old_parent.lock().take() else { let Some(old_parent) = spatial.old_parent.read().clone() else {
return; return;
}; };
let _ = spatial.set_spatial_parent_in_place(&old_parent); let _ = spatial.set_spatial_parent_in_place(&old_parent);
let mut spatial_zone = spatial.zone.lock(); let mut spatial_zone = spatial.zone.write();
if let Some(spatial_zone) = spatial_zone.upgrade() { if let Some(spatial_zone) = spatial_zone.upgrade() {
spatial_zone.captured.remove_aspect(spatial.as_ref()); spatial_zone.captured.remove_aspect(spatial.as_ref());