From a4eea6c3b86a1489acb9e1e4ccb1eff4eaf8e682 Mon Sep 17 00:00:00 2001 From: Nicola Papale Date: Thu, 21 Apr 2022 13:38:08 +0200 Subject: [PATCH] Add hot reloading for `Scene`s * Merge code in `SceneSpawner` for managing `DynamicScene` and `Scene`. * Add back hot reloading for `Scene`s, fixes #3759 * Add ability to despawn `Scene`s * Add ability to spawn `DynamicScene` as child of existing entities * Add documentation to `SceneSpawner` methods. * Add `write_to_world` method to `Scene` (does the same as `DynamicScene::write_to_world`) This fixes #3759 see this commit's merging PR for further details about this change. Merge PR: https://github.com/bevyengine/bevy/pull/4552 --- crates/bevy_scene/src/scene.rs | 54 +++- crates/bevy_scene/src/scene_spawner.rs | 395 +++++++++++++------------ examples/3d/update_gltf_scene.rs | 4 +- examples/animation/animated_fox.rs | 2 +- examples/asset/hot_asset_reloading.rs | 2 +- examples/scene/scene.rs | 13 +- 6 files changed, 276 insertions(+), 194 deletions(-) diff --git a/crates/bevy_scene/src/scene.rs b/crates/bevy_scene/src/scene.rs index 0a8bd50b82492e..e60d572a6179e1 100644 --- a/crates/bevy_scene/src/scene.rs +++ b/crates/bevy_scene/src/scene.rs @@ -1,4 +1,11 @@ -use bevy_ecs::world::World; +use crate::SceneSpawnError; +use anyhow::Result; +use bevy_ecs::{ + entity::EntityMap, + reflect::{ReflectComponent, ReflectMapEntities}, + world::World, +}; +use bevy_reflect::TypeRegistryArc; use bevy_reflect::TypeUuid; #[derive(Debug, TypeUuid)] @@ -11,4 +18,49 @@ impl Scene { pub fn new(world: World) -> Self { Self { world } } + /// Write the scene's world into the provided world, given a entity mapping. + pub fn write_to_world( + &self, + world: &mut World, + entity_map: &mut EntityMap, + ) -> Result<(), SceneSpawnError> { + let type_registry = world.resource::().clone(); + let type_registry = type_registry.read(); + for archetype in self.world.archetypes().iter() { + for scene_entity in archetype.entities() { + let entity = entity_map + .entry(*scene_entity) + .or_insert_with(|| world.spawn().id()); + for component_id in archetype.components() { + let component_info = self + .world + .components() + .get_info(component_id) + .expect("component_ids in archetypes should have ComponentInfo"); + + let reflect_component = type_registry + .get(component_info.type_id().unwrap()) + .ok_or_else(|| SceneSpawnError::UnregisteredType { + type_name: component_info.name().to_string(), + }) + .and_then(|registration| { + registration.data::().ok_or_else(|| { + SceneSpawnError::UnregisteredComponent { + type_name: component_info.name().to_string(), + } + }) + })?; + reflect_component.copy_component(&self.world, world, *scene_entity, *entity); + } + } + } + for registration in type_registry.iter() { + if let Some(map_entities_reflect) = registration.data::() { + map_entities_reflect + .map_entities(world, entity_map) + .unwrap(); + } + } + Ok(()) + } } diff --git a/crates/bevy_scene/src/scene_spawner.rs b/crates/bevy_scene/src/scene_spawner.rs index 142d299743efd6..059a710e97b19e 100644 --- a/crates/bevy_scene/src/scene_spawner.rs +++ b/crates/bevy_scene/src/scene_spawner.rs @@ -2,13 +2,11 @@ use crate::{DynamicScene, Scene}; use bevy_asset::{AssetEvent, Assets, Handle}; use bevy_ecs::{ entity::{Entity, EntityMap}, - event::{Events, ManualEventReader}, - reflect::{ReflectComponent, ReflectMapEntities}, + event::ManualEventReader, system::Command, - world::{Mut, World}, + world::{EntityRef, Mut, World}, }; use bevy_hierarchy::{AddChild, Parent}; -use bevy_reflect::TypeRegistryArc; use bevy_utils::{tracing::error, HashMap}; use thiserror::Error; use uuid::Uuid; @@ -27,18 +25,93 @@ impl InstanceId { } } +/// Either a `Handle`, a `Handle` or a reference to those. +/// +/// Those are used to enable a more ergonomic API in [`SceneSpawner`] public methods. +#[derive(Debug)] +pub struct SceneHandle(InnerSceneHandle); +impl From for SceneHandle { + fn from(what: InnerSceneHandle) -> Self { + Self(what) + } +} +impl From> for SceneHandle { + fn from(what: Handle) -> Self { + Self(InnerSceneHandle::World(what)) + } +} +impl From> for SceneHandle { + fn from(what: Handle) -> Self { + Self(InnerSceneHandle::Reflected(what)) + } +} +impl<'a> From<&'a Handle> for SceneHandle { + fn from(what: &'a Handle) -> Self { + Self(InnerSceneHandle::World(what.clone())) + } +} +impl<'a> From<&'a Handle> for SceneHandle { + fn from(what: &'a Handle) -> Self { + Self(InnerSceneHandle::Reflected(what.clone())) + } +} + +#[derive(Hash, Debug, Clone, PartialEq, Eq)] +enum InnerSceneHandle { + World(Handle), + Reflected(Handle), +} +impl InnerSceneHandle { + fn write_to_world( + &self, + world: &mut World, + entity_map: &mut EntityMap, + ) -> Result<(), SceneSpawnError> { + let err = || self.non_existing(); + match self { + Self::Reflected(scene) => { + world.resource_scope(|world, scenes: Mut>| { + let scene = scenes.get(scene).ok_or_else(err)?; + scene.write_to_world(world, entity_map) + })? + } + Self::World(scene) => world.resource_scope(|world, scenes: Mut>| { + let scene = scenes.get(scene).ok_or_else(err)?; + scene.write_to_world(world, entity_map) + })?, + }; + Ok(()) + } + + fn non_existing(&self) -> SceneSpawnError { + let weak_clone = match self { + Self::Reflected(scene) => Self::Reflected(scene.clone_weak()), + Self::World(scene) => Self::World(scene.clone_weak()), + }; + SceneSpawnError::NonExistentScene { + handle: SceneHandle(weak_clone), + } + } +} + #[derive(Default)] pub struct SceneSpawner { - spawned_scenes: HashMap, Vec>, - spawned_dynamic_scenes: HashMap, Vec>, - spawned_instances: HashMap, - scene_asset_event_reader: ManualEventReader>, - dynamic_scenes_to_spawn: Vec>, - scenes_to_spawn: Vec<(Handle, InstanceId)>, - scenes_to_despawn: Vec>, + instances: HashMap>, + instances_info: HashMap, + readers: SceneEventReaders, + scenes_to_spawn: Vec<(InnerSceneHandle, InstanceId)>, + scenes_to_despawn: Vec, scenes_with_parent: Vec<(InstanceId, Entity)>, } +/// Helper struct to wrap `ManualEventReader` for the various scene handle +/// types. +#[derive(Default)] +struct SceneEventReaders { + dynamic: ManualEventReader>, + real: ManualEventReader>, +} + #[derive(Error, Debug)] pub enum SceneSpawnError { #[error("scene contains the unregistered component `{type_name}`. consider adding `#[reflect(Component)]` to your type")] @@ -46,186 +119,148 @@ pub enum SceneSpawnError { #[error("scene contains the unregistered type `{type_name}`. consider registering the type using `app.register_type::()`")] UnregisteredType { type_name: String }, #[error("scene does not exist")] - NonExistentScene { handle: Handle }, - #[error("scene does not exist")] - NonExistentRealScene { handle: Handle }, + NonExistentScene { handle: SceneHandle }, } impl SceneSpawner { - pub fn spawn_dynamic(&mut self, scene_handle: Handle) { - self.dynamic_scenes_to_spawn.push(scene_handle); + /// Please use [`SceneSpawner::spawn`] instead. + #[deprecated( + since = "0.8.0", + note = "spawn_dynamic is now equivalent to calling spawn, since spawn now accepts `Handle` as argument. In a future release this method will be removed." + )] + pub fn spawn_dynamic>(&mut self, scene_handle: T) -> InstanceId { + self.spawn(scene_handle) } - pub fn spawn(&mut self, scene_handle: Handle) -> InstanceId { + /// Spawn a scene. + /// + /// This will only update the world when [`scene_spawner_system`] runs, see + /// [`SceneSpawner::spawn_sync`] for a method with immediate world update. + /// + /// The returned [`InstanceId`] can be used later to refer to the specific + /// instance of the scene you spawned. + #[doc(alias = "spawn_dynamic")] + pub fn spawn>(&mut self, scene_handle: T) -> InstanceId { + let scene_handle = scene_handle.into().0; let instance_id = InstanceId::new(); self.scenes_to_spawn.push((scene_handle, instance_id)); instance_id } - pub fn spawn_as_child(&mut self, scene_handle: Handle, parent: Entity) -> InstanceId { + /// Spawn a scene as a child of an existing entity. + /// + /// The returned [`InstanceId`] can be used later to refer to the specific + /// instance of the scene you spawned. + pub fn spawn_as_child>( + &mut self, + scene_handle: T, + parent: Entity, + ) -> InstanceId { + let scene_handle = scene_handle.into().0; let instance_id = InstanceId::new(); self.scenes_to_spawn.push((scene_handle, instance_id)); self.scenes_with_parent.push((instance_id, parent)); instance_id } - pub fn despawn(&mut self, scene_handle: Handle) { + /// Despawn the provided scene. This will remove the scene and + /// all its related entities from the world. + /// + /// This will only update the world when [`scene_spawner_system`] runs, see + /// [`SceneSpawner::despawn_sync`] for a method with immediate world + /// update. + pub fn despawn>(&mut self, scene_handle: T) { + let scene_handle = scene_handle.into().0; self.scenes_to_despawn.push(scene_handle); } - pub fn despawn_sync( + /// Despawns the provided scene. This will remove the scene and + /// all its related entities from the world. + /// + /// The world will be updated before this method returns. Requires + /// exclusive world acces through [`&mut World`]. + /// + /// [`SceneSpawner::despawn`] does the same thing, but does not require + /// exclusive world access, it will update the world when + /// [`scene_spawner_system`] runs. + pub fn despawn_sync>( &mut self, world: &mut World, - scene_handle: Handle, + scene_handle: T, ) -> Result<(), SceneSpawnError> { - if let Some(instance_ids) = self.spawned_dynamic_scenes.get(&scene_handle) { - for instance_id in instance_ids { - if let Some(instance) = self.spawned_instances.get(instance_id) { - for entity in instance.entity_map.values() { - let _ = world.despawn(entity); // Ignore the result, despawn only cares if - // it exists. - } - } + let scene_handle = scene_handle.into().0; + let err = || scene_handle.non_existing(); + for instance_id in self.instances.get(&scene_handle).ok_or_else(err)? { + let instance_info = self.instances_info.remove(instance_id).ok_or_else(err)?; + for entity in instance_info.entity_map.values() { + // Ignore result: if it is already despawned, good! + let _ = world.despawn(entity); } - - self.spawned_dynamic_scenes.remove(&scene_handle); } + self.instances.remove(&scene_handle); Ok(()) } - pub fn spawn_dynamic_sync( + /// Please use [`SceneSpawner::spawn_sync`] instead. + #[deprecated( + since = "0.8.0", + note = "spawn_dynamic_sync is now equivalent to calling spawn_sync, since spawn_sync now accepts `Handle` as argument. In a future release this method will be removed." + )] + pub fn spawn_dynamic_sync>( &mut self, world: &mut World, - scene_handle: &Handle, + scene_handle: T, ) -> Result<(), SceneSpawnError> { - let mut entity_map = EntityMap::default(); - Self::spawn_dynamic_internal(world, scene_handle, &mut entity_map)?; - let instance_id = InstanceId::new(); - self.spawned_instances - .insert(instance_id, InstanceInfo { entity_map }); - let spawned = self - .spawned_dynamic_scenes - .entry(scene_handle.clone()) - .or_insert_with(Vec::new); - spawned.push(instance_id); - Ok(()) + self.spawn_sync(world, scene_handle) } - fn spawn_dynamic_internal( - world: &mut World, - scene_handle: &Handle, - entity_map: &mut EntityMap, - ) -> Result<(), SceneSpawnError> { - world.resource_scope(|world, scenes: Mut>| { - let scene = - scenes - .get(scene_handle) - .ok_or_else(|| SceneSpawnError::NonExistentScene { - handle: scene_handle.clone_weak(), - })?; - scene.write_to_world(world, entity_map) - }) - } - - pub fn spawn_sync( + /// Spawn a scene into the world immediately. + /// + /// The world will be updated before this method returns. Requires + /// exclusive world acces through [`&mut World`]. + #[doc(alias = "spawn_dynamic_sync")] + pub fn spawn_sync>( &mut self, world: &mut World, - scene_handle: Handle, - ) -> Result { - self.spawn_sync_internal(world, scene_handle, InstanceId::new()) + scene_handle: T, + ) -> Result<(), SceneSpawnError> { + self.spawn_instance(world, scene_handle, InstanceId::new()) } - fn spawn_sync_internal( + /// Spawn a scene instance, using the provided [`InstanceId`]. + fn spawn_instance>( &mut self, world: &mut World, - scene_handle: Handle, + scene_handle: T, instance_id: InstanceId, - ) -> Result { - let mut instance_info = InstanceInfo { - entity_map: EntityMap::default(), - }; - let type_registry = world.resource::().clone(); - let type_registry = type_registry.read(); - world.resource_scope(|world, scenes: Mut>| { - let scene = - scenes - .get(&scene_handle) - .ok_or_else(|| SceneSpawnError::NonExistentRealScene { - handle: scene_handle.clone(), - })?; - - for archetype in scene.world.archetypes().iter() { - for scene_entity in archetype.entities() { - let entity = *instance_info - .entity_map - .entry(*scene_entity) - .or_insert_with(|| world.spawn().id()); - for component_id in archetype.components() { - let component_info = scene - .world - .components() - .get_info(component_id) - .expect("component_ids in archetypes should have ComponentInfo"); + ) -> Result<(), SceneSpawnError> { + let scene_handle: InnerSceneHandle = scene_handle.into().0; + let mut entity_map = EntityMap::default(); + scene_handle.write_to_world(world, &mut entity_map)?; - let reflect_component = type_registry - .get(component_info.type_id().unwrap()) - .ok_or_else(|| SceneSpawnError::UnregisteredType { - type_name: component_info.name().to_string(), - }) - .and_then(|registration| { - registration.data::().ok_or_else(|| { - SceneSpawnError::UnregisteredComponent { - type_name: component_info.name().to_string(), - } - }) - })?; - reflect_component.copy_component( - &scene.world, - world, - *scene_entity, - entity, - ); - } - } - } - for registration in type_registry.iter() { - if let Some(map_entities_reflect) = registration.data::() { - map_entities_reflect - .map_entities(world, &instance_info.entity_map) - .unwrap(); - } - } - self.spawned_instances.insert(instance_id, instance_info); - let spawned = self - .spawned_scenes - .entry(scene_handle) - .or_insert_with(Vec::new); - spawned.push(instance_id); - Ok(instance_id) - }) + self.instances_info + .insert(instance_id, InstanceInfo { entity_map }); + let spawned = self.instances.entry(scene_handle).or_insert_with(Vec::new); + spawned.push(instance_id); + Ok(()) } - pub fn update_spawned_scenes( + fn update_spawned_scenes( &mut self, world: &mut World, - scene_handles: &[Handle], + scene_handles: &[InnerSceneHandle], ) -> Result<(), SceneSpawnError> { for scene_handle in scene_handles { - if let Some(spawned_instances) = self.spawned_dynamic_scenes.get(scene_handle) { - for instance_id in spawned_instances.iter() { - if let Some(instance_info) = self.spawned_instances.get_mut(instance_id) { - Self::spawn_dynamic_internal( - world, - scene_handle, - &mut instance_info.entity_map, - )?; - } - } + let err = || scene_handle.non_existing(); + for instance_id in self.instances.get(scene_handle).ok_or_else(err)? { + let instance_info = self.instances_info.get_mut(instance_id).ok_or_else(err)?; + scene_handle.write_to_world(world, &mut instance_info.entity_map)?; } } Ok(()) } + /// Manually despawn scenes marked for elimination pub fn despawn_queued_scenes(&mut self, world: &mut World) -> Result<(), SceneSpawnError> { let scenes_to_despawn = std::mem::take(&mut self.scenes_to_despawn); @@ -235,51 +270,40 @@ impl SceneSpawner { Ok(()) } + /// Manually spawn scenes marked for creation pub fn spawn_queued_scenes(&mut self, world: &mut World) -> Result<(), SceneSpawnError> { - let scenes_to_spawn = std::mem::take(&mut self.dynamic_scenes_to_spawn); - - for scene_handle in scenes_to_spawn { - match self.spawn_dynamic_sync(world, &scene_handle) { - Ok(_) => {} - Err(SceneSpawnError::NonExistentScene { .. }) => { - self.dynamic_scenes_to_spawn.push(scene_handle); - } - Err(err) => return Err(err), - } - } - let scenes_to_spawn = std::mem::take(&mut self.scenes_to_spawn); - for (scene_handle, instance_id) in scenes_to_spawn { - match self.spawn_sync_internal(world, scene_handle, instance_id) { + match self.spawn_instance(world, scene_handle, instance_id) { Ok(_) => {} - Err(SceneSpawnError::NonExistentRealScene { handle }) => { - self.scenes_to_spawn.push((handle, instance_id)); + // The scene to spawn did not exist in the Assets (or + // Assets) collection, meaning it still didn't + // finish loading, so we keep it tucked into the spawn queue to + // try loading it later, once it fully loaded. + Err(SceneSpawnError::NonExistentScene { handle }) => { + // NOTE: the handle in NonExistentScene is a weak handle, I + // found that cloning scene_handle **would break Scene (but + // not DynamicScene) loading** + self.scenes_to_spawn.push((handle.0, instance_id)); } Err(err) => return Err(err), } } - Ok(()) } pub(crate) fn set_scene_instance_parent_sync(&mut self, world: &mut World) { let scenes_with_parent = std::mem::take(&mut self.scenes_with_parent); + // Only the root of the scene _does not_ have a parent. + let has_no_parents = |entity: EntityRef| !entity.contains::(); + let is_scene_root = + |entity, world: &World| world.get_entity(entity).map_or(false, has_no_parents); + for (instance_id, parent) in scenes_with_parent { - if let Some(instance) = self.spawned_instances.get(&instance_id) { + if let Some(instance) = self.instances_info.get(&instance_id) { for entity in instance.entity_map.values() { - // Add the `Parent` component to the scene root, and update the `Children` component of - // the scene parent - if !world - .get_entity(entity) - // This will filter only the scene root entity, as all other from the - // scene have a parent - .map(|entity| entity.contains::()) - // Default is true so that it won't run on an entity that wouldn't exist anymore - // this case shouldn't happen anyway - .unwrap_or(true) - { + if is_scene_root(entity, world) { AddChild { parent, child: entity, @@ -293,9 +317,9 @@ impl SceneSpawner { } } - /// Check that an scene instance spawned previously is ready to use + /// Check that a scene instance spawned previously is ready to use pub fn instance_is_ready(&self, instance_id: InstanceId) -> bool { - self.spawned_instances.contains_key(&instance_id) + self.instances_info.contains_key(&instance_id) } /// Get an iterator over the entities in an instance, once it's spawned @@ -303,33 +327,36 @@ impl SceneSpawner { &'_ self, instance_id: InstanceId, ) -> Option + '_> { - self.spawned_instances + self.instances_info .get(&instance_id) .map(|instance| instance.entity_map.values()) } } +/// Update the world according to queued scene commands. pub fn scene_spawner_system(world: &mut World) { world.resource_scope(|world, mut scene_spawner: Mut| { - let scene_asset_events = world.resource::>>(); - - let mut updated_spawned_scenes = Vec::new(); let scene_spawner = &mut *scene_spawner; - for event in scene_spawner - .scene_asset_event_reader - .iter(scene_asset_events) - { + let mut updated_spawned_scenes = Vec::new(); + for event in scene_spawner.readers.dynamic.iter(world.resource()) { if let AssetEvent::Modified { handle } = event { - if scene_spawner.spawned_dynamic_scenes.contains_key(handle) { - updated_spawned_scenes.push(handle.clone_weak()); + let scene_handle = InnerSceneHandle::Reflected(handle.clone_weak()); + if scene_spawner.instances.contains_key(&scene_handle) { + updated_spawned_scenes.push(scene_handle); + } + } + } + for event in scene_spawner.readers.real.iter(world.resource()) { + if let AssetEvent::Modified { handle } = event { + let scene_handle = InnerSceneHandle::World(handle.clone_weak()); + if scene_spawner.instances.contains_key(&scene_handle) { + updated_spawned_scenes.push(scene_handle); } } } scene_spawner.despawn_queued_scenes(world).unwrap(); - scene_spawner - .spawn_queued_scenes(world) - .unwrap_or_else(|err| panic!("{}", err)); + scene_spawner.spawn_queued_scenes(world).unwrap(); scene_spawner .update_spawned_scenes(world, &updated_spawned_scenes) .unwrap(); diff --git a/examples/3d/update_gltf_scene.rs b/examples/3d/update_gltf_scene.rs index 3a23e82028ab1b..f86b5a85c9e4bc 100644 --- a/examples/3d/update_gltf_scene.rs +++ b/examples/3d/update_gltf_scene.rs @@ -44,8 +44,8 @@ fn setup( }); // Spawn a second scene, and keep its `instance_id` - let instance_id = - scene_spawner.spawn(asset_server.load("models/FlightHelmet/FlightHelmet.gltf#Scene0")); + let instance_id = scene_spawner + .spawn::>(asset_server.load("models/FlightHelmet/FlightHelmet.gltf#Scene0")); scene_instance.0 = Some(instance_id); } diff --git a/examples/animation/animated_fox.rs b/examples/animation/animated_fox.rs index 772208dd267e19..de88eb40afc31f 100644 --- a/examples/animation/animated_fox.rs +++ b/examples/animation/animated_fox.rs @@ -59,7 +59,7 @@ fn setup( }); // Fox - scene_spawner.spawn(asset_server.load("models/animated/Fox.glb#Scene0")); + scene_spawner.spawn::>(asset_server.load("models/animated/Fox.glb#Scene0")); println!("Animation controls:"); println!(" - spacebar: play / pause"); diff --git a/examples/asset/hot_asset_reloading.rs b/examples/asset/hot_asset_reloading.rs index 008133059068b2..8e3b052b789671 100644 --- a/examples/asset/hot_asset_reloading.rs +++ b/examples/asset/hot_asset_reloading.rs @@ -19,7 +19,7 @@ fn setup(mut commands: Commands, asset_server: Res) { // Load our mesh: let scene_handle = asset_server.load("models/monkey/Monkey.gltf#Scene0"); - // Any changes to the mesh will be reloaded automatically! Try making a change to Monkey.gltf. + // Any changes to the scene will be reloaded automatically! Try making a change to Monkey.gltf. // You should see the changes immediately show up in your app. // mesh diff --git a/examples/scene/scene.rs b/examples/scene/scene.rs index e7b566dc1332b8..46e0df5619b1de 100644 --- a/examples/scene/scene.rs +++ b/examples/scene/scene.rs @@ -1,8 +1,15 @@ +use bevy::asset::AssetServerSettings; use bevy::{prelude::*, reflect::TypeRegistry, utils::Duration}; /// This example illustrates loading and saving scenes from files fn main() { App::new() + // This tells the AssetServer to watch for changes to assets. + // It enables our scenes to automatically reload in game when we modify their files + .insert_resource(AssetServerSettings { + watch_for_changes: true, + ..default() + }) .add_plugins(DefaultPlugins) .register_type::() .register_type::() @@ -55,11 +62,7 @@ fn load_scene_system(asset_server: Res, mut scene_spawner: ResMut