2020-01-21 11:15:28 +00:00
|
|
|
extern crate proc_macro;
|
|
|
|
|
2020-02-10 05:41:51 +00:00
|
|
|
use darling::FromMeta;
|
2020-02-10 02:04:18 +00:00
|
|
|
use inflector::Inflector;
|
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-03-22 05:35:57 +00:00
|
|
|
use syn::{parse_macro_input, Data, DataStruct, DeriveInput, Field, Fields, Ident, 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(EntityArchetype, attributes(tag))]
|
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-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-02-18 04:23:00 +00:00
|
|
|
impl #impl_generics bevy::prelude::EntityArchetype for #struct_name#ty_generics {
|
2020-02-18 03:53:48 +00:00
|
|
|
fn insert(self, world: &mut bevy::prelude::World) -> 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
|
|
|
|
|
|
|
fn insert_command_buffer(self, command_buffer: &mut bevy::prelude::CommandBuffer) -> Entity {
|
|
|
|
*command_buffer.insert((#(self.#tag_fields,)*),
|
|
|
|
vec![(
|
|
|
|
#(self.#component_fields,)*
|
|
|
|
)
|
|
|
|
]).first().unwrap()
|
|
|
|
}
|
2020-02-10 02:04:18 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
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>,
|
2020-03-15 19:35:02 +00:00
|
|
|
#[darling(default)]
|
2020-03-22 01:12:30 +00:00
|
|
|
pub instance: Option<bool>,
|
|
|
|
#[darling(default)]
|
|
|
|
pub vertex: Option<bool>,
|
2020-02-10 05:41:51 +00:00
|
|
|
}
|
2020-02-10 02:04:18 +00:00
|
|
|
|
2020-02-10 05:41:51 +00:00
|
|
|
#[proc_macro_derive(Uniforms, attributes(uniform))]
|
2020-02-10 02:04:18 +00:00
|
|
|
pub fn derive_uniforms(input: TokenStream) -> TokenStream {
|
2020-03-18 01:25:27 +00:00
|
|
|
static UNIFORM_ATTRIBUTE_NAME: &'static str = "uniform";
|
2020-02-10 02:04:18 +00:00
|
|
|
let ast = parse_macro_input!(input as DeriveInput);
|
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()
|
2020-02-21 05:52:45 +00:00
|
|
|
.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,
|
2020-02-21 05:52:45 +00:00
|
|
|
},
|
|
|
|
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);
|
2020-02-21 05:52:45 +00:00
|
|
|
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;
|
2020-03-18 01:25:27 +00:00
|
|
|
let struct_name_string = struct_name.to_string();
|
2020-02-10 02:04:18 +00:00
|
|
|
let struct_name_screaming_snake = struct_name.to_string().to_screaming_snake_case();
|
2020-03-15 08:12:56 +00:00
|
|
|
let field_infos_ident = format_ident!("{}_FIELD_INFO", struct_name_screaming_snake);
|
2020-03-18 23:06:33 +00:00
|
|
|
let vertex_buffer_descriptor_ident =
|
|
|
|
format_ident!("{}_VERTEX_BUFFER_DESCRIPTOR", struct_name_screaming_snake);
|
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
|
|
|
|
2020-03-22 01:12:30 +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-03-15 19:35:02 +00:00
|
|
|
}
|
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 {
|
2020-03-22 01:12:30 +00:00
|
|
|
Some(attrs) => match attrs.instance {
|
|
|
|
Some(instance) => instance,
|
2020-03-18 23:06:33 +00:00
|
|
|
None => false,
|
|
|
|
},
|
|
|
|
None => false,
|
|
|
|
};
|
|
|
|
quote!(bevy::render::shader::FieldInfo {
|
|
|
|
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-03-18 01:25:27 +00:00
|
|
|
static #field_infos_ident: &[bevy::render::shader::FieldInfo] = &[
|
2020-03-15 08:12:56 +00:00
|
|
|
#(#field_infos,)*
|
2020-02-10 02:04:18 +00:00
|
|
|
];
|
|
|
|
|
2020-03-18 23:06:33 +00:00
|
|
|
static #vertex_buffer_descriptor_ident: bevy::once_cell::sync::Lazy<bevy::render::pipeline::VertexBufferDescriptor> =
|
2020-03-18 01:25:27 +00:00
|
|
|
bevy::once_cell::sync::Lazy::new(|| {
|
2020-03-22 01:12:30 +00:00
|
|
|
use bevy::render::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,
|
|
|
|
offset,
|
|
|
|
format: *format,
|
|
|
|
shader_location,
|
|
|
|
};
|
|
|
|
offset += size;
|
|
|
|
shader_location += 1;
|
|
|
|
descriptor
|
|
|
|
}).collect::<Vec<VertexAttributeDescriptor>>()
|
|
|
|
}).flatten().collect::<Vec<VertexAttributeDescriptor>>();
|
2020-03-18 01:25:27 +00:00
|
|
|
|
|
|
|
bevy::render::pipeline::VertexBufferDescriptor {
|
2020-03-22 01:12:30 +00:00
|
|
|
attributes: vertex_attribute_descriptors,
|
2020-03-18 01:25:27 +00:00
|
|
|
name: #struct_name_string.to_string(),
|
|
|
|
step_mode: bevy::render::pipeline::InputStepMode::Instance,
|
2020-03-22 01:12:30 +00:00
|
|
|
stride: offset,
|
2020-03-18 01:25:27 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2020-03-10 06:08:09 +00:00
|
|
|
impl bevy::render::shader::AsUniforms for #struct_name {
|
2020-03-15 08:12:56 +00:00
|
|
|
fn get_field_infos(&self) -> &[bevy::render::shader::FieldInfo] {
|
|
|
|
#field_infos_ident
|
2020-02-10 02:04:18 +00:00
|
|
|
}
|
|
|
|
|
2020-03-10 06:08:09 +00:00
|
|
|
fn get_field_bind_type(&self, name: &str) -> Option<bevy::render::shader::FieldBindType> {
|
|
|
|
use bevy::render::shader::AsFieldBindType;
|
2020-02-22 20:42:40 +00:00
|
|
|
match name {
|
2020-03-18 05:02:01 +00:00
|
|
|
#(#active_uniform_field_name_strings => self.#active_uniform_field_names.get_field_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-02-18 03:53:48 +00:00
|
|
|
use bevy::core::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]> {
|
|
|
|
use bevy::core::bytes::GetBytes;
|
|
|
|
match name {
|
|
|
|
#(#uniform_name_strings => self.#active_uniform_field_names.get_bytes_ref(),)*
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-14 19:56:37 +00:00
|
|
|
fn get_uniform_texture(&self, name: &str) -> Option<bevy::asset::Handle<bevy::render::texture::Texture>> {
|
2020-03-10 06:08:09 +00:00
|
|
|
use bevy::render::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,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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-03-10 06:08:09 +00:00
|
|
|
use bevy::render::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())
|
2020-02-19 04:26:02 +00:00
|
|
|
.map(|(f, shader_def)| format!("{}_{}{}", #struct_name_screaming_snake, f, shader_def.unwrap()))
|
2020-02-11 03:26:04 +00:00
|
|
|
.collect::<Vec<String>>())
|
2020-02-10 05:41:51 +00:00
|
|
|
}
|
2020-03-18 01:25:27 +00:00
|
|
|
|
|
|
|
fn get_vertex_buffer_descriptor() -> Option<&'static bevy::render::pipeline::VertexBufferDescriptor> {
|
2020-03-22 04:10:58 +00:00
|
|
|
if #vertex_buffer_descriptor_ident.attributes.len() == 0 {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(&#vertex_buffer_descriptor_ident)
|
|
|
|
}
|
2020-03-18 01:25:27 +00:00
|
|
|
}
|
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
|
|
|
|
|
|
|
#[proc_macro_derive(RegisterAppPlugin)]
|
|
|
|
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-02-18 03:53:48 +00:00
|
|
|
pub extern "C" fn _create_plugin() -> *mut bevy::plugin::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)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|