derive_vertex.rs
1 use crate::bail; 2 use proc_macro2::{Span, TokenStream}; 3 use quote::quote; 4 use syn::{ 5 Data, DataStruct, Fields, Ident, LitStr, Result, Token, 6 parse::{Parse, ParseStream}, 7 punctuated::Punctuated, 8 }; 9 10 pub fn derive_vertex(crate_ident: &Ident, ast: syn::DeriveInput) -> Result<TokenStream> { 11 let struct_name = &ast.ident; 12 13 let fields = match &ast.data { 14 Data::Struct(DataStruct { 15 fields: Fields::Named(fields), 16 .. 17 }) => &fields.named, 18 _ => bail!("expected a struct with named fields"), 19 }; 20 21 let mut members = quote! { 22 let mut offset = 0; 23 let mut members = ::std::collections::HashMap::default(); 24 }; 25 26 for field in fields.iter() { 27 let field_name = field.ident.to_owned().unwrap(); 28 let field_name_lit = LitStr::new(&field_name.to_string(), Span::call_site()); 29 let field_ty = &field.ty; 30 let mut names = vec![field_name_lit.clone()]; 31 let mut format = quote! {}; 32 for attr in &field.attrs { 33 let attr_ident = if let Some(ident) = attr.path().get_ident() { 34 ident 35 } else { 36 continue; 37 }; 38 if attr_ident == "name" { 39 let meta = attr.parse_args_with(NameMeta::parse)?; 40 names = meta.lit_str_list.into_iter().collect(); 41 } else if attr_ident == "format" { 42 let format_ident = attr.parse_args_with(Ident::parse)?; 43 format = quote! { 44 ::#crate_ident::resources::Format::#format_ident; 45 }; 46 } 47 } 48 if format.is_empty() { 49 bail!( 50 field_name, 51 "expected `#[format(...)]`-attribute with valid `let_engine_core::resources::Format`", 52 ); 53 } 54 for name in &names { 55 members = quote! { 56 #members 57 58 { 59 let field_align = ::std::mem::align_of::<#field_ty>(); 60 offset = (offset + field_align - 1) & !(field_align - 1); 61 62 let field_size = ::std::mem::size_of::<#field_ty>(); 63 let format = #format; 64 let format_size = usize::try_from(format.block_size()).unwrap(); 65 let num_elements = field_size / format_size; 66 let remainder = field_size % format_size; 67 ::std::assert!( 68 remainder == 0, 69 "struct field `{}` size does not fit multiple of format size", 70 #field_name_lit, 71 ); 72 73 members.insert( 74 #name.to_string(), 75 ::#crate_ident::resources::model::VertexMemberInfo { 76 offset: offset.try_into().unwrap(), 77 format, 78 num_elements: num_elements.try_into().unwrap(), 79 stride: format_size.try_into().unwrap(), 80 }, 81 ); 82 83 offset += field_size; 84 } 85 }; 86 } 87 } 88 89 let function_body = quote! { 90 #members 91 92 ::#crate_ident::resources::model::VertexBufferDescription { 93 members, 94 stride: ::std::mem::size_of::<#struct_name>() as u32, 95 } 96 }; 97 98 Ok(quote! { 99 #[allow(unsafe_code)] 100 unsafe impl ::#crate_ident::resources::model::Vertex for #struct_name { 101 #[inline(always)] 102 fn description() -> ::#crate_ident::resources::model::VertexBufferDescription { 103 #function_body 104 } 105 106 } 107 }) 108 } 109 110 struct NameMeta { 111 lit_str_list: Punctuated<LitStr, Token![,]>, 112 } 113 114 impl Parse for NameMeta { 115 fn parse(input: ParseStream) -> Result<Self> { 116 Ok(Self { 117 lit_str_list: input.parse_terminated(<LitStr as Parse>::parse, Token![,])?, 118 }) 119 } 120 }