scripting: improve macros to make it easier to create wrappers

This commit is contained in:
SeanOMik 2024-04-27 19:43:45 -04:00
parent 29c68abbbb
commit d1f1e03cbb
Signed by: SeanOMik
GPG Key ID: FEC9E2FC15235964
9 changed files with 921 additions and 1289 deletions

View File

@ -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<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.
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 {
@ -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::<Block(syn::Block) {
let block = input.parse()?;
s.extra_builds = block;
} */
}
if let Ok(block) = input.parse::<syn::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 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())
});

View File

@ -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<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]
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<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()
}

View File

@ -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<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.
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<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.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<Self> {
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()),
}
}
@ -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>()
}
}

View File

@ -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)*
}
}
}

View File

@ -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<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!(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)

View File

@ -37,4 +37,4 @@ impl LuaWrapper for LuaDeltaTime {
fn wrapped_type_id() -> std::any::TypeId {
TypeId::of::<DeltaTime>()
}
}
}

View File

@ -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()))
});

View File

@ -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::*;
mod asset;
pub use asset::*;
mod delta_time;
pub use delta_time::*;

View File

@ -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>()
}
}