bevy/bevy_derive/src/lib.rs

225 lines
8.3 KiB
Rust
Raw Normal View History

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};
use syn::{parse_macro_input, Data, DataStruct, DeriveInput, Field, Fields, Type};
2020-01-19 10:02:12 +00: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-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-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;
let field_name = fields.iter().map(|field| &field.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-01-19 10:02:12 +00:00
*world.insert((), vec![(
2020-02-10 02:04:18 +00:00
#(self.#field_name,)*
)]).first().unwrap()
}
}
})
}
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-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-02-10 05:41:51 +00:00
const 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()
.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_screaming_snake = struct_name.to_string().to_screaming_snake_case();
let info_ident = format_ident!("{}_UNIFORM_INFO", struct_name_screaming_snake);
let layout_ident = format_ident!("{}_UNIFORM_LAYOUTS", struct_name_screaming_snake);
2020-02-11 03:26:04 +00:00
let get_uniform_bytes_field_name = active_uniform_fields.iter().map(|field| {
&field.ident
});
eprintln!("hitit");
let mut uniform_info = Vec::new();
let mut uniform_name_uniform_info = Vec::new();
for field in active_uniform_fields.iter() {
let name = format!("{}_{}", struct_name, field.ident.as_ref().unwrap());
if let Type::Path(ref type_path) = field.ty {
let field_type_name = type_path.path.get_ident().unwrap().to_string();
if field_type_name == "ColorSource" || field_type_name == "Handle<Texture>" {
eprintln!("madeit");
let texture_name = format!("{}_texture", name);
let sampler_name = format!("{}_sampler", name);
uniform_name_uniform_info.push(texture_name.clone());
uniform_name_uniform_info.push(sampler_name.clone());
uniform_info.push(quote!(bevy::render::render_graph::UniformInfo {
name: #texture_name,
bind_type: bevy::render::render_graph::BindType::SampledTexture {
multisampled: false,
dimension: bevy::render::render_graph::TextureViewDimension::D2,
},
}));
uniform_info.push(quote!(bevy::render::render_graph::UniformInfo {
name: #sampler_name,
bind_type: bevy::render::render_graph::BindType::Sampler,
}));
}
}
uniform_info.push(quote!(bevy::render::render_graph::UniformInfo {
name: #name,
bind_type: bevy::render::render_graph::BindType::Uniform {
dynamic: false,
// TODO: fill this in with properties
properties: Vec::new(),
},
}));
};
let layout_arrays = (0..uniform_info.len()).map(|_| quote!(&[]));
2020-02-10 05:41:51 +00:00
let get_uniform_info_array_refs =
(0..uniform_info.len()).map(|i| quote!(&#info_ident[#i]));
2020-02-10 02:04:18 +00:00
TokenStream::from(quote! {
2020-02-18 03:53:48 +00:00
const #info_ident: &[bevy::render::render_graph::UniformInfo] = &[
#(#uniform_info,)*
2020-02-10 02:04:18 +00:00
];
2020-02-18 03:53:48 +00:00
const #layout_ident: &[&[bevy::render::render_graph::UniformPropertyType]] = &[
2020-02-10 02:04:18 +00:00
#(#layout_arrays,)*
];
2020-02-18 03:53:48 +00:00
impl bevy::render::render_graph::AsUniforms for #struct_name {
fn get_uniform_infos(&self) -> &[bevy::render::render_graph::UniformInfo] {
2020-02-10 02:04:18 +00:00
#info_ident
}
2020-02-18 03:53:48 +00:00
fn get_uniform_layouts(&self) -> &[&[bevy::render::render_graph::UniformPropertyType]] {
2020-02-10 02:04:18 +00:00
#layout_ident
}
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 {
#(#uniform_name_uniform_info => Some(self.#get_uniform_bytes_field_name.get_bytes()),)*
2020-02-10 02:04:18 +00:00
_ => None,
}
}
2020-02-18 03:53:48 +00:00
fn get_uniform_info(&self, name: &str) -> Option<&bevy::render::render_graph::UniformInfo> {
2020-02-10 02:04:18 +00:00
match name {
#(#uniform_name_uniform_info => Some(#get_uniform_info_array_refs),)*
2020-02-10 02:04:18 +00:00
_ => None,
}
2020-01-19 10:02:12 +00:00
}
2020-02-10 05:41:51 +00:00
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>> {
use bevy::render::render_graph::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_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-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)
}
})
}