diff --git a/lyra-scripting/lyra-scripting-derive/src/handle_macro.rs b/lyra-scripting/lyra-scripting-derive/src/handle_macro.rs index ea50a1f..a00323e 100644 --- a/lyra-scripting/lyra-scripting-derive/src/handle_macro.rs +++ b/lyra-scripting/lyra-scripting-derive/src/handle_macro.rs @@ -1,12 +1,81 @@ use quote::{format_ident, quote}; -use syn::{parse_macro_input, Ident, Token}; +use syn::{braced, parenthesized, parse_macro_input, token, Ident, Path, Token}; -pub(crate) struct HandleWrapUsage { - pub type_path: syn::Path, +struct FieldGetter { + field: Ident, + body: Option, + wrapper_type: Option, +} + +impl FieldGetter { + fn parse_extended(input: syn::parse::ParseStream) -> syn::Result { + let field_name = input.parse()?; + + let mut s = Self { + field: field_name, + body: None, + wrapper_type: None + }; + + while input.peek(Token![,]) { + let _: Token![,] = input.parse()?; + + if input.peek(syn::Ident) { + let ident: syn::Ident = input.parse()?; + let ident_str = ident.to_string(); + let ident_str = ident_str.as_str(); + + match ident_str { + "wrapper" => { + let _eq: Token![=] = input.parse()?; + + let name: Path = input.parse()?; + s.wrapper_type = Some(name); + }, + _ => { + return Err(syn::Error::new_spanned(ident, "unknown wrapper command")); + } + } + } + + if let Ok(block) = input.parse::() { + s.body = Some(block); + } + } + + if s.body.is_some() && s.wrapper_type.is_some() { + return Err(syn::Error::new(input.span(), "cannot use body and wrapper_type, choose one")); + } + + Ok(s) + } +} + +impl syn::parse::Parse for FieldGetter { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + if input.peek(token::Paren) { + let content; + let _parens: token::Paren = parenthesized!(content in input); + + Self::parse_extended(&content) + } else { + let field_name = input.parse()?; + Ok(Self { + field: field_name, + body: None, + wrapper_type: None, + }) + } + } +} + +struct HandleWrapUsage { + type_path: syn::Path, /// The extra derives of the type. - pub override_name: Option, + override_name: Option, + field_getters: Vec, - pub extra_builds: Option, + extra_builds: Option, } impl syn::parse::Parse for HandleWrapUsage { @@ -15,6 +84,7 @@ impl syn::parse::Parse for HandleWrapUsage { let mut s = Self { type_path, override_name: None, + field_getters: vec![], extra_builds: None, }; @@ -30,17 +100,24 @@ impl syn::parse::Parse for HandleWrapUsage { "name" => { let _eq: Token![=] = input.parse()?; - let name: Ident = input.parse()?; - s.override_name = Some(name); + s.override_name = Some(input.parse()?); }, + "field_getters" => { + let _eq: Token![=] = input.parse()?; + + if input.peek(token::Brace) { + let content; + let _braced: token::Brace = braced!(content in input); + + let terminated = content.parse_terminated(FieldGetter::parse, Token![,])?; + s.field_getters = terminated.into_iter().collect(); + } + } _ => { return Err(syn::Error::new_spanned(ident, "unknown wrapper command")); } } - }/* else if input.parse::() { s.extra_builds = Some(block); @@ -63,6 +140,34 @@ pub(crate) fn lua_wrap_handle_impl(input: proc_macro::TokenStream) -> proc_macro let extras = input.extra_builds; + let custom_getters = input.field_getters.iter().map(|g| { + let field = &g.field; + + let field_creator = match &g.wrapper_type { + Some(wrap) => { + quote!(#wrap(data.#field).as_lua(lua)) + }, + None => match &g.body { + Some(body) => { + quote!(#body) + }, + None => { + quote!(data.#field.clone().as_lua(lua)) + } + } + }; + + quote! { + builder.field_getter(stringify!($field), |lua, this| { + if let Some(data) = this.0.data_ref() { + #field_creator + } else { + Ok(Value::Nil) + } + }); + } + }); + quote! { #[derive(Clone, Reflect)] pub struct #wrapper_name(pub ResHandle<#handle_name>); @@ -100,6 +205,8 @@ pub(crate) fn lua_wrap_handle_impl(input: proc_macro::TokenStream) -> proc_macro Ok(name) }); + #(#custom_getters)* + builder.method("is_watched", |_, this, ()| { Ok(this.is_watched()) }); diff --git a/lyra-scripting/lyra-scripting-derive/src/lib.rs b/lyra-scripting/lyra-scripting-derive/src/lib.rs index e2f7d8b..c9a4185 100644 --- a/lyra-scripting/lyra-scripting-derive/src/lib.rs +++ b/lyra-scripting/lyra-scripting-derive/src/lib.rs @@ -1,735 +1,19 @@ use handle_macro::lua_wrap_handle_impl; use lua_macro::wrap_lua_struct_impl; -use proc_macro2::{Ident, Span}; use quote::quote; -use syn::{parse_macro_input, Path, Token, token, parenthesized, punctuated::Punctuated, braced}; +use syn::{parse_macro_input, Token}; mod mat_wrapper; -use mat_wrapper::MatWrapper; +mod vec_wrapper; +use vec_wrapper::VecWrapper; mod lua_macro; mod handle_macro; -// pub use lua_macro::wrap_lua_struct; - pub(crate) const FN_NAME_INTERNAL_REFLECT_TYPE: &str = "__lyra_internal_reflect_type"; pub(crate) const FN_NAME_INTERNAL_REFLECT: &str = "__lyra_internal_reflect"; -pub(crate) struct MetaMethod { - pub ident: Ident, - pub mods: Vec, -} - -impl syn::parse::Parse for MetaMethod { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let ident: Ident = input.parse()?; - - let mods = if input.peek(token::Paren) { - let content; - let _parens: token::Paren = parenthesized!(content in input); - content.parse_terminated(Ident::parse, Token![,])? - .into_iter().collect() - } else { vec![] }; - - Ok(Self { - ident, - mods, - }) - } -} - -impl MetaMethod { - /// Returns a boolean if an identifier is a lua wrapper, and therefore also userdata - fn is_arg_wrapper(ident: &Ident) -> bool { - let s = ident.to_string(); - s.starts_with("Lua") - } - - /// Returns a boolean indiciating if the metamethod has takes in any arguments - fn does_metamethod_have_arg(metamethod: &Ident) -> bool { - let mm_str = metamethod.to_string(); - let mm_str = mm_str.as_str(); - match mm_str { - "Add" | "Sub" | "Div" | "Mul" | "Mod" | "Eq" | "Shl" | "Shr" | "BAnd" | "BOr" - | "BXor" => { - true - }, - "Unm" | "BNot" | "ToString" => { - false - }, - _ => todo!(), - } - } - - /// returns the tokens of the body of the metamethod - /// - /// Parameters - /// * `metamethod` - The ident of the metamethod that is being implemented. - /// * `other` - The tokens of the argument used in the metamethod. - fn get_method_body(metamethod: &Ident, other: proc_macro2::TokenStream) -> proc_macro2::TokenStream { - let mm_str = metamethod.to_string(); - let mm_str = mm_str.as_str(); - match mm_str { - "Add" | "Sub" | "Div" | "Mul" | "Mod" => { - let symbol = match mm_str { - "Add" => quote!(+), - "Sub" => quote!(-), - "Div" => quote!(/), - "Mul" => quote!(*), - "Mod" => quote!(%), - _ => unreachable!(), // the string was just checked to be one of these - }; - - quote! { - Ok(Self(this.0 #symbol #other)) - } - }, - "Unm" => { - quote! { - Ok(Self(-this.0)) - } - }, - "Eq" => { - quote! { - Ok(this.0 == #other) - } - }, - "Shl" => { - quote! { - Ok(Self(this.0 << #other)) - } - } - "Shr" => { - quote! { - Ok(Self(this.0 >> #other)) - } - }, - "BAnd" | "BOr" | "BXor" => { - let symbol = match mm_str { - "BAnd" => { - quote!(&) - }, - "BOr" => { - quote!(|) - }, - "BXor" => { - quote!(^) - }, - _ => unreachable!() // the string was just checked to be one of these - }; - - quote! { - Ok(Self(this.0 #symbol #other)) - } - }, - "BNot" => { - quote! { - Ok(Self(!this.0)) - } - }, - "ToString" => { - quote! { - Ok(format!("{:?}", this.0)) - } - }, - _ => syn::Error::new_spanned(metamethod, - "unsupported auto implementation of metamethod").to_compile_error(), - } - } - - fn get_body_for_arg(mt_ident: &Ident, arg_ident: &Ident, arg_param: proc_macro2::TokenStream) -> proc_macro2::TokenStream { - let other: proc_macro2::TokenStream = if Self::is_arg_wrapper(arg_ident) { - // Lua wrappers must be dereferenced - quote! { - #arg_param.0 - } - } else { - quote! { - #arg_param - } - }; - Self::get_method_body(&mt_ident, other) - } - - pub fn to_tokens(&self, wrapper_ident: &Ident) -> proc_macro2::TokenStream { - let wrapped_str = &wrapper_ident.to_string()[3..]; // removes starting 'Lua' from name - let mt_ident = &self.ident; - let mt_lua_name = mt_ident.to_string().to_lowercase(); - - if self.mods.is_empty() { - let other = quote! { - v.0 - }; - let body = Self::get_method_body(&self.ident, other); - - if Self::does_metamethod_have_arg(&self.ident) { - quote! { - builder.meta_method(elua::MetaMethod::#mt_ident, |_, this, (v,): (#wrapper_ident,)| { - #body - }); - } - } else { - quote! { - builder.meta_method(elua::MetaMethod::#mt_ident, |_, this, ()| { - #body - }); - } - } - - } else if self.mods.len() == 1 { - let first = self.mods.iter().next().unwrap(); - let body = Self::get_body_for_arg(&self.ident, first, quote!(v)); - - quote! { - builder.meta_method(elua::MetaMethod::#mt_ident, |_, this, (v,): (#first,)| { - #body - }); - } - } else { - // an optional match arm that matches elua::Value:Number - let number_arm = { - let num_ident = self.mods.iter().find(|i| { - let is = i.to_string(); - let is = is.as_str(); - match is { - "u8" | "u16" | "u32" | "u64" | "u128" - | "i8" | "i16" | "i32" | "i64" | "i128" - | "f32" | "f64" => true, - _ => false, - } - }); - - if let Some(num_ident) = num_ident { - let body = Self::get_body_for_arg(&self.ident, num_ident, quote!(n as #num_ident)); - - quote! { - elua::Value::Number(n) => { - #body - }, - } - } else { quote!() } - }; - - let userdata_arm = { - let wrappers: Vec<&Ident> = self.mods.iter() - .filter(|i| Self::is_arg_wrapper(i)) - .collect(); - - let if_statements = wrappers.iter().map(|i| { - let body = Self::get_method_body(&self.ident, quote!(other.0)); - - quote! { - if let Ok(other) = ud.as_ref::<#i>() { - #body - } - } - - }); - - quote! { - elua::Value::Userdata(ud) => { - #(#if_statements else)* - // this is the body of the else statement - { - // try to get the name of the userdata for the error message - if let Ok(mt) = ud.get_metatable() { - if let Ok(name) = mt.get::<_, String>("__name") { - return Err(elua::Error::BadArgument { - func: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)), - arg_index: 2, - arg_name: Some("rhs".to_string()), - error: Arc::new(elua::Error::Runtime( - format!("cannot multiply with unknown userdata named {}", name) - )) - }); - } - } - - Err(elua::Error::BadArgument { - func: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)), - arg_index: 2, - arg_name: Some("rhs".to_string()), - error: Arc::new( - elua::Error::runtime("cannot multiply with unknown userdata") - ) - }) - } - }, - } - }; - - quote! { - builder.meta_method(elua::MetaMethod::#mt_ident, |_, this, (v,): (elua::Value,)| { - match v { - #number_arm - #userdata_arm - _ => Err(elua::Error::BadArgument { - func: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)), - arg_index: 2, - arg_name: Some("rhs".to_string()), - error: Arc::new( - elua::Error::Runtime(format!("cannot multiply with {}", v.type_name())) - ) - }) - } - }); - } - } - } -} - -pub(crate) struct VecWrapper { - -} - -impl VecWrapper { - fn vec_size(&self, wrapper_ident: &Ident) -> usize { - let name = wrapper_ident.to_string(); - name[name.len() - 1..].parse::() - .or_else(|_| name[name.len() - 2.. name.len() - 1].parse::()) - .expect("Failure to grab Vec size from ident name") - } - - /// Returns the token stream of the type of the axis of the vec (Vec2 vs IVec2 vs I64Vec2, etc.) - fn vec_axis_type(&self, wrapper_ident: &Ident) -> &'static str { - let name = wrapper_ident.to_string(); - let start = name.find("Vec").unwrap(); - - let before = &name[start - 1.. start]; - match before { - "D" => return "f64", - "I" => return "i32", - "U" => return "u32", - "B" => return "bool", - _ => {}, - } - //println!("before is {before}"); - - let three_before = &name[start - 3.. start]; - match three_before { - "I64" => return "i64", - "U64" => return "u64", - _ => {}, - } - //println!("three before is {three_before}"); - - "f32" - } - - pub fn to_field_tokens(&self, wrapped_path: &Path, wrapper_ident: &Ident) -> proc_macro2::TokenStream { - let mut consts = vec![quote!(ZERO), quote!(ONE), quote!(X), - quote!(Y), ]; // , quote!(AXES) - - let vec_size = self.vec_size(wrapper_ident); - let axis_type_name = self.vec_axis_type(wrapper_ident); - - if axis_type_name.contains("b") { - return quote! { - builder.field("FALSE", #wrapper_ident(#wrapped_path::FALSE)); - builder.field("TRUE", #wrapper_ident(#wrapped_path::TRUE)); - }; - } - - if vec_size >= 3 { - consts.push(quote!(Z)); - - // no negative numbers for unsigned vecs - if !axis_type_name.contains("u") { - consts.push(quote!(NEG_Z)); - } - } - - if vec_size == 4 { - consts.push(quote!(W)); - - // no negative numbers for unsigned vecs - if !axis_type_name.contains("u") { - consts.push(quote!(NEG_W)); - } - } - - // no negative numbers for unsigned vecs - if !axis_type_name.contains("u") { - consts.push(quote!(NEG_X)); - consts.push(quote!(NEG_Y)); - consts.push(quote!(NEG_ONE)); - } - - if axis_type_name.contains("f") { - consts.push(quote!(NAN)) - } - - let const_tokens = consts.iter().map(|cnst| { - let const_name = cnst.to_string(); - - quote! { - builder.field(#const_name, #wrapper_ident(#wrapped_path::#cnst)); - } - }); - - quote! { - #(#const_tokens)* - } - } - - pub fn to_method_tokens(&self, wrapped_path: &Path, wrapper_ident: &Ident) -> proc_macro2::TokenStream { - let vec_size = self.vec_size(wrapper_ident); - let axis_type_name = self.vec_axis_type(wrapper_ident); - // methods that only some vecs have - let mut optional_methods = vec![]; - - // boolean vectors dont have much :( - if axis_type_name.contains("b") { - return quote!(); // TODO: all, any, bitmask, splat - } - - if axis_type_name.contains("f") { - let type_id = Ident::new(axis_type_name, Span::call_site()); - - optional_methods.push( - quote! { - builder.method("clamp_length", - |_, this, (min, max): (#type_id, #type_id)| { - Ok(#wrapper_ident(this.clamp_length(min, max))) - }); - - builder.method("abs_diff_eq", - |_, this, (rhs, max_abs_diff): (#wrapper_ident, #type_id)| { - Ok(this.abs_diff_eq(rhs.0, max_abs_diff)) - }); - - builder.method("ceil", - |_, this, (): ()| { - Ok(#wrapper_ident(this.ceil())) - }); - } - ); - - if vec_size != 4 { - optional_methods.push( - quote! { - builder.method("angle_between", - |_, this, (rhs,): (#wrapper_ident,)| { - Ok(this.angle_between(rhs.0)) - }); - } - ) - } - } - - if !axis_type_name.contains("u") { - optional_methods.push( - quote! { - builder.method("abs", - |_, this, (): ()| { - Ok(#wrapper_ident(this.abs())) - }); - } - ) - } - - let optional_methods_iter = optional_methods.iter(); - quote! { - - - builder.method("clamp", - |_, this, (min, max): (#wrapper_ident, #wrapper_ident)| { - Ok(#wrapper_ident(this.clamp(min.0, max.0))) - }); - - // TODO: Not all Vecs have this - /* builder.method("clamp_length", - |_, this, (min, max): (f32, f32)| { - Ok(#wrapper_ident(this.clamp_length(min, max))) - }); */ - - - builder.method("to_array", - |_, this, (): ()| { - Ok(this.to_array()) - }); - - #(#optional_methods_iter)* - } - } -} - -pub(crate) struct WrapUsage { - pub type_path: Path, - /// The extra derives of the type. - pub derive_idents: Punctuated, - /// The field idents of the type that will be exposed with gets and sets - pub field_idents: Punctuated, - pub skip_new_fn: bool, - /// The identifiers that are taken as parameters in the types 'new' function - pub new_fn_idents: Punctuated, - pub meta_method_idents: Punctuated, - - pub matrix: Option, - pub vec: Option, - - pub custom_methods: Option, - pub custom_fields: Option, -} - -impl syn::parse::Parse for WrapUsage { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let type_path: Path = input.parse()?; - let mut s = Self { - type_path, - derive_idents: Punctuated::default(), - field_idents: Punctuated::default(), - skip_new_fn: false, - new_fn_idents: Punctuated::default(), - meta_method_idents: Punctuated::default(), - matrix: None, - vec: None, - custom_methods: None, - custom_fields: None, - }; - /* let mut derive_idents = None; - let mut field_idents = None; - let mut new_fn_idents = None; */ - - while input.peek(Token![,]) { - let _: Token![,] = input.parse()?; - //println!("Peeked a , ({:?})", input); - - if input.peek(syn::Ident) { - let ident: Ident = input.parse()?; - let ident_str = ident.to_string(); - let ident_str = ident_str.as_str(); - - match ident_str { - "derives" => { - if input.peek(token::Paren) { - let content; - let _parens: token::Paren = parenthesized!(content in input); - - let derives: Punctuated = content.parse_terminated(Ident::parse, Token![,])?; - s.derive_idents = derives; - //println!("read derives: {:?}", s.derive_idents); - } - }, - "fields" => { - if input.peek(token::Paren) { - let content; - let _parens: token::Paren = parenthesized!(content in input); - - let fields: Punctuated = content.parse_terminated(Ident::parse, Token![,])?; - s.field_idents = fields; - //println!("read fields: {:?}", s.field_idents); - } - }, - "new" => { - if input.peek(token::Paren) { - let content; - let _parens: token::Paren = parenthesized!(content in input); - - let fields: Punctuated = content.parse_terminated(Ident::parse, Token![,])?; - s.new_fn_idents = fields; - //println!("read fields: {:?}", s.new_fn_idents); - } - }, - "no_new" => { - s.skip_new_fn = true; - }, - "matrix" => { - if input.peek(token::Brace) { - let content; - let _braces = braced!(content in input); - s.matrix = Some(content.parse()?); - } - }, - "metamethods" => { - if input.peek(token::Paren) { - let content; - let _bracket: token::Paren = parenthesized!(content in input); - - let meta_methods: Punctuated = content.parse_terminated(MetaMethod::parse, Token![,])?; - s.meta_method_idents = meta_methods; - } - }, - "custom_methods" => { - let methods_block = input.parse()?; - s.custom_methods = Some(methods_block); - }, - "custom_fields" => { - let block = input.parse()?; - s.custom_fields = Some(block); - } - _ => { - return Err(syn::Error::new_spanned(ident, "unknown wrapper command")); - } - } - } - } - - Ok(s) - } -} - -/// Creates a wrapper type for a VecN from the engine math library. -#[proc_macro] -pub fn wrap_math_vec_copy(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let input = parse_macro_input!(input as WrapUsage); - - let path: Path = input.type_path; - let type_name = &path.segments.last() - .expect("Failure to find typename in macro usage!") - .ident; - let wrapper_typename = Ident::new(&format!("Lua{}", type_name), Span::call_site()); - - let vec_wrapper = { - let name_str = type_name.to_string(); - if name_str.contains("Vec") { - Some(VecWrapper {}) - } else { - None - } - }; - // TODO: fix this so it doesn't cause a stack overflow - /* let vec_wrapper_fields = vec_wrapper.as_ref().map(|vec| - vec.to_field_tokens(&path, &wrapper_typename)); */ - let vec_wrapper_fields: Option = None; - let vec_wrapper_methods = vec_wrapper.as_ref().map(|vec| - vec.to_method_tokens(&path, &wrapper_typename)); - - let derive_idents_iter = input.derive_idents.iter(); - let custom_methods = input.custom_methods; - let custom_fields = input.custom_fields; - - let field_get_set_pairs = input.field_idents.iter().map(|i| { - let is = i.to_string(); - quote! { - builder.field_getter(#is, |_, this| { - Ok(this.#i) - }); - builder.field_setter(#is, |_, this, #i| { - this.#i = #i; - Ok(()) - }); - } - }); - - let new_fn_idents = { - let idents = if input.new_fn_idents.is_empty() { - input.field_idents.iter() - } else { - input.new_fn_idents.iter() - }; - - let idents_c = idents.clone(); - - if !input.skip_new_fn { - quote! { - builder.function("new", |_, ( #(#idents_c),* )| { - Ok(#wrapper_typename(#path::new( #(#idents),* ))) - }); - } - } else { quote!() } - }; - - let matrix_wrapper_methods = input.matrix.as_ref().map(|m| - m.to_method_tokens(&path, &wrapper_typename)); - let matrix_wrapper_fields = input.matrix.as_ref().map(|m| - m.to_field_tokens(&path, &wrapper_typename)); - - let meta_method_idents = { - let idents = input.meta_method_idents.iter().map(|metamethod| { - metamethod.to_tokens(&wrapper_typename) - }); - - quote! { - #(#idents)* - } - }; - - proc_macro::TokenStream::from(quote! { - #[derive(Clone, Copy, lyra_reflect::Reflect, #(#derive_idents_iter),*)] - pub struct #wrapper_typename(#[reflect(skip)] pub(crate) #path); - - impl std::ops::Deref for #wrapper_typename { - type Target = #path; - - fn deref(&self) -> &Self::Target { - &self.0 - } - } - - impl std::ops::DerefMut for #wrapper_typename { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } - } - - impl<'lua> elua::FromLua<'lua> for #wrapper_typename { - fn from_lua(_lua: &'lua elua::State, value: elua::Value<'lua>) -> elua::Result { - match value { - elua::Value::Userdata(ud) => Ok(*ud.as_ref::()?), - _ => panic!("Attempt to get {} from a {} value", stringify!(#wrapper_typename), value.type_name()), - } - } - } - - impl elua::Userdata for #wrapper_typename { - fn name() -> String { - stringify!(#type_name).to_string() - } - - fn build<'a>(builder: &mut elua::UserdataBuilder<'a, Self>) { - #(#field_get_set_pairs)* - - #matrix_wrapper_fields - #vec_wrapper_fields - - #custom_fields - - #new_fn_idents - - builder.method(#FN_NAME_INTERNAL_REFLECT, |_, this, ()| { - Ok(crate::ScriptBorrow::from_component::<#path>(Some(this.0.clone()))) - }); - - builder.function(#FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| { - Ok(crate::ScriptBorrow::from_component::<#path>(None)) - }); - - #meta_method_idents - - #matrix_wrapper_methods - #vec_wrapper_methods - - #custom_methods - } - } - - impl lyra_scripting::lua::LuaWrapper for #wrapper_typename { - fn wrapped_type_id() -> std::any::TypeId { - let t = std::any::TypeId::of::<#path>(); - println!("Got id of {}, it is {:?}", stringify!(#path), t); - t - } - } - - impl<'a> elua::FromLuaVec<'a> for #wrapper_typename { - fn from_lua_value_vec(state: &'a elua::State, mut values: elua::ValueVec<'a>) -> elua::Result { - use elua::FromLua; - - if let Some(val) = values.pop_front() { - #wrapper_typename::from_lua(state, val) - } else { - Err(elua::Error::IncorrectArgCount { - arg_expected: 1, - arg_count: values.len() as i32, - }) - } - } - } - }) -} - #[proc_macro] pub fn wrap_lua_struct(input: proc_macro::TokenStream) -> proc_macro::TokenStream { wrap_lua_struct_impl(input) @@ -738,4 +22,35 @@ pub fn wrap_lua_struct(input: proc_macro::TokenStream) -> proc_macro::TokenStrea #[proc_macro] pub fn lua_wrap_handle(input: proc_macro::TokenStream) -> proc_macro::TokenStream { lua_wrap_handle_impl(input) +} + +pub(crate) struct VecExtensionInputs { + #[allow(dead_code)] + pub type_path: syn::Path, + pub wrapper_ident: syn::Ident, +} + +impl syn::parse::Parse for VecExtensionInputs { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let type_path: syn::Path = input.parse()?; + let _comma: Token![,] = input.parse()?; + let wrapper_ident: syn::Ident = input.parse()?; + + Ok(Self { + type_path, + wrapper_ident + }) + } +} + +#[proc_macro] +pub fn lua_vec_wrap_extension(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = parse_macro_input!(input as VecExtensionInputs); + + let wrapper = VecWrapper; + let method_tokens = wrapper.to_method_tokens(&input.wrapper_ident); + + quote! { + #method_tokens + }.into() } \ No newline at end of file diff --git a/lyra-scripting/lyra-scripting-derive/src/lua_macro.rs b/lyra-scripting/lyra-scripting-derive/src/lua_macro.rs index 80a1a52..a4ad512 100644 --- a/lyra-scripting/lyra-scripting-derive/src/lua_macro.rs +++ b/lyra-scripting/lyra-scripting-derive/src/lua_macro.rs @@ -1,8 +1,397 @@ use proc_macro2::Span; use quote::quote; -use syn::{parse_macro_input, Path, Ident}; +use syn::{parenthesized, parse_macro_input, punctuated::Punctuated, token, Ident, Path, Token}; -use crate::{VecWrapper, WrapUsage, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE}; +use crate::{FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE}; + +#[derive(Clone, Copy, Debug, PartialEq)] +enum SkipType { + /// Skips implementing + LuaReflect +} + +impl syn::parse::Parse for SkipType { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let name: Ident = input.parse()?; + let name_str = name.to_string(); + + match name_str.as_str() { + "lua_reflect" => Ok(Self::LuaReflect), + _ => { + Err(syn::Error::new_spanned(name, "unknown skip type")) + } + } + } +} + +pub(crate) struct MetaMethod { + name: Ident, + // If empty, assume `Self` + arg: Vec, +} + +impl MetaMethod { + /// Returns a boolean if an identifier is a lua wrapper, and therefore also userdata + fn is_arg_wrapper(ident: &Ident) -> bool { + let s = ident.to_string(); + s.starts_with("Lua") + } + + /// Returns a boolean indiciating if the metamethod has takes in any arguments + fn does_metamethod_have_arg(metamethod: &Ident) -> bool { + let mm_str = metamethod.to_string(); + let mm_str = mm_str.as_str(); + match mm_str { + "Add" | "Sub" | "Div" | "Mul" | "Mod" | "Eq" | "Shl" | "Shr" | "BAnd" | "BOr" + | "BXor" => { + true + }, + "Unm" | "BNot" | "ToString" => { + false + }, + _ => todo!(), + } + } + + /// returns the tokens of the body of the metamethod + /// + /// Parameters + /// * `metamethod` - The ident of the metamethod that is being implemented. + /// * `other` - The tokens of the argument used in the metamethod. + fn get_method_body(metamethod: &Ident, other: proc_macro2::TokenStream) -> proc_macro2::TokenStream { + let mm_str = metamethod.to_string(); + let mm_str = mm_str.as_str(); + match mm_str { + "Add" | "Sub" | "Div" | "Mul" | "Mod" => { + let symbol = match mm_str { + "Add" => quote!(+), + "Sub" => quote!(-), + "Div" => quote!(/), + "Mul" => quote!(*), + "Mod" => quote!(%), + _ => unreachable!(), // the string was just checked to be one of these + }; + + quote! { + Ok(Self(this.0 #symbol #other)) + } + }, + "Unm" => { + quote! { + Ok(Self(-this.0)) + } + }, + "Eq" => { + quote! { + Ok(this.0 == #other) + } + }, + "Shl" => { + quote! { + Ok(Self(this.0 << #other)) + } + } + "Shr" => { + quote! { + Ok(Self(this.0 >> #other)) + } + }, + "BAnd" | "BOr" | "BXor" => { + let symbol = match mm_str { + "BAnd" => { + quote!(&) + }, + "BOr" => { + quote!(|) + }, + "BXor" => { + quote!(^) + }, + _ => unreachable!() // the string was just checked to be one of these + }; + + quote! { + Ok(Self(this.0 #symbol #other)) + } + }, + "BNot" => { + quote! { + Ok(Self(!this.0)) + } + }, + "ToString" => { + quote! { + Ok(format!("{:?}", this.0)) + } + }, + _ => syn::Error::new_spanned(metamethod, + "unsupported auto implementation of metamethod").to_compile_error(), + } + } + + fn get_body_for_arg(mt_ident: &Ident, arg_ident: &Ident, arg_param: proc_macro2::TokenStream) -> proc_macro2::TokenStream { + let other: proc_macro2::TokenStream = if Self::is_arg_wrapper(arg_ident) { + // Lua wrappers must be dereferenced + quote! { + #arg_param.0 + } + } else { + quote! { + #arg_param + } + }; + Self::get_method_body(&mt_ident, other) + } + + pub fn to_tokens(&self, wrapper_ident: &Ident) -> proc_macro2::TokenStream { + let wrapped_str = &wrapper_ident.to_string()[3..]; // removes starting 'Lua' from name + let mt_ident = &self.name; + let mt_lua_name = mt_ident.to_string().to_lowercase(); + + if self.arg.is_empty() { + let other = quote! { + v.0 + }; + let body = Self::get_method_body(&self.name, other); + + if Self::does_metamethod_have_arg(&self.name) { + quote! { + builder.meta_method(elua::MetaMethod::#mt_ident, |_, this, (v,): (#wrapper_ident,)| { + #body + }); + } + } else { + quote! { + builder.meta_method(elua::MetaMethod::#mt_ident, |_, this, ()| { + #body + }); + } + } + + } else if self.arg.len() == 1 { + let first = self.arg.iter().next().unwrap(); + let body = Self::get_body_for_arg(&self.name, first, quote!(v)); + + quote! { + builder.meta_method(elua::MetaMethod::#mt_ident, |_, this, (v,): (#first,)| { + #body + }); + } + } else { + // an optional match arm that matches elua::Value:Number + let number_arm = { + let num_ident = self.arg.iter().find(|i| { + let is = i.to_string(); + let is = is.as_str(); + match is { + "u8" | "u16" | "u32" | "u64" | "u128" + | "i8" | "i16" | "i32" | "i64" | "i128" + | "f32" | "f64" => true, + _ => false, + } + }); + + if let Some(num_ident) = num_ident { + let body = Self::get_body_for_arg(&self.name, num_ident, quote!(n as #num_ident)); + + quote! { + elua::Value::Number(n) => { + #body + }, + } + } else { quote!() } + }; + + let userdata_arm = { + let wrappers: Vec<&Ident> = self.arg.iter() + .filter(|i| Self::is_arg_wrapper(i)) + .collect(); + + let if_statements = wrappers.iter().map(|i| { + let body = Self::get_method_body(&self.name, quote!(other.0)); + + quote! { + if let Ok(other) = ud.as_ref::<#i>() { + #body + } + } + + }); + + quote! { + elua::Value::Userdata(ud) => { + #(#if_statements else)* + // this is the body of the else statement + { + // try to get the name of the userdata for the error message + if let Ok(mt) = ud.get_metatable() { + if let Ok(name) = mt.get::<_, String>("__name") { + return Err(elua::Error::BadArgument { + func: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)), + arg_index: 2, + arg_name: Some("rhs".to_string()), + error: std::sync::Arc::new(elua::Error::Runtime( + format!("cannot multiply with unknown userdata named {}", name) + )) + }); + } + } + + Err(elua::Error::BadArgument { + func: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)), + arg_index: 2, + arg_name: Some("rhs".to_string()), + error: std::sync::Arc::new( + elua::Error::runtime("cannot multiply with unknown userdata") + ) + }) + } + }, + } + }; + + quote! { + builder.meta_method(elua::MetaMethod::#mt_ident, |_, this, (v,): (elua::Value,)| { + match v { + #number_arm + #userdata_arm + _ => Err(elua::Error::BadArgument { + func: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)), + arg_index: 2, + arg_name: Some("rhs".to_string()), + error: std::sync::Arc::new( + elua::Error::Runtime(format!("cannot multiply with {}", v.type_name())) + ) + }) + } + }); + } + } + } +} + +impl syn::parse::Parse for MetaMethod { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let name: Ident = input.parse()?; + + let mut s = Self { + name, + arg: vec![], + }; + + // try to parse args + if input.peek(syn::token::Paren) { + let content; + let _parens: syn::token::Paren = parenthesized!(content in input); + + let arg: Punctuated = content.parse_terminated(Ident::parse, Token![,])?; + s.arg = arg.into_iter().collect(); // convert to Vec + } + + Ok(s) + } +} + +struct WrapUsage { + type_path: syn::Path, + /// The extra derives of the type. + override_name: Option, + auto_fields: Vec, + auto_derives: Vec, + auto_new: bool, + meta_methods: Vec, + skips: Vec, + + extra_builds: Option, +} + +impl syn::parse::Parse for WrapUsage { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let type_path: syn::Path = input.parse()?; + let mut s = Self { + type_path, + override_name: None, + auto_fields: vec![], + auto_derives: vec![], + extra_builds: None, + auto_new: false, + meta_methods: vec![], + skips: vec![], + }; + + let mut new_ident = None; + + while input.peek(Token![,]) { + let _: Token![,] = input.parse()?; + + if input.peek(syn::Ident) { + let ident: syn::Ident = input.parse()?; + let ident_str = ident.to_string(); + let ident_str = ident_str.as_str(); + + match ident_str { + "name" => { + let _eq: Token![=] = input.parse()?; + + let name: Ident = input.parse()?; + s.override_name = Some(name); + }, + "new" => { + s.auto_new = true; + new_ident = Some(ident.clone()); + }, + "skip" => { + let content; + let _parens: token::Paren = parenthesized!(content in input); + + let terminated = content.parse_terminated(SkipType::parse, Token![,])?; + s.skips = terminated.into_iter().collect(); + }, + "derives" => { + if input.peek(token::Paren) { + let content; + let _parens: token::Paren = parenthesized!(content in input); + + let derives: Punctuated = content.parse_terminated(Ident::parse, Token![,])?; + s.auto_derives = derives.into_iter().collect(); + } + }, + "fields" => { + if input.peek(token::Paren) { + let content; + let _parens: token::Paren = parenthesized!(content in input); + + let fields: Punctuated = content.parse_terminated(Ident::parse, Token![,])?; + s.auto_fields = fields.into_iter().collect(); + } + }, + "metamethods" => { + if input.peek(token::Paren) { + let content; + let _bracket: token::Paren = parenthesized!(content in input); + + let meta_methods: Punctuated = content.parse_terminated(MetaMethod::parse, Token![,])?; + s.meta_methods = meta_methods.into_iter().collect(); + } + }, + _ => { + return Err(syn::Error::new_spanned(ident, "unknown wrapper command")); + } + } + } + + if let Ok(block) = input.parse::() { + s.extra_builds = Some(block); + } + } + + if s.auto_new && s.auto_fields.is_empty() { + return Err(syn::Error::new_spanned(new_ident.unwrap(), "must specify 'fields' when auto creating new function")); + } + + Ok(s) + } +} /// Creates a wrapper type for a VecN from the engine math library. pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStream { @@ -12,28 +401,13 @@ pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::Token let type_name = &path.segments.last() .expect("Failure to find typename in macro usage!") .ident; - let wrapper_typename = Ident::new(&format!("Lua{}", type_name), Span::call_site()); + let wrapper_typename = input.override_name + .unwrap_or_else(|| Ident::new(&format!("Lua{}", type_name), Span::call_site())); - let vec_wrapper = { - let name_str = type_name.to_string(); - if name_str.contains("Vec") { - Some(VecWrapper {}) - } else { - None - } - }; - // TODO: fix this so it doesn't cause a stack overflow - /* let vec_wrapper_fields = vec_wrapper.as_ref().map(|vec| - vec.to_field_tokens(&path, &wrapper_typename)); */ - let vec_wrapper_fields: Option = None; - let vec_wrapper_methods = vec_wrapper.as_ref().map(|vec| - vec.to_method_tokens(&path, &wrapper_typename)); + let derive_idents_iter = input.auto_derives.iter(); + let extra_builds = input.extra_builds; - let derive_idents_iter = input.derive_idents.iter(); - let custom_methods = input.custom_methods; - let custom_fields = input.custom_fields; - - let field_get_set_pairs = input.field_idents.iter().map(|i| { + let field_get_set_pairs = input.auto_fields.iter().map(|i| { let is = i.to_string(); quote! { builder.field_getter(#is, |_, this| { @@ -46,41 +420,50 @@ pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::Token } }); - let new_fn_idents = { - let idents = if input.new_fn_idents.is_empty() { - input.field_idents.iter() - } else { - input.new_fn_idents.iter() - }; + // the tokens for the new function + let new_func_tokens = if input.auto_new { + let arg_names = input.auto_fields.iter().map(|i| { + Ident::new(&i.to_string().to_lowercase(), Span::call_site()) + }); - let idents_c = idents.clone(); + let arg_names_clone = arg_names.clone(); - if !input.skip_new_fn { - quote! { - builder.function("new", |_, ( #(#idents_c),* )| { - Ok(#wrapper_typename(#path::new( #(#idents),* ))) - }); - } - } else { quote!() } - }; + quote! { + // arguments for function are not specified since they can be implied from the call + // to new(...) + builder.function("new", |_, ( #(#arg_names_clone),* )| { + Ok(#wrapper_typename(#path::new( #(#arg_names),* ))) + }); + } - let matrix_wrapper_methods = input.matrix.as_ref().map(|m| - m.to_method_tokens(&path, &wrapper_typename)); - let matrix_wrapper_fields = input.matrix.as_ref().map(|m| - m.to_field_tokens(&path, &wrapper_typename)); + } else { quote!() }; - let meta_method_idents = { - let idents = input.meta_method_idents.iter().map(|metamethod| { - metamethod.to_tokens(&wrapper_typename) + let meta_methods_tokens = { + let method_tokens = input.meta_methods.iter().map(|mm| { + mm.to_tokens(&wrapper_typename) }); quote! { - #(#idents)* + #(#method_tokens)* + } + }; + + let lua_reflects = if input.skips.contains(&SkipType::LuaReflect) { + quote!() + } else { + quote! { + builder.method(#FN_NAME_INTERNAL_REFLECT, |_, this, ()| { + Ok(crate::ScriptBorrow::from_component::<#path>(Some(this.0.clone()))) + }); + + builder.function(#FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| { + Ok(crate::ScriptBorrow::from_component::<#path>(None)) + }); } }; proc_macro::TokenStream::from(quote! { - #[derive(Clone, Copy, lyra_reflect::Reflect, #(#derive_idents_iter),*)] + #[derive(Clone, lyra_reflect::Reflect, #(#derive_idents_iter),*)] pub struct #wrapper_typename(#[reflect(skip)] pub(crate) #path); impl std::ops::Deref for #wrapper_typename { @@ -100,7 +483,7 @@ pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::Token impl<'lua> elua::FromLua<'lua> for #wrapper_typename { fn from_lua(_lua: &'lua elua::State, value: elua::Value<'lua>) -> elua::Result { match value { - elua::Value::Userdata(ud) => Ok(*ud.as_ref::()?), + elua::Value::Userdata(ud) => Ok(ud.as_ref::()?.clone()), _ => panic!("Attempt to get {} from a {} value", stringify!(#wrapper_typename), value.type_name()), } } @@ -113,36 +496,17 @@ pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::Token fn build<'a>(builder: &mut elua::UserdataBuilder<'a, Self>) { #(#field_get_set_pairs)* + #lua_reflects - #matrix_wrapper_fields - #vec_wrapper_fields - - #custom_fields - - #new_fn_idents - - builder.method(#FN_NAME_INTERNAL_REFLECT, |_, this, ()| { - Ok(crate::ScriptBorrow::from_component::<#path>(Some(this.0.clone()))) - }); - - builder.function(#FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| { - Ok(crate::ScriptBorrow::from_component::<#path>(None)) - }); - - #meta_method_idents - - #matrix_wrapper_methods - #vec_wrapper_methods - - #custom_methods + #new_func_tokens + #meta_methods_tokens + #extra_builds } } impl lyra_scripting::lua::LuaWrapper for #wrapper_typename { fn wrapped_type_id() -> std::any::TypeId { - let t = std::any::TypeId::of::<#path>(); - println!("Got id of {}, it is {:?}", stringify!(#path), t); - t + std::any::TypeId::of::<#path>() } } diff --git a/lyra-scripting/lyra-scripting-derive/src/vec_wrapper.rs b/lyra-scripting/lyra-scripting-derive/src/vec_wrapper.rs new file mode 100644 index 0000000..abd217b --- /dev/null +++ b/lyra-scripting/lyra-scripting-derive/src/vec_wrapper.rs @@ -0,0 +1,179 @@ +use quote::quote; +use syn::{Path, Ident}; +use proc_macro2::Span; + +#[derive(Default)] +pub(crate) struct VecWrapper; + +#[allow(dead_code)] +impl VecWrapper { + fn vec_size(&self, wrapper_ident: &Ident) -> usize { + let name = wrapper_ident.to_string(); + name[name.len() - 1..].parse::() + .or_else(|_| name[name.len() - 2.. name.len() - 1].parse::()) + .expect("Failure to grab Vec size from ident name") + } + + /// Returns the token stream of the type of the axis of the vec (Vec2 vs IVec2 vs I64Vec2, etc.) + fn vec_axis_type(&self, wrapper_ident: &Ident) -> &'static str { + let name = wrapper_ident.to_string(); + let start = name.find("Vec").unwrap(); + + let before = &name[start - 1.. start]; + match before { + "D" => return "f64", + "I" => return "i32", + "U" => return "u32", + "B" => return "bool", + _ => {}, + } + //println!("before is {before}"); + + let three_before = &name[start - 3.. start]; + match three_before { + "I64" => return "i64", + "U64" => return "u64", + _ => {}, + } + //println!("three before is {three_before}"); + + "f32" + } + + pub fn to_field_tokens(&self, wrapped_path: &Path, wrapper_ident: &Ident) -> proc_macro2::TokenStream { + let mut consts = vec![quote!(ZERO), quote!(ONE), quote!(X), + quote!(Y), ]; // , quote!(AXES) + + let vec_size = self.vec_size(wrapper_ident); + let axis_type_name = self.vec_axis_type(wrapper_ident); + + if axis_type_name.contains("b") { + return quote! { + builder.field("FALSE", #wrapper_ident(#wrapped_path::FALSE)); + builder.field("TRUE", #wrapper_ident(#wrapped_path::TRUE)); + }; + } + + if vec_size >= 3 { + consts.push(quote!(Z)); + + // no negative numbers for unsigned vecs + if !axis_type_name.contains("u") { + consts.push(quote!(NEG_Z)); + } + } + + if vec_size == 4 { + consts.push(quote!(W)); + + // no negative numbers for unsigned vecs + if !axis_type_name.contains("u") { + consts.push(quote!(NEG_W)); + } + } + + // no negative numbers for unsigned vecs + if !axis_type_name.contains("u") { + consts.push(quote!(NEG_X)); + consts.push(quote!(NEG_Y)); + consts.push(quote!(NEG_ONE)); + } + + if axis_type_name.contains("f") { + consts.push(quote!(NAN)) + } + + let const_tokens = consts.iter().map(|cnst| { + let const_name = cnst.to_string(); + + quote! { + builder.field(#const_name, #wrapper_ident(#wrapped_path::#cnst)); + } + }); + + quote! { + #(#const_tokens)* + } + } + + pub fn to_method_tokens(&self, wrapper_ident: &Ident) -> proc_macro2::TokenStream { + let vec_size = self.vec_size(wrapper_ident); + let axis_type_name = self.vec_axis_type(wrapper_ident); + // methods that only some vecs have + let mut optional_methods = vec![]; + + // boolean vectors dont have much :( + if axis_type_name.contains("b") { + return quote!(); // TODO: all, any, bitmask, splat + } + + if axis_type_name.contains("f") { + let type_id = Ident::new(axis_type_name, Span::call_site()); + + optional_methods.push( + quote! { + builder.method("clamp_length", + |_, this, (min, max): (#type_id, #type_id)| { + Ok(#wrapper_ident(this.clamp_length(min, max))) + }); + + builder.method("abs_diff_eq", + |_, this, (rhs, max_abs_diff): (#wrapper_ident, #type_id)| { + Ok(this.abs_diff_eq(rhs.0, max_abs_diff)) + }); + + builder.method("ceil", + |_, this, (): ()| { + Ok(#wrapper_ident(this.ceil())) + }); + } + ); + + if vec_size != 4 { + optional_methods.push( + quote! { + builder.method("angle_between", + |_, this, (rhs,): (#wrapper_ident,)| { + Ok(this.angle_between(rhs.0)) + }); + } + ) + } + } + + if !axis_type_name.contains("u") { + optional_methods.push( + quote! { + builder.method("abs", + |_, this, (): ()| { + Ok(#wrapper_ident(this.abs())) + }); + } + ) + } + + let optional_methods_iter = optional_methods.iter(); + quote! { + + + builder.method("clamp", + |_, this, (min, max): (#wrapper_ident, #wrapper_ident)| { + Ok(#wrapper_ident(this.clamp(min.0, max.0))) + }); + + // TODO: Not all Vecs have this + /* builder.method("clamp_length", + |_, this, (min, max): (f32, f32)| { + Ok(#wrapper_ident(this.clamp_length(min, max))) + }); */ + + + builder.method("to_array", + |_, this, (): ()| { + Ok(this.to_array()) + }); + + #(#optional_methods_iter)* + } + } +} \ No newline at end of file diff --git a/lyra-scripting/src/lua/wrappers/asset/mod.rs b/lyra-scripting/src/lua/wrappers/asset/mod.rs index 10c471d..28075c5 100644 --- a/lyra-scripting/src/lua/wrappers/asset/mod.rs +++ b/lyra-scripting/src/lua/wrappers/asset/mod.rs @@ -1,12 +1,14 @@ use std::{any::TypeId, ops::Deref}; use elua::{AnyUserdata, AsLua, FromLua, State, Value}; -use lyra_resource::{gltf::Gltf, ResHandle, UntypedResHandle}; +use lyra_resource::{gltf::{Gltf, Material, Mesh}, Texture, ResHandle, UntypedResHandle}; use lyra_game::scene::SceneGraph; use lyra_reflect::{Reflect, TypeData}; -use lyra_scripting_derive::lua_wrap_handle; +use lyra_scripting_derive::{lua_wrap_handle, wrap_lua_struct}; use crate::{lua::{LuaWrapper, FN_NAME_INTERNAL_AS_COMPONENT, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE}, lyra_engine, ScriptBorrow}; +use crate as lyra_scripting; + pub struct LuaResHandleToComponent { /// Create the userdata component that pub fn_to_lua: fn(&State, UntypedResHandle) -> Option, @@ -109,7 +111,91 @@ impl<'a> FromLua<'a> for LuaResHandle { } } +wrap_lua_struct!(lyra_resource::gltf::PbrGlossiness, skip(lua_reflect)); // doesn't need internal lua reflection methods +wrap_lua_struct!(lyra_resource::gltf::Specular, skip(lua_reflect)); // doesn't need internal lua reflection methods + lua_wrap_handle!(SceneGraph, name=Scene, {}); + +lua_wrap_handle!(Mesh, + field_getters={ + (material, { + data.material.clone() + .map(|v| LuaMaterialHandle(v.clone())) + .as_lua(lua) + }) + }, + { + builder.method("indices", |lua, this, ()| { + if let Some(data) = this.0.data_ref() { + let table = lua.create_table()?; + + match &data.indices { + Some(lyra_resource::gltf::MeshIndices::U16(v)) => { + for (i, ind) in v.iter().enumerate() { + let i = i as i64 + 1; // lua indexes start at 1 + table.raw_seti(i, *ind)?; + } + }, + Some(lyra_resource::gltf::MeshIndices::U32(v)) => { + for (i, ind) in v.iter().enumerate() { + let i = i as i64 + 1; // lua indexes start at 1 + table.raw_seti(i, *ind)?; + } + }, + None => {}, + } + + Ok(Value::Table(table)) + } else { + Ok(Value::Nil) + } + }); + + // TODO: attributes + } +); +lua_wrap_handle!(Texture, {}); + +lua_wrap_handle!(Material, + field_getters={ + shader_uuid, + name, + double_sided, + (base_color, wrapper=crate::lua::wrappers::LuaVec4), + metallic, + roughness, + (base_color_texture, { + data.base_color_texture.clone() + .map(|v| LuaTextureHandle(v.clone())) + .as_lua(lua) + }), + (metallic_roughness_texture, { + data.metallic_roughness_texture.clone() + .map(|v| LuaTextureHandle(v.clone())) + .as_lua(lua) + }), + (pbr_glossiness, { + data.pbr_glossiness.clone() + .map(|v| LuaPbrGlossiness(v.clone())) + .as_lua(lua) + }), + alpha_cutoff, + (alpha_mode, { + match data.alpha_mode { + // TODO: Lua enums + lyra_resource::gltf::AlphaMode::Opaque => "opaque", + lyra_resource::gltf::AlphaMode::Mask => "mask", + lyra_resource::gltf::AlphaMode::Blend => "blend", + }.as_lua(lua) + }), + (specular, { + data.specular.clone() + .map(|v| LuaSpecular(v.clone())) + .as_lua(lua) + }), + } +); + lua_wrap_handle!(Gltf, { builder.method("scenes", |lua, this, ()| { if let Some(data) = this.0.data_ref() { @@ -120,6 +206,32 @@ lua_wrap_handle!(Gltf, { table.raw_seti(i, LuaSceneHandle(scene.clone()))?; } + Ok(Value::Table(table)) + } else { + Ok(Value::Nil) + } + }).method("materials", |lua, this, ()| { + if let Some(data) = this.0.data_ref() { + let table = lua.create_table()?; + + for (i, mat) in data.materials.iter().enumerate() { + let i = i as i64 + 1; // lua indexes start at 1 + table.raw_seti(i, LuaMaterialHandle(mat.clone()))?; + } + + Ok(Value::Table(table)) + } else { + Ok(Value::Nil) + } + }).method("meshes", |lua, this, ()| { + if let Some(data) = this.0.data_ref() { + let table = lua.create_table()?; + + for (i, mesh) in data.meshes.iter().enumerate() { + let i = i as i64 + 1; // lua indexes start at 1 + table.raw_seti(i, LuaMeshHandle(mesh.clone()))?; + } + Ok(Value::Table(table)) } else { Ok(Value::Nil) diff --git a/lyra-scripting/src/lua/wrappers/delta_time.rs b/lyra-scripting/src/lua/wrappers/delta_time.rs index 027985c..d0bcf7b 100644 --- a/lyra-scripting/src/lua/wrappers/delta_time.rs +++ b/lyra-scripting/src/lua/wrappers/delta_time.rs @@ -37,4 +37,4 @@ impl LuaWrapper for LuaDeltaTime { fn wrapped_type_id() -> std::any::TypeId { TypeId::of::() } -} \ No newline at end of file +} diff --git a/lyra-scripting/src/lua/wrappers/math.rs b/lyra-scripting/src/lua/wrappers/math.rs index 38cd41a..6a4bf9e 100644 --- a/lyra-scripting/src/lua/wrappers/math.rs +++ b/lyra-scripting/src/lua/wrappers/math.rs @@ -1,15 +1,13 @@ -use std::sync::Arc; - +use lyra_scripting_derive::{lua_vec_wrap_extension, wrap_lua_struct}; use crate::lyra_engine; use lyra_game::math; -use lyra_scripting_derive::wrap_math_vec_copy; use crate as lyra_scripting; -// f32 types -wrap_math_vec_copy!( +wrap_lua_struct!( math::Vec2, - derives(PartialEq), + derives(PartialEq, Copy), + new, fields(x, y), metamethods( Add(LuaVec2, f32), @@ -19,7 +17,9 @@ wrap_math_vec_copy!( Mod(LuaVec2, f32), Eq, Unm, ToString ), - custom_methods { + { + lua_vec_wrap_extension!(math::Vec2, LuaVec2); + builder.method_mut("move_by", |lua, this, vals: elua::ValueVec| { let vals_clone = vals.clone(); if let Some((x, y)) = vals.try_into_vals::<(f32, f32)>(lua)? { @@ -33,9 +33,11 @@ wrap_math_vec_copy!( }); } ); -wrap_math_vec_copy!( + +wrap_lua_struct!( math::Vec3, - derives(PartialEq), + derives(PartialEq, Copy), + new, fields(x, y, z), metamethods( Add(LuaVec3, f32), @@ -45,7 +47,9 @@ wrap_math_vec_copy!( Mod(LuaVec3, f32), Eq, Unm, ToString ), - custom_methods { + { + lua_vec_wrap_extension!(math::Vec3, LuaVec3); + builder.method_mut("move_by", |lua, this, vals: elua::ValueVec| { let vals_clone = vals.clone(); if let Some((x, y, z)) = vals.try_into_vals::<(f32, f32, f32)>(lua)? { @@ -61,412 +65,38 @@ wrap_math_vec_copy!( } ); -wrap_math_vec_copy!( +wrap_lua_struct!( math::Vec4, - derives(PartialEq), - fields(w, x, y, z), + derives(PartialEq, Copy), + new, + fields(x, y, z, w), metamethods( Add(LuaVec4, f32), Sub(LuaVec4, f32), Div(LuaVec4, f32), Mul(LuaVec4, f32), Mod(LuaVec4, f32), - Eq, Unm - ) + Eq, Unm, ToString + ), + { + lua_vec_wrap_extension!(math::Vec4, LuaVec4); + } ); -// ================================================= - - -/* wrap_math_vec_copy!( - math::Vec3A, - derives(PartialEq), - fields(x, y, z), - metamethods( - Add(LuaVec3A, f32), - Sub(LuaVec3A, f32), - Div(LuaVec3A, f32), - Mul(LuaVec3A, f32), - Mod(LuaVec3A, f32), - Eq, Unm - ) -); - - -// f64 types -wrap_math_vec_copy!( - math::DVec2, - derives(PartialEq), - fields(x, y), - metamethods( - Add(LuaDVec2, f64), - Sub(LuaDVec2, f64), - Div(LuaDVec2, f64), - Mul(LuaDVec2, f64), - Mod(LuaDVec2, f64), - Eq, Unm - ) -); -wrap_math_vec_copy!( - math::DVec3, - derives(PartialEq), - fields(x, y, z), - metamethods( - Add(LuaDVec3, f64), - Sub(LuaDVec3, f64), - Div(LuaDVec3, f64), - Mul(LuaDVec3, f64), - Mod(LuaDVec3, f64), - Eq, Unm - ) -); -wrap_math_vec_copy!( - math::DVec4, - derives(PartialEq), - fields(w, x, y, z), - metamethods( - Add(LuaDVec4, f64), - Sub(LuaDVec4, f64), - Div(LuaDVec4, f64), - Mul(LuaDVec4, f64), - Mod(LuaDVec4, f64), - Eq, Unm - ) -); - -// i32 types -wrap_math_vec_copy!( - math::IVec2, - derives(PartialEq, Eq, Hash), - fields(x, y), - metamethods( - Add(LuaIVec2, i32), - Sub(LuaIVec2, i32), - Div(LuaIVec2, i32), - Mul(LuaIVec2, i32), - Mod(LuaIVec2, i32), - Shl(LuaIVec2, LuaUVec2, i32), - Shr(LuaIVec2, LuaUVec2, i32), - BAnd(LuaIVec2, i32), - BOr(LuaIVec2, i32), - BXor(LuaIVec2, i32), - Eq, Unm, BNot - ) -); -wrap_math_vec_copy!( - math::IVec3, - derives(PartialEq, Eq, Hash), - fields(x, y, z), - metamethods( - Add(LuaIVec3, i32), - Sub(LuaIVec3, i32), - Div(LuaIVec3, i32), - Mul(LuaIVec3, i32), - Mod(LuaIVec3, i32), - Shl(LuaIVec3, LuaUVec3, i32), - Shr(LuaIVec3, LuaUVec3, i32), - BAnd(LuaIVec3, i32), - BOr(LuaIVec3, i32), - BXor(LuaIVec3, i32), - Eq, Unm, BNot - ) -); -wrap_math_vec_copy!( - math::IVec4, - derives(PartialEq, Eq, Hash), - fields(w, x, y, z), - metamethods( - Add(LuaIVec4, i32), - Sub(LuaIVec4, i32), - Div(LuaIVec4, i32), - Mul(LuaIVec4, i32), - Mod(LuaIVec4, i32), - Shl(LuaIVec4, LuaUVec4, i32), - Shr(LuaIVec4, LuaUVec4, i32), - BAnd(LuaIVec4, i32), - BOr(LuaIVec4, i32), - BXor(LuaIVec4, i32), - Eq, Unm, BNot - ) -); - -// u32 types -wrap_math_vec_copy!( - math::UVec2, - derives(PartialEq, Eq, Hash), - fields(x, y), - metamethods( - Add(LuaUVec2, u32), - Sub(LuaUVec2, u32), - Div(LuaUVec2, u32), - Mul(LuaUVec2, u32), - Mod(LuaUVec2, u32), - Shl(LuaUVec2, LuaIVec2, i32), - Shr(LuaUVec2, LuaIVec2, i32), - BAnd(LuaUVec2, u32), - BOr(LuaUVec2, u32), - BXor(LuaUVec2, u32), - Eq, BNot - ) -); -wrap_math_vec_copy!( - math::UVec3, - derives(PartialEq, Eq, Hash), - fields(x, y, z), - metamethods( - Add(LuaUVec3, u32), - Sub(LuaUVec3, u32), - Div(LuaUVec3, u32), - Mul(LuaUVec3, u32), - Mod(LuaUVec3, u32), - Shl(LuaUVec3, LuaIVec3, i32), - Shr(LuaUVec3, LuaIVec3, i32), - BAnd(LuaUVec3, u32), - BOr(LuaUVec3, u32), - BXor(LuaUVec3, u32), - Eq, BNot - ) -); -wrap_math_vec_copy!( - math::UVec4, - derives(PartialEq, Eq, Hash), - fields(w, x, y, z), - metamethods( - Add(LuaUVec4, u32), - Sub(LuaUVec4, u32), - Div(LuaUVec4, u32), - Mul(LuaUVec4, u32), - Mod(LuaUVec4, u32), - Shl(LuaUVec4, LuaIVec4, i32), - Shr(LuaUVec4, LuaIVec4, i32), - BAnd(LuaUVec4, u32), - BOr(LuaUVec4, u32), - BXor(LuaUVec4, u32), - Eq, BNot - ) -); - -// i64 types -wrap_math_vec_copy!( - math::I64Vec2, - derives(PartialEq, Eq, Hash), - fields(x, y), - metamethods( - Add(LuaI64Vec2, i64), - Sub(LuaI64Vec2, i64), - Div(LuaI64Vec2, i64), - Mul(LuaI64Vec2, i64), - Mod(LuaI64Vec2, i64), - Shl(i64), - Shr(i64), - BAnd(LuaI64Vec2, i64), - BOr(LuaI64Vec2, i64), - BXor(LuaI64Vec2, i64), - Eq, BNot - ) -); -wrap_math_vec_copy!( - math::I64Vec3, - derives(PartialEq, Eq, Hash), - fields(x, y, z), - metamethods( - Add(LuaI64Vec3, i64), - Sub(LuaI64Vec3, i64), - Div(LuaI64Vec3, i64), - Mul(LuaI64Vec3, i64), - Mod(LuaI64Vec3, i64), - Shl(i64), - Shr(i64), - BAnd(LuaI64Vec3, i64), - BOr(LuaI64Vec3, i64), - BXor(LuaI64Vec3, i64), - Eq, BNot - ) -); -wrap_math_vec_copy!( - math::I64Vec4, - derives(PartialEq, Eq, Hash), - fields(w, x, y, z), - metamethods( - Add(LuaI64Vec4, i64), - Sub(LuaI64Vec4, i64), - Div(LuaI64Vec4, i64), - Mul(LuaI64Vec4, i64), - Mod(LuaI64Vec4, i64), - Shl(i64), - Shr(i64), - BAnd(LuaI64Vec4, i64), - BOr(LuaI64Vec4, i64), - BXor(LuaI64Vec4, i64), - Eq, BNot - ) -); - -// u64 types -wrap_math_vec_copy!( - math::U64Vec2, - derives(PartialEq, Eq, Hash), - fields(x, y), - metamethods( - Add(LuaU64Vec2, u64), - Sub(LuaU64Vec2, u64), - Div(LuaU64Vec2, u64), - Mul(LuaU64Vec2, u64), - Mod(LuaU64Vec2, u64), - Shl(i64), - Shr(i64), - BAnd(LuaU64Vec2, u64), - BOr(LuaU64Vec2, u64), - BXor(LuaU64Vec2, u64), - Eq, BNot - ) -); -wrap_math_vec_copy!( - math::U64Vec3, - derives(PartialEq, Eq, Hash), - fields(x, y, z), - metamethods( - Add(LuaU64Vec3, u64), - Sub(LuaU64Vec3, u64), - Div(LuaU64Vec3, u64), - Mul(LuaU64Vec3, u64), - Mod(LuaU64Vec3, u64), - Shl(i64), - Shr(i64), - BAnd(LuaU64Vec3, u64), - BOr(LuaU64Vec3, u64), - BXor(LuaU64Vec3, u64), - Eq, BNot - ) -); -wrap_math_vec_copy!( - math::U64Vec4, - derives(PartialEq, Eq, Hash), - fields(w, x, y, z), - metamethods( - Add(LuaU64Vec4, u64), - Sub(LuaU64Vec4, u64), - Div(LuaU64Vec4, u64), - Mul(LuaU64Vec4, u64), - Mod(LuaU64Vec4, u64), - Shl(i64), - Shr(i64), - BAnd(LuaU64Vec4, u64), - BOr(LuaU64Vec4, u64), - BXor(LuaU64Vec4, u64), - Eq, BNot - ) -);*/ - -// bool types -/* wrap_math_vec_copy!( - math::BVec2, - derives(PartialEq, Eq, Hash), - fields(x, y), - metamethods(Eq, BAnd, BOr, BXOr, BNot) -); -wrap_math_vec_copy!( - math::BVec3, - derives(PartialEq, Eq, Hash), - fields(x, y, z), - metamethods(Eq, BAnd, BOr, BXOr, BNot) -); -wrap_math_vec_copy!( - math::BVec4, - derives(PartialEq, Eq, Hash), - fields(w, x, y, z), - metamethods(Eq, BAnd, BOr, BXOr, BNot) -); */ - -// mat2 -/* wrap_math_vec_copy!( - math::Mat2, - derives(PartialEq), - no_new, - matrix { - col_type = LuaVec2 - }, - metamethods( - Eq, - Add, - Sub, - Mul(LuaMat2, f32), - Unm - ) -); */ - -// TODO -/* wrap_math_vec_copy!( - math::Mat4, - derives(PartialEq), - no_new, - matrix { - col_type = LuaVec4 - }, - metamethods( - Eq, - Add, - Sub, - Mul(LuaMat4, f32), - Unm - ) -); */ - - - - - - -// ============================================================ - - - -/// A macro that generates field getters and setters for lua wrapped types. -macro_rules! wrapped_field_getsetters { - ($builder: ident, $name: literal, $field: ident, $type: ident) => { - $builder.field_getter($name, |_, this| { - Ok($type(this.$field)) - }); - $builder.field_setter($name, |_, this, v: $type| { - this.$field = *v; - Ok(()) - }); - }; -} - -/// A macro that generates field getters and setters for types that already implement As/FromLua. -macro_rules! field_getsetters { - ($builder: ident, $name: literal, $field: ident, $type: ty) => { - $builder.field_getter($name, |_, this| { - Ok(this.$field) - }); - $builder.field_setter($name, |_, this, v: $type| { - this.$field = v; - Ok(()) - }); - }; -} - - -wrap_math_vec_copy!( +wrap_lua_struct!( math::Quat, - derives(PartialEq), - no_new, + derives(PartialEq, Copy), + //new, + fields(x, y, z, w), metamethods( Eq, - // __mul for LuaVec3 is manually implemented below since it doesn't return Self - //Mul(LuaQuat, f32), Add, Sub, Div(f32), + // __mul for LuaVec3 is manually implemented below since it doesn't return Self + //Mul(LuaQuat, f32), ), - custom_fields { - field_getsetters!(builder, "x", x, f32); - field_getsetters!(builder, "y", y, f32); - field_getsetters!(builder, "z", z, f32); - field_getsetters!(builder, "w", w, f32); - }, - custom_methods { + { // manually implemented since Quat doesn't have a `new` function builder.function("new", |_, (x, y, z, w)| { Ok(Self(math::Quat::from_xyzw(x, y, z, w))) @@ -545,17 +175,11 @@ wrap_math_vec_copy!( } ); -wrap_math_vec_copy!( +wrap_lua_struct!( math::Transform, - derives(PartialEq), - no_new, + derives(PartialEq, Copy), metamethods(ToString, Eq), - custom_fields { - wrapped_field_getsetters!(builder, "translation", translation, LuaVec3); - wrapped_field_getsetters!(builder, "rotation", rotation, LuaQuat); - wrapped_field_getsetters!(builder, "scale", scale, LuaVec3); - }, - custom_methods { + { builder.function("default", |_, ()| { Ok(Self(math::Transform::default())) }); diff --git a/lyra-scripting/src/lua/wrappers/mod.rs b/lyra-scripting/src/lua/wrappers/mod.rs index db6639a..889d9b9 100644 --- a/lyra-scripting/src/lua/wrappers/mod.rs +++ b/lyra-scripting/src/lua/wrappers/mod.rs @@ -1,11 +1,11 @@ -pub mod math; +mod math; pub use math::*; -pub mod delta_time; -pub use delta_time::*; - -pub mod input_actions; +mod input_actions; pub use input_actions::*; -pub mod asset; -pub use asset::*; \ No newline at end of file +mod asset; +pub use asset::*; + +mod delta_time; +pub use delta_time::*; \ No newline at end of file diff --git a/lyra-scripting/src/lua/wrappers/model_comp.rs b/lyra-scripting/src/lua/wrappers/model_comp.rs deleted file mode 100644 index 5f32198..0000000 --- a/lyra-scripting/src/lua/wrappers/model_comp.rs +++ /dev/null @@ -1,69 +0,0 @@ -use std::any::TypeId; -use std::{cell::Ref, sync::Arc}; - -use elua::FromLua; -use lyra_game::scene::ModelComponent; -use lyra_reflect::Reflect; -use lyra_resource::{Model, ResHandle}; - -use crate::lua::LuaWrapper; -use crate::lyra_engine; -use crate::{lua::{FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE}, ScriptBorrow}; - -use super::LuaResHandle; - -#[derive(Clone, Reflect)] -pub struct LuaModelComponent(pub ModelComponent); - -impl elua::Userdata for LuaModelComponent { - fn name() -> String { - "ModelComponent".to_string() - } - - fn build<'a>(builder: &mut elua::UserdataBuilder<'a, Self>) { - builder - .function("new", |_, model: Ref| { - let res = model.0.clone(); - let res_any = res.as_arc_any(); - match res_any.downcast::>() { - Ok(handle) => { - let res = ResHandle::::clone(&handle); - Ok(Self(ModelComponent(res))) - }, - Err(_) => { - Err(elua::Error::BadArgument { - func: Some("ModelComponent:new".to_string()), - arg_index: 1, - arg_name: Some("model".to_string()), - error: Arc::new( - elua::Error::runtime("resource handle is not a handle to a Model") - ) - }) - } - } - }) - .function(FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| { - Ok(ScriptBorrow::from_component::(None)) - }) - .method(FN_NAME_INTERNAL_REFLECT, |_, this, ()| { - Ok(ScriptBorrow::from_component(Some(this.0.clone()))) - }); - } -} - -impl<'a> FromLua<'a> for LuaModelComponent { - fn from_lua(_: &'a elua::State, val: elua::Value<'a>) -> elua::Result { - let tyname = val.type_name(); - let ud = val.as_userdata() - .ok_or(elua::Error::type_mismatch("Model", &tyname))?; - let ud = ud.as_ref::()?; - - Ok(ud.clone()) - } -} - -impl LuaWrapper for LuaModelComponent { - fn wrapped_type_id() -> std::any::TypeId { - TypeId::of::() - } -} \ No newline at end of file