use std::path::Path;
use bevy::prelude::*;
use bevy::ecs::all_tuples;
use bevy::ecs::system::SystemState;
use bevy::ecs::component::ComponentId;
use bevy::ecs::query::ReadOnlyWorldQuery;
use bevy::scene::DynamicEntity;
use bevy::utils::{HashMap, HashSet};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum SceneExportError {
#[error("Bevy Scene serialization to RON format failed")]
Ron(#[from] ron::Error),
#[error("Error writing to output file")]
Io(#[from] std::io::Error),
}
pub fn scene_from_query_components<Q, F>(
world: &mut World,
) -> DynamicScene
where
Q: ComponentList,
F: ReadOnlyWorldQuery + 'static,
{
let mut ss = SystemState::<Query<Entity, (Q::QueryFilter, F)>>::new(world);
let type_registry = world.get_resource::<AppTypeRegistry>()
.expect("The World provided for scene generation does not contain a TypeRegistry")
.read();
let q = ss.get(world);
let entities = q.iter().map(|entity| {
let get_reflect_by_id = |id|
world.components()
.get_info(id)
.and_then(|info| type_registry.get(info.type_id().unwrap()))
.and_then(|reg| reg.data::<ReflectComponent>())
.and_then(|rc| rc.reflect(world, entity))
.map(|c| c.clone_value());
let mut ids = HashSet::new();
Q::do_component_ids(world, &mut |id| {ids.insert(id);});
let components = ids.into_iter()
.filter_map(get_reflect_by_id)
.collect();
DynamicEntity {
entity: entity.index(),
components,
}
}).collect();
DynamicScene {
entities,
}
}
pub fn scene_file_from_query_components<Q, F>(
world: &mut World,
path: impl AsRef<Path>,
) -> Result<DynamicScene, SceneExportError>
where
Q: ComponentList,
F: ReadOnlyWorldQuery + 'static,
{
let scene = scene_from_query_components::<Q, F>(world);
let type_registry = world.get_resource::<AppTypeRegistry>()
.expect("The World provided for scene generation does not contain a TypeRegistry");
let data = scene.serialize_ron(type_registry)?;
std::fs::write(path, &data)?;
Ok(scene)
}
pub fn add_scene_from_query_components<Q, F>(
world: &mut World,
) -> Handle<DynamicScene>
where
Q: ComponentList,
F: ReadOnlyWorldQuery + 'static,
{
let scene = scene_from_query_components::<Q, F>(world);
let mut assets = world.get_resource_mut::<Assets<DynamicScene>>()
.expect("World does not have an Assets<DynamicScene> to add the new scene to");
assets.add(scene)
}
pub fn scene_from_query_filter<F>(
world: &mut World,
) -> DynamicScene
where
F: ReadOnlyWorldQuery + 'static,
{
let mut ss = SystemState::<Query<Entity, F>>::new(world);
let type_registry = world.get_resource::<AppTypeRegistry>()
.expect("The World provided for scene generation does not contain a TypeRegistry")
.read();
let q = ss.get(world);
let entities = q.iter().map(|entity| {
let get_reflect_by_id = |id|
world.components()
.get_info(id)
.and_then(|info| type_registry.get(info.type_id().unwrap()))
.and_then(|reg| reg.data::<ReflectComponent>())
.and_then(|rc| rc.reflect(world, entity))
.map(|c| c.clone_value());
let components = world.entities()
.get(entity)
.and_then(|eloc| world.archetypes().get(eloc.archetype_id))
.into_iter()
.flat_map(|a| a.components())
.filter_map(get_reflect_by_id)
.collect();
DynamicEntity {
entity: entity.index(),
components,
}
}).collect();
DynamicScene {
entities,
}
}
pub fn scene_file_from_query_filter<F>(
world: &mut World,
path: impl AsRef<Path>,
) -> Result<DynamicScene, SceneExportError>
where
F: ReadOnlyWorldQuery + 'static,
{
let scene = scene_from_query_filter::<F>(world);
let type_registry = world.get_resource::<AppTypeRegistry>()
.expect("The World provided for scene generation does not contain a TypeRegistry");
let data = scene.serialize_ron(type_registry)?;
std::fs::write(path, &data)?;
Ok(scene)
}
pub fn add_scene_from_query_filter<F>(
world: &mut World,
) -> Handle<DynamicScene>
where
F: ReadOnlyWorldQuery + 'static,
{
let scene = scene_from_query_filter::<F>(world);
let mut assets = world.get_resource_mut::<Assets<DynamicScene>>()
.expect("World does not have an Assets<DynamicScene> to add the new scene to");
assets.add(scene)
}
enum ComponentSelection {
All,
ByIds(HashSet<ComponentId>),
}
pub struct SceneBuilder<'w> {
world: &'w mut World,
ec: HashMap<Entity, ComponentSelection>,
ignored: HashSet<ComponentId>,
}
impl<'w> SceneBuilder<'w> {
pub fn new(world: &'w mut World) -> SceneBuilder<'w> {
SceneBuilder {
world,
ec: Default::default(),
ignored: Default::default(),
}
}
pub fn ignore_components<Q>(&mut self) -> &mut Self
where
Q: ComponentList,
{
Q::do_component_ids(self.world, &mut |id| {self.ignored.insert(id);});
self
}
pub fn add_from_query_filter<F>(&mut self) -> &mut Self
where
F: ReadOnlyWorldQuery + 'static,
{
let mut ss = SystemState::<Query<Entity, F>>::new(self.world);
let q = ss.get(self.world);
for e in q.iter() {
self.ec.insert(e, ComponentSelection::All);
}
self
}
pub fn add_entity(&mut self, e: Entity) -> &mut Self {
self.ec.insert(e, ComponentSelection::All);
self
}
pub fn add_components_to_entity<Q>(&mut self, e: Entity) -> &mut Self
where
Q: ComponentList,
{
if let Some(item) = self.ec.get_mut(&e) {
if let ComponentSelection::ByIds(c) = item {
Q::do_component_ids(self.world, &mut |id| {c.insert(id);});
}
} else {
let mut c = HashSet::default();
Q::do_component_ids(self.world, &mut |id| {c.insert(id);});
self.ec.insert(e, ComponentSelection::ByIds(c));
}
self
}
pub fn add_entities<I>(&mut self, entities: I) -> &mut Self
where
I: IntoIterator<Item = Entity>,
{
for e in entities {
self.ec.insert(e, ComponentSelection::All);
}
self
}
pub fn add_components_to_entities<I, Q>(&mut self, entities: I) -> &mut Self
where
I: IntoIterator<Item = Entity>,
Q: ComponentList,
{
for e in entities {
if let Some(item) = self.ec.get_mut(&e) {
if let ComponentSelection::ByIds(c) = item {
Q::do_component_ids(self.world, &mut |id| {c.insert(id);});
}
} else {
let mut c = HashSet::default();
Q::do_component_ids(self.world, &mut |id| {c.insert(id);});
self.ec.insert(e, ComponentSelection::ByIds(c));
}
}
self
}
pub fn add_with_components<Q, F>(&mut self) -> &mut Self
where
Q: ComponentList,
F: ReadOnlyWorldQuery + 'static,
{
let mut ss = SystemState::<Query<Entity, (Q::QueryFilter, F)>>::new(self.world);
let q = ss.get(self.world);
for e in q.iter() {
if let Some(item) = self.ec.get_mut(&e) {
if let ComponentSelection::ByIds(c) = item {
Q::do_component_ids(self.world, &mut |id| {c.insert(id);});
}
} else {
let mut c = HashSet::default();
Q::do_component_ids(self.world, &mut |id| {c.insert(id);});
self.ec.insert(e, ComponentSelection::ByIds(c));
}
}
self
}
pub fn build_scene(&self) -> DynamicScene {
let type_registry = self.world.get_resource::<AppTypeRegistry>()
.expect("The World provided to the SceneBuilder does not contain a TypeRegistry")
.read();
let entities = self.ec.iter().map(|(entity, csel)| {
let get_reflect_by_id = |id|
self.world.components()
.get_info(id)
.and_then(|info| type_registry.get(info.type_id().unwrap()))
.and_then(|reg| reg.data::<ReflectComponent>())
.and_then(|rc| rc.reflect(self.world, *entity))
.map(|c| c.clone_value());
let components = match csel {
ComponentSelection::All => {
self.world.entities()
.get(*entity)
.and_then(|eloc| self.world.archetypes().get(eloc.archetype_id))
.into_iter()
.flat_map(|a| a.components())
.filter(|id| !self.ignored.contains(&id))
.filter_map(get_reflect_by_id)
.collect()
},
ComponentSelection::ByIds(ids) => {
ids.iter()
.cloned()
.filter_map(get_reflect_by_id)
.collect()
},
};
DynamicEntity {
entity: entity.index(),
components,
}
}).collect();
DynamicScene {
entities,
}
}
pub fn export_to_file(&self, path: impl AsRef<Path>) -> Result<DynamicScene, SceneExportError> {
let scene = self.build_scene();
let type_registry = self.world.get_resource::<AppTypeRegistry>()
.expect("The World provided to the SceneBuilder does not contain a TypeRegistry");
let data = scene.serialize_ron(type_registry)?;
std::fs::write(path, &data)?;
Ok(scene)
}
pub fn build_scene_and_add(&mut self) -> Handle<DynamicScene> {
let scene = self.build_scene();
let mut assets = self.world.get_resource_mut::<Assets<DynamicScene>>()
.expect("World does not have an Assets<DynamicScene> to add the new scene to");
assets.add(scene)
}
}
pub trait ComponentList {
type QueryFilter: ReadOnlyWorldQuery + 'static;
fn do_component_ids<F: FnMut(ComponentId)>(world: &World, f: &mut F);
}
impl<T: Component + Reflect> ComponentList for &T {
type QueryFilter = With<T>;
#[inline]
fn do_component_ids<F: FnMut(ComponentId)>(world: &World, f: &mut F) {
if let Some(id) = world.component_id::<T>() {
f(id);
}
}
}
impl<T: Component + Reflect> ComponentList for Option<&T> {
type QueryFilter = ();
#[inline]
fn do_component_ids<F: FnMut(ComponentId)>(world: &World, f: &mut F) {
if let Some(id) = world.component_id::<T>() {
f(id);
}
}
}
macro_rules! componentlist_impl {
($($x:ident),*) => {
impl<$($x: ComponentList),*> ComponentList for ($($x,)*) {
type QueryFilter = ($($x::QueryFilter,)*);
#[inline]
fn do_component_ids<F: FnMut(ComponentId)>(_world: &World, _f: &mut F) {
$($x::do_component_ids(_world, _f);)*
}
}
};
}
all_tuples!(componentlist_impl, 0, 15, T);
#[cfg(test)]
mod test {
}