diff --git a/bevy_derive/src/lib.rs b/bevy_derive/src/lib.rs index 7d650a054d..fe39ffc6ec 100644 --- a/bevy_derive/src/lib.rs +++ b/bevy_derive/src/lib.rs @@ -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::>(); 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::>(); 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() + } } }) } diff --git a/examples/setup_system.rs b/examples/setup_system.rs new file mode 100644 index 0000000000..d87c51a8ea --- /dev/null +++ b/examples/setup_system.rs @@ -0,0 +1,65 @@ +use bevy::prelude::*; + +fn main() { + App::build().add_defaults().add_setup_system(setup()).run(); +} + +pub fn setup() -> Box { + SystemBuilder::new("setup") + .write_resource::>() + .write_resource::>() + .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(); + }) +} \ No newline at end of file diff --git a/src/app/app_builder.rs b/src/app/app_builder.rs index 6a37d459e7..1c13cd94e7 100644 --- a/src/app/app_builder.rs +++ b/src/app/app_builder.rs @@ -27,6 +27,7 @@ pub struct AppBuilder { pub universe: Universe, pub renderer: Option>, pub render_graph_builder: RenderGraphBuilder, + pub setup_systems: Vec>, pub system_stages: HashMap>>, pub runnable_stages: HashMap>>, pub stage_order: Vec, @@ -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) -> Self { + self.setup_systems.push(system); + self + } + pub fn add_system_to_stage(mut self, stage_name: &str, system: Box) -> Self { if let None = self.system_stages.get(stage_name) { self.system_stages diff --git a/src/ecs/entity_archetype.rs b/src/ecs/entity_archetype.rs index 790686d032..15a8e34756 100644 --- a/src/ecs/entity_archetype.rs +++ b/src/ecs/entity_archetype.rs @@ -3,6 +3,7 @@ use legion::prelude::*; // builder macro that makes defaults easy? Object3dBuilder { Option } 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 diff --git a/src/ecs/world_builder.rs b/src/ecs/world_builder.rs index 386e76712f..b4ccbaba0b 100644 --- a/src/ecs/world_builder.rs +++ b/src/ecs/world_builder.rs @@ -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, + parent_entity: Option, +} + +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(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(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(self, tags: T, components: C) -> Self + where + T: TagSet + TagLayout + for<'b> Filter> + '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()); + } + } +} diff --git a/src/prelude.rs b/src/prelude.rs index d5ebaf11af..1f527b1988 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -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,