CommandBufferBuilder and setup systems

This commit is contained in:
Carter Anderson 2020-03-21 22:35:57 -07:00
parent d2e160d44a
commit c9aec26f88
6 changed files with 185 additions and 4 deletions

View File

@ -4,7 +4,7 @@ use darling::FromMeta;
use inflector::Inflector;
use proc_macro::TokenStream;
use quote::{format_ident, quote};
use syn::{parse_macro_input, Data, DataStruct, DeriveInput, Field, Fields, Type};
use syn::{parse_macro_input, Data, DataStruct, DeriveInput, Field, Fields, Ident, Type};
#[derive(FromMeta, Debug, Default)]
struct EntityArchetypeAttributeArgs {
@ -31,7 +31,8 @@ pub fn derive_entity_archetype(input: TokenStream) -> TokenStream {
.find(|a| a.path.get_ident().as_ref().unwrap().to_string() == "tag")
.is_some()
})
.map(|field| &field.ident);
.map(|field| field.ident.as_ref().unwrap())
.collect::<Vec<&Ident>>();
let component_fields = fields
.iter()
@ -41,7 +42,8 @@ pub fn derive_entity_archetype(input: TokenStream) -> TokenStream {
.find(|a| a.path.get_ident().as_ref().unwrap().to_string() == "tag")
.is_none()
})
.map(|field| &field.ident);
.map(|field| field.ident.as_ref().unwrap())
.collect::<Vec<&Ident>>();
let generics = ast.generics;
let (impl_generics, ty_generics, _where_clause) = generics.split_for_impl();
@ -57,6 +59,14 @@ pub fn derive_entity_archetype(input: TokenStream) -> TokenStream {
)
]).first().unwrap()
}
fn insert_command_buffer(self, command_buffer: &mut bevy::prelude::CommandBuffer) -> Entity {
*command_buffer.insert((#(self.#tag_fields,)*),
vec![(
#(self.#component_fields,)*
)
]).first().unwrap()
}
}
})
}

65
examples/setup_system.rs Normal file
View File

@ -0,0 +1,65 @@
use bevy::prelude::*;
fn main() {
App::build().add_defaults().add_setup_system(setup()).run();
}
pub fn setup() -> Box<dyn Schedulable> {
SystemBuilder::new("setup")
.write_resource::<AssetStorage<Mesh>>()
.write_resource::<AssetStorage<StandardMaterial>>()
.build(move |command_buffer, _, (meshes, materials), _| {
let cube_handle = meshes.add(Mesh::load(MeshType::Cube));
let plane_handle = meshes.add(Mesh::load(MeshType::Plane { size: 10.0 }));
let cube_material_handle = materials.add(StandardMaterial {
albedo: Color::rgb(0.5, 0.4, 0.3),
..Default::default()
});
let plane_material_handle = materials.add(StandardMaterial {
albedo: Color::rgb(0.1, 0.2, 0.1),
..Default::default()
});
command_buffer
.build()
// plane
.add_entity(MeshEntity {
mesh: plane_handle,
material: plane_material_handle,
// renderable: Renderable::instanced(),
..Default::default()
})
// cube
.add_entity(MeshEntity {
mesh: cube_handle,
material: cube_material_handle,
// renderable: Renderable::instanced(),
translation: Translation::new(-1.5, 0.0, 1.0),
..Default::default()
})
// cube
.add_entity(MeshEntity {
mesh: cube_handle,
material: cube_material_handle,
// renderable: Renderable::instanced(),
translation: Translation::new(1.5, 0.0, 1.0),
..Default::default()
})
// light
.add_entity(LightEntity {
translation: Translation::new(4.0, -4.0, 5.0),
rotation: Rotation::from_euler_angles(0.0, 0.0, 0.0),
..Default::default()
})
// camera
.add_entity(CameraEntity {
local_to_world: LocalToWorld(Mat4::look_at_rh(
Vec3::new(3.0, 8.0, 5.0),
Vec3::new(0.0, 0.0, 0.0),
Vec3::new(0.0, 0.0, 1.0),
)),
..Default::default()
})
.build();
})
}

View File

@ -27,6 +27,7 @@ pub struct AppBuilder {
pub universe: Universe,
pub renderer: Option<Box<dyn Renderer>>,
pub render_graph_builder: RenderGraphBuilder,
pub setup_systems: Vec<Box<dyn Schedulable>>,
pub system_stages: HashMap<String, Vec<Box<dyn Schedulable>>>,
pub runnable_stages: HashMap<String, Vec<Box<dyn Runnable>>>,
pub stage_order: Vec<String>,
@ -43,6 +44,7 @@ impl AppBuilder {
resources,
render_graph_builder: RenderGraphBuilder::new(),
renderer: None,
setup_systems: Vec::new(),
system_stages: HashMap::new(),
runnable_stages: HashMap::new(),
stage_order: Vec::new(),
@ -50,6 +52,14 @@ impl AppBuilder {
}
pub fn build(mut self) -> App {
let mut setup_schedule_builder = Schedule::builder();
for setup_system in self.setup_systems {
setup_schedule_builder = setup_schedule_builder.add_system(setup_system);
}
let mut setup_schedule = setup_schedule_builder.build();
setup_schedule.execute(&mut self.world, &mut self.resources);
let mut schedule_builder = Schedule::builder();
for stage_name in self.stage_order.iter() {
if let Some((_name, stage_systems)) = self.system_stages.remove_entry(stage_name) {
@ -99,6 +109,11 @@ impl AppBuilder {
self.add_system_to_stage(system_stage::UPDATE, system)
}
pub fn add_setup_system(mut self, system: Box<dyn Schedulable>) -> Self {
self.setup_systems.push(system);
self
}
pub fn add_system_to_stage(mut self, stage_name: &str, system: Box<dyn Schedulable>) -> Self {
if let None = self.system_stages.get(stage_name) {
self.system_stages

View File

@ -3,6 +3,7 @@ use legion::prelude::*;
// builder macro that makes defaults easy? Object3dBuilder { Option<Material> } impl Builder for Object3dBuilder { }
pub trait EntityArchetype {
fn insert(self, world: &mut World) -> Entity;
fn insert_command_buffer(self, command_buffer: &mut CommandBuffer) -> Entity;
// this would make composing entities from multiple archetypes possible
// add_components appears to be missing from World. it will be less efficient without that

View File

@ -95,3 +95,93 @@ impl<'a> WorldBuilder<'a> {
}
}
}
pub trait CommandBufferBuilderSource {
fn build(&mut self) -> CommandBufferBuilder;
}
impl CommandBufferBuilderSource for CommandBuffer {
fn build(&mut self) -> CommandBufferBuilder {
CommandBufferBuilder {
command_buffer: self,
current_entity: None,
parent_entity: None,
}
}
}
pub struct CommandBufferBuilder<'a> {
command_buffer: &'a mut CommandBuffer,
current_entity: Option<Entity>,
parent_entity: Option<Entity>,
}
impl<'a> CommandBufferBuilder<'a> {
pub fn build_entity(mut self) -> Self {
let entity = *self.command_buffer.insert((), vec![()]).first().unwrap();
self.current_entity = Some(entity);
self.add_parent_to_current_entity();
self
}
pub fn build(self) {}
// note: this is slow and does a full entity copy
pub fn add<T>(self, component: T) -> Self
where
T: legion::storage::Component,
{
let _ = self
.command_buffer
.add_component(*self.current_entity.as_ref().unwrap(), component);
self
}
pub fn tag<T>(self, tag: T) -> Self
where
T: legion::storage::Tag,
{
let _ = self
.command_buffer
.add_tag(*self.current_entity.as_ref().unwrap(), tag);
self
}
pub fn add_entities<T, C>(self, tags: T, components: C) -> Self
where
T: TagSet + TagLayout + for<'b> Filter<ChunksetFilterData<'b>> + 'static,
C: IntoComponentSource + 'static,
{
self.command_buffer.insert(tags, components);
self
}
pub fn add_entity(mut self, entity_archetype: impl EntityArchetype) -> Self {
let current_entity = entity_archetype.insert_command_buffer(self.command_buffer);
self.current_entity = Some(current_entity);
self.add_parent_to_current_entity();
self
}
pub fn add_children(mut self, build_children: impl Fn(CommandBufferBuilder) -> CommandBufferBuilder) -> Self {
self.parent_entity = self.current_entity;
self.current_entity = None;
self = build_children(self);
self.current_entity = self.parent_entity;
self.parent_entity = None;
self
}
fn add_parent_to_current_entity(&mut self) {
let current_entity = self.current_entity.unwrap();
if let Some(parent_entity) = self.parent_entity {
let _ = self
.command_buffer
.add_component(current_entity, Parent(parent_entity));
let _ = self
.command_buffer
.add_component(current_entity, LocalToParent::identity());
}
}
}

View File

@ -3,7 +3,7 @@ pub use crate::{
asset::{Asset, AssetStorage, Handle},
core::Time,
ecs,
ecs::{default_archetypes::*, EntityArchetype, WorldBuilder, WorldBuilderSource},
ecs::{default_archetypes::*, EntityArchetype, WorldBuilder, WorldBuilderSource, CommandBufferBuilderSource},
render::{
mesh::{Mesh, MeshType},
pipeline::PipelineDescriptor,