diff --git a/Cargo.toml b/Cargo.toml index abb490e3e5..4e2392f314 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -106,6 +106,10 @@ path = "examples/2d/contributors.rs" name = "text2d" path = "examples/2d/text2d.rs" +[[example]] +name = "3d_scene" +path = "examples/3d/3d_scene.rs" + [[example]] name = "load_gltf" path = "examples/3d/load_gltf.rs" @@ -118,10 +122,6 @@ path = "examples/3d/msaa.rs" name = "parenting" path = "examples/3d/parenting.rs" -[[example]] -name = "3d_scene" -path = "examples/3d/3d_scene.rs" - [[example]] name = "spawner" path = "examples/3d/spawner.rs" @@ -130,6 +130,10 @@ path = "examples/3d/spawner.rs" name = "texture" path = "examples/3d/texture.rs" +[[example]] +name = "update_gltf_scene" +path = "examples/3d/update_gltf_scene.rs" + [[example]] name = "z_sort_debug" path = "examples/3d/z_sort_debug.rs" diff --git a/crates/bevy_scene/src/scene_spawner.rs b/crates/bevy_scene/src/scene_spawner.rs index 428eda2920..794bd2eec2 100644 --- a/crates/bevy_scene/src/scene_spawner.rs +++ b/crates/bevy_scene/src/scene_spawner.rs @@ -14,10 +14,10 @@ struct InstanceInfo { } #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] -struct InstanceId(Uuid); +pub struct InstanceId(Uuid); impl InstanceId { - pub fn new() -> Self { + fn new() -> Self { InstanceId(Uuid::new_v4()) } } @@ -51,15 +51,17 @@ impl SceneSpawner { self.dynamic_scenes_to_spawn.push(scene_handle); } - pub fn spawn(&mut self, scene_handle: Handle) { + pub fn spawn(&mut self, scene_handle: Handle) -> InstanceId { 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) { + pub fn spawn_as_child(&mut self, scene_handle: Handle, parent: Entity) -> InstanceId { 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) { @@ -155,7 +157,7 @@ impl SceneSpawner { world: &mut World, resources: &Resources, scene_handle: Handle, - ) -> Result<(), SceneSpawnError> { + ) -> Result { self.spawn_sync_internal(world, resources, scene_handle, InstanceId::new()) } @@ -165,7 +167,7 @@ impl SceneSpawner { resources: &Resources, scene_handle: Handle, instance_id: InstanceId, - ) -> Result<(), SceneSpawnError> { + ) -> Result { let mut instance_info = InstanceInfo { entity_map: EntityMap::default(), }; @@ -220,7 +222,7 @@ impl SceneSpawner { .entry(scene_handle) .or_insert_with(Vec::new); spawned.push(instance_id); - Ok(()) + Ok(instance_id) } pub fn update_spawned_scenes( @@ -304,6 +306,21 @@ impl SceneSpawner { } } } + + /// Check that an 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) + } + + /// Get an iterator over the entities in an instance, once it's spawned + pub fn iter_instance_entities( + &'_ self, + instance_id: InstanceId, + ) -> Option + '_> { + self.spawned_instances + .get(&instance_id) + .map(|instance| instance.entity_map.values()) + } } pub fn scene_spawner_system(world: &mut World, resources: &mut Resources) { diff --git a/examples/3d/update_gltf_scene.rs b/examples/3d/update_gltf_scene.rs new file mode 100644 index 0000000000..1695d3bc14 --- /dev/null +++ b/examples/3d/update_gltf_scene.rs @@ -0,0 +1,93 @@ +use bevy::{prelude::*, scene::InstanceId}; + +fn main() { + App::build() + .add_resource(Msaa { samples: 4 }) + .add_plugins(DefaultPlugins) + .add_resource(SceneInstance::default()) + .add_startup_system(setup.system()) + .add_system(scene_update.system()) + .add_system(move_scene_entities.system()) + .run(); +} + +// Resource to hold the scene `instance_id` until it is loaded +#[derive(Default)] +struct SceneInstance(Option); + +// Component that will be used to tag entities in the scene +struct EntityInMyScene; + +fn setup( + commands: &mut Commands, + asset_server: Res, + mut scene_spawner: ResMut, + mut scene_instance: ResMut, +) { + commands + .spawn(LightBundle { + transform: Transform::from_translation(Vec3::new(4.0, 5.0, 4.0)), + ..Default::default() + }) + .spawn(Camera3dBundle { + transform: Transform::from_translation(Vec3::new(1.05, 0.9, 1.5)) + .looking_at(Vec3::new(0.0, 0.3, 0.0), Vec3::unit_y()), + ..Default::default() + }); + + // Spawn the scene as a child of another entity. This first scene will be translated backward + // with its parent + commands + .spawn(( + Transform::from_translation(Vec3::new(0.0, 0.0, -1.0)), + GlobalTransform::default(), + )) + .with_children(|parent| { + parent.spawn_scene(asset_server.load("models/FlightHelmet/FlightHelmet.gltf")); + }); + + // Spawn a second scene, and keep its `instance_id` + let instance_id = + scene_spawner.spawn(asset_server.load("models/FlightHelmet/FlightHelmet.gltf")); + scene_instance.0 = Some(instance_id); +} + +// This system will wait for the scene to be ready, and then tag entities from +// the scene with `EntityInMyScene`. All entities from the second scene will be +// tagged +fn scene_update( + commands: &mut Commands, + scene_spawner: Res, + scene_instance: Res, + mut done: Local, +) { + if !*done { + if let Some(instance_id) = scene_instance.0 { + if let Some(entity_iter) = scene_spawner.iter_instance_entities(instance_id) { + entity_iter.for_each(|entity| { + commands.insert_one(entity, EntityInMyScene); + }); + *done = true; + } + } + } +} + +// This system will move all entities with component `EntityInMyScene`, so all +// entities from the second scene +fn move_scene_entities( + time: Res