Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions crates/bevy_hierarchy/src/components/parent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ impl FromWorld for Parent {

impl MapEntities for Parent {
fn map_entities(&mut self, entity_map: &EntityMap) -> Result<(), MapEntitiesError> {
self.0 = entity_map.get(self.0)?;
// Parent of an entity in the new world can be in outside world, in which case it
// should not be mapped.
if let Ok(mapped_entity) = entity_map.get(self.0) {
self.0 = mapped_entity;
}
Ok(())
}
}
Expand All @@ -51,7 +55,11 @@ pub struct PreviousParent(pub(crate) Entity);

impl MapEntities for PreviousParent {
fn map_entities(&mut self, entity_map: &EntityMap) -> Result<(), MapEntitiesError> {
self.0 = entity_map.get(self.0)?;
// PreviousParent of an entity in the new world can be in outside world, in which
// case it should not be mapped.
if let Ok(mapped_entity) = entity_map.get(self.0) {
self.0 = mapped_entity;
}
Ok(())
}
}
Expand Down
2 changes: 2 additions & 0 deletions crates/bevy_scene/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ keywords = ["bevy"]
# bevy
bevy_app = { path = "../bevy_app", version = "0.8.0-dev" }
bevy_asset = { path = "../bevy_asset", version = "0.8.0-dev" }
bevy_derive = { path = "../bevy_derive", version = "0.8.0-dev" }
bevy_ecs = { path = "../bevy_ecs", version = "0.8.0-dev" }
bevy_reflect = { path = "../bevy_reflect", version = "0.8.0-dev", features = ["bevy"] }
bevy_hierarchy = { path = "../bevy_hierarchy", version = "0.8.0-dev" }
bevy_transform = { path = "../bevy_transform", version = "0.8.0-dev" }
bevy_utils = { path = "../bevy_utils", version = "0.8.0-dev" }

# other
Expand Down
74 changes: 74 additions & 0 deletions crates/bevy_scene/src/bundle.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use bevy_asset::Handle;
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{
bundle::Bundle,
change_detection::ResMut,
entity::Entity,
prelude::{Changed, Component, Without},
system::{Commands, Query},
};
use bevy_transform::components::{GlobalTransform, Transform};

use crate::{DynamicScene, InstanceId, Scene, SceneSpawner};

/// [`InstanceId`] of a spawned scene. It can be used with the [`SceneSpawner`] to
/// interact with the spawned scene.
#[derive(Component, Deref, DerefMut)]
pub struct SceneInstance(InstanceId);

/// A component bundle for a [`Scene`] root.
///
/// The scene from `scene` will be spawn as a child of the entity with this component.
/// Once it's spawned, the entity will have a [`SceneInstance`] component.
#[derive(Default, Bundle)]
pub struct SceneBundle {
/// Handle to the scene to spawn
pub scene: Handle<Scene>,
pub transform: Transform,
pub global_transform: GlobalTransform,
}

/// A component bundle for a [`DynamicScene`] root.
///
/// The dynamic scene from `scene` will be spawn as a child of the entity with this component.
/// Once it's spawned, the entity will have a [`SceneInstance`] component.
#[derive(Default, Bundle)]
pub struct DynamicSceneBundle {
/// Handle to the scene to spawn
pub scene: Handle<DynamicScene>,
pub transform: Transform,
pub global_transform: GlobalTransform,
}

/// System that will spawn scenes from [`SceneBundle`].
pub fn scene_spawner(
mut commands: Commands,
mut scene_to_spawn: Query<
(Entity, &Handle<Scene>, Option<&mut SceneInstance>),
(Changed<Handle<Scene>>, Without<Handle<DynamicScene>>),
>,
mut dynamic_scene_to_spawn: Query<
(Entity, &Handle<DynamicScene>, Option<&mut SceneInstance>),
(Changed<Handle<DynamicScene>>, Without<Handle<Scene>>),
>,
mut scene_spawner: ResMut<SceneSpawner>,
) {
for (entity, scene, instance) in scene_to_spawn.iter_mut() {
let new_instance = scene_spawner.spawn_as_child(scene.clone(), entity);
if let Some(mut old_instance) = instance {
scene_spawner.despawn_instance(**old_instance);
*old_instance = SceneInstance(new_instance);
} else {
commands.entity(entity).insert(SceneInstance(new_instance));
}
}
for (entity, dynamic_scene, instance) in dynamic_scene_to_spawn.iter_mut() {
let new_instance = scene_spawner.spawn_dynamic_as_child(dynamic_scene.clone(), entity);
if let Some(mut old_instance) = instance {
scene_spawner.despawn_instance(**old_instance);
*old_instance = SceneInstance(new_instance);
} else {
commands.entity(entity).insert(SceneInstance(new_instance));
}
}
}
56 changes: 0 additions & 56 deletions crates/bevy_scene/src/command.rs

This file was deleted.

6 changes: 6 additions & 0 deletions crates/bevy_scene/src/dynamic_scene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ use bevy_reflect::{Reflect, TypeRegistryArc, TypeUuid};
use serde::Serialize;

/// A collection of serializable dynamic entities, each with its own run-time defined set of components.
/// To spawn a dynamic scene, you can use either:
/// * [`SceneSpawner::spawn_dynamic`](crate::SceneSpawner::spawn_dynamic)
/// * adding the [`DynamicSceneBundle`](crate::DynamicSceneBundle) to an entity
/// * adding the [`Handle<DynamicScene>`](bevy_asset::Handle) to an entity (the scene will only be
/// visible if the entity already has [`Transform`](bevy_transform::components::Transform) and
/// [`GlobalTransform`](bevy_transform::components::GlobalTransform) components)
#[derive(Default, TypeUuid)]
#[uuid = "749479b1-fb8c-4ff8-a775-623aa76014f5"]
pub struct DynamicScene {
Expand Down
12 changes: 6 additions & 6 deletions crates/bevy_scene/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
mod command;
mod bundle;
mod dynamic_scene;
mod scene;
mod scene_loader;
mod scene_spawner;
pub mod serde;

pub use command::*;
pub use bundle::*;
pub use dynamic_scene::*;
pub use scene::*;
pub use scene_loader::*;
pub use scene_spawner::*;

pub mod prelude {
#[doc(hidden)]
pub use crate::{
DynamicScene, Scene, SceneSpawner, SpawnSceneAsChildCommands, SpawnSceneCommands,
};
pub use crate::{DynamicScene, DynamicSceneBundle, Scene, SceneBundle, SceneSpawner};
}

use bevy_app::prelude::*;
Expand All @@ -34,6 +32,8 @@ impl Plugin for ScenePlugin {
.add_system_to_stage(
CoreStage::PreUpdate,
scene_spawner_system.exclusive_system().at_end(),
);
)
// Systems `*_bundle_spawner` must run before `scene_spawner_system`
.add_system_to_stage(CoreStage::PreUpdate, scene_spawner);
}
}
6 changes: 6 additions & 0 deletions crates/bevy_scene/src/scene.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
use bevy_ecs::world::World;
use bevy_reflect::TypeUuid;

/// To spawn a scene, you can use either:
/// * [`SceneSpawner::spawn`](crate::SceneSpawner::spawn)
/// * adding the [`SceneBundle`](crate::SceneBundle) to an entity
/// * adding the [`Handle<Scene>`](bevy_asset::Handle) to an entity (the scene will only be
/// visible if the entity already has [`Transform`](bevy_transform::components::Transform) and
/// [`GlobalTransform`](bevy_transform::components::GlobalTransform) components)
#[derive(Debug, TypeUuid)]
#[uuid = "c156503c-edd9-4ec7-8d33-dab392df03cd"]
pub struct Scene {
Expand Down
70 changes: 55 additions & 15 deletions crates/bevy_scene/src/scene_spawner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,10 @@ pub struct SceneSpawner {
spawned_dynamic_scenes: HashMap<Handle<DynamicScene>, Vec<InstanceId>>,
spawned_instances: HashMap<InstanceId, InstanceInfo>,
scene_asset_event_reader: ManualEventReader<AssetEvent<DynamicScene>>,
dynamic_scenes_to_spawn: Vec<Handle<DynamicScene>>,
dynamic_scenes_to_spawn: Vec<(Handle<DynamicScene>, InstanceId)>,
scenes_to_spawn: Vec<(Handle<Scene>, InstanceId)>,
scenes_to_despawn: Vec<Handle<DynamicScene>>,
instances_to_despawn: Vec<InstanceId>,
scenes_with_parent: Vec<(InstanceId, Entity)>,
}

Expand All @@ -53,7 +54,21 @@ pub enum SceneSpawnError {

impl SceneSpawner {
pub fn spawn_dynamic(&mut self, scene_handle: Handle<DynamicScene>) {
self.dynamic_scenes_to_spawn.push(scene_handle);
let instance_id = InstanceId::new();
self.dynamic_scenes_to_spawn
.push((scene_handle, instance_id));
}

pub fn spawn_dynamic_as_child(
&mut self,
scene_handle: Handle<DynamicScene>,
parent: Entity,
) -> InstanceId {
let instance_id = InstanceId::new();
self.dynamic_scenes_to_spawn
.push((scene_handle, instance_id));
self.scenes_with_parent.push((instance_id, parent));
instance_id
}

pub fn spawn(&mut self, scene_handle: Handle<Scene>) -> InstanceId {
Expand All @@ -73,26 +88,31 @@ impl SceneSpawner {
self.scenes_to_despawn.push(scene_handle);
}

pub fn despawn_instance(&mut self, instance_id: InstanceId) {
self.instances_to_despawn.push(instance_id);
}

pub fn despawn_sync(
&mut self,
world: &mut World,
scene_handle: Handle<DynamicScene>,
) -> Result<(), SceneSpawnError> {
if let Some(instance_ids) = self.spawned_dynamic_scenes.get(&scene_handle) {
if let Some(instance_ids) = self.spawned_dynamic_scenes.remove(&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.
}
}
self.despawn_instance_sync(world, &instance_id);
}

self.spawned_dynamic_scenes.remove(&scene_handle);
}
Ok(())
}

pub fn despawn_instance_sync(&mut self, world: &mut World, instance_id: &InstanceId) {
if let Some(instance) = self.spawned_instances.remove(instance_id) {
for entity in instance.entity_map.values() {
let _ = world.despawn(entity);
}
}
}

pub fn spawn_dynamic_sync(
&mut self,
world: &mut World,
Expand Down Expand Up @@ -235,14 +255,33 @@ impl SceneSpawner {
Ok(())
}

pub fn despawn_queued_instances(&mut self, world: &mut World) {
let instances_to_despawn = std::mem::take(&mut self.instances_to_despawn);

for instance_id in instances_to_despawn {
self.despawn_instance_sync(world, &instance_id);
}
}

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(_) => {}
for (scene_handle, instance_id) in scenes_to_spawn {
let mut entity_map = EntityMap::default();

match Self::spawn_dynamic_internal(world, &scene_handle, &mut entity_map) {
Ok(_) => {
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);
}
Err(SceneSpawnError::NonExistentScene { .. }) => {
self.dynamic_scenes_to_spawn.push(scene_handle);
self.dynamic_scenes_to_spawn
.push((scene_handle, instance_id));
}
Err(err) => return Err(err),
}
Expand Down Expand Up @@ -327,6 +366,7 @@ pub fn scene_spawner_system(world: &mut World) {
}

scene_spawner.despawn_queued_scenes(world).unwrap();
scene_spawner.despawn_queued_instances(world);
scene_spawner
.spawn_queued_scenes(world)
.unwrap_or_else(|err| panic!("{}", err));
Expand Down
5 changes: 4 additions & 1 deletion examples/3d/load_gltf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ fn main() {
}

fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn_scene(asset_server.load("models/FlightHelmet/FlightHelmet.gltf#Scene0"));
commands.spawn_bundle(Camera3dBundle {
transform: Transform::from_xyz(0.7, 0.7, 1.0).looking_at(Vec3::new(0.0, 0.3, 0.0), Vec3::Y),
..default()
Expand All @@ -37,6 +36,10 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
},
..default()
});
commands.spawn_bundle(SceneBundle {
scene: asset_server.load("models/FlightHelmet/FlightHelmet.gltf#Scene0"),
..default()
});
}

fn animate_light_direction(
Expand Down
5 changes: 4 additions & 1 deletion examples/3d/split_screen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ fn setup(
..default()
});

commands.spawn_scene(asset_server.load("models/animated/Fox.glb#Scene0"));
commands.spawn_bundle(SceneBundle {
scene: asset_server.load("models/animated/Fox.glb#Scene0"),
..default()
});

// Light
commands.spawn_bundle(DirectionalLightBundle {
Expand Down
Loading