derive struct as uniform
This commit is contained in:
		
							parent
							
								
									736faa3f46
								
							
						
					
					
						commit
						05dbf31fd1
					
				@ -1,5 +1,8 @@
 | 
			
		||||
extern crate proc_macro;
 | 
			
		||||
 | 
			
		||||
mod modules;
 | 
			
		||||
 | 
			
		||||
use modules::{get_modules, get_path};
 | 
			
		||||
use darling::FromMeta;
 | 
			
		||||
use inflector::Inflector;
 | 
			
		||||
use proc_macro::TokenStream;
 | 
			
		||||
@ -12,112 +15,6 @@ struct EntityArchetypeAttributeArgs {
 | 
			
		||||
    pub tag: Option<bool>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(FromMeta, Debug)]
 | 
			
		||||
struct ModuleAttributeArgs {
 | 
			
		||||
    #[darling(default)]
 | 
			
		||||
    pub bevy_render: Option<String>,
 | 
			
		||||
    #[darling(default)]
 | 
			
		||||
    pub bevy_asset: Option<String>,
 | 
			
		||||
    #[darling(default)]
 | 
			
		||||
    pub bevy_core: Option<String>,
 | 
			
		||||
    #[darling(default)]
 | 
			
		||||
    pub bevy_app: Option<String>,
 | 
			
		||||
    #[darling(default)]
 | 
			
		||||
    pub legion: Option<String>,
 | 
			
		||||
 | 
			
		||||
    /// If true, it will use the meta "bevy" crate for dependencies by default (ex: bevy:app). If this is set to false, the individual bevy crates
 | 
			
		||||
    /// will be used (ex: "bevy_app"). Defaults to "true"
 | 
			
		||||
    #[darling(default)]
 | 
			
		||||
    pub meta: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct Modules {
 | 
			
		||||
    pub bevy_render: String,
 | 
			
		||||
    pub bevy_asset: String,
 | 
			
		||||
    pub bevy_core: String,
 | 
			
		||||
    pub bevy_app: String,
 | 
			
		||||
    pub legion: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Modules {
 | 
			
		||||
    pub fn meta() -> Modules {
 | 
			
		||||
        Modules {
 | 
			
		||||
            bevy_asset: "bevy::asset".to_string(),
 | 
			
		||||
            bevy_render: "bevy::render".to_string(),
 | 
			
		||||
            bevy_core: "bevy::core".to_string(),
 | 
			
		||||
            bevy_app: "bevy::app".to_string(),
 | 
			
		||||
            legion: "bevy".to_string(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn external() -> Modules {
 | 
			
		||||
        Modules {
 | 
			
		||||
            bevy_asset: "bevy_asset".to_string(),
 | 
			
		||||
            bevy_render: "bevy_render".to_string(),
 | 
			
		||||
            bevy_core: "bevy_core".to_string(),
 | 
			
		||||
            bevy_app: "bevy_app".to_string(),
 | 
			
		||||
            legion: "legion".to_string(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for ModuleAttributeArgs {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        ModuleAttributeArgs {
 | 
			
		||||
            bevy_asset: None,
 | 
			
		||||
            bevy_render: None,
 | 
			
		||||
            bevy_core: None,
 | 
			
		||||
            bevy_app: None,
 | 
			
		||||
            legion: None,
 | 
			
		||||
            meta: true,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static MODULE_ATTRIBUTE_NAME: &'static str = "module";
 | 
			
		||||
 | 
			
		||||
fn get_modules(ast: &DeriveInput) -> Modules {
 | 
			
		||||
    let module_attribute_args = ast
 | 
			
		||||
        .attrs
 | 
			
		||||
        .iter()
 | 
			
		||||
        .find(|a| a.path.get_ident().as_ref().unwrap().to_string() == MODULE_ATTRIBUTE_NAME)
 | 
			
		||||
        .map(|a| {
 | 
			
		||||
            ModuleAttributeArgs::from_meta(&a.parse_meta().unwrap())
 | 
			
		||||
                .unwrap_or_else(|_err| ModuleAttributeArgs::default())
 | 
			
		||||
        });
 | 
			
		||||
    if let Some(module_attribute_args) = module_attribute_args {
 | 
			
		||||
        let mut modules = if module_attribute_args.meta {
 | 
			
		||||
            Modules::meta()
 | 
			
		||||
        } else {
 | 
			
		||||
            Modules::external()
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if let Some(path) = module_attribute_args.bevy_asset {
 | 
			
		||||
            modules.bevy_asset = path;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if let Some(path) = module_attribute_args.bevy_render {
 | 
			
		||||
            modules.bevy_render = path;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if let Some(path) = module_attribute_args.bevy_core {
 | 
			
		||||
            modules.bevy_core = path;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if let Some(path) = module_attribute_args.bevy_app {
 | 
			
		||||
            modules.bevy_app = path;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        modules
 | 
			
		||||
    } else {
 | 
			
		||||
        Modules::meta()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn get_path(path_str: &str) -> Path {
 | 
			
		||||
    syn::parse(path_str.parse::<TokenStream>().unwrap()).unwrap()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[proc_macro_derive(Resource)]
 | 
			
		||||
pub fn derive_resource(input: TokenStream) -> TokenStream {
 | 
			
		||||
    let ast = parse_macro_input!(input as DeriveInput);
 | 
			
		||||
@ -153,6 +50,78 @@ pub fn derive_resource(input: TokenStream) -> TokenStream {
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[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
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[proc_macro_derive(EntityArchetype, attributes(tag, module))]
 | 
			
		||||
pub fn derive_entity_archetype(input: TokenStream) -> TokenStream {
 | 
			
		||||
    let ast = parse_macro_input!(input as DeriveInput);
 | 
			
		||||
@ -449,7 +418,7 @@ pub fn derive_uniforms(input: TokenStream) -> TokenStream {
 | 
			
		||||
            fn get_field_bind_type(&self, name: &str) -> Option<#bevy_render_path::shader::FieldBindType> {
 | 
			
		||||
                use #bevy_render_path::shader::AsFieldBindType;
 | 
			
		||||
                match name {
 | 
			
		||||
                    #(#active_uniform_field_name_strings => self.#active_uniform_field_names.get_field_bind_type(),)*
 | 
			
		||||
                    #(#active_uniform_field_name_strings => self.#active_uniform_field_names.get_bind_type(),)*
 | 
			
		||||
                    _ => None,
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										109
									
								
								crates/bevy_derive/src/modules.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								crates/bevy_derive/src/modules.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,109 @@
 | 
			
		||||
use darling::FromMeta;
 | 
			
		||||
use proc_macro::TokenStream;
 | 
			
		||||
use syn::{DeriveInput, Path};
 | 
			
		||||
 | 
			
		||||
#[derive(FromMeta, Debug)]
 | 
			
		||||
pub struct ModuleAttributeArgs {
 | 
			
		||||
    #[darling(default)]
 | 
			
		||||
    pub bevy_render: Option<String>,
 | 
			
		||||
    #[darling(default)]
 | 
			
		||||
    pub bevy_asset: Option<String>,
 | 
			
		||||
    #[darling(default)]
 | 
			
		||||
    pub bevy_core: Option<String>,
 | 
			
		||||
    #[darling(default)]
 | 
			
		||||
    pub bevy_app: Option<String>,
 | 
			
		||||
    #[darling(default)]
 | 
			
		||||
    pub legion: Option<String>,
 | 
			
		||||
 | 
			
		||||
    /// If true, it will use the meta "bevy" crate for dependencies by default (ex: bevy:app). If this is set to false, the individual bevy crates
 | 
			
		||||
    /// will be used (ex: "bevy_app"). Defaults to "true"
 | 
			
		||||
    #[darling(default)]
 | 
			
		||||
    pub meta: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct Modules {
 | 
			
		||||
    pub bevy_render: String,
 | 
			
		||||
    pub bevy_asset: String,
 | 
			
		||||
    pub bevy_core: String,
 | 
			
		||||
    pub bevy_app: String,
 | 
			
		||||
    pub legion: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Modules {
 | 
			
		||||
    pub fn meta() -> Modules {
 | 
			
		||||
        Modules {
 | 
			
		||||
            bevy_asset: "bevy::asset".to_string(),
 | 
			
		||||
            bevy_render: "bevy::render".to_string(),
 | 
			
		||||
            bevy_core: "bevy::core".to_string(),
 | 
			
		||||
            bevy_app: "bevy::app".to_string(),
 | 
			
		||||
            legion: "bevy".to_string(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn external() -> Modules {
 | 
			
		||||
        Modules {
 | 
			
		||||
            bevy_asset: "bevy_asset".to_string(),
 | 
			
		||||
            bevy_render: "bevy_render".to_string(),
 | 
			
		||||
            bevy_core: "bevy_core".to_string(),
 | 
			
		||||
            bevy_app: "bevy_app".to_string(),
 | 
			
		||||
            legion: "legion".to_string(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for ModuleAttributeArgs {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        ModuleAttributeArgs {
 | 
			
		||||
            bevy_asset: None,
 | 
			
		||||
            bevy_render: None,
 | 
			
		||||
            bevy_core: None,
 | 
			
		||||
            bevy_app: None,
 | 
			
		||||
            legion: None,
 | 
			
		||||
            meta: true,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub static MODULE_ATTRIBUTE_NAME: &'static str = "module";
 | 
			
		||||
 | 
			
		||||
pub fn get_modules(ast: &DeriveInput) -> Modules {
 | 
			
		||||
    let module_attribute_args = ast
 | 
			
		||||
        .attrs
 | 
			
		||||
        .iter()
 | 
			
		||||
        .find(|a| a.path.get_ident().as_ref().unwrap().to_string() == MODULE_ATTRIBUTE_NAME)
 | 
			
		||||
        .map(|a| {
 | 
			
		||||
            ModuleAttributeArgs::from_meta(&a.parse_meta().unwrap())
 | 
			
		||||
                .unwrap_or_else(|_err| ModuleAttributeArgs::default())
 | 
			
		||||
        });
 | 
			
		||||
    if let Some(module_attribute_args) = module_attribute_args {
 | 
			
		||||
        let mut modules = if module_attribute_args.meta {
 | 
			
		||||
            Modules::meta()
 | 
			
		||||
        } else {
 | 
			
		||||
            Modules::external()
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if let Some(path) = module_attribute_args.bevy_asset {
 | 
			
		||||
            modules.bevy_asset = path;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if let Some(path) = module_attribute_args.bevy_render {
 | 
			
		||||
            modules.bevy_render = path;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if let Some(path) = module_attribute_args.bevy_core {
 | 
			
		||||
            modules.bevy_core = path;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if let Some(path) = module_attribute_args.bevy_app {
 | 
			
		||||
            modules.bevy_app = path;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        modules
 | 
			
		||||
    } else {
 | 
			
		||||
        Modules::meta()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn get_path(path_str: &str) -> Path {
 | 
			
		||||
    syn::parse(path_str.parse::<TokenStream>().unwrap()).unwrap()
 | 
			
		||||
}
 | 
			
		||||
@ -82,20 +82,20 @@ pub struct FieldInfo {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait AsFieldBindType {
 | 
			
		||||
    fn get_field_bind_type(&self) -> Option<FieldBindType>;
 | 
			
		||||
    fn get_bind_type(&self) -> Option<FieldBindType>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl AsFieldBindType for ColorSource {
 | 
			
		||||
    fn get_field_bind_type(&self) -> Option<FieldBindType> {
 | 
			
		||||
    fn get_bind_type(&self) -> Option<FieldBindType> {
 | 
			
		||||
        match *self {
 | 
			
		||||
            ColorSource::Texture(_) => Some(FieldBindType::Texture),
 | 
			
		||||
            ColorSource::Color(color) => color.get_field_bind_type(),
 | 
			
		||||
            ColorSource::Color(color) => color.get_bind_type(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl AsFieldBindType for Option<Handle<Texture>> {
 | 
			
		||||
    fn get_field_bind_type(&self) -> Option<FieldBindType> {
 | 
			
		||||
    fn get_bind_type(&self) -> Option<FieldBindType> {
 | 
			
		||||
        match *self {
 | 
			
		||||
            Some(_) => Some(FieldBindType::Texture),
 | 
			
		||||
            None => None,
 | 
			
		||||
@ -104,7 +104,7 @@ impl AsFieldBindType for Option<Handle<Texture>> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl AsFieldBindType for Handle<Texture> {
 | 
			
		||||
    fn get_field_bind_type(&self) -> Option<FieldBindType> {
 | 
			
		||||
    fn get_bind_type(&self) -> Option<FieldBindType> {
 | 
			
		||||
        Some(FieldBindType::Texture)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -114,7 +114,7 @@ where
 | 
			
		||||
    T: GetBytes,
 | 
			
		||||
{
 | 
			
		||||
    // TODO: this breaks if get_bytes_ref() isn't supported for a datatype
 | 
			
		||||
    default fn get_field_bind_type(&self) -> Option<FieldBindType> {
 | 
			
		||||
    default fn get_bind_type(&self) -> Option<FieldBindType> {
 | 
			
		||||
        Some(FieldBindType::Uniform {
 | 
			
		||||
            size: self.get_bytes_ref().unwrap().len(),
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
@ -65,7 +65,7 @@ impl AsUniforms for bevy_transform::prelude::LocalToWorld {
 | 
			
		||||
    }
 | 
			
		||||
    fn get_field_bind_type(&self, name: &str) -> Option<FieldBindType> {
 | 
			
		||||
        match name {
 | 
			
		||||
            "object" => self.0.get_field_bind_type(),
 | 
			
		||||
            "object" => self.0.get_bind_type(),
 | 
			
		||||
            _ => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,10 @@
 | 
			
		||||
use glam::Vec2;
 | 
			
		||||
use bevy_core::bytes::GetBytes;
 | 
			
		||||
use bevy_derive::Uniform;
 | 
			
		||||
use bevy_render::Color;
 | 
			
		||||
use bevy_derive::Uniforms;
 | 
			
		||||
use glam::Vec2;
 | 
			
		||||
use zerocopy::AsBytes;
 | 
			
		||||
#[repr(C)]
 | 
			
		||||
#[derive(Default, Clone, Copy, Debug, Uniforms, AsBytes)]
 | 
			
		||||
#[derive(Default, Clone, Copy, Debug, Uniform, AsBytes)]
 | 
			
		||||
#[module(meta = "false")]
 | 
			
		||||
pub struct Rect {
 | 
			
		||||
    pub position: Vec2,
 | 
			
		||||
@ -11,3 +12,8 @@ pub struct Rect {
 | 
			
		||||
    pub color: Color,
 | 
			
		||||
    pub z_index: f32,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl GetBytes for Rect {
 | 
			
		||||
    fn get_bytes(&self) -> Vec<u8> { self.as_bytes().to_vec() }
 | 
			
		||||
    fn get_bytes_ref(&self) -> Option<&[u8]> { Some(self.as_bytes()) }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user