Skip to content

Commit 7601a89

Browse files
mockersfItsDoot
authored andcommitted
can clone a scene (bevyengine#5855)
# Objective - Easier to work with model assets - Models are often one mesh, many textures. This can be hard to use in Bevy as it's not possible to clone the scene to have one scene for each material. It's still possible to instantiate the texture-less scene, then modify the texture material once spawned but that means happening during play and is quite more painful ## Solution - Expose the code to clone a scene. This code already existed but was only possible to use to spawn the scene
1 parent 5f35dea commit 7601a89

File tree

3 files changed

+81
-47
lines changed

3 files changed

+81
-47
lines changed

crates/bevy_scene/src/dynamic_scene.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ impl DynamicScene {
7878

7979
/// Write the dynamic entities and their corresponding components to the given world.
8080
///
81-
/// This method will return a `SceneSpawnError` if either a type is not registered
82-
/// or doesn't reflect the `Component` trait.
81+
/// This method will return a [`SceneSpawnError`] if a type either is not registered
82+
/// or doesn't reflect the [`Component`](bevy_ecs::component::Component) trait.
8383
pub fn write_to_world(
8484
&self,
8585
world: &mut World,

crates/bevy_scene/src/scene.rs

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
1-
use bevy_ecs::world::World;
1+
use bevy_app::AppTypeRegistry;
2+
use bevy_ecs::{
3+
entity::EntityMap,
4+
reflect::{ReflectComponent, ReflectMapEntities},
5+
world::World,
6+
};
27
use bevy_reflect::TypeUuid;
38

9+
use crate::{InstanceInfo, SceneSpawnError};
10+
411
/// To spawn a scene, you can use either:
512
/// * [`SceneSpawner::spawn`](crate::SceneSpawner::spawn)
613
/// * adding the [`SceneBundle`](crate::SceneBundle) to an entity
@@ -17,4 +24,68 @@ impl Scene {
1724
pub fn new(world: World) -> Self {
1825
Self { world }
1926
}
27+
28+
/// Clone the scene.
29+
///
30+
/// This method will return a [`SceneSpawnError`] if a type either is not registered in the
31+
/// provided [`AppTypeRegistry`] or doesn't reflect the [`Component`](bevy_ecs::component::Component) trait.
32+
pub fn clone_with(&self, type_registry: &AppTypeRegistry) -> Result<Scene, SceneSpawnError> {
33+
let mut new_world = World::new();
34+
self.write_to_world_with(&mut new_world, type_registry)?;
35+
Ok(Self { world: new_world })
36+
}
37+
38+
/// Write the entities and their corresponding components to the given world.
39+
///
40+
/// This method will return a [`SceneSpawnError`] if a type either is not registered in the
41+
/// provided [`AppTypeRegistry`] or doesn't reflect the [`Component`](bevy_ecs::component::Component) trait.
42+
pub fn write_to_world_with(
43+
&self,
44+
world: &mut World,
45+
type_registry: &AppTypeRegistry,
46+
) -> Result<InstanceInfo, SceneSpawnError> {
47+
let mut instance_info = InstanceInfo {
48+
entity_map: EntityMap::default(),
49+
};
50+
51+
let type_registry = type_registry.read();
52+
for archetype in self.world.archetypes().iter() {
53+
for scene_entity in archetype.entities() {
54+
let entity = *instance_info
55+
.entity_map
56+
.entry(*scene_entity)
57+
.or_insert_with(|| world.spawn().id());
58+
for component_id in archetype.components() {
59+
let component_info = self
60+
.world
61+
.components()
62+
.get_info(component_id)
63+
.expect("component_ids in archetypes should have ComponentInfo");
64+
65+
let reflect_component = type_registry
66+
.get(component_info.type_id().unwrap())
67+
.ok_or_else(|| SceneSpawnError::UnregisteredType {
68+
type_name: component_info.name().to_string(),
69+
})
70+
.and_then(|registration| {
71+
registration.data::<ReflectComponent>().ok_or_else(|| {
72+
SceneSpawnError::UnregisteredComponent {
73+
type_name: component_info.name().to_string(),
74+
}
75+
})
76+
})?;
77+
reflect_component.copy(&self.world, world, *scene_entity, entity);
78+
}
79+
}
80+
}
81+
for registration in type_registry.iter() {
82+
if let Some(map_entities_reflect) = registration.data::<ReflectMapEntities>() {
83+
map_entities_reflect
84+
.map_entities(world, &instance_info.entity_map)
85+
.unwrap();
86+
}
87+
}
88+
89+
Ok(instance_info)
90+
}
2091
}

crates/bevy_scene/src/scene_spawner.rs

Lines changed: 7 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ use bevy_asset::{AssetEvent, Assets, Handle};
44
use bevy_ecs::{
55
entity::{Entity, EntityMap},
66
event::{Events, ManualEventReader},
7-
reflect::{ReflectComponent, ReflectMapEntities},
87
system::{Command, Resource},
98
world::{Mut, World},
109
};
@@ -13,9 +12,11 @@ use bevy_utils::{tracing::error, HashMap};
1312
use thiserror::Error;
1413
use uuid::Uuid;
1514

15+
/// Informations about a scene instance.
1616
#[derive(Debug)]
17-
struct InstanceInfo {
18-
entity_map: EntityMap,
17+
pub struct InstanceInfo {
18+
/// Mapping of entities from the scene world to the instance world.
19+
pub entity_map: EntityMap,
1920
}
2021

2122
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
@@ -161,11 +162,6 @@ impl SceneSpawner {
161162
scene_handle: Handle<Scene>,
162163
instance_id: InstanceId,
163164
) -> Result<InstanceId, SceneSpawnError> {
164-
let mut instance_info = InstanceInfo {
165-
entity_map: EntityMap::default(),
166-
};
167-
let type_registry = world.resource::<AppTypeRegistry>().clone();
168-
let type_registry = type_registry.read();
169165
world.resource_scope(|world, scenes: Mut<Assets<Scene>>| {
170166
let scene =
171167
scenes
@@ -174,42 +170,9 @@ impl SceneSpawner {
174170
handle: scene_handle.clone(),
175171
})?;
176172

177-
for archetype in scene.world.archetypes().iter() {
178-
for scene_entity in archetype.entities() {
179-
let entity = *instance_info
180-
.entity_map
181-
.entry(*scene_entity)
182-
.or_insert_with(|| world.spawn().id());
183-
for component_id in archetype.components() {
184-
let component_info = scene
185-
.world
186-
.components()
187-
.get_info(component_id)
188-
.expect("component_ids in archetypes should have ComponentInfo");
189-
190-
let reflect_component = type_registry
191-
.get(component_info.type_id().unwrap())
192-
.ok_or_else(|| SceneSpawnError::UnregisteredType {
193-
type_name: component_info.name().to_string(),
194-
})
195-
.and_then(|registration| {
196-
registration.data::<ReflectComponent>().ok_or_else(|| {
197-
SceneSpawnError::UnregisteredComponent {
198-
type_name: component_info.name().to_string(),
199-
}
200-
})
201-
})?;
202-
reflect_component.copy(&scene.world, world, *scene_entity, entity);
203-
}
204-
}
205-
}
206-
for registration in type_registry.iter() {
207-
if let Some(map_entities_reflect) = registration.data::<ReflectMapEntities>() {
208-
map_entities_reflect
209-
.map_entities(world, &instance_info.entity_map)
210-
.unwrap();
211-
}
212-
}
173+
let instance_info =
174+
scene.write_to_world_with(world, &world.resource::<AppTypeRegistry>().clone())?;
175+
213176
self.spawned_instances.insert(instance_id, instance_info);
214177
let spawned = self
215178
.spawned_scenes

0 commit comments

Comments
 (0)