diff --git a/src/nodes/audio.rs b/src/nodes/audio.rs index 7c165ba..7373093 100644 --- a/src/nodes/audio.rs +++ b/src/nodes/audio.rs @@ -57,27 +57,24 @@ fn update_sound_event( for sound in SOUND_REGISTRY.get_valid_contents() { if sound.entity.get().is_none() { let handle = asset_server.load(sound.pending_audio_path.as_path()); - sound - .entity - .set( - cmds.spawn(( - Name::new("Audio Node"), - SpatialNode(Arc::downgrade(&sound.spatial)), - AudioPlayer::new(handle), - PlaybackSettings { - mode: PlaybackMode::Once, - volume: Volume::Linear(sound.volume), - speed: 1.0, - paused: true, - muted: false, - spatial: true, - spatial_scale: None, - }, - )) - .id() - .into(), - ) - .unwrap(); + let entity = cmds + .spawn(( + Name::new("Audio Node"), + SpatialNode(Arc::downgrade(&sound.spatial)), + AudioPlayer::new(handle), + PlaybackSettings { + mode: PlaybackMode::Once, + volume: Volume::Linear(sound.volume), + speed: 1.0, + paused: true, + muted: false, + spatial: true, + spatial_scale: None, + }, + )) + .id(); + sound.spatial.set_entity(entity); + sound.entity.set(entity.into()).unwrap(); } if let Some(sink) = sound.entity.get().and_then(|e| sinks.get(e.0).ok()) { if sound.play.lock().take().is_some() { diff --git a/src/nodes/drawable/lines.rs b/src/nodes/drawable/lines.rs index d587df9..e6a48a2 100644 --- a/src/nodes/drawable/lines.rs +++ b/src/nodes/drawable/lines.rs @@ -242,6 +242,7 @@ fn build_line_mesh( extension: LineExtension { unused: 0 }, })), )); + lines.spatial.set_entity(e.id()); _ = lines.entity.set(e.id().into()); e } diff --git a/src/nodes/drawable/model.rs b/src/nodes/drawable/model.rs index a83e593..6567b18 100644 --- a/src/nodes/drawable/model.rs +++ b/src/nodes/drawable/model.rs @@ -243,6 +243,7 @@ fn gen_model_parts( }); spatial.set_local_transform(transform.compute_matrix()); + spatial.set_entity(entity); cmds.entity(entity) .insert(SpatialNode(Arc::downgrade(&spatial))); let mesh_entity = children_query @@ -570,7 +571,6 @@ impl Model { ); } None => { - // TODO: this could be a denail of service vector let client = self.spatial.node().unwrap().get_client().unwrap(); let part_node = client.scenegraph.add_node(Node::generate(&client, false)); let spatial = Spatial::add_to( diff --git a/src/nodes/drawable/text.rs b/src/nodes/drawable/text.rs index 1fd6906..22568c1 100644 --- a/src/nodes/drawable/text.rs +++ b/src/nodes/drawable/text.rs @@ -168,6 +168,7 @@ fn spawn_text( .add_children(&letters) .id(); text.entity.lock().replace(EntityHandle(entity)); + text.spatial.set_entity(entity); } } diff --git a/src/nodes/spatial/mod.rs b/src/nodes/spatial/mod.rs index d23e6c9..4e0e7b8 100644 --- a/src/nodes/spatial/mod.rs +++ b/src/nodes/spatial/mod.rs @@ -6,6 +6,7 @@ use super::fields::{Field, FieldTrait}; use super::{Aspect, AspectIdentifier}; use crate::bail; use crate::core::client::Client; +use crate::core::entity_handle::EntityHandle; use crate::core::error::Result; use crate::core::registry::Registry; use crate::nodes::{Node, OWNED_ASPECT_ALIAS_INFO}; @@ -27,11 +28,52 @@ impl Plugin for SpatialNodePlugin { fn build(&self, app: &mut App) { app.add_systems( PostUpdate, - update_spatial_nodes.before(TransformSystem::TransformPropagate), + ( + spawn_spatial_nodes, + update_spatial_node_parenting, + update_spatial_nodes, + ) + .chain() + .before(TransformSystem::TransformPropagate), ); } } +fn spawn_spatial_nodes(mut cmds: Commands) { + for spatial in SPATIAL_REGISTRY + .get_valid_contents() + .into_iter() + .filter(|v| v.entity.lock().is_none()) + { + let entity = cmds + .spawn((SpatialNode(Arc::downgrade(&spatial)), Name::new("Spatial"))) + .id(); + _ = spatial.set_entity(entity); + } +} + +fn update_spatial_node_parenting( + query: Query<(Entity, Option<&ChildOf>, &SpatialNode)>, + mut cmds: Commands, +) { + for (entity, parent, spatial) in &query { + let Some(spatial) = spatial.0.upgrade() else { + continue; + }; + let parent_entity = spatial + .get_parent() + .and_then(|v| v.entity.lock().as_ref().map(|v| v.0)); + // no changes needed, early exit + if parent.map(|v| v.0) == parent_entity { + continue; + } + match parent_entity { + Some(e) => cmds.entity(entity).insert(ChildOf(e)), + None => cmds.entity(entity).remove::(), + }; + } +} + fn update_spatial_nodes( mut query: Query<( &mut BevyTransform, @@ -69,22 +111,25 @@ fn update_spatial_nodes( } _ => {} } - match child_of { - Some(child_of) => { - let Ok(parent) = parent_query.get(child_of.0) else { - warn!("SpatialNode bevy Parent doesn't have global transform"); - return; - }; - *transform = - BevyTransform::from_matrix(parent.compute_matrix().inverse() * mat4); - } - None => { - *transform = BevyTransform::from_matrix(mat4); - } - } + *transform = BevyTransform::from_matrix(spatial.local_transform()); + // match child_of { + // Some(child_of) => { + // let Ok(parent) = parent_query.get(child_of.0) else { + // warn!("SpatialNode bevy Parent doesn't have global transform"); + // return; + // }; + // *transform = + // BevyTransform::from_matrix(parent.compute_matrix().inverse() * mat4); + // } + // None => { + // *transform = BevyTransform::from_matrix(mat4); + // } + // } }); } +static SPATIAL_REGISTRY: Registry = Registry::new(); + #[derive(Clone, Component, Debug)] #[require(BevyTransform, Visibility)] pub struct SpatialNode(pub Weak); @@ -123,6 +168,7 @@ static ZONEABLE_REGISTRY: Registry = Registry::new(); pub struct Spatial { pub node: Weak, + entity: Mutex>, parent: Mutex>>, old_parent: Mutex>>, transform: Mutex, @@ -133,8 +179,9 @@ pub struct Spatial { impl Spatial { pub fn new(node: Weak, parent: Option>, transform: Mat4) -> Arc { - Arc::new(Spatial { + SPATIAL_REGISTRY.add(Spatial { node, + entity: Mutex::new(None), parent: Mutex::new(parent), old_parent: Mutex::new(None), transform: Mutex::new(transform), @@ -143,6 +190,9 @@ impl Spatial { bounding_box_calc: OnceLock::default(), }) } + pub fn set_entity(&self, entity: Entity) { + self.entity.lock().replace(entity.into()); + } pub fn add_to( node: &Arc, parent: Option>, @@ -396,6 +446,7 @@ impl Drop for Spatial { fn drop(&mut self) { zone::release(self); ZONEABLE_REGISTRY.remove(self); + SPATIAL_REGISTRY.remove(self); } }