scripting: improve macros to make it easier to create wrappers
This commit is contained in:
parent
29c68abbbb
commit
d1f1e03cbb
|
@ -1,12 +1,81 @@
|
||||||
use quote::{format_ident, quote};
|
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 {
|
struct FieldGetter {
|
||||||
pub type_path: syn::Path,
|
field: Ident,
|
||||||
|
body: Option<syn::Block>,
|
||||||
|
wrapper_type: Option<syn::Path>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FieldGetter {
|
||||||
|
fn parse_extended(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||||
|
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::<syn::Block>() {
|
||||||
|
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<Self> {
|
||||||
|
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.
|
/// The extra derives of the type.
|
||||||
pub override_name: Option<Ident>,
|
override_name: Option<Ident>,
|
||||||
|
field_getters: Vec<FieldGetter>,
|
||||||
|
|
||||||
pub extra_builds: Option<syn::Block>,
|
extra_builds: Option<syn::Block>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl syn::parse::Parse for HandleWrapUsage {
|
impl syn::parse::Parse for HandleWrapUsage {
|
||||||
|
@ -15,6 +84,7 @@ impl syn::parse::Parse for HandleWrapUsage {
|
||||||
let mut s = Self {
|
let mut s = Self {
|
||||||
type_path,
|
type_path,
|
||||||
override_name: None,
|
override_name: None,
|
||||||
|
field_getters: vec![],
|
||||||
extra_builds: None,
|
extra_builds: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -30,17 +100,24 @@ impl syn::parse::Parse for HandleWrapUsage {
|
||||||
"name" => {
|
"name" => {
|
||||||
let _eq: Token![=] = input.parse()?;
|
let _eq: Token![=] = input.parse()?;
|
||||||
|
|
||||||
let name: Ident = input.parse()?;
|
s.override_name = Some(input.parse()?);
|
||||||
s.override_name = Some(name);
|
|
||||||
},
|
},
|
||||||
|
"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"));
|
return Err(syn::Error::new_spanned(ident, "unknown wrapper command"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}/* else if input.parse::<Block(syn::Block) {
|
}
|
||||||
let block = input.parse()?;
|
|
||||||
s.extra_builds = block;
|
|
||||||
} */
|
|
||||||
|
|
||||||
if let Ok(block) = input.parse::<syn::Block>() {
|
if let Ok(block) = input.parse::<syn::Block>() {
|
||||||
s.extra_builds = Some(block);
|
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 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! {
|
quote! {
|
||||||
#[derive(Clone, Reflect)]
|
#[derive(Clone, Reflect)]
|
||||||
pub struct #wrapper_name(pub ResHandle<#handle_name>);
|
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)
|
Ok(name)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
#(#custom_getters)*
|
||||||
|
|
||||||
builder.method("is_watched", |_, this, ()| {
|
builder.method("is_watched", |_, this, ()| {
|
||||||
Ok(this.is_watched())
|
Ok(this.is_watched())
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,735 +1,19 @@
|
||||||
use handle_macro::lua_wrap_handle_impl;
|
use handle_macro::lua_wrap_handle_impl;
|
||||||
use lua_macro::wrap_lua_struct_impl;
|
use lua_macro::wrap_lua_struct_impl;
|
||||||
use proc_macro2::{Ident, Span};
|
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::{parse_macro_input, Path, Token, token, parenthesized, punctuated::Punctuated, braced};
|
use syn::{parse_macro_input, Token};
|
||||||
|
|
||||||
mod mat_wrapper;
|
mod mat_wrapper;
|
||||||
use mat_wrapper::MatWrapper;
|
mod vec_wrapper;
|
||||||
|
use vec_wrapper::VecWrapper;
|
||||||
|
|
||||||
mod lua_macro;
|
mod lua_macro;
|
||||||
|
|
||||||
mod handle_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_TYPE: &str = "__lyra_internal_reflect_type";
|
||||||
pub(crate) const FN_NAME_INTERNAL_REFLECT: &str = "__lyra_internal_reflect";
|
pub(crate) const FN_NAME_INTERNAL_REFLECT: &str = "__lyra_internal_reflect";
|
||||||
|
|
||||||
pub(crate) struct MetaMethod {
|
|
||||||
pub ident: Ident,
|
|
||||||
pub mods: Vec<Ident>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl syn::parse::Parse for MetaMethod {
|
|
||||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
|
||||||
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::<usize>()
|
|
||||||
.or_else(|_| name[name.len() - 2.. name.len() - 1].parse::<usize>())
|
|
||||||
.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<Ident, Token![,]>,
|
|
||||||
/// The field idents of the type that will be exposed with gets and sets
|
|
||||||
pub field_idents: Punctuated<Ident, Token![,]>,
|
|
||||||
pub skip_new_fn: bool,
|
|
||||||
/// The identifiers that are taken as parameters in the types 'new' function
|
|
||||||
pub new_fn_idents: Punctuated<Ident, Token![,]>,
|
|
||||||
pub meta_method_idents: Punctuated<MetaMethod, Token![,]>,
|
|
||||||
|
|
||||||
pub matrix: Option<MatWrapper>,
|
|
||||||
pub vec: Option<VecWrapper>,
|
|
||||||
|
|
||||||
pub custom_methods: Option<syn::Block>,
|
|
||||||
pub custom_fields: Option<syn::Block>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl syn::parse::Parse for WrapUsage {
|
|
||||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
|
||||||
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<Ident, Token![,]> = 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<Ident, Token![,]> = 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<Ident, Token![,]> = 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<MetaMethod, Token![,]> = 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<proc_macro2::TokenStream> = 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<Self> {
|
|
||||||
match value {
|
|
||||||
elua::Value::Userdata(ud) => Ok(*ud.as_ref::<Self>()?),
|
|
||||||
_ => 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<Self> {
|
|
||||||
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]
|
#[proc_macro]
|
||||||
pub fn wrap_lua_struct(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
pub fn wrap_lua_struct(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
wrap_lua_struct_impl(input)
|
wrap_lua_struct_impl(input)
|
||||||
|
@ -739,3 +23,34 @@ pub fn wrap_lua_struct(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
|
||||||
pub fn lua_wrap_handle(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
pub fn lua_wrap_handle(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
lua_wrap_handle_impl(input)
|
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<Self> {
|
||||||
|
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()
|
||||||
|
}
|
|
@ -1,8 +1,397 @@
|
||||||
use proc_macro2::Span;
|
use proc_macro2::Span;
|
||||||
use quote::quote;
|
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<Self> {
|
||||||
|
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<Ident>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Self> {
|
||||||
|
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<Ident, Token![,]> = content.parse_terminated(Ident::parse, Token![,])?;
|
||||||
|
s.arg = arg.into_iter().collect(); // convert to Vec<Ident>
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct WrapUsage {
|
||||||
|
type_path: syn::Path,
|
||||||
|
/// The extra derives of the type.
|
||||||
|
override_name: Option<Ident>,
|
||||||
|
auto_fields: Vec<Ident>,
|
||||||
|
auto_derives: Vec<Ident>,
|
||||||
|
auto_new: bool,
|
||||||
|
meta_methods: Vec<MetaMethod>,
|
||||||
|
skips: Vec<SkipType>,
|
||||||
|
|
||||||
|
extra_builds: Option<syn::Block>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl syn::parse::Parse for WrapUsage {
|
||||||
|
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||||
|
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<Ident, Token![,]> = 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<Ident, Token![,]> = 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<MetaMethod, Token![,]> = 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::<syn::Block>() {
|
||||||
|
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.
|
/// 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 {
|
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()
|
let type_name = &path.segments.last()
|
||||||
.expect("Failure to find typename in macro usage!")
|
.expect("Failure to find typename in macro usage!")
|
||||||
.ident;
|
.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 derive_idents_iter = input.auto_derives.iter();
|
||||||
let name_str = type_name.to_string();
|
let extra_builds = input.extra_builds;
|
||||||
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<proc_macro2::TokenStream> = 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 field_get_set_pairs = input.auto_fields.iter().map(|i| {
|
||||||
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();
|
let is = i.to_string();
|
||||||
quote! {
|
quote! {
|
||||||
builder.field_getter(#is, |_, this| {
|
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 = {
|
// the tokens for the new function
|
||||||
let idents = if input.new_fn_idents.is_empty() {
|
let new_func_tokens = if input.auto_new {
|
||||||
input.field_idents.iter()
|
let arg_names = input.auto_fields.iter().map(|i| {
|
||||||
} else {
|
Ident::new(&i.to_string().to_lowercase(), Span::call_site())
|
||||||
input.new_fn_idents.iter()
|
});
|
||||||
};
|
|
||||||
|
|
||||||
let idents_c = idents.clone();
|
let arg_names_clone = arg_names.clone();
|
||||||
|
|
||||||
if !input.skip_new_fn {
|
|
||||||
quote! {
|
quote! {
|
||||||
builder.function("new", |_, ( #(#idents_c),* )| {
|
// arguments for function are not specified since they can be implied from the call
|
||||||
Ok(#wrapper_typename(#path::new( #(#idents),* )))
|
// to new(...)
|
||||||
|
builder.function("new", |_, ( #(#arg_names_clone),* )| {
|
||||||
|
Ok(#wrapper_typename(#path::new( #(#arg_names),* )))
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else { quote!() }
|
|
||||||
};
|
|
||||||
|
|
||||||
let matrix_wrapper_methods = input.matrix.as_ref().map(|m|
|
} else { quote!() };
|
||||||
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 meta_methods_tokens = {
|
||||||
let idents = input.meta_method_idents.iter().map(|metamethod| {
|
let method_tokens = input.meta_methods.iter().map(|mm| {
|
||||||
metamethod.to_tokens(&wrapper_typename)
|
mm.to_tokens(&wrapper_typename)
|
||||||
});
|
});
|
||||||
|
|
||||||
quote! {
|
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! {
|
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);
|
pub struct #wrapper_typename(#[reflect(skip)] pub(crate) #path);
|
||||||
|
|
||||||
impl std::ops::Deref for #wrapper_typename {
|
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 {
|
impl<'lua> elua::FromLua<'lua> for #wrapper_typename {
|
||||||
fn from_lua(_lua: &'lua elua::State, value: elua::Value<'lua>) -> elua::Result<Self> {
|
fn from_lua(_lua: &'lua elua::State, value: elua::Value<'lua>) -> elua::Result<Self> {
|
||||||
match value {
|
match value {
|
||||||
elua::Value::Userdata(ud) => Ok(*ud.as_ref::<Self>()?),
|
elua::Value::Userdata(ud) => Ok(ud.as_ref::<Self>()?.clone()),
|
||||||
_ => panic!("Attempt to get {} from a {} value", stringify!(#wrapper_typename), value.type_name()),
|
_ => 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>) {
|
fn build<'a>(builder: &mut elua::UserdataBuilder<'a, Self>) {
|
||||||
#(#field_get_set_pairs)*
|
#(#field_get_set_pairs)*
|
||||||
|
#lua_reflects
|
||||||
|
|
||||||
#matrix_wrapper_fields
|
#new_func_tokens
|
||||||
#vec_wrapper_fields
|
#meta_methods_tokens
|
||||||
|
#extra_builds
|
||||||
#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 {
|
impl lyra_scripting::lua::LuaWrapper for #wrapper_typename {
|
||||||
fn wrapped_type_id() -> std::any::TypeId {
|
fn wrapped_type_id() -> std::any::TypeId {
|
||||||
let t = std::any::TypeId::of::<#path>();
|
std::any::TypeId::of::<#path>()
|
||||||
println!("Got id of {}, it is {:?}", stringify!(#path), t);
|
|
||||||
t
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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::<usize>()
|
||||||
|
.or_else(|_| name[name.len() - 2.. name.len() - 1].parse::<usize>())
|
||||||
|
.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)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +1,14 @@
|
||||||
use std::{any::TypeId, ops::Deref};
|
use std::{any::TypeId, ops::Deref};
|
||||||
use elua::{AnyUserdata, AsLua, FromLua, State, Value};
|
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_game::scene::SceneGraph;
|
||||||
use lyra_reflect::{Reflect, TypeData};
|
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::{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 {
|
pub struct LuaResHandleToComponent {
|
||||||
/// Create the userdata component that
|
/// Create the userdata component that
|
||||||
pub fn_to_lua: fn(&State, UntypedResHandle) -> Option<AnyUserdata>,
|
pub fn_to_lua: fn(&State, UntypedResHandle) -> Option<AnyUserdata>,
|
||||||
|
@ -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!(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, {
|
lua_wrap_handle!(Gltf, {
|
||||||
builder.method("scenes", |lua, this, ()| {
|
builder.method("scenes", |lua, this, ()| {
|
||||||
if let Some(data) = this.0.data_ref() {
|
if let Some(data) = this.0.data_ref() {
|
||||||
|
@ -120,6 +206,32 @@ lua_wrap_handle!(Gltf, {
|
||||||
table.raw_seti(i, LuaSceneHandle(scene.clone()))?;
|
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))
|
Ok(Value::Table(table))
|
||||||
} else {
|
} else {
|
||||||
Ok(Value::Nil)
|
Ok(Value::Nil)
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
use std::sync::Arc;
|
use lyra_scripting_derive::{lua_vec_wrap_extension, wrap_lua_struct};
|
||||||
|
|
||||||
use crate::lyra_engine;
|
use crate::lyra_engine;
|
||||||
use lyra_game::math;
|
use lyra_game::math;
|
||||||
use lyra_scripting_derive::wrap_math_vec_copy;
|
|
||||||
|
|
||||||
use crate as lyra_scripting;
|
use crate as lyra_scripting;
|
||||||
|
|
||||||
// f32 types
|
wrap_lua_struct!(
|
||||||
wrap_math_vec_copy!(
|
|
||||||
math::Vec2,
|
math::Vec2,
|
||||||
derives(PartialEq),
|
derives(PartialEq, Copy),
|
||||||
|
new,
|
||||||
fields(x, y),
|
fields(x, y),
|
||||||
metamethods(
|
metamethods(
|
||||||
Add(LuaVec2, f32),
|
Add(LuaVec2, f32),
|
||||||
|
@ -19,7 +17,9 @@ wrap_math_vec_copy!(
|
||||||
Mod(LuaVec2, f32),
|
Mod(LuaVec2, f32),
|
||||||
Eq, Unm, ToString
|
Eq, Unm, ToString
|
||||||
),
|
),
|
||||||
custom_methods {
|
{
|
||||||
|
lua_vec_wrap_extension!(math::Vec2, LuaVec2);
|
||||||
|
|
||||||
builder.method_mut("move_by", |lua, this, vals: elua::ValueVec| {
|
builder.method_mut("move_by", |lua, this, vals: elua::ValueVec| {
|
||||||
let vals_clone = vals.clone();
|
let vals_clone = vals.clone();
|
||||||
if let Some((x, y)) = vals.try_into_vals::<(f32, f32)>(lua)? {
|
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,
|
math::Vec3,
|
||||||
derives(PartialEq),
|
derives(PartialEq, Copy),
|
||||||
|
new,
|
||||||
fields(x, y, z),
|
fields(x, y, z),
|
||||||
metamethods(
|
metamethods(
|
||||||
Add(LuaVec3, f32),
|
Add(LuaVec3, f32),
|
||||||
|
@ -45,7 +47,9 @@ wrap_math_vec_copy!(
|
||||||
Mod(LuaVec3, f32),
|
Mod(LuaVec3, f32),
|
||||||
Eq, Unm, ToString
|
Eq, Unm, ToString
|
||||||
),
|
),
|
||||||
custom_methods {
|
{
|
||||||
|
lua_vec_wrap_extension!(math::Vec3, LuaVec3);
|
||||||
|
|
||||||
builder.method_mut("move_by", |lua, this, vals: elua::ValueVec| {
|
builder.method_mut("move_by", |lua, this, vals: elua::ValueVec| {
|
||||||
let vals_clone = vals.clone();
|
let vals_clone = vals.clone();
|
||||||
if let Some((x, y, z)) = vals.try_into_vals::<(f32, f32, f32)>(lua)? {
|
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,
|
math::Vec4,
|
||||||
derives(PartialEq),
|
derives(PartialEq, Copy),
|
||||||
fields(w, x, y, z),
|
new,
|
||||||
|
fields(x, y, z, w),
|
||||||
metamethods(
|
metamethods(
|
||||||
Add(LuaVec4, f32),
|
Add(LuaVec4, f32),
|
||||||
Sub(LuaVec4, f32),
|
Sub(LuaVec4, f32),
|
||||||
Div(LuaVec4, f32),
|
Div(LuaVec4, f32),
|
||||||
Mul(LuaVec4, f32),
|
Mul(LuaVec4, f32),
|
||||||
Mod(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.
|
wrap_lua_struct!(
|
||||||
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!(
|
|
||||||
math::Quat,
|
math::Quat,
|
||||||
derives(PartialEq),
|
derives(PartialEq, Copy),
|
||||||
no_new,
|
//new,
|
||||||
|
fields(x, y, z, w),
|
||||||
metamethods(
|
metamethods(
|
||||||
Eq,
|
Eq,
|
||||||
// __mul for LuaVec3 is manually implemented below since it doesn't return Self
|
|
||||||
//Mul(LuaQuat, f32),
|
|
||||||
Add,
|
Add,
|
||||||
Sub,
|
Sub,
|
||||||
Div(f32),
|
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
|
// manually implemented since Quat doesn't have a `new` function
|
||||||
builder.function("new", |_, (x, y, z, w)| {
|
builder.function("new", |_, (x, y, z, w)| {
|
||||||
Ok(Self(math::Quat::from_xyzw(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,
|
math::Transform,
|
||||||
derives(PartialEq),
|
derives(PartialEq, Copy),
|
||||||
no_new,
|
|
||||||
metamethods(ToString, Eq),
|
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", |_, ()| {
|
builder.function("default", |_, ()| {
|
||||||
Ok(Self(math::Transform::default()))
|
Ok(Self(math::Transform::default()))
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
pub mod math;
|
mod math;
|
||||||
pub use math::*;
|
pub use math::*;
|
||||||
|
|
||||||
pub mod delta_time;
|
mod input_actions;
|
||||||
pub use delta_time::*;
|
|
||||||
|
|
||||||
pub mod input_actions;
|
|
||||||
pub use input_actions::*;
|
pub use input_actions::*;
|
||||||
|
|
||||||
pub mod asset;
|
mod asset;
|
||||||
pub use asset::*;
|
pub use asset::*;
|
||||||
|
|
||||||
|
mod delta_time;
|
||||||
|
pub use delta_time::*;
|
|
@ -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<LuaResHandle>| {
|
|
||||||
let res = model.0.clone();
|
|
||||||
let res_any = res.as_arc_any();
|
|
||||||
match res_any.downcast::<ResHandle<Model>>() {
|
|
||||||
Ok(handle) => {
|
|
||||||
let res = ResHandle::<Model>::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::<ModelComponent>(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<Self> {
|
|
||||||
let tyname = val.type_name();
|
|
||||||
let ud = val.as_userdata()
|
|
||||||
.ok_or(elua::Error::type_mismatch("Model", &tyname))?;
|
|
||||||
let ud = ud.as_ref::<LuaModelComponent>()?;
|
|
||||||
|
|
||||||
Ok(ud.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LuaWrapper for LuaModelComponent {
|
|
||||||
fn wrapped_type_id() -> std::any::TypeId {
|
|
||||||
TypeId::of::<ModelComponent>()
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue