Path: blob/main/crates/bevy_scene/src/dynamic_scene_builder.rs
9421 views
use core::any::TypeId;12use crate::reflect_utils::clone_reflect_value;3use crate::{DynamicEntity, DynamicScene, SceneFilter};4use alloc::collections::BTreeMap;5use bevy_ecs::resource::IS_RESOURCE;6use bevy_ecs::{7component::{Component, ComponentId},8entity_disabling::DefaultQueryFilters,9prelude::Entity,10reflect::{AppTypeRegistry, ReflectComponent, ReflectResource},11resource::Resource,12world::World,13};14use bevy_reflect::PartialReflect;15use bevy_utils::default;1617/// A [`DynamicScene`] builder, used to build a scene from a [`World`] by extracting some entities and resources.18///19/// # Component Extraction20///21/// By default, all components registered with [`ReflectComponent`] type data in a world's [`AppTypeRegistry`] will be extracted.22/// (this type data is added automatically during registration if [`Reflect`] is derived with the `#[reflect(Component)]` attribute).23/// This can be changed by [specifying a filter](DynamicSceneBuilder::with_component_filter) or by explicitly24/// [allowing](DynamicSceneBuilder::allow_component)/[denying](DynamicSceneBuilder::deny_component) certain components.25///26/// Extraction happens immediately and uses the filter as it exists during the time of extraction.27///28/// # Resource Extraction29///30/// By default, all resources registered with [`ReflectResource`] type data in a world's [`AppTypeRegistry`] will be extracted.31/// (this type data is added automatically during registration if [`Reflect`] is derived with the `#[reflect(Resource)]` attribute).32/// This can be changed by [specifying a filter](DynamicSceneBuilder::with_resource_filter) or by explicitly33/// [allowing](DynamicSceneBuilder::allow_resource)/[denying](DynamicSceneBuilder::deny_resource) certain resources.34///35/// Extraction happens immediately and uses the filter as it exists during the time of extraction.36///37/// # Entity Order38///39/// Extracted entities will always be stored in ascending order based on their [index](Entity::index).40/// This means that inserting `Entity(1v0)` then `Entity(0v0)` will always result in the entities41/// being ordered as `[Entity(0v0), Entity(1v0)]`.42///43/// # Example44/// ```45/// # use bevy_scene::DynamicSceneBuilder;46/// # use bevy_ecs::reflect::AppTypeRegistry;47/// # use bevy_ecs::{48/// # component::Component, prelude::Entity, query::With, reflect::ReflectComponent, world::World,49/// # };50/// # use bevy_reflect::Reflect;51/// # #[derive(Component, Reflect, Default, Eq, PartialEq, Debug)]52/// # #[reflect(Component)]53/// # struct ComponentA;54/// # let mut world = World::default();55/// # world.init_resource::<AppTypeRegistry>();56/// # let entity = world.spawn(ComponentA).id();57/// let dynamic_scene = DynamicSceneBuilder::from_world(&world).extract_entity(entity).build();58/// ```59///60/// [`Reflect`]: bevy_reflect::Reflect61pub struct DynamicSceneBuilder<'w> {62extracted_resources: BTreeMap<ComponentId, Box<dyn PartialReflect>>,63extracted_scene: BTreeMap<Entity, DynamicEntity>,64component_filter: SceneFilter,65resource_filter: SceneFilter,66original_world: &'w World,67}6869impl<'w> DynamicSceneBuilder<'w> {70/// Prepare a builder that will extract entities and their component from the given [`World`].71pub fn from_world(world: &'w World) -> Self {72Self {73extracted_resources: default(),74extracted_scene: default(),75component_filter: SceneFilter::default(),76resource_filter: SceneFilter::default(),77original_world: world,78}79}8081/// Specify a custom component [`SceneFilter`] to be used with this builder.82#[must_use]83pub fn with_component_filter(mut self, filter: SceneFilter) -> Self {84self.component_filter = filter;85self86}8788/// Specify a custom resource [`SceneFilter`] to be used with this builder.89#[must_use]90pub fn with_resource_filter(mut self, filter: SceneFilter) -> Self {91self.resource_filter = filter;92self93}9495/// Updates the filter to allow all component and resource types.96///97/// This is useful for resetting the filter so that types may be selectively denied98/// with [`deny_component`](`Self::deny_component`) and [`deny_resource`](`Self::deny_resource`).99pub fn allow_all(mut self) -> Self {100self.component_filter = SceneFilter::allow_all();101self.resource_filter = SceneFilter::allow_all();102self103}104105/// Updates the filter to deny all component and resource types.106///107/// This is useful for resetting the filter so that types may be selectively allowed108/// with [`allow_component`](`Self::allow_component`) and [`allow_resource`](`Self::allow_resource`).109pub fn deny_all(mut self) -> Self {110self.component_filter = SceneFilter::deny_all();111self.resource_filter = SceneFilter::deny_all();112self113}114115/// Allows the given component type, `T`, to be included in the generated scene.116///117/// This method may be called multiple times for any number of components.118///119/// This is the inverse of [`deny_component`](Self::deny_component).120/// If `T` has already been denied, then it will be removed from the denylist.121#[must_use]122pub fn allow_component<T: Component>(mut self) -> Self {123self.component_filter = self.component_filter.allow::<T>();124self125}126127/// Denies the given component type, `T`, from being included in the generated scene.128///129/// This method may be called multiple times for any number of components.130///131/// This is the inverse of [`allow_component`](Self::allow_component).132/// If `T` has already been allowed, then it will be removed from the allowlist.133#[must_use]134pub fn deny_component<T: Component>(mut self) -> Self {135self.component_filter = self.component_filter.deny::<T>();136self137}138139/// Updates the filter to allow all component types.140///141/// This is useful for resetting the filter so that types may be selectively [denied].142///143/// [denied]: Self::deny_component144#[must_use]145pub fn allow_all_components(mut self) -> Self {146self.component_filter = SceneFilter::allow_all();147self148}149150/// Updates the filter to deny all component types.151///152/// This is useful for resetting the filter so that types may be selectively [allowed].153///154/// [allowed]: Self::allow_component155#[must_use]156pub fn deny_all_components(mut self) -> Self {157self.component_filter = SceneFilter::deny_all();158self159}160161/// Allows the given resource type, `T`, to be included in the generated scene.162///163/// This method may be called multiple times for any number of resources.164///165/// This is the inverse of [`deny_resource`](Self::deny_resource).166/// If `T` has already been denied, then it will be removed from the denylist.167#[must_use]168pub fn allow_resource<T: Resource>(mut self) -> Self {169self.resource_filter = self.resource_filter.allow::<T>();170self171}172173/// Denies the given resource type, `T`, from being included in the generated scene.174///175/// This method may be called multiple times for any number of resources.176///177/// This is the inverse of [`allow_resource`](Self::allow_resource).178/// If `T` has already been allowed, then it will be removed from the allowlist.179#[must_use]180pub fn deny_resource<T: Resource>(mut self) -> Self {181self.resource_filter = self.resource_filter.deny::<T>();182self183}184185/// Updates the filter to allow all resource types.186///187/// This is useful for resetting the filter so that types may be selectively [denied].188///189/// [denied]: Self::deny_resource190#[must_use]191pub fn allow_all_resources(mut self) -> Self {192self.resource_filter = SceneFilter::allow_all();193self194}195196/// Updates the filter to deny all resource types.197///198/// This is useful for resetting the filter so that types may be selectively [allowed].199///200/// [allowed]: Self::allow_resource201#[must_use]202pub fn deny_all_resources(mut self) -> Self {203self.resource_filter = SceneFilter::deny_all();204self205}206207/// Consume the builder, producing a [`DynamicScene`].208///209/// To make sure the dynamic scene doesn't contain entities without any components, call210/// [`Self::remove_empty_entities`] before building the scene.211#[must_use]212pub fn build(self) -> DynamicScene {213DynamicScene {214resources: self.extracted_resources.into_values().collect(),215entities: self.extracted_scene.into_values().collect(),216}217}218219/// Extract one entity from the builder's [`World`].220///221/// Re-extracting an entity that was already extracted will have no effect.222#[must_use]223pub fn extract_entity(self, entity: Entity) -> Self {224self.extract_entities(core::iter::once(entity))225}226227/// Despawns all entities with no components.228///229/// These were likely created because none of their components were present in the provided type registry upon extraction.230#[must_use]231pub fn remove_empty_entities(mut self) -> Self {232self.extracted_scene233.retain(|_, entity| !entity.components.is_empty());234235self236}237238/// Extract entities from the builder's [`World`].239///240/// Re-extracting an entity that was already extracted will have no effect.241///242/// To control which components are extracted, use the [`allow`] or243/// [`deny`] helper methods.244///245/// This method may be used to extract entities from a query:246/// ```247/// # use bevy_scene::DynamicSceneBuilder;248/// # use bevy_ecs::reflect::AppTypeRegistry;249/// # use bevy_ecs::{250/// # component::Component, prelude::Entity, query::With, reflect::ReflectComponent, world::World,251/// # };252/// # use bevy_reflect::Reflect;253/// #[derive(Component, Default, Reflect)]254/// #[reflect(Component)]255/// struct MyComponent;256///257/// # let mut world = World::default();258/// # world.init_resource::<AppTypeRegistry>();259/// # let _entity = world.spawn(MyComponent).id();260/// let mut query = world.query_filtered::<Entity, With<MyComponent>>();261///262/// let scene = DynamicSceneBuilder::from_world(&world)263/// .extract_entities(query.iter(&world))264/// .build();265/// ```266///267/// Note that components extracted from queried entities must still pass through the filter if one is set.268///269/// [`allow`]: Self::allow_component270/// [`deny`]: Self::deny_component271#[must_use]272pub fn extract_entities(mut self, entities: impl Iterator<Item = Entity>) -> Self {273let type_registry = self.original_world.resource::<AppTypeRegistry>().read();274275for entity in entities {276if self.extracted_scene.contains_key(&entity) {277continue;278}279280let mut entry = DynamicEntity {281entity,282components: Vec::new(),283};284285let original_entity = self.original_world.entity(entity);286if original_entity.contains_id(IS_RESOURCE) {287continue;288}289290for &component_id in original_entity.archetype().components().iter() {291let mut extract_and_push = || {292let type_id = self293.original_world294.components()295.get_info(component_id)?296.type_id()?;297298let is_denied = self.component_filter.is_denied_by_id(type_id);299300if is_denied {301// Component is either in the denylist or _not_ in the allowlist302return None;303}304305let type_registration = type_registry.get(type_id)?;306307let component = type_registration308.data::<ReflectComponent>()?309.reflect(original_entity)?;310311let component =312clone_reflect_value(component.as_partial_reflect(), type_registration);313314entry.components.push(component);315Some(())316};317extract_and_push();318}319self.extracted_scene.insert(entity, entry);320}321322self323}324325/// Extract resources from the builder's [`World`].326///327/// Re-extracting a resource that was already extracted will have no effect.328///329/// To control which resources are extracted, use the [`allow_resource`] or330/// [`deny_resource`] helper methods.331///332/// ```333/// # use bevy_scene::DynamicSceneBuilder;334/// # use bevy_ecs::reflect::AppTypeRegistry;335/// # use bevy_ecs::prelude::{ReflectResource, Resource, World};336/// # use bevy_reflect::Reflect;337/// #[derive(Resource, Default, Reflect)]338/// #[reflect(Resource)]339/// struct MyResource;340///341/// # let mut world = World::default();342/// # world.init_resource::<AppTypeRegistry>();343/// world.insert_resource(MyResource);344///345/// let mut builder = DynamicSceneBuilder::from_world(&world).extract_resources();346/// let scene = builder.build();347/// ```348///349/// [`allow_resource`]: Self::allow_resource350/// [`deny_resource`]: Self::deny_resource351#[must_use]352pub fn extract_resources(mut self) -> Self {353// Don't extract the DefaultQueryFilters resource354let original_world_dqf_id = self355.original_world356.components()357.get_valid_id(TypeId::of::<DefaultQueryFilters>());358359let type_registry = self.original_world.resource::<AppTypeRegistry>().read();360361for (component_id, entity) in self.original_world.resource_entities().iter() {362if Some(*component_id) == original_world_dqf_id {363continue;364}365let mut extract_and_push = || {366let type_id = self367.original_world368.components()369.get_info(*component_id)?370.type_id()?;371372let is_denied = self.resource_filter.is_denied_by_id(type_id);373374if is_denied {375// Resource is either in the denylist or _not_ in the allowlist376return None;377}378379let type_registration = type_registry.get(type_id)?;380381type_registration.data::<ReflectResource>()?;382let component = type_registration383.data::<ReflectComponent>()?384.reflect(self.original_world.entity(*entity))?;385386let component =387clone_reflect_value(component.as_partial_reflect(), type_registration);388389self.extracted_resources.insert(*component_id, component);390Some(())391};392extract_and_push();393}394395drop(type_registry);396self397}398}399400#[cfg(test)]401mod tests {402use bevy_ecs::{403component::Component,404prelude::{Entity, Resource},405query::With,406reflect::{AppTypeRegistry, ReflectComponent, ReflectResource},407world::World,408};409410use bevy_reflect::Reflect;411412use super::DynamicSceneBuilder;413414#[derive(Component, Reflect, Default, Eq, PartialEq, Debug)]415#[reflect(Component)]416struct ComponentA;417418#[derive(Component, Reflect, Default, Eq, PartialEq, Debug)]419#[reflect(Component)]420struct ComponentB;421422#[derive(Resource, Reflect, Default, Eq, PartialEq, Debug)]423#[reflect(Resource)]424struct ResourceA;425426#[derive(Resource, Reflect, Default, Eq, PartialEq, Debug)]427#[reflect(Resource)]428struct ResourceB;429430#[test]431fn extract_one_entity() {432let mut world = World::default();433434let atr = AppTypeRegistry::default();435atr.write().register::<ComponentA>();436world.insert_resource(atr);437438let entity = world.spawn((ComponentA, ComponentB)).id();439440let scene = DynamicSceneBuilder::from_world(&world)441.extract_entity(entity)442.build();443444assert_eq!(scene.entities.len(), 1);445assert_eq!(scene.entities[0].entity, entity);446assert_eq!(scene.entities[0].components.len(), 1);447assert!(scene.entities[0].components[0].represents::<ComponentA>());448}449450#[test]451fn extract_one_entity_twice() {452let mut world = World::default();453454let atr = AppTypeRegistry::default();455atr.write().register::<ComponentA>();456world.insert_resource(atr);457458let entity = world.spawn((ComponentA, ComponentB)).id();459460let scene = DynamicSceneBuilder::from_world(&world)461.extract_entity(entity)462.extract_entity(entity)463.build();464465assert_eq!(scene.entities.len(), 1);466assert_eq!(scene.entities[0].entity, entity);467assert_eq!(scene.entities[0].components.len(), 1);468assert!(scene.entities[0].components[0].represents::<ComponentA>());469}470471#[test]472fn extract_one_entity_two_components() {473let mut world = World::default();474475let atr = AppTypeRegistry::default();476{477let mut register = atr.write();478register.register::<ComponentA>();479register.register::<ComponentB>();480}481world.insert_resource(atr);482483let entity = world.spawn((ComponentA, ComponentB)).id();484485let scene = DynamicSceneBuilder::from_world(&world)486.extract_entity(entity)487.build();488489assert_eq!(scene.entities.len(), 1);490assert_eq!(scene.entities[0].entity, entity);491assert_eq!(scene.entities[0].components.len(), 2);492assert!(scene.entities[0].components[0].represents::<ComponentA>());493assert!(scene.entities[0].components[1].represents::<ComponentB>());494}495496#[test]497fn extract_entity_order() {498let mut world = World::default();499world.init_resource::<AppTypeRegistry>();500501// Spawn entities in order502let entity_a = world.spawn_empty().id();503let entity_b = world.spawn_empty().id();504let entity_c = world.spawn_empty().id();505let entity_d = world.spawn_empty().id();506507// Insert entities out of order508let builder = DynamicSceneBuilder::from_world(&world)509.extract_entity(entity_b)510.extract_entities([entity_d, entity_a].into_iter())511.extract_entity(entity_c);512513let mut entities = builder.build().entities.into_iter();514515// Assert entities are ordered516assert_eq!(entity_d, entities.next().map(|e| e.entity).unwrap());517assert_eq!(entity_c, entities.next().map(|e| e.entity).unwrap());518assert_eq!(entity_b, entities.next().map(|e| e.entity).unwrap());519assert_eq!(entity_a, entities.next().map(|e| e.entity).unwrap());520}521522#[test]523fn extract_query() {524let mut world = World::default();525526let atr = AppTypeRegistry::default();527{528let mut register = atr.write();529register.register::<ComponentA>();530register.register::<ComponentB>();531}532world.insert_resource(atr);533534let entity_a_b = world.spawn((ComponentA, ComponentB)).id();535let entity_a = world.spawn(ComponentA).id();536let _entity_b = world.spawn(ComponentB).id();537538let mut query = world.query_filtered::<Entity, With<ComponentA>>();539let scene = DynamicSceneBuilder::from_world(&world)540.extract_entities(query.iter(&world))541.build();542543assert_eq!(scene.entities.len(), 2);544let mut scene_entities = vec![scene.entities[0].entity, scene.entities[1].entity];545scene_entities.sort();546assert_eq!(scene_entities, [entity_a, entity_a_b]);547}548549#[test]550fn remove_componentless_entity() {551let mut world = World::default();552553let atr = AppTypeRegistry::default();554atr.write().register::<ComponentA>();555world.insert_resource(atr);556557let entity_a = world.spawn(ComponentA).id();558let entity_b = world.spawn(ComponentB).id();559560let scene = DynamicSceneBuilder::from_world(&world)561.extract_entities([entity_a, entity_b].into_iter())562.remove_empty_entities()563.build();564565assert_eq!(scene.entities.len(), 1);566assert_eq!(scene.entities[0].entity, entity_a);567}568569#[test]570fn extract_one_resource() {571let mut world = World::default();572573let atr = AppTypeRegistry::default();574atr.write().register::<ResourceA>();575world.insert_resource(atr);576577world.insert_resource(ResourceA);578579let scene = DynamicSceneBuilder::from_world(&world)580.extract_resources()581.build();582583assert_eq!(scene.resources.len(), 1);584assert!(scene.resources[0].represents::<ResourceA>());585}586587#[test]588fn extract_one_resource_twice() {589let mut world = World::default();590591let atr = AppTypeRegistry::default();592atr.write().register::<ResourceA>();593world.insert_resource(atr);594595world.insert_resource(ResourceA);596597let scene = DynamicSceneBuilder::from_world(&world)598.extract_resources()599.extract_resources()600.build();601602assert_eq!(scene.resources.len(), 1);603assert!(scene.resources[0].represents::<ResourceA>());604}605606#[test]607fn should_extract_allowed_components() {608let mut world = World::default();609610let atr = AppTypeRegistry::default();611{612let mut register = atr.write();613register.register::<ComponentA>();614register.register::<ComponentB>();615}616world.insert_resource(atr);617618let entity_a_b = world.spawn((ComponentA, ComponentB)).id();619let entity_a = world.spawn(ComponentA).id();620let entity_b = world.spawn(ComponentB).id();621622let scene = DynamicSceneBuilder::from_world(&world)623.allow_component::<ComponentA>()624.extract_entities([entity_a_b, entity_a, entity_b].into_iter())625.build();626627assert_eq!(scene.entities.len(), 3);628assert!(scene.entities[2].components[0].represents::<ComponentA>());629assert!(scene.entities[1].components[0].represents::<ComponentA>());630assert_eq!(scene.entities[0].components.len(), 0);631}632633#[test]634fn should_not_extract_denied_components() {635let mut world = World::default();636637let atr = AppTypeRegistry::default();638{639let mut register = atr.write();640register.register::<ComponentA>();641register.register::<ComponentB>();642}643world.insert_resource(atr);644645let entity_a_b = world.spawn((ComponentA, ComponentB)).id();646let entity_a = world.spawn(ComponentA).id();647let entity_b = world.spawn(ComponentB).id();648649let scene = DynamicSceneBuilder::from_world(&world)650.deny_component::<ComponentA>()651.extract_entities([entity_a_b, entity_a, entity_b].into_iter())652.build();653654assert_eq!(scene.entities.len(), 3);655assert!(scene.entities[0].components[0].represents::<ComponentB>());656assert_eq!(scene.entities[1].components.len(), 0);657assert!(scene.entities[2].components[0].represents::<ComponentB>());658}659660#[test]661fn should_extract_allowed_resources() {662let mut world = World::default();663664let atr = AppTypeRegistry::default();665{666let mut register = atr.write();667register.register::<ResourceA>();668register.register::<ResourceB>();669}670world.insert_resource(atr);671672world.insert_resource(ResourceA);673world.insert_resource(ResourceB);674675let scene = DynamicSceneBuilder::from_world(&world)676.allow_resource::<ResourceA>()677.extract_resources()678.build();679680assert_eq!(scene.resources.len(), 1);681assert!(scene.resources[0].represents::<ResourceA>());682}683684#[test]685fn should_not_extract_denied_resources() {686let mut world = World::default();687688let atr = AppTypeRegistry::default();689{690let mut register = atr.write();691register.register::<ResourceA>();692register.register::<ResourceB>();693}694world.insert_resource(atr);695696world.insert_resource(ResourceA);697world.insert_resource(ResourceB);698699let scene = DynamicSceneBuilder::from_world(&world)700.deny_resource::<ResourceA>()701.extract_resources()702.build();703704assert_eq!(scene.resources.len(), 1);705assert!(scene.resources[0].represents::<ResourceB>());706}707708#[test]709fn should_use_from_reflect() {710#[derive(Component, Reflect)]711#[reflect(Component)]712struct SomeType(i32);713714#[derive(Resource, Reflect)]715#[reflect(Resource)]716struct SomeResource(i32);717718let mut world = World::default();719let atr = AppTypeRegistry::default();720{721let mut register = atr.write();722register.register::<SomeType>();723register.register::<SomeResource>();724}725world.insert_resource(atr);726727world.insert_resource(SomeResource(123));728let entity = world.spawn(SomeType(123)).id();729730let scene = DynamicSceneBuilder::from_world(&world)731.extract_resources()732.extract_entities(vec![entity].into_iter())733.build();734735let component = &scene.entities[0].components[0];736assert!(component737.try_as_reflect()738.expect("component should be concrete due to `FromReflect`")739.is::<SomeType>());740741let resource = &scene.resources[0];742assert!(resource743.try_as_reflect()744.expect("resource should be concrete due to `FromReflect`")745.is::<SomeResource>());746}747}748749750