Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - can clone a scene #5855

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions crates/bevy_scene/src/dynamic_scene.rs
Expand Up @@ -78,8 +78,8 @@ impl DynamicScene {

/// Write the dynamic entities and their corresponding components to the given world.
///
/// This method will return a `SceneSpawnError` if either a type is not registered
/// or doesn't reflect the `Component` trait.
/// This method will return a [`SceneSpawnError`] if a type either is not registered
/// or doesn't reflect the [`Component`](bevy_ecs::component::Component) trait.
pub fn write_to_world(
&self,
world: &mut World,
Expand Down
73 changes: 72 additions & 1 deletion crates/bevy_scene/src/scene.rs
@@ -1,6 +1,13 @@
use bevy_ecs::world::World;
use bevy_app::AppTypeRegistry;
use bevy_ecs::{
entity::EntityMap,
reflect::{ReflectComponent, ReflectMapEntities},
world::World,
};
use bevy_reflect::TypeUuid;

use crate::{InstanceInfo, SceneSpawnError};

/// To spawn a scene, you can use either:
/// * [`SceneSpawner::spawn`](crate::SceneSpawner::spawn)
/// * adding the [`SceneBundle`](crate::SceneBundle) to an entity
Expand All @@ -17,4 +24,68 @@ impl Scene {
pub fn new(world: World) -> Self {
Self { world }
}

/// Clone the scene.
///
/// This method will return a [`SceneSpawnError`] if a type either is not registered in the
/// provided [`AppTypeRegistry`] or doesn't reflect the [`Component`](bevy_ecs::component::Component) trait.
pub fn clone_with(&self, type_registry: &AppTypeRegistry) -> Result<Scene, SceneSpawnError> {
let mut new_world = World::new();
self.write_to_world_with(&mut new_world, type_registry)?;
Ok(Self { world: new_world })
}

/// Write the entities and their corresponding components to the given world.
///
/// This method will return a [`SceneSpawnError`] if a type either is not registered in the
/// provided [`AppTypeRegistry`] or doesn't reflect the [`Component`](bevy_ecs::component::Component) trait.
pub fn write_to_world_with(
&self,
world: &mut World,
type_registry: &AppTypeRegistry,
) -> Result<InstanceInfo, SceneSpawnError> {
let mut instance_info = InstanceInfo {
entity_map: EntityMap::default(),
};

let type_registry = type_registry.read();
for archetype in self.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 = 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::<ReflectComponent>().ok_or_else(|| {
SceneSpawnError::UnregisteredComponent {
type_name: component_info.name().to_string(),
}
})
})?;
reflect_component.copy(&self.world, world, *scene_entity, entity);
}
}
}
for registration in type_registry.iter() {
if let Some(map_entities_reflect) = registration.data::<ReflectMapEntities>() {
map_entities_reflect
.map_entities(world, &instance_info.entity_map)
.unwrap();
}
}

Ok(instance_info)
}
}
51 changes: 7 additions & 44 deletions crates/bevy_scene/src/scene_spawner.rs
Expand Up @@ -4,7 +4,6 @@ use bevy_asset::{AssetEvent, Assets, Handle};
use bevy_ecs::{
entity::{Entity, EntityMap},
event::{Events, ManualEventReader},
reflect::{ReflectComponent, ReflectMapEntities},
system::{Command, Resource},
world::{Mut, World},
};
Expand All @@ -13,9 +12,11 @@ use bevy_utils::{tracing::error, HashMap};
use thiserror::Error;
use uuid::Uuid;

/// Informations about a scene instance.
#[derive(Debug)]
struct InstanceInfo {
entity_map: EntityMap,
pub struct InstanceInfo {
/// Mapping of entities from the scene world to the instance world.
pub entity_map: EntityMap,
}

#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
Expand Down Expand Up @@ -161,11 +162,6 @@ impl SceneSpawner {
scene_handle: Handle<Scene>,
instance_id: InstanceId,
) -> Result<InstanceId, SceneSpawnError> {
let mut instance_info = InstanceInfo {
entity_map: EntityMap::default(),
};
let type_registry = world.resource::<AppTypeRegistry>().clone();
let type_registry = type_registry.read();
world.resource_scope(|world, scenes: Mut<Assets<Scene>>| {
let scene =
scenes
Expand All @@ -174,42 +170,9 @@ impl SceneSpawner {
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");

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::<ReflectComponent>().ok_or_else(|| {
SceneSpawnError::UnregisteredComponent {
type_name: component_info.name().to_string(),
}
})
})?;
reflect_component.copy(&scene.world, world, *scene_entity, entity);
}
}
}
for registration in type_registry.iter() {
if let Some(map_entities_reflect) = registration.data::<ReflectMapEntities>() {
map_entities_reflect
.map_entities(world, &instance_info.entity_map)
.unwrap();
}
}
let instance_info =
scene.write_to_world_with(world, &world.resource::<AppTypeRegistry>().clone())?;

self.spawned_instances.insert(instance_id, instance_info);
let spawned = self
.spawned_scenes
Expand Down