Store the parent of a scene in its InstanceInfo.

This commit is contained in:
andriyDev 2025-07-15 22:05:56 -07:00
parent 065043d687
commit 0c687143e3

View File

@ -39,6 +39,8 @@ pub struct SceneInstanceReady {
struct InstanceInfo { struct InstanceInfo {
/// Mapping of entities from the scene world to the instance world. /// Mapping of entities from the scene world to the instance world.
entity_map: EntityHashMap<Entity>, entity_map: EntityHashMap<Entity>,
/// The parent to attach this instance to.
parent: Option<Entity>,
} }
/// Unique id identifying a scene instance. /// Unique id identifying a scene instance.
@ -86,7 +88,6 @@ pub struct SceneSpawner {
scenes_to_despawn: Vec<AssetId<Scene>>, scenes_to_despawn: Vec<AssetId<Scene>>,
dynamic_scenes_to_despawn: Vec<AssetId<DynamicScene>>, dynamic_scenes_to_despawn: Vec<AssetId<DynamicScene>>,
instances_to_despawn: Vec<InstanceId>, instances_to_despawn: Vec<InstanceId>,
scenes_with_parent: Vec<(InstanceId, Entity)>,
instances_ready: Vec<(InstanceId, Option<Entity>)>, instances_ready: Vec<(InstanceId, Option<Entity>)>,
} }
@ -162,7 +163,6 @@ impl SceneSpawner {
let instance_id = InstanceId::new(); let instance_id = InstanceId::new();
self.dynamic_scenes_to_spawn self.dynamic_scenes_to_spawn
.push((id.into(), instance_id, Some(parent))); .push((id.into(), instance_id, Some(parent)));
self.scenes_with_parent.push((instance_id, parent));
instance_id instance_id
} }
@ -178,7 +178,6 @@ impl SceneSpawner {
let instance_id = InstanceId::new(); let instance_id = InstanceId::new();
self.scenes_to_spawn self.scenes_to_spawn
.push((id.into(), instance_id, Some(parent))); .push((id.into(), instance_id, Some(parent)));
self.scenes_with_parent.push((instance_id, parent));
instance_id instance_id
} }
@ -261,10 +260,18 @@ impl SceneSpawner {
let id = id.into(); let id = id.into();
Self::spawn_dynamic_internal(world, id, &mut entity_map)?; Self::spawn_dynamic_internal(world, id, &mut entity_map)?;
let instance_id = InstanceId::new(); let instance_id = InstanceId::new();
self.spawned_instances self.spawned_instances.insert(
.insert(instance_id, InstanceInfo { entity_map }); instance_id,
InstanceInfo {
entity_map,
parent: None,
},
);
let spawned = self.spawned_dynamic_scenes.entry(id).or_default(); let spawned = self.spawned_dynamic_scenes.entry(id).or_default();
spawned.insert(instance_id); spawned.insert(instance_id);
// We trigger `SceneInstanceReady` events after processing all scenes
// SceneSpawner may not be available in the observer.
self.instances_ready.push((instance_id, None));
Ok(instance_id) Ok(instance_id)
} }
@ -292,10 +299,18 @@ impl SceneSpawner {
let id = id.into(); let id = id.into();
Self::spawn_sync_internal(world, id, &mut entity_map)?; Self::spawn_sync_internal(world, id, &mut entity_map)?;
let instance_id = InstanceId::new(); let instance_id = InstanceId::new();
self.spawned_instances self.spawned_instances.insert(
.insert(instance_id, InstanceInfo { entity_map }); instance_id,
InstanceInfo {
entity_map,
parent: None,
},
);
let spawned = self.spawned_scenes.entry(id).or_default(); let spawned = self.spawned_scenes.entry(id).or_default();
spawned.insert(instance_id); spawned.insert(instance_id);
// We trigger `SceneInstanceReady` events after processing all scenes
// SceneSpawner may not be available in the observer.
self.instances_ready.push((instance_id, None));
Ok(instance_id) Ok(instance_id)
} }
@ -335,6 +350,7 @@ impl SceneSpawner {
// invalid state (e.g., invalid relationships). // invalid state (e.g., invalid relationships).
Self::despawn_instance_internal(world, instance_info); Self::despawn_instance_internal(world, instance_info);
Self::spawn_sync_internal(world, *id, &mut instance_info.entity_map)?; Self::spawn_sync_internal(world, *id, &mut instance_info.entity_map)?;
Self::set_scene_instance_parent_sync(world, instance_info);
} }
} }
} }
@ -360,6 +376,7 @@ impl SceneSpawner {
// invalid state (e.g., invalid relationships). // invalid state (e.g., invalid relationships).
Self::despawn_instance_internal(world, instance_info); Self::despawn_instance_internal(world, instance_info);
Self::spawn_dynamic_internal(world, *id, &mut instance_info.entity_map)?; Self::spawn_dynamic_internal(world, *id, &mut instance_info.entity_map)?;
Self::set_scene_instance_parent_sync(world, instance_info);
} }
} }
} }
@ -398,18 +415,15 @@ impl SceneSpawner {
match Self::spawn_dynamic_internal(world, handle.id(), &mut entity_map) { match Self::spawn_dynamic_internal(world, handle.id(), &mut entity_map) {
Ok(_) => { Ok(_) => {
self.spawned_instances let instance_info = InstanceInfo { entity_map, parent };
.insert(instance_id, InstanceInfo { entity_map }); Self::set_scene_instance_parent_sync(world, &instance_info);
self.spawned_instances.insert(instance_id, instance_info);
let spawned = self.spawned_dynamic_scenes.entry(handle.id()).or_default(); let spawned = self.spawned_dynamic_scenes.entry(handle.id()).or_default();
spawned.insert(instance_id); spawned.insert(instance_id);
// We trigger `SceneInstanceReady` events after processing all scenes
// Scenes with parents need more setup before they are ready. // SceneSpawner may not be available in the observer.
// See `set_scene_instance_parent_sync()`. self.instances_ready.push((instance_id, parent));
if parent.is_none() {
// We trigger `SceneInstanceReady` events after processing all scenes
// SceneSpawner may not be available in the observer.
self.instances_ready.push((instance_id, None));
}
} }
Err(SceneSpawnError::NonExistentScene { .. }) => { Err(SceneSpawnError::NonExistentScene { .. }) => {
self.dynamic_scenes_to_spawn self.dynamic_scenes_to_spawn
@ -426,18 +440,16 @@ impl SceneSpawner {
match Self::spawn_sync_internal(world, scene_handle.id(), &mut entity_map) { match Self::spawn_sync_internal(world, scene_handle.id(), &mut entity_map) {
Ok(_) => { Ok(_) => {
self.spawned_instances let instance_info = InstanceInfo { entity_map, parent };
.insert(instance_id, InstanceInfo { entity_map }); Self::set_scene_instance_parent_sync(world, &instance_info);
self.spawned_instances.insert(instance_id, instance_info);
let spawned = self.spawned_scenes.entry(scene_handle.id()).or_default(); let spawned = self.spawned_scenes.entry(scene_handle.id()).or_default();
spawned.insert(instance_id); spawned.insert(instance_id);
// Scenes with parents need more setup before they are ready. // We trigger `SceneInstanceReady` events after processing all scenes
// See `set_scene_instance_parent_sync()`. // SceneSpawner may not be available in the observer.
if parent.is_none() { self.instances_ready.push((instance_id, parent));
// We trigger `SceneInstanceReady` events after processing all scenes
// SceneSpawner may not be available in the observer.
self.instances_ready.push((instance_id, None));
}
} }
Err(SceneSpawnError::NonExistentRealScene { .. }) => { Err(SceneSpawnError::NonExistentRealScene { .. }) => {
self.scenes_to_spawn self.scenes_to_spawn
@ -450,32 +462,23 @@ impl SceneSpawner {
Ok(()) Ok(())
} }
pub(crate) fn set_scene_instance_parent_sync(&mut self, world: &mut World) { fn set_scene_instance_parent_sync(world: &mut World, instance: &InstanceInfo) {
let scenes_with_parent = core::mem::take(&mut self.scenes_with_parent); let Some(parent) = instance.parent else {
return;
for (instance_id, parent) in scenes_with_parent { };
if let Some(instance) = self.spawned_instances.get(&instance_id) { for &entity in instance.entity_map.values() {
for &entity in instance.entity_map.values() { // Add the `ChildOf` component to the scene root, and update the `Children` component of
// Add the `ChildOf` component to the scene root, and update the `Children` component of // the scene parent
// the scene parent if !world
if !world .get_entity(entity)
.get_entity(entity) .ok()
.ok() // This will filter only the scene root entity, as all other from the
// This will filter only the scene root entity, as all other from the // scene have a parent
// scene have a parent // Entities that wouldn't exist anymore are also skipped
// Entities that wouldn't exist anymore are also skipped // this case shouldn't happen anyway
// this case shouldn't happen anyway .is_none_or(|entity| entity.contains::<ChildOf>())
.is_none_or(|entity| entity.contains::<ChildOf>()) {
{ world.entity_mut(parent).add_child(entity);
world.entity_mut(parent).add_child(entity);
}
}
// We trigger `SceneInstanceReady` events after processing all scenes
// SceneSpawner may not be available in the observer.
self.instances_ready.push((instance_id, Some(parent)));
} else {
self.scenes_with_parent.push((instance_id, parent));
} }
} }
} }
@ -520,24 +523,17 @@ impl SceneSpawner {
pub fn scene_spawner_system(world: &mut World) { pub fn scene_spawner_system(world: &mut World) {
world.resource_scope(|world, mut scene_spawner: Mut<SceneSpawner>| { world.resource_scope(|world, mut scene_spawner: Mut<SceneSpawner>| {
// remove any loading instances where parent is deleted // remove any loading instances where parent is deleted
let mut dead_instances = <HashSet<_>>::default(); let is_parent_alive = |parent: &Option<Entity>| {
scene_spawner parent
.scenes_with_parent .map(|parent| world.get_entity(parent).is_ok())
.retain(|(instance, parent)| { .unwrap_or(true) // If we don't have a parent, then consider the parent alive.
let retain = world.get_entity(*parent).is_ok(); };
if !retain {
dead_instances.insert(*instance);
}
retain
});
scene_spawner scene_spawner
.dynamic_scenes_to_spawn .dynamic_scenes_to_spawn
.retain(|(_, instance, _)| !dead_instances.contains(instance)); .retain(|(_, _, parent)| is_parent_alive(parent));
scene_spawner scene_spawner
.scenes_to_spawn .scenes_to_spawn
.retain(|(_, instance, _)| !dead_instances.contains(instance)); .retain(|(_, _, parent)| is_parent_alive(parent));
let scene_asset_events = world.resource::<Events<AssetEvent<Scene>>>(); let scene_asset_events = world.resource::<Events<AssetEvent<Scene>>>();
let dynamic_scene_asset_events = world.resource::<Events<AssetEvent<DynamicScene>>>(); let dynamic_scene_asset_events = world.resource::<Events<AssetEvent<DynamicScene>>>();
@ -577,7 +573,6 @@ pub fn scene_spawner_system(world: &mut World) {
scene_spawner scene_spawner
.update_spawned_dynamic_scenes(world, &updated_spawned_dynamic_scenes) .update_spawned_dynamic_scenes(world, &updated_spawned_dynamic_scenes)
.unwrap(); .unwrap();
scene_spawner.set_scene_instance_parent_sync(world);
scene_spawner.trigger_scene_ready_events(world); scene_spawner.trigger_scene_ready_events(world);
}); });
} }