2020-01-21 03:15:28 -08:00
|
|
|
extern crate proc_macro;
|
|
|
|
|
2020-02-09 21:41:51 -08:00
|
|
|
use darling::FromMeta;
|
2020-02-09 18:04:18 -08:00
|
|
|
use inflector::Inflector;
|
2020-01-19 02:02:12 -08:00
|
|
|
use proc_macro::TokenStream;
|
2020-02-09 18:04:18 -08:00
|
|
|
use quote::{format_ident, quote};
|
2020-02-22 15:01:11 -08:00
|
|
|
use syn::{parse_macro_input, Data, DataStruct, DeriveInput, Field, Fields};
|
2020-01-19 02:02:12 -08:00
|
|
|
|
|
|
|
#[proc_macro_derive(EntityArchetype)]
|
|
|
|
pub fn derive_entity_archetype(input: TokenStream) -> TokenStream {
|
|
|
|
let ast = parse_macro_input!(input as DeriveInput);
|
|
|
|
let fields = match &ast.data {
|
2020-02-09 18:04:18 -08:00
|
|
|
Data::Struct(DataStruct {
|
|
|
|
fields: Fields::Named(fields),
|
|
|
|
..
|
|
|
|
}) => &fields.named,
|
2020-01-19 02:02:12 -08:00
|
|
|
_ => panic!("expected a struct with named fields"),
|
|
|
|
};
|
|
|
|
|
2020-02-17 20:23:00 -08:00
|
|
|
let generics = ast.generics;
|
|
|
|
let (impl_generics, ty_generics, _where_clause) = generics.split_for_impl();
|
|
|
|
|
2020-01-19 02:02:12 -08:00
|
|
|
let struct_name = &ast.ident;
|
|
|
|
let field_name = fields.iter().map(|field| &field.ident);
|
|
|
|
|
|
|
|
TokenStream::from(quote! {
|
2020-02-17 20:23:00 -08:00
|
|
|
impl #impl_generics bevy::prelude::EntityArchetype for #struct_name#ty_generics {
|
2020-02-17 19:53:48 -08:00
|
|
|
fn insert(self, world: &mut bevy::prelude::World) -> Entity {
|
2020-01-19 02:02:12 -08:00
|
|
|
*world.insert((), vec![(
|
2020-02-09 18:04:18 -08:00
|
|
|
#(self.#field_name,)*
|
|
|
|
)]).first().unwrap()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2020-02-09 21:41:51 -08:00
|
|
|
#[derive(FromMeta, Debug, Default)]
|
|
|
|
struct UniformAttributeArgs {
|
|
|
|
#[darling(default)]
|
|
|
|
pub ignore: Option<bool>,
|
|
|
|
#[darling(default)]
|
2020-02-10 19:26:04 -08:00
|
|
|
pub shader_def: Option<bool>,
|
2020-02-09 21:41:51 -08:00
|
|
|
}
|
2020-02-09 18:04:18 -08:00
|
|
|
|
2020-02-09 21:41:51 -08:00
|
|
|
#[proc_macro_derive(Uniforms, attributes(uniform))]
|
2020-02-09 18:04:18 -08:00
|
|
|
pub fn derive_uniforms(input: TokenStream) -> TokenStream {
|
2020-02-09 21:41:51 -08:00
|
|
|
const UNIFORM_ATTRIBUTE_NAME: &'static str = "uniform";
|
2020-02-09 18:04:18 -08:00
|
|
|
let ast = parse_macro_input!(input as DeriveInput);
|
2020-02-09 21:41:51 -08:00
|
|
|
|
2020-02-09 18:04:18 -08:00
|
|
|
let fields = match &ast.data {
|
|
|
|
Data::Struct(DataStruct {
|
|
|
|
fields: Fields::Named(fields),
|
|
|
|
..
|
|
|
|
}) => &fields.named,
|
|
|
|
_ => panic!("expected a struct with named fields"),
|
|
|
|
};
|
|
|
|
|
2020-02-09 21:41:51 -08: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-10 19:26:04 -08:00
|
|
|
let shader_def_fields = uniform_fields
|
2020-02-09 21:41:51 -08:00
|
|
|
.iter()
|
2020-02-20 21:52:45 -08:00
|
|
|
.filter(|(_field, attrs)| match attrs {
|
|
|
|
Some(attrs) => match attrs.shader_def {
|
|
|
|
Some(shader_def) => shader_def,
|
2020-02-10 19:26:04 -08:00
|
|
|
None => false,
|
2020-02-20 21:52:45 -08:00
|
|
|
},
|
|
|
|
None => false,
|
2020-02-09 21:41:51 -08:00
|
|
|
})
|
2020-02-10 19:26:04 -08:00
|
|
|
.map(|(f, _attr)| *f)
|
|
|
|
.collect::<Vec<&Field>>();
|
|
|
|
|
|
|
|
let shader_def_field_names = shader_def_fields.iter().map(|field| &field.ident);
|
2020-02-20 21:52:45 -08: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-09 21:41:51 -08:00
|
|
|
|
2020-02-09 18:04:18 -08:00
|
|
|
let struct_name = &ast.ident;
|
|
|
|
let struct_name_screaming_snake = struct_name.to_string().to_screaming_snake_case();
|
2020-02-21 00:15:24 -08:00
|
|
|
let field_uniform_names_ident = format_ident!("{}_FIELD_UNIFORM_NAMES", struct_name_screaming_snake);
|
2020-02-10 19:26:04 -08:00
|
|
|
|
2020-02-21 00:15:24 -08:00
|
|
|
let active_uniform_field_names = active_uniform_fields.iter().map(|field| {
|
2020-02-20 21:52:45 -08:00
|
|
|
&field.ident
|
2020-02-22 15:01:11 -08:00
|
|
|
}).collect::<Vec<_>>();
|
2020-02-21 00:15:24 -08:00
|
|
|
|
2020-02-22 12:42:40 -08: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 15:01:11 -08:00
|
|
|
let mut uniform_name_strings = Vec::new();
|
2020-02-23 23:41:48 -08:00
|
|
|
let mut texture_and_sampler_name_strings = Vec::new();
|
|
|
|
let mut texture_and_sampler_name_idents = Vec::new();
|
|
|
|
let field_uniform_names = active_uniform_fields.iter().map(|f| {
|
|
|
|
let field_name = f.ident.as_ref().unwrap().to_string();
|
|
|
|
let uniform = format!("{}_{}", struct_name, field_name);
|
2020-02-22 12:42:40 -08:00
|
|
|
let texture = format!("{}_texture", uniform);
|
|
|
|
let sampler = format!("{}_sampler", uniform);
|
2020-02-22 15:01:11 -08:00
|
|
|
uniform_name_strings.push(uniform.clone());
|
2020-02-23 23:41:48 -08:00
|
|
|
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());
|
2020-03-09 23:08:09 -07:00
|
|
|
quote!(bevy::render::shader::FieldUniformName {
|
2020-02-23 23:41:48 -08:00
|
|
|
field: #field_name,
|
2020-02-22 12:42:40 -08:00
|
|
|
uniform: #uniform,
|
2020-02-21 00:15:24 -08:00
|
|
|
texture: #texture,
|
|
|
|
sampler: #sampler,
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
2020-02-22 15:01:11 -08:00
|
|
|
TokenStream::from(quote! {
|
2020-03-09 23:08:09 -07:00
|
|
|
const #field_uniform_names_ident: &[bevy::render::shader::FieldUniformName] = &[
|
2020-02-21 00:15:24 -08:00
|
|
|
#(#field_uniform_names,)*
|
2020-02-09 18:04:18 -08:00
|
|
|
];
|
|
|
|
|
2020-03-09 23:08:09 -07:00
|
|
|
impl bevy::render::shader::AsUniforms for #struct_name {
|
2020-02-21 00:15:24 -08:00
|
|
|
// TODO: max this an iterator that feeds on field_uniform_names_ident
|
2020-03-09 23:08:09 -07:00
|
|
|
fn get_field_uniform_names(&self) -> &[bevy::render::shader::FieldUniformName] {
|
2020-02-22 12:42:40 -08:00
|
|
|
#field_uniform_names_ident
|
2020-02-09 18:04:18 -08:00
|
|
|
}
|
|
|
|
|
2020-03-09 23:08:09 -07:00
|
|
|
fn get_field_bind_type(&self, name: &str) -> Option<bevy::render::shader::FieldBindType> {
|
|
|
|
use bevy::render::shader::AsFieldBindType;
|
2020-02-22 12:42:40 -08:00
|
|
|
match name {
|
2020-02-22 15:01:11 -08:00
|
|
|
#(#active_uniform_field_name_strings => Some(self.#active_uniform_field_names.get_field_bind_type()),)*
|
2020-02-22 12:42:40 -08:00
|
|
|
_ => None,
|
|
|
|
}
|
2020-02-09 18:04:18 -08:00
|
|
|
}
|
|
|
|
|
2020-02-21 00:15:24 -08:00
|
|
|
// TODO: Fix this so uniform_name_uniform_info lines up with getbytes
|
2020-02-09 18:04:18 -08:00
|
|
|
fn get_uniform_bytes(&self, name: &str) -> Option<Vec<u8>> {
|
2020-02-17 19:53:48 -08:00
|
|
|
use bevy::core::bytes::GetBytes;
|
2020-02-09 18:04:18 -08:00
|
|
|
match name {
|
2020-02-22 15:01:11 -08:00
|
|
|
#(#uniform_name_strings => Some(self.#active_uniform_field_names.get_bytes()),)*
|
2020-02-09 18:04:18 -08:00
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
2020-02-09 21:41:51 -08:00
|
|
|
|
2020-03-14 12:56:37 -07:00
|
|
|
fn get_uniform_texture(&self, name: &str) -> Option<bevy::asset::Handle<bevy::render::texture::Texture>> {
|
2020-03-09 23:08:09 -07:00
|
|
|
use bevy::render::shader::GetTexture;
|
2020-02-23 23:41:48 -08:00
|
|
|
match name {
|
|
|
|
#(#texture_and_sampler_name_strings => self.#texture_and_sampler_name_idents.get_texture(),)*
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-10 19:26:04 -08: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-09 23:08:09 -07:00
|
|
|
use bevy::render::shader::ShaderDefSuffixProvider;
|
2020-02-10 19:26:04 -08:00
|
|
|
let mut potential_shader_defs: Vec<(&'static str, Option<&'static str>)> = vec![
|
2020-02-18 22:59:33 -08:00
|
|
|
#((#shader_def_field_names_screaming_snake, self.#shader_def_field_names.get_shader_def()),)*
|
2020-02-10 19:26:04 -08:00
|
|
|
];
|
|
|
|
|
|
|
|
Some(potential_shader_defs.drain(..)
|
|
|
|
.filter(|(f, shader_def)| shader_def.is_some())
|
2020-02-18 20:26:02 -08:00
|
|
|
.map(|(f, shader_def)| format!("{}_{}{}", #struct_name_screaming_snake, f, shader_def.unwrap()))
|
2020-02-10 19:26:04 -08:00
|
|
|
.collect::<Vec<String>>())
|
2020-02-09 21:41:51 -08:00
|
|
|
}
|
2020-01-19 02:02:12 -08:00
|
|
|
}
|
2020-02-22 15:01:11 -08:00
|
|
|
})
|
2020-01-19 02:02:12 -08:00
|
|
|
}
|
2020-01-20 20:10:40 -08: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-17 19:53:48 -08:00
|
|
|
pub extern "C" fn _create_plugin() -> *mut bevy::plugin::AppPlugin {
|
2020-01-20 20:10:40 -08: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)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|