bevy/crates/bevy_derive/src/lib.rs

493 lines
18 KiB
Rust
Raw Normal View History

2020-01-21 11:15:28 +00:00
extern crate proc_macro;
2020-05-04 00:54:16 +00:00
mod modules;
2020-02-10 05:41:51 +00:00
use darling::FromMeta;
2020-02-10 02:04:18 +00:00
use inflector::Inflector;
2020-05-06 01:44:32 +00:00
use modules::{get_modules, get_path};
2020-01-19 10:02:12 +00:00
use proc_macro::TokenStream;
2020-02-10 02:04:18 +00:00
use quote::{format_ident, quote};
2020-04-06 21:20:53 +00:00
use syn::{parse_macro_input, Data, DataStruct, DeriveInput, Field, Fields, Ident, Path, Type};
2020-01-19 10:02:12 +00:00
2020-03-18 23:06:33 +00:00
#[derive(FromMeta, Debug, Default)]
struct EntityArchetypeAttributeArgs {
#[darling(default)]
pub tag: Option<bool>,
}
#[proc_macro_derive(Resource, attributes(module))]
2020-04-30 20:26:01 +00:00
pub fn derive_resource(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let fields = match &ast.data {
Data::Struct(DataStruct {
fields: Fields::Named(fields),
..
}) => &fields.named,
_ => panic!("expected a struct with named fields"),
};
let modules = get_modules(&ast);
let bevy_app_path = get_path(&modules.bevy_app);
2020-05-01 05:30:51 +00:00
let field_types = fields.iter().map(|field| &field.ty);
2020-04-30 20:26:01 +00:00
2020-05-01 05:30:51 +00:00
let fields = fields.iter().map(|field| field.ident.as_ref().unwrap());
2020-04-30 20:26:01 +00:00
let generics = ast.generics;
let (impl_generics, ty_generics, _where_clause) = generics.split_for_impl();
let struct_name = &ast.ident;
TokenStream::from(quote! {
impl #impl_generics #bevy_app_path::FromResources for #struct_name#ty_generics {
fn from_resources(resources: &Resources) -> Self {
2020-04-30 20:26:01 +00:00
use #bevy_app_path::FromResources;
#struct_name {
#(#fields: <#field_types>::from_resources(resources),)*
}
}
}
})
}
2020-05-04 00:54:16 +00:00
#[proc_macro_derive(Uniform, attributes(uniform, module))]
pub fn derive_uniform(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let modules = get_modules(&ast);
let bevy_asset_path = get_path(&modules.bevy_asset);
let bevy_core_path = get_path(&modules.bevy_core);
let bevy_render_path = get_path(&modules.bevy_render);
let generics = ast.generics;
let (impl_generics, ty_generics, _where_clause) = generics.split_for_impl();
let struct_name = &ast.ident;
let struct_name_string = struct_name.to_string();
TokenStream::from(quote! {
impl #impl_generics #bevy_render_path::shader::AsUniforms for #struct_name#ty_generics {
fn get_field_infos() -> &'static [#bevy_render_path::shader::FieldInfo] {
static FIELD_INFOS: &[#bevy_render_path::shader::FieldInfo] = &[
#bevy_render_path::shader::FieldInfo {
name: #struct_name_string,
uniform_name: #struct_name_string,
texture_name: #struct_name_string,
sampler_name: #struct_name_string,
is_instanceable: false,
}
];
&FIELD_INFOS
}
fn get_field_bind_type(&self, name: &str) -> Option<#bevy_render_path::shader::FieldBindType> {
use #bevy_render_path::shader::AsFieldBindType;
match name {
#struct_name_string => self.get_bind_type(),
_ => None,
}
}
fn get_uniform_bytes(&self, name: &str) -> Option<Vec<u8>> {
use #bevy_core_path::bytes::GetBytes;
match name {
#struct_name_string => Some(self.get_bytes()),
_ => None,
}
}
fn get_uniform_bytes_ref(&self, name: &str) -> Option<&[u8]> {
use #bevy_core_path::bytes::GetBytes;
match name {
#struct_name_string => self.get_bytes_ref(),
_ => None,
}
}
fn get_uniform_texture(&self, name: &str) -> Option<#bevy_asset_path::Handle<#bevy_render_path::texture::Texture>> {
None
}
// TODO: move this to field_info and add has_shader_def(&self, &str) -> bool
// TODO: this will be very allocation heavy. find a way to either make this allocation free
// or alternatively only run it when the shader_defs have changed
fn get_shader_defs(&self) -> Option<Vec<String>> {
None
}
fn get_vertex_buffer_descriptor() -> Option<&'static #bevy_render_path::pipeline::VertexBufferDescriptor> {
None
}
}
})
}
2020-04-06 21:20:53 +00:00
#[proc_macro_derive(EntityArchetype, attributes(tag, module))]
2020-01-19 10:02:12 +00:00
pub fn derive_entity_archetype(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let fields = match &ast.data {
2020-02-10 02:04:18 +00:00
Data::Struct(DataStruct {
fields: Fields::Named(fields),
..
}) => &fields.named,
2020-01-19 10:02:12 +00:00
_ => panic!("expected a struct with named fields"),
};
2020-04-06 21:20:53 +00:00
let modules = get_modules(&ast);
let bevy_app_path = get_path(&modules.bevy_app);
let legion_path = get_path(&modules.legion);
2020-03-18 23:06:33 +00:00
let tag_fields = fields
.iter()
.filter(|f| {
f.attrs
.iter()
.find(|a| a.path.get_ident().as_ref().unwrap().to_string() == "tag")
.is_some()
})
2020-03-22 05:35:57 +00:00
.map(|field| field.ident.as_ref().unwrap())
.collect::<Vec<&Ident>>();
2020-03-18 23:06:33 +00:00
let component_fields = fields
.iter()
.filter(|f| {
f.attrs
.iter()
.find(|a| a.path.get_ident().as_ref().unwrap().to_string() == "tag")
.is_none()
})
2020-03-22 05:35:57 +00:00
.map(|field| field.ident.as_ref().unwrap())
.collect::<Vec<&Ident>>();
2020-03-18 23:06:33 +00:00
2020-02-18 04:23:00 +00:00
let generics = ast.generics;
let (impl_generics, ty_generics, _where_clause) = generics.split_for_impl();
2020-01-19 10:02:12 +00:00
let struct_name = &ast.ident;
TokenStream::from(quote! {
2020-04-06 21:20:53 +00:00
impl #impl_generics #bevy_app_path::EntityArchetype for #struct_name#ty_generics {
fn insert(self, world: &mut #legion_path::prelude::World) -> #legion_path::prelude::Entity {
2020-03-18 23:06:33 +00:00
*world.insert((#(self.#tag_fields,)*),
vec![(
#(self.#component_fields,)*
)
]).first().unwrap()
2020-02-10 02:04:18 +00:00
}
2020-03-22 05:35:57 +00:00
2020-04-06 21:20:53 +00:00
fn insert_command_buffer(self, command_buffer: &mut #legion_path::prelude::CommandBuffer) -> #legion_path::prelude::Entity {
2020-03-22 05:35:57 +00:00
*command_buffer.insert((#(self.#tag_fields,)*),
vec![(
#(self.#component_fields,)*
)
]).first().unwrap()
}
2020-02-10 02:04:18 +00:00
}
})
}
// TODO: ensure shader_def and instance/vertex are mutually exclusive
2020-02-10 05:41:51 +00:00
#[derive(FromMeta, Debug, Default)]
struct UniformAttributeArgs {
#[darling(default)]
pub ignore: Option<bool>,
#[darling(default)]
2020-02-11 03:26:04 +00:00
pub shader_def: Option<bool>,
#[darling(default)]
pub instance: Option<bool>,
#[darling(default)]
pub vertex: Option<bool>,
#[darling(default)]
2020-04-06 03:19:02 +00:00
pub bevy_render_path: Option<String>,
#[darling(default)]
pub bevy_asset_path: Option<String>,
#[darling(default)]
pub bevy_core_path: Option<String>,
2020-02-10 05:41:51 +00:00
}
2020-02-10 02:04:18 +00:00
2020-04-06 21:20:53 +00:00
static UNIFORM_ATTRIBUTE_NAME: &'static str = "uniform";
#[proc_macro_derive(Uniforms, attributes(uniform, module))]
2020-02-10 02:04:18 +00:00
pub fn derive_uniforms(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
2020-04-06 21:20:53 +00:00
let modules = get_modules(&ast);
2020-04-06 21:20:53 +00:00
let bevy_render_path: Path = get_path(&modules.bevy_render);
let bevy_core_path: Path = get_path(&modules.bevy_core);
let bevy_asset_path: Path = get_path(&modules.bevy_asset);
2020-02-10 05:41:51 +00:00
2020-02-10 02:04:18 +00:00
let fields = match &ast.data {
Data::Struct(DataStruct {
fields: Fields::Named(fields),
..
}) => &fields.named,
_ => panic!("expected a struct with named fields"),
};
2020-02-10 05:41:51 +00:00
let uniform_fields = fields
.iter()
.map(|f| {
(
f,
f.attrs
.iter()
.find(|a| {
a.path.get_ident().as_ref().unwrap().to_string() == UNIFORM_ATTRIBUTE_NAME
})
.map(|a| {
UniformAttributeArgs::from_meta(&a.parse_meta().unwrap())
.unwrap_or_else(|_err| UniformAttributeArgs::default())
}),
)
})
.collect::<Vec<(&Field, Option<UniformAttributeArgs>)>>();
let active_uniform_fields = uniform_fields
.iter()
.filter(|(_field, attrs)| {
attrs.is_none()
|| match attrs.as_ref().unwrap().ignore {
Some(ignore) => !ignore,
None => true,
}
})
.map(|(f, _attr)| *f)
.collect::<Vec<&Field>>();
2020-02-11 03:26:04 +00:00
let shader_def_fields = uniform_fields
2020-02-10 05:41:51 +00:00
.iter()
.filter(|(_field, attrs)| match attrs {
Some(attrs) => match attrs.shader_def {
Some(shader_def) => shader_def,
2020-02-11 03:26:04 +00:00
None => false,
},
None => false,
2020-02-10 05:41:51 +00:00
})
2020-02-11 03:26:04 +00:00
.map(|(f, _attr)| *f)
.collect::<Vec<&Field>>();
let shader_def_field_names = shader_def_fields.iter().map(|field| &field.ident);
let shader_def_field_names_screaming_snake = shader_def_fields.iter().map(|field| {
field
.ident
.as_ref()
.unwrap()
.to_string()
.to_screaming_snake_case()
});
2020-02-10 05:41:51 +00:00
2020-02-10 02:04:18 +00:00
let struct_name = &ast.ident;
let struct_name_string = struct_name.to_string();
let struct_name_uppercase = struct_name.to_string().to_uppercase();
let field_infos_ident = format_ident!("{}_FIELD_INFO", struct_name_uppercase);
2020-03-18 23:06:33 +00:00
let vertex_buffer_descriptor_ident =
format_ident!("{}_VERTEX_BUFFER_DESCRIPTOR", struct_name_uppercase);
2020-02-11 03:26:04 +00:00
2020-03-18 23:06:33 +00:00
let active_uniform_field_names = active_uniform_fields
.iter()
.map(|field| &field.ident)
.collect::<Vec<_>>();
2020-02-21 08:15:24 +00:00
2020-03-18 23:06:33 +00:00
let active_uniform_field_name_strings = active_uniform_fields
.iter()
.map(|field| field.ident.as_ref().unwrap().to_string())
.collect::<Vec<String>>();
2020-02-22 20:42:40 +00:00
let vertex_buffer_fields = uniform_fields
.iter()
.map(|(field, attrs)| {
(
field,
match attrs {
Some(attrs) => (
(match attrs.instance {
Some(instance) => instance,
None => false,
}),
(match attrs.vertex {
Some(vertex) => vertex,
None => false,
}),
),
None => (false, false),
},
)
})
.filter(|(_f, (instance, vertex))| *instance || *vertex);
let vertex_buffer_field_types = vertex_buffer_fields
.clone()
.map(|(f, _)| &f.ty)
.collect::<Vec<&Type>>();
let vertex_buffer_field_names_pascal = vertex_buffer_fields
.map(|(f, (instance, _vertex))| {
let pascal_field = f.ident.as_ref().unwrap().to_string().to_pascal_case();
if instance {
format!("I_{}_{}", struct_name, pascal_field)
} else {
format!("{}_{}", struct_name, pascal_field)
}
})
.collect::<Vec<String>>();
2020-02-22 23:01:11 +00:00
let mut uniform_name_strings = Vec::new();
2020-02-24 07:41:48 +00:00
let mut texture_and_sampler_name_strings = Vec::new();
let mut texture_and_sampler_name_idents = Vec::new();
2020-03-18 23:06:33 +00:00
let field_infos = uniform_fields
.iter()
.filter(|(_field, attrs)| {
attrs.is_none()
|| match attrs.as_ref().unwrap().ignore {
Some(ignore) => !ignore,
None => true,
}
2020-02-21 08:15:24 +00:00
})
2020-03-18 23:06:33 +00:00
.map(|(f, attrs)| {
let field_name = f.ident.as_ref().unwrap().to_string();
let uniform = format!("{}_{}", struct_name, field_name);
let texture = format!("{}", uniform);
let sampler = format!("{}_sampler", uniform);
uniform_name_strings.push(uniform.clone());
texture_and_sampler_name_strings.push(texture.clone());
texture_and_sampler_name_strings.push(sampler.clone());
texture_and_sampler_name_idents.push(f.ident.clone());
texture_and_sampler_name_idents.push(f.ident.clone());
let is_instanceable = match attrs {
Some(attrs) => match attrs.instance {
Some(instance) => instance,
2020-03-18 23:06:33 +00:00
None => false,
},
None => false,
};
2020-04-06 03:19:02 +00:00
quote!(#bevy_render_path::shader::FieldInfo {
2020-03-18 23:06:33 +00:00
name: #field_name,
uniform_name: #uniform,
texture_name: #texture,
sampler_name: #sampler,
is_instanceable: #is_instanceable,
})
});
2020-02-21 08:15:24 +00:00
2020-02-22 23:01:11 +00:00
TokenStream::from(quote! {
2020-04-06 03:19:02 +00:00
static #field_infos_ident: &[#bevy_render_path::shader::FieldInfo] = &[
2020-03-15 08:12:56 +00:00
#(#field_infos,)*
2020-02-10 02:04:18 +00:00
];
2020-04-06 03:19:02 +00:00
static #vertex_buffer_descriptor_ident: #bevy_render_path::once_cell::sync::Lazy<#bevy_render_path::pipeline::VertexBufferDescriptor> =
#bevy_render_path::once_cell::sync::Lazy::new(|| {
use #bevy_render_path::pipeline::{VertexFormat, AsVertexFormats, VertexAttributeDescriptor};
let mut vertex_formats: Vec<(&str,&[VertexFormat])> = vec![
#((#vertex_buffer_field_names_pascal, <#vertex_buffer_field_types>::as_vertex_formats()),)*
];
let mut shader_location = 0;
let mut offset = 0;
let vertex_attribute_descriptors = vertex_formats.drain(..).map(|(name, formats)| {
formats.iter().enumerate().map(|(i, format)| {
let size = format.get_size();
let formatted_name = if formats.len() > 1 {
format!("{}_{}", name, i)
} else {
format!("{}", name)
};
let descriptor = VertexAttributeDescriptor {
name: formatted_name.into(),
offset,
format: *format,
shader_location,
};
offset += size;
shader_location += 1;
descriptor
}).collect::<Vec<VertexAttributeDescriptor>>()
}).flatten().collect::<Vec<VertexAttributeDescriptor>>();
2020-04-06 03:19:02 +00:00
#bevy_render_path::pipeline::VertexBufferDescriptor {
attributes: vertex_attribute_descriptors,
name: #struct_name_string.into(),
2020-04-06 03:19:02 +00:00
step_mode: #bevy_render_path::pipeline::InputStepMode::Instance,
stride: offset,
}
});
2020-04-06 03:19:02 +00:00
impl #bevy_render_path::shader::AsUniforms for #struct_name {
fn get_field_infos() -> &'static [#bevy_render_path::shader::FieldInfo] {
2020-03-15 08:12:56 +00:00
#field_infos_ident
2020-02-10 02:04:18 +00:00
}
2020-04-06 03:19:02 +00:00
fn get_field_bind_type(&self, name: &str) -> Option<#bevy_render_path::shader::FieldBindType> {
use #bevy_render_path::shader::AsFieldBindType;
2020-02-22 20:42:40 +00:00
match name {
2020-05-04 00:54:16 +00:00
#(#active_uniform_field_name_strings => self.#active_uniform_field_names.get_bind_type(),)*
2020-02-22 20:42:40 +00:00
_ => None,
}
2020-02-10 02:04:18 +00:00
}
fn get_uniform_bytes(&self, name: &str) -> Option<Vec<u8>> {
2020-04-06 03:19:02 +00:00
use #bevy_core_path::bytes::GetBytes;
2020-02-10 02:04:18 +00:00
match name {
2020-02-22 23:01:11 +00:00
#(#uniform_name_strings => Some(self.#active_uniform_field_names.get_bytes()),)*
2020-02-10 02:04:18 +00:00
_ => None,
}
}
2020-02-10 05:41:51 +00:00
2020-03-15 01:32:33 +00:00
fn get_uniform_bytes_ref(&self, name: &str) -> Option<&[u8]> {
2020-04-06 03:19:02 +00:00
use #bevy_core_path::bytes::GetBytes;
2020-03-15 01:32:33 +00:00
match name {
#(#uniform_name_strings => self.#active_uniform_field_names.get_bytes_ref(),)*
_ => None,
}
}
2020-04-06 03:19:02 +00:00
fn get_uniform_texture(&self, name: &str) -> Option<#bevy_asset_path::Handle<#bevy_render_path::texture::Texture>> {
use #bevy_render_path::shader::GetTexture;
2020-02-24 07:41:48 +00:00
match name {
#(#texture_and_sampler_name_strings => self.#texture_and_sampler_name_idents.get_texture(),)*
_ => None,
}
}
// TODO: move this to field_info and add has_shader_def(&self, &str) -> bool
2020-02-11 03:26:04 +00:00
// TODO: this will be very allocation heavy. find a way to either make this allocation free
// or alternatively only run it when the shader_defs have changed
fn get_shader_defs(&self) -> Option<Vec<String>> {
2020-04-06 03:19:02 +00:00
use #bevy_render_path::shader::ShaderDefSuffixProvider;
2020-02-11 03:26:04 +00:00
let mut potential_shader_defs: Vec<(&'static str, Option<&'static str>)> = vec![
2020-02-19 06:59:33 +00:00
#((#shader_def_field_names_screaming_snake, self.#shader_def_field_names.get_shader_def()),)*
2020-02-11 03:26:04 +00:00
];
Some(potential_shader_defs.drain(..)
.filter(|(f, shader_def)| shader_def.is_some())
.map(|(f, shader_def)| format!("{}_{}{}", #struct_name_uppercase, f, shader_def.unwrap()))
2020-02-11 03:26:04 +00:00
.collect::<Vec<String>>())
2020-02-10 05:41:51 +00:00
}
2020-04-06 03:19:02 +00:00
fn get_vertex_buffer_descriptor() -> Option<&'static #bevy_render_path::pipeline::VertexBufferDescriptor> {
if #vertex_buffer_descriptor_ident.attributes.len() == 0 {
None
} else {
Some(&#vertex_buffer_descriptor_ident)
}
}
2020-01-19 10:02:12 +00:00
}
2020-02-22 23:01:11 +00:00
})
2020-01-19 10:02:12 +00:00
}
2020-01-21 04:10:40 +00:00
2020-04-05 21:12:14 +00:00
#[proc_macro_derive(DynamicAppPlugin)]
2020-01-21 04:10:40 +00:00
pub fn derive_app_plugin(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let struct_name = &ast.ident;
TokenStream::from(quote! {
#[no_mangle]
2020-04-25 01:23:37 +00:00
pub extern "C" fn _create_plugin() -> *mut bevy::app::AppPlugin {
2020-01-21 04:10:40 +00:00
// TODO: without this the assembly does nothing. why is that the case?
print!("");
// make sure the constructor is the correct type.
let object = #struct_name {};
let boxed = Box::new(object);
Box::into_raw(boxed)
}
})
}