Expose structs to Lua and write Lua type annotations #28

Merged
SeanOMik merged 15 commits from feat/lua-type-defs into main 2024-10-19 15:17:00 +00:00
23 changed files with 1021 additions and 163 deletions
Showing only changes of commit e9cbb48653 - Show all commits

View File

@ -69,12 +69,23 @@ function on_update()
---@type number ---@type number
local dt = world:resource(DeltaTime) local dt = world:resource(DeltaTime)
world:view( --[[ world:view(
---@param t Transform ---@param t Transform
function (t) function (t)
t:translate(0, 0.15 * dt, 0) t:translate(0, 0.15 * dt, 0)
return t return t
end, Transform end, Transform
) ]]
world:view(
---@param c Camera
function (c)
c.transform:translate(0, 0.15 * dt, 0)
print("Moving camera to: " .. tostring(c.transform))
return c
end, Camera
) )
end end

View File

@ -1,8 +1,9 @@
use lyra_reflect::Reflect;
use winit::dpi::PhysicalSize; use winit::dpi::PhysicalSize;
use crate::{math::{Angle, OPENGL_TO_WGPU_MATRIX}, scene::CameraComponent}; use crate::{math::{Angle, OPENGL_TO_WGPU_MATRIX}, scene::CameraComponent};
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Reflect)]
pub enum CameraProjectionMode { pub enum CameraProjectionMode {
/// 3d camera projection /// 3d camera projection
Perspective, Perspective,

View File

@ -1,8 +1,9 @@
use lyra_ecs::Component; use lyra_ecs::Component;
use lyra_reflect::Reflect;
use crate::{math::{Angle, Transform}, render::camera::CameraProjectionMode}; use crate::{math::{Angle, Transform}, render::camera::CameraProjectionMode};
#[derive(Clone, Component)] #[derive(Clone, Component, Reflect)]
pub struct CameraComponent { pub struct CameraComponent {
pub transform: Transform, pub transform: Transform,
pub fov: Angle, pub fov: Angle,

View File

@ -1,6 +1,7 @@
use lyra_math::Angle;
use lyra_reflect_derive::{impl_reflect_simple_struct, impl_reflect_trait_value}; use lyra_reflect_derive::{impl_reflect_simple_struct, impl_reflect_trait_value};
use crate::{lyra_engine, Method, Reflect}; use crate::{lyra_engine, Enum, Method, Reflect, ReflectMut, ReflectRef};
impl_reflect_simple_struct!(lyra_math::Vec2, fields(x = f32, y = f32)); impl_reflect_simple_struct!(lyra_math::Vec2, fields(x = f32, y = f32));
impl_reflect_simple_struct!(lyra_math::Vec3, fields(x = f32, y = f32, z = f32)); impl_reflect_simple_struct!(lyra_math::Vec3, fields(x = f32, y = f32, z = f32));
@ -17,3 +18,131 @@ impl_reflect_simple_struct!(
); );
impl_reflect_trait_value!(lyra_math::Mat4); impl_reflect_trait_value!(lyra_math::Mat4);
impl Reflect for Angle {
fn name(&self) -> String {
"Angle".into()
}
fn type_id(&self) -> std::any::TypeId {
std::any::TypeId::of::<Self>()
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn as_boxed_any(self: Box<Self>) -> Box<dyn std::any::Any> {
self
}
fn apply(&mut self, val: &dyn Reflect) {
if let ReflectRef::Enum(e) = val.reflect_ref() {
let s = e.as_any().downcast_ref::<Self>()
.expect("cannot apply mismatched reflected enum");
*self = *s;
} else {
panic!("Provided value was not an enum!");
}
}
fn clone_inner(&self) -> Box<dyn Reflect> {
Box::new(self.clone())
}
fn reflect_ref(&self) -> crate::ReflectRef {
ReflectRef::Enum(self)
}
fn reflect_mut(&mut self) -> crate::ReflectMut {
ReflectMut::Enum(self)
}
fn reflect_val(&self) -> &dyn Reflect {
self
}
fn reflect_val_mut(&mut self) -> &mut dyn Reflect {
self
}
}
impl Enum for Angle {
fn field(&self, _: &str) -> Option<&dyn Reflect> {
// no struct variants
None
}
fn field_mut(&mut self, _: &str) -> Option<&mut dyn Reflect> {
// no struct variants
None
}
fn field_at(&self, idx: usize) -> Option<&dyn Reflect> {
// all variants only have one tuple field
if idx != 0 {
return None;
}
match self {
Angle::Degrees(v) => Some(v),
Angle::Radians(v) => Some(v),
}
}
fn field_at_mut(&mut self, idx: usize) -> Option<&mut dyn Reflect> {
// all variants only have one tuple field
if idx != 0 {
return None;
}
match self {
Angle::Degrees(v) => Some(v),
Angle::Radians(v) => Some(v),
}
}
fn field_name_at(&self, _: usize) -> Option<String> {
// no struct variants
None
}
fn has_field(&self, _: &str) -> bool {
// no struct variants
false
}
fn fields_len(&self) -> usize {
1
}
fn variants_len(&self) -> usize {
2
}
fn variant_name(&self) -> String {
match self {
Angle::Degrees(_) => "degrees".into(),
Angle::Radians(_) => "radians".into(),
}
}
fn variant_index(&self) -> usize {
match self {
Angle::Degrees(_) => 0,
Angle::Radians(_) => 1,
}
}
fn is_variant_name(&self, name: &str) -> bool {
self.variant_name() == name
}
fn variant_type(&self) -> crate::EnumType {
crate::EnumType::Tuple
}
}

View File

@ -0,0 +1,143 @@
use syn::{parenthesized, token, Token};
pub(crate) enum FieldType {
Unknown,
Type(syn::Path),
Wrapped(syn::Path),
}
impl FieldType {
pub fn is_unknown(&self) -> bool {
matches!(self, FieldType::Unknown)
}
pub fn is_wrapped(&self) -> bool {
matches!(self, FieldType::Wrapped(_))
}
pub fn get_type_path(&self) -> Option<&syn::Path> {
match self {
FieldType::Unknown => None,
FieldType::Type(path) => Some(path),
FieldType::Wrapped(path) => Some(path),
}
}
}
pub(crate) struct Field {
pub field: syn::Ident,
pub field_ty: FieldType,
pub skip_setter: bool,
pub setter: Option<syn::Block>,
pub getter: Option<syn::Block>,
}
impl Field {
fn parse_extended(input: syn::parse::ParseStream) -> syn::Result<Self> {
let field_name = input.parse()?;
let fty = if input.peek(Token![:]) {
let _col: Token![:] = input.parse()?;
let s: syn::Path = input.parse()?;
let mut fty = FieldType::Type(s.clone());
if let Some(ident) = s.get_ident() {
if ident.to_string() == "wrap" {
let content;
let _parens: token::Paren = parenthesized!(content in input);
fty = FieldType::Wrapped(content.parse()?);
}
}
fty
} else {
FieldType::Unknown
};
let mut s = Self {
field: field_name,
field_ty: fty,
skip_setter: false,
setter: None,
getter: 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 {
"skip_set" => {
s.skip_setter = true;
}
"set" => {
let _eq: Token![=] = input.parse()?;
s.setter = Some(input.parse()?);
}
"get" => {
let _eq: Token![=] = input.parse()?;
s.getter = Some(input.parse()?);
}
_ => {
return Err(syn::Error::new_spanned(ident, "unknown wrapper command"));
}
}
}
}
if (s.getter.is_some() || s.setter.is_some()) && s.field_ty.is_wrapped() {
return Err(syn::Error::new(
input.span(),
"cannot specify custom getter or setter \
with wrapped type",
));
}
Ok(s)
}
}
impl syn::parse::Parse for Field {
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()?;
let fty = if input.peek(Token![:]) {
let _col: Token![:] = input.parse()?;
let s: syn::Path = input.parse()?;
let mut fty = FieldType::Type(s.clone());
if let Some(ident) = s.get_ident() {
if ident.to_string() == "wrap" {
let content;
let _parens: token::Paren = parenthesized!(content in input);
fty = FieldType::Wrapped(content.parse()?);
}
}
fty
} else {
FieldType::Unknown
};
Ok(Self {
field: field_name,
field_ty: fty,
skip_setter: false,
setter: None,
getter: None,
})
}
}
}

View File

@ -247,9 +247,15 @@ pub(crate) fn lua_wrap_handle_impl(input: proc_macro::TokenStream) -> proc_macro
} }
impl LuaWrapper for #wrapper_name { impl LuaWrapper for #wrapper_name {
type Wrap = ResHandle<#handle_path>;
fn wrapped_type_id() -> std::any::TypeId { fn wrapped_type_id() -> std::any::TypeId {
TypeId::of::<ResHandle<#handle_path>>() TypeId::of::<ResHandle<#handle_path>>()
} }
fn into_wrapped(self) -> Self::Wrap {
self.0
}
} }
}.into() }.into()
} }

View File

@ -5,9 +5,12 @@ use syn::{parse_macro_input, Token};
mod mat_wrapper; mod mat_wrapper;
mod vec_wrapper; mod vec_wrapper;
use to_lua_macro::to_lua_struct_impl;
use vec_wrapper::VecWrapper; use vec_wrapper::VecWrapper;
mod lua_macro; mod lua_macro;
mod to_lua_macro;
mod field;
mod handle_macro; mod handle_macro;
@ -24,6 +27,11 @@ pub fn lua_wrap_handle(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
lua_wrap_handle_impl(input) lua_wrap_handle_impl(input)
} }
#[proc_macro]
pub fn to_lua_convert(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
to_lua_struct_impl(input)
}
pub(crate) struct VecExtensionInputs { pub(crate) struct VecExtensionInputs {
#[allow(dead_code)] #[allow(dead_code)]
pub type_path: syn::Path, pub type_path: syn::Path,

View File

@ -1,13 +1,15 @@
use proc_macro2::Span; use proc_macro2::Span;
use quote::quote; use quote::quote;
use syn::{braced, parenthesized, parse_macro_input, punctuated::Punctuated, token, Ident, Path, Token}; use syn::{
braced, parenthesized, parse_macro_input, punctuated::Punctuated, token, Ident, Path, Token,
};
use crate::{handle_macro::FieldGetter, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE}; use crate::{field::{Field, FieldType}, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE};
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
enum SkipType { enum SkipType {
/// Skips implementing /// Skips implementing
LuaReflect LuaReflect,
} }
impl syn::parse::Parse for SkipType { impl syn::parse::Parse for SkipType {
@ -17,9 +19,7 @@ impl syn::parse::Parse for SkipType {
match name_str.as_str() { match name_str.as_str() {
"lua_reflect" => Ok(Self::LuaReflect), "lua_reflect" => Ok(Self::LuaReflect),
_ => { _ => Err(syn::Error::new_spanned(name, "unknown skip type")),
Err(syn::Error::new_spanned(name, "unknown skip type"))
}
} }
} }
} }
@ -43,22 +43,21 @@ impl MetaMethod {
let mm_str = mm_str.as_str(); let mm_str = mm_str.as_str();
match mm_str { match mm_str {
"Add" | "Sub" | "Div" | "Mul" | "Mod" | "Eq" | "Shl" | "Shr" | "BAnd" | "BOr" "Add" | "Sub" | "Div" | "Mul" | "Mod" | "Eq" | "Shl" | "Shr" | "BAnd" | "BOr"
| "BXor" => { | "BXor" => true,
true "Unm" | "BNot" | "ToString" => false,
},
"Unm" | "BNot" | "ToString" => {
false
},
_ => todo!(), _ => todo!(),
} }
} }
/// returns the tokens of the body of the metamethod /// returns the tokens of the body of the metamethod
/// ///
/// Parameters /// Parameters
/// * `metamethod` - The ident of the metamethod that is being implemented. /// * `metamethod` - The ident of the metamethod that is being implemented.
/// * `other` - The tokens of the argument used in the metamethod. /// * `other` - The tokens of the argument used in the metamethod.
fn get_method_body(metamethod: &Ident, other: proc_macro2::TokenStream) -> proc_macro2::TokenStream { fn get_method_body(
metamethod: &Ident,
other: proc_macro2::TokenStream,
) -> proc_macro2::TokenStream {
let mm_str = metamethod.to_string(); let mm_str = metamethod.to_string();
let mm_str = mm_str.as_str(); let mm_str = mm_str.as_str();
match mm_str { match mm_str {
@ -75,17 +74,17 @@ impl MetaMethod {
quote! { quote! {
Ok(Self(this.0 #symbol #other)) Ok(Self(this.0 #symbol #other))
} }
}, }
"Unm" => { "Unm" => {
quote! { quote! {
Ok(Self(-this.0)) Ok(Self(-this.0))
} }
}, }
"Eq" => { "Eq" => {
quote! { quote! {
Ok(this.0 == #other) Ok(this.0 == #other)
} }
}, }
"Shl" => { "Shl" => {
quote! { quote! {
Ok(Self(this.0 << #other)) Ok(Self(this.0 << #other))
@ -95,41 +94,47 @@ impl MetaMethod {
quote! { quote! {
Ok(Self(this.0 >> #other)) Ok(Self(this.0 >> #other))
} }
}, }
"BAnd" | "BOr" | "BXor" => { "BAnd" | "BOr" | "BXor" => {
let symbol = match mm_str { let symbol = match mm_str {
"BAnd" => { "BAnd" => {
quote!(&) quote!(&)
}, }
"BOr" => { "BOr" => {
quote!(|) quote!(|)
}, }
"BXor" => { "BXor" => {
quote!(^) quote!(^)
}, }
_ => unreachable!() // the string was just checked to be one of these _ => unreachable!(), // the string was just checked to be one of these
}; };
quote! { quote! {
Ok(Self(this.0 #symbol #other)) Ok(Self(this.0 #symbol #other))
} }
}, }
"BNot" => { "BNot" => {
quote! { quote! {
Ok(Self(!this.0)) Ok(Self(!this.0))
} }
}, }
"ToString" => { "ToString" => {
quote! { quote! {
Ok(format!("{:?}", this.0)) Ok(format!("{:?}", this.0))
} }
}, }
_ => syn::Error::new_spanned(metamethod, _ => {
"unsupported auto implementation of metamethod").to_compile_error(), 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 { 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) { let other: proc_macro2::TokenStream = if Self::is_arg_wrapper(arg_ident) {
// Lua wrappers must be dereferenced // Lua wrappers must be dereferenced
quote! { quote! {
@ -167,7 +172,6 @@ impl MetaMethod {
}); });
} }
} }
} else if self.arg.len() == 1 { } else if self.arg.len() == 1 {
let first = self.arg.iter().next().unwrap(); let first = self.arg.iter().next().unwrap();
let body = Self::get_body_for_arg(&self.name, first, quote!(v)); let body = Self::get_body_for_arg(&self.name, first, quote!(v));
@ -184,38 +188,41 @@ impl MetaMethod {
let is = i.to_string(); let is = i.to_string();
let is = is.as_str(); let is = is.as_str();
match is { match is {
"u8" | "u16" | "u32" | "u64" | "u128" "u8" | "u16" | "u32" | "u64" | "u128" | "i8" | "i16" | "i32" | "i64"
| "i8" | "i16" | "i32" | "i64" | "i128" | "i128" | "f32" | "f64" => true,
| "f32" | "f64" => true,
_ => false, _ => false,
} }
}); });
if let Some(num_ident) = num_ident { if let Some(num_ident) = num_ident {
let body = Self::get_body_for_arg(&self.name, num_ident, quote!(n as #num_ident)); let body =
Self::get_body_for_arg(&self.name, num_ident, quote!(n as #num_ident));
quote! { quote! {
mlua::Value::Number(n) => { mlua::Value::Number(n) => {
#body #body
}, },
} }
} else { quote!() } } else {
quote!()
}
}; };
let userdata_arm = { let userdata_arm = {
let wrappers: Vec<&Ident> = self.arg.iter() let wrappers: Vec<&Ident> = self
.arg
.iter()
.filter(|i| Self::is_arg_wrapper(i)) .filter(|i| Self::is_arg_wrapper(i))
.collect(); .collect();
let if_statements = wrappers.iter().map(|i| { let if_statements = wrappers.iter().map(|i| {
let body = Self::get_method_body(&self.name, quote!(other.0)); let body = Self::get_method_body(&self.name, quote!(other.0));
quote! { quote! {
if let Ok(other) = ud.borrow::<#i>() { if let Ok(other) = ud.borrow::<#i>() {
#body #body
} }
} }
}); });
quote! { quote! {
@ -226,7 +233,7 @@ impl MetaMethod {
// try to get the name of the userdata for the error message // try to get the name of the userdata for the error message
if let Ok(mt) = ud.metatable() { if let Ok(mt) = ud.metatable() {
if let Ok(name) = mt.get::<String>("__name") { if let Ok(name) = mt.get::<String>("__name") {
return Err(mlua::Error::BadArgument { return Err(mlua::Error::BadArgument {
to: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)), to: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)),
pos: 2, pos: 2,
name: Some("rhs".to_string()), name: Some("rhs".to_string()),
@ -236,8 +243,8 @@ impl MetaMethod {
}); });
} }
} }
Err(mlua::Error::BadArgument { Err(mlua::Error::BadArgument {
to: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)), to: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)),
pos: 2, pos: 2,
name: Some("rhs".to_string()), name: Some("rhs".to_string()),
@ -249,13 +256,13 @@ impl MetaMethod {
}, },
} }
}; };
quote! { quote! {
methods.add_meta_method(mlua::MetaMethod::#mt_ident, |_, this, (v,): (mlua::Value,)| { methods.add_meta_method(mlua::MetaMethod::#mt_ident, |_, this, (v,): (mlua::Value,)| {
match v { match v {
#number_arm #number_arm
#userdata_arm #userdata_arm
_ => Err(mlua::Error::BadArgument { _ => Err(mlua::Error::BadArgument {
to: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)), to: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)),
pos: 2, pos: 2,
name: Some("rhs".to_string()), name: Some("rhs".to_string()),
@ -274,17 +281,15 @@ impl syn::parse::Parse for MetaMethod {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let name: Ident = input.parse()?; let name: Ident = input.parse()?;
let mut s = Self { let mut s = Self { name, arg: vec![] };
name,
arg: vec![],
};
// try to parse args // try to parse args
if input.peek(syn::token::Paren) { if input.peek(syn::token::Paren) {
let content; let content;
let _parens: syn::token::Paren = parenthesized!(content in input); let _parens: syn::token::Paren = parenthesized!(content in input);
let arg: Punctuated<Ident, Token![,]> = content.parse_terminated(Ident::parse, Token![,])?; let arg: Punctuated<Ident, Token![,]> =
content.parse_terminated(Ident::parse, Token![,])?;
s.arg = arg.into_iter().collect(); // convert to Vec<Ident> s.arg = arg.into_iter().collect(); // convert to Vec<Ident>
} }
@ -296,8 +301,7 @@ struct WrapUsage {
type_path: syn::Path, type_path: syn::Path,
/// The extra derives of the type. /// The extra derives of the type.
override_name: Option<Ident>, override_name: Option<Ident>,
auto_fields: Vec<Ident>, auto_fields: Vec<Field>,
manual_field_getters: Vec<FieldGetter>,
auto_derives: Vec<Ident>, auto_derives: Vec<Ident>,
auto_new: bool, auto_new: bool,
meta_methods: Vec<MetaMethod>, meta_methods: Vec<MetaMethod>,
@ -315,7 +319,6 @@ impl syn::parse::Parse for WrapUsage {
override_name: None, override_name: None,
auto_fields: vec![], auto_fields: vec![],
auto_derives: vec![], auto_derives: vec![],
manual_field_getters: vec![],
extra_fields: None, extra_fields: None,
extra_methods: None, extra_methods: None,
auto_new: false, auto_new: false,
@ -324,7 +327,7 @@ impl syn::parse::Parse for WrapUsage {
}; };
let mut new_ident = None; let mut new_ident = None;
while input.peek(Token![,]) { while input.peek(Token![,]) {
let _: Token![,] = input.parse()?; let _: Token![,] = input.parse()?;
@ -339,73 +342,72 @@ impl syn::parse::Parse for WrapUsage {
let name: Ident = input.parse()?; let name: Ident = input.parse()?;
s.override_name = Some(name); s.override_name = Some(name);
}, }
"new" => { "new" => {
s.auto_new = true; s.auto_new = true;
new_ident = Some(ident.clone()); new_ident = Some(ident.clone());
}, }
"skip" => { "skip" => {
let content; let content;
let _parens: token::Paren = parenthesized!(content in input); let _parens: token::Paren = parenthesized!(content in input);
let terminated = content.parse_terminated(SkipType::parse, Token![,])?; let terminated = content.parse_terminated(SkipType::parse, Token![,])?;
s.skips = terminated.into_iter().collect(); s.skips = terminated.into_iter().collect();
}, }
"derives" => { "derives" => {
if input.peek(token::Paren) { if input.peek(token::Paren) {
let content; let content;
let _parens: token::Paren = parenthesized!(content in input); let _parens: token::Paren = parenthesized!(content in input);
let derives: Punctuated<Ident, Token![,]> = content.parse_terminated(Ident::parse, Token![,])?; let derives: Punctuated<Ident, Token![,]> =
content.parse_terminated(Ident::parse, Token![,])?;
s.auto_derives = derives.into_iter().collect(); s.auto_derives = derives.into_iter().collect();
} }
}, }
"fields" => { "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();
}
},
"extra_fields" => {
let _eq: Token![=] = input.parse()?;
s.extra_fields = Some(input.parse::<syn::Block>()?);
},
"extra_methods" => {
let _eq: Token![=] = input.parse()?;
s.extra_methods = Some(input.parse::<syn::Block>()?);
},
"field_getters" => {
let _eq: Token![=] = input.parse()?; let _eq: Token![=] = input.parse()?;
if input.peek(token::Brace) { if input.peek(token::Brace) {
let content; let content;
let _braced: token::Brace = braced!(content in input); let _braced: token::Brace = braced!(content in input);
let terminated = content.parse_terminated(FieldGetter::parse, Token![,])?; let terminated = content.parse_terminated(Field::parse, Token![,])?;
s.manual_field_getters = terminated.into_iter().collect(); s.auto_fields.extend(terminated.into_iter());
} }
} }
"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();
}
}
"extra_fields" => {
let _eq: Token![=] = input.parse()?;
s.extra_fields = Some(input.parse::<syn::Block>()?);
}
"extra_methods" => {
let _eq: Token![=] = input.parse()?;
s.extra_methods = Some(input.parse::<syn::Block>()?);
}
_ => { _ => {
return Err(syn::Error::new_spanned(ident, format!("unknown wrapper command: '{}'", ident_str))); return Err(syn::Error::new_spanned(
ident,
format!("unknown wrapper command: '{}'", ident_str),
));
} }
} }
} }
} }
if s.auto_new && s.auto_fields.is_empty() { 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")); return Err(syn::Error::new_spanned(
new_ident.unwrap(),
"must specify 'fields' when auto creating new function",
));
} }
Ok(s) Ok(s)
@ -417,51 +419,50 @@ pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::Token
let input = parse_macro_input!(input as WrapUsage); let input = parse_macro_input!(input as WrapUsage);
let path: Path = input.type_path; let path: Path = input.type_path;
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 = input.override_name let wrapper_typename = input
.override_name
.unwrap_or_else(|| Ident::new(&format!("Lua{}", type_name), Span::call_site())); .unwrap_or_else(|| Ident::new(&format!("Lua{}", type_name), Span::call_site()));
let derive_idents_iter = input.auto_derives.iter(); let derive_idents_iter = input.auto_derives.iter();
let extra_fields = input.extra_fields; let extra_fields = input.extra_fields;
let extra_methods = input.extra_methods; let extra_methods = input.extra_methods;
let field_get_set_pairs = input.auto_fields.iter().map(|i| {
let is = i.to_string();
quote! {
fields.add_field_method_get(#is, |_, this| {
Ok(this.#i)
});
fields.add_field_method_set(#is, |_, this, #i| {
this.#i = #i;
Ok(())
});
}
});
// the tokens for the new function // the tokens for the new function
let new_func_tokens = if input.auto_new { let new_func_tokens = if input.auto_new {
let arg_names = input.auto_fields.iter().map(|i| { let arg_names = input
Ident::new(&i.to_string().to_lowercase(), Span::call_site()) .auto_fields
}); .iter()
.map(|i| Ident::new(&i.field.to_string().to_lowercase(), Span::call_site()));
let arg_types = input
.auto_fields
.iter()
.map(|i| i.field_ty.get_type_path().unwrap());
let arg_names_clone = arg_names.clone(); let arg_names_clone = arg_names.clone();
let arg_types_clone = arg_types.clone();
quote! { quote! {
// arguments for function are not specified since they can be implied from the call // arguments for function are not specified since they can be implied from the call
// to new(...) // to new(...)
methods.add_function("new", |_, ( #(#arg_names_clone),* )| { methods.add_function("new", |_, ( #(#arg_names_clone),* ): ( #(#arg_types_clone),* ) | {
Ok(#wrapper_typename(#path::new( #(#arg_names),* ))) Ok(#wrapper_typename(#path::new( #(#arg_names),* )))
}); });
} }
} else {
} else { quote!() }; quote!()
};
let meta_methods_tokens = { let meta_methods_tokens = {
let method_tokens = input.meta_methods.iter().map(|mm| { let method_tokens = input
mm.to_tokens(&wrapper_typename) .meta_methods
}); .iter()
.map(|mm| mm.to_tokens(&wrapper_typename));
quote! { quote! {
#(#method_tokens)* #(#method_tokens)*
@ -475,34 +476,80 @@ pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::Token
methods.add_method(#FN_NAME_INTERNAL_REFLECT, |_, this, ()| { methods.add_method(#FN_NAME_INTERNAL_REFLECT, |_, this, ()| {
Ok(crate::ScriptBorrow::from_component::<#path>(Some(this.0.clone()))) Ok(crate::ScriptBorrow::from_component::<#path>(Some(this.0.clone())))
}); });
methods.add_function(#FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| { methods.add_function(#FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| {
Ok(crate::ScriptBorrow::from_component::<#path>(None)) Ok(crate::ScriptBorrow::from_component::<#path>(None))
}); });
} }
}; };
let custom_getters = input.manual_field_getters.iter().map(|g| { for field in input.auto_fields.iter() {
if field.field_ty.is_unknown() && !field.skip_setter {
return syn::Error::new(
field.field.span(),
"missing type of field, must be specified to generate setters",
)
.to_compile_error()
.into();
}
}
let fields = input.auto_fields.iter().map(|g| {
let field = &g.field; let field = &g.field;
let field_creator = match &g.wrapper_type { let field_getter = match &g.field_ty {
Some(wrap) => { FieldType::Wrapped(wrap) => {
quote!(#wrap(this.#field).into_lua(lua)) quote!(#wrap(this.#field.clone()).into_lua(lua))
}, }
None => match &g.body { _ => match &g.getter {
Some(body) => { Some(body) => {
quote!(#body) quote!(#body)
}, }
None => { None => {
quote!(this.#field.clone().into_lua(lua)) quote!(this.#field.clone().into_lua(lua))
} }
} }
}; };
let field_setter = if g.skip_setter {
quote! {}
} else {
let fty = g
.field_ty
.get_type_path()
// should be unreachable due to the checks right before this closure
.expect("no field type specified");
let s = if g.field_ty.is_wrapped() {
quote! {
this.#field = #field.0.clone();
Ok(())
}
} else {
match &g.setter {
Some(body) => {
quote!(#body)
}
None => {
quote! {
this.#field = #field.clone();
Ok(())
}
}
}
};
quote! {
fields.add_field_method_set(stringify!(#field), |_, this, #field: #fty| {
#s
});
}
};
quote! { quote! {
fields.add_field_method_get(stringify!($field), |lua, this| { fields.add_field_method_get(stringify!(#field), |lua, this| {
#field_creator #field_getter
}); });
#field_setter
} }
}); });
@ -512,12 +559,12 @@ pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::Token
impl std::ops::Deref for #wrapper_typename { impl std::ops::Deref for #wrapper_typename {
type Target = #path; type Target = #path;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.0 &self.0
} }
} }
impl std::ops::DerefMut for #wrapper_typename { impl std::ops::DerefMut for #wrapper_typename {
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0 &mut self.0
@ -535,13 +582,16 @@ pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::Token
impl mlua::UserData for #wrapper_typename { impl mlua::UserData for #wrapper_typename {
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) { fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
#(#field_get_set_pairs)* use mlua::IntoLua;
#(#custom_getters)*
#(#fields)*
#extra_fields #extra_fields
} }
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) { fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
use mlua::IntoLua;
#lua_reflects #lua_reflects
#new_func_tokens #new_func_tokens
@ -551,9 +601,15 @@ pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::Token
} }
impl lyra_scripting::lua::LuaWrapper for #wrapper_typename { impl lyra_scripting::lua::LuaWrapper for #wrapper_typename {
type Wrap = #path;
fn wrapped_type_id() -> std::any::TypeId { fn wrapped_type_id() -> std::any::TypeId {
std::any::TypeId::of::<#path>() std::any::TypeId::of::<#path>()
} }
fn into_wrapped(self) -> Self::Wrap {
self.0
}
} }
}) })
} }

View File

@ -0,0 +1,317 @@
use syn::{braced, parenthesized, parse_macro_input, punctuated::Punctuated, token, Token};
use quote::{quote, ToTokens};
use crate::{field::Field, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE};
fn field_table_setter(field: &Field) -> proc_macro2::TokenStream {
let ident = &field.field;
match &field.setter {
Some(set) => {
quote! {
table.set(stringify!(#ident), #set)?;
}
},
None => {
let ty = field.field_ty.get_type_path()
.expect("no field type specified");
let arg = if field.field_ty.is_wrapped() {
quote!(#ty(self.#ident))
} else { quote!(self.#ident) };
quote! {
table.set(stringify!(#ident), #arg)?;
}
},
}
}
fn field_table_getter(field: &Field) -> proc_macro2::TokenStream {
let ident = &field.field;
match &field.getter {
Some(get) => {
quote! {
let #ident = #get;
}
},
None => {
let ty = field.field_ty.get_type_path()
.expect("no field type specified");
quote! {
let #ident: #ty = table.get(stringify!(#ident))?;
}
},
}
}
fn wrapper_creation(wrapper: &syn::Ident, type_path: &syn::Path, create: Option<&syn::Block>, fields: &Vec<Field>) -> proc_macro2::TokenStream {
match create {
Some(b) => quote!(#b),
None => {
/* let field_iter = fields.iter().map(|f| match &f.field_ty {
crate::field::FieldType::Type(path) => quote!(#path),
crate::field::FieldType::Wrapped(path) => quote!(*#path),
_ => todo!()
}); */
let field_iter = fields.iter().map(|f| {
let ident = &f.field;
if f.field_ty.is_wrapped() {
quote!(#ident: (*#ident).clone())
} else {
quote!(#ident)
}
});
quote! {
#wrapper(#type_path {
#(
#field_iter
),*
})
}
}
}
}
fn get_reflect_lua_functions(ty: &ReflectType, type_path: &syn::Path, set_data: bool) -> proc_macro2::TokenStream {
let data = if set_data {
quote!(Some(this.into_wrapped()))
} else { quote!(None) };
match ty {
ReflectType::Component => {
quote! {
Ok(ScriptBorrow::from_component::<#type_path>(#data))
}
},
ReflectType::Resource => {
quote! {
Ok(ScriptBorrow::from_component::<#type_path>(#data))
}
},
}
}
#[derive(Debug, Clone, Copy)]
enum ReflectType {
//Unknown,
Component,
Resource,
}
struct IntoLuaUsage {
type_path: syn::Path,
override_name: Option<syn::Ident>,
table_name: String,
derives: Vec<syn::Ident>,
fields: Vec<Field>,
create: Option<syn::Block>,
reflection_type: Option<ReflectType>,
}
impl syn::parse::Parse for IntoLuaUsage {
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,
table_name: String::new(),
derives: vec![],
fields: vec![],
create: None,
reflection_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 {
"name" => {
let _eq: Token![=] = input.parse()?;
let name: syn::Ident = input.parse()?;
s.override_name = Some(name);
},
"lua_name" => {
let _eq: Token![=] = input.parse()?;
s.table_name = input.parse::<syn::LitStr>()?.value();
},
"derives" => {
if input.peek(token::Paren) {
let content;
let _parens: token::Paren = parenthesized!(content in input);
let derives: Punctuated<syn::Ident, Token![,]> =
content.parse_terminated(syn::Ident::parse, Token![,])?;
s.derives = derives.into_iter().collect();
}
},
"fields" => {
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(Field::parse, Token![,])?;
s.fields.extend(terminated.into_iter());
}
},
"create" => {
let _eq: Token![=] = input.parse()?;
s.create = Some(input.parse()?);
},
"reflect" => {
let _eq: Token![=] = input.parse()?;
let ty: syn::Ident = input.parse()?;
let ty_str = ty.to_string();
let ty_str = ty_str.as_str();
let ty = match ty_str {
"component" => ReflectType::Component,
"resource" => ReflectType::Resource,
_ => return Err(syn::Error::new_spanned(
ident,
format!("unknown wrapper type: '{}', expected 'component' or 'resource'", ty_str),
)),
};
s.reflection_type = Some(ty);
},
_ => {
return Err(syn::Error::new_spanned(
ident,
format!("unknown wrapper command: '{}'", ident_str),
));
}
}
}
}
if s.reflection_type.is_none() {
return Err(syn::Error::new(
input.span(),
format!("Wrapper type not specified! Expected 'type=component' or 'type=resource'"),
));
}
if s.table_name.is_empty() {
return Err(syn::Error::new(
input.span(),
format!("No lua table specified. Use 'lua_name=\"Camera\"'"),
))
}
Ok(s)
}
}
pub fn to_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as IntoLuaUsage);
// unwrap is fine since `Some` is ensured in parse impl
let reflect_type = input.reflection_type.as_ref().unwrap();
let type_path = &input.type_path;
let type_name = &type_path
.segments
.last()
.expect("Failure to find typename in macro usage!")
.ident;
let wrapper = input.override_name.unwrap_or_else(|| syn::Ident::new(format!("Lua{}", type_name.to_string()).as_str(), type_name.span()));
let derives_iter = input.derives.into_iter();
let lua_name = &input.table_name;
let field_getters_iter = input.fields.iter().map(|f| field_table_getter(f));
let field_setters_iter = input.fields.iter().map(|f| field_table_setter(f));
let struct_creator = wrapper_creation(&wrapper, type_path, input.create.as_ref(), &input.fields);
let reflect_fn = get_reflect_lua_functions(reflect_type, &input.type_path, true);
let reflect_type_fn = get_reflect_lua_functions(reflect_type, &input.type_path, false);
quote! {
#[derive(Clone, #(#derives_iter),*)]
pub struct #wrapper(pub(crate) #type_path);
impl std::ops::Deref for #wrapper {
type Target = #type_path;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl std::ops::DerefMut for #wrapper {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl mlua::FromLua for #wrapper {
fn from_lua(val: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
let ty = val.type_name();
let table = val.as_table().ok_or(mlua::Error::FromLuaConversionError {
from: ty,
to: "Table".into(),
message: Some("expected Table".into()),
})?;
#(
#field_getters_iter
)*
Ok(#struct_creator)
}
}
impl mlua::IntoLua for #wrapper {
fn into_lua(self, lua: &mlua::Lua) -> mlua::Result<mlua::Value> {
let table = lua.create_table()?;
#(
#field_setters_iter
)*
table.set(
#FN_NAME_INTERNAL_REFLECT,
lua.create_function(|_, this: Self| {
#reflect_fn
})?,
)?;
table.set(
#FN_NAME_INTERNAL_REFLECT_TYPE,
lua.create_function(|_, ()| {
#reflect_type_fn
})?,
)?;
table.set(mlua::MetaMethod::Type.name(), #lua_name)?;
Ok(mlua::Value::Table(table))
}
}
impl LuaWrapper for #wrapper {
type Wrap = #type_path;
#[inline(always)]
fn wrapped_type_id() -> std::any::TypeId {
std::any::TypeId::of::<#type_path>()
}
#[inline(always)]
fn into_wrapped(self) -> Self::Wrap {
self.0
}
}
}.into_token_stream().into()
}

View File

@ -60,4 +60,10 @@ WrappingMode = {
CLAMP_TO_EDGE = "clamp_to_edge", CLAMP_TO_EDGE = "clamp_to_edge",
MIRRORED_REPEAT = "mirrored_repeat", MIRRORED_REPEAT = "mirrored_repeat",
REPEAT = "repeat", REPEAT = "repeat",
}
---@enum CameraProjectionMode
CameraProjectionMode = {
PERSPECTIVE = "perspective",
ORTHOGRAPHIC = "orthographic",
} }

View File

@ -0,0 +1,18 @@
---@meta
---@class Camera: userdata
Camera = {
---The position of the camera
---@type Transform
transform = nil,
---The field of view of the camera
---@type Angle
fov = nil,
---The projection mode the camera.
---Can be used to specify if the camera is 2D (orthographic), or 3D (perspective).
---@type CameraProjectionMode
mode = nil,
---Flag to enable some debug rendering stuff.
---@type boolean
debug = nil,
}

View File

@ -0,0 +1,25 @@
---@meta
---@class Angle: userdata
Angle = {}
---Create a new angle in degrees.
---@param deg number
function Angle.new_degrees(deg) end
---Create a new angle in radians.
---@param rad number
function Angle.new_radians(rad) end
---Get the angle in radians.
---
---Will convert from degrees automatically.
---
---@return number radians
function Angle:radians() end
---Get the angle in degrees.
---
---Will convert from radians automatically.
---
---@return number degrees
function Angle:degrees() end

View File

@ -131,6 +131,11 @@ pub trait RegisterLuaType {
fn register_lua_convert<T>(&mut self) fn register_lua_convert<T>(&mut self)
where where
T: Clone + mlua::FromLua + mlua::IntoLua + LuaWrapper + 'static; T: Clone + mlua::FromLua + mlua::IntoLua + LuaWrapper + 'static;
fn register_lua_convert_component<T>(&mut self, name: &str)
where
T: Clone + mlua::FromLua + mlua::IntoLua + LuaWrapper + 'static,
T::Wrap: lyra_ecs::Component + Reflect;
} }
impl RegisterLuaType for World { impl RegisterLuaType for World {
@ -165,6 +170,18 @@ impl RegisterLuaType for World {
let reg_type = registry.get_type_or_default(T::wrapped_type_id()); let reg_type = registry.get_type_or_default(T::wrapped_type_id());
reg_type.add_data(ReflectLuaProxy::from_as_and_from_lua::<T>()); reg_type.add_data(ReflectLuaProxy::from_as_and_from_lua::<T>());
} }
fn register_lua_convert_component<T>(&mut self, name: &str)
where
T: Clone + mlua::FromLua + mlua::IntoLua + LuaWrapper + 'static,
T::Wrap: lyra_ecs::Component + Reflect
{
self.register_lua_convert::<T>();
let mut lookup = self.get_resource_or_default::<LuaTableProxyLookup>();
lookup.comp_info_from_name.insert(name.into(), lyra_ecs::ComponentInfo::new::<T::Wrap>());
lookup.typeid_from_name.insert(name.into(), std::any::TypeId::of::<T::Wrap>());
}
} }
impl mlua::FromLua for ScriptBorrow { impl mlua::FromLua for ScriptBorrow {

View File

@ -4,7 +4,9 @@ use lyra_ecs::ResourceObject;
use lyra_reflect::{Reflect, TypeRegistry}; use lyra_reflect::{Reflect, TypeRegistry};
use lyra_resource::gltf::Gltf; use lyra_resource::gltf::Gltf;
use crate::{lua::{wrappers::{LuaActionHandler, LuaDeltaTime, LuaGltfHandle, LuaResHandleToComponent, LuaSceneHandle, LuaWindow}, LuaContext, ReflectLuaProxy, RegisterLuaType, FN_NAME_INTERNAL_REFLECT_TYPE}, ScriptApiProvider, ScriptBorrow, ScriptData, ScriptDynamicBundle, ScriptWorldPtr}; use crate::{lua::{wrappers::*, LuaContext, LuaTableProxyLookup, LuaWrapper, ReflectLuaProxy, RegisterLuaType, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE}, ScriptApiProvider, ScriptBorrow, ScriptData, ScriptDynamicBundle, ScriptWorldPtr};
//fn register_lua_proxy::<T:
#[derive(Default)] #[derive(Default)]
pub struct LyraEcsApiProvider; pub struct LyraEcsApiProvider;
@ -17,6 +19,12 @@ impl ScriptApiProvider for LyraEcsApiProvider {
world.register_lua_wrapper::<LuaSceneHandle>(); world.register_lua_wrapper::<LuaSceneHandle>();
world.register_lua_wrapper::<LuaActionHandler>(); world.register_lua_wrapper::<LuaActionHandler>();
world.register_lua_wrapper::<LuaWindow>(); world.register_lua_wrapper::<LuaWindow>();
world.register_lua_convert::<LuaCamera>();
let mut lookup = world.get_resource_or_default::<LuaTableProxyLookup>();
lookup.comp_info_from_name.insert("Camera".into(), lyra_ecs::ComponentInfo::new::<lyra_game::scene::CameraComponent>());
lookup.typeid_from_name.insert("Camera".into(), std::any::TypeId::of::<lyra_game::scene::CameraComponent>());
drop(lookup);
let mut registry = world.get_resource_mut::<TypeRegistry>().unwrap(); let mut registry = world.get_resource_mut::<TypeRegistry>().unwrap();
@ -33,8 +41,6 @@ impl ScriptApiProvider for LyraEcsApiProvider {
} }
); );
reg_type.add_data(l); reg_type.add_data(l);
//reg_type.add_data(ReflectLuaProxy::from_lua_proxy::<LuaGltfHandle>());
} }
fn expose_api(&mut self, _: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { fn expose_api(&mut self, _: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> {
@ -51,6 +57,9 @@ impl ScriptApiProvider for LyraEcsApiProvider {
globals.set("ActionHandler", ctx.create_proxy::<LuaActionHandler>()?)?; globals.set("ActionHandler", ctx.create_proxy::<LuaActionHandler>()?)?;
globals.set("Window", ctx.create_proxy::<LuaWindow>()?)?; globals.set("Window", ctx.create_proxy::<LuaWindow>()?)?;
let cam_table = create_reflect_comp_table::<LuaCamera>(&ctx, "Camera")?;
globals.set("Camera", cam_table)?;
let dt_table = create_reflect_table::<lyra_game::DeltaTime>(&ctx)?; let dt_table = create_reflect_table::<lyra_game::DeltaTime>(&ctx)?;
globals.set("DeltaTime", dt_table)?; globals.set("DeltaTime", dt_table)?;
@ -72,5 +81,25 @@ fn create_reflect_table<T: Reflect + ResourceObject + Default + 'static>(lua: &m
Ok(ScriptBorrow::from_resource::<T>(None)) Ok(ScriptBorrow::from_resource::<T>(None))
})?)?; })?)?;
Ok(table)
}
fn create_reflect_comp_table<T>(lua: &mlua::Lua, name: &str) -> mlua::Result<mlua::Table>
where
T: LuaWrapper + mlua::FromLua,
T::Wrap: lyra_ecs::Component + Reflect
{
let table = lua.create_table()?;
table.set(FN_NAME_INTERNAL_REFLECT, lua.create_function(|_, this: T| {
Ok(ScriptBorrow::from_component::<T::Wrap>(Some(this.into_wrapped())))
})?)?;
table.set(FN_NAME_INTERNAL_REFLECT_TYPE, lua.create_function(|_, ()| {
Ok(ScriptBorrow::from_component::<T::Wrap>(None))
})?)?;
table.set(mlua::MetaMethod::Type.name(), name)?;
Ok(table) Ok(table)
} }

View File

@ -1,5 +1,5 @@
use lyra_ecs::World; use lyra_ecs::World;
use crate::lua::wrappers::{LuaQuat, LuaTransform, LuaVec3, LuaVec2}; use crate::lua::wrappers::{LuaAngle, LuaQuat, LuaTransform, LuaVec2, LuaVec3};
use crate::ScriptData; use crate::ScriptData;
use crate::lua::RegisterLuaType; use crate::lua::RegisterLuaType;
@ -16,6 +16,7 @@ impl ScriptApiProvider for LyraMathApiProvider {
world.register_lua_wrapper::<LuaVec3>(); world.register_lua_wrapper::<LuaVec3>();
world.register_lua_wrapper::<LuaQuat>(); world.register_lua_wrapper::<LuaQuat>();
world.register_lua_wrapper::<LuaTransform>(); world.register_lua_wrapper::<LuaTransform>();
world.register_lua_wrapper::<LuaAngle>();
} }
fn expose_api(&mut self, _data: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { fn expose_api(&mut self, _data: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> {
@ -29,6 +30,7 @@ impl ScriptApiProvider for LyraMathApiProvider {
globals.set("Vec3", ctx.create_proxy::<LuaVec3>()?)?; globals.set("Vec3", ctx.create_proxy::<LuaVec3>()?)?;
globals.set("Quat", ctx.create_proxy::<LuaQuat>()?)?; globals.set("Quat", ctx.create_proxy::<LuaQuat>()?)?;
globals.set("Transform", ctx.create_proxy::<LuaTransform>()?)?; globals.set("Transform", ctx.create_proxy::<LuaTransform>()?)?;
globals.set("Angle", ctx.create_proxy::<LuaAngle>()?)?;
Ok(()) Ok(())
} }

View File

@ -9,8 +9,11 @@ use crate::{ScriptBorrow, ScriptDynamicBundle};
use super::{Error, FN_NAME_INTERNAL_REFLECT}; use super::{Error, FN_NAME_INTERNAL_REFLECT};
pub trait LuaWrapper { pub trait LuaWrapper {
type Wrap: Reflect + 'static;
/// The type id of the wrapped type. /// The type id of the wrapped type.
fn wrapped_type_id() -> TypeId; fn wrapped_type_id() -> TypeId;
fn into_wrapped(self) -> Self::Wrap;
} }
/// A trait that used to convert something into lua, or to set something to a value from lua. /// A trait that used to convert something into lua, or to set something to a value from lua.

View File

@ -129,28 +129,29 @@ fn wrapping_mode_to_str(wm: WrappingMode) -> &'static str {
wrap_lua_struct!(lyra_resource::TextureSampler, wrap_lua_struct!(lyra_resource::TextureSampler,
// this can be safely skipped since it wont be a component or resource. // this can be safely skipped since it wont be a component or resource.
skip(lua_reflect), skip(lua_reflect),
field_getters={ fields={
(mag_filter, { // don't need to specify field types since setters are skipped
(mag_filter, skip_set, get={
this.mag_filter.map(|f| filter_mode_to_str(f)) this.mag_filter.map(|f| filter_mode_to_str(f))
.into_lua(lua) .into_lua(lua)
}), }),
(min_filter, { (min_filter, skip_set, get={
this.min_filter.map(|f| filter_mode_to_str(f)) this.min_filter.map(|f| filter_mode_to_str(f))
.into_lua(lua) .into_lua(lua)
}), }),
(mipmap_filter, { (mipmap_filter, skip_set, get={
this.mipmap_filter.map(|f| filter_mode_to_str(f)) this.mipmap_filter.map(|f| filter_mode_to_str(f))
.into_lua(lua) .into_lua(lua)
}), }),
(wrap_u, { (wrap_u, skip_set, get={
wrapping_mode_to_str(this.wrap_u) wrapping_mode_to_str(this.wrap_u)
.into_lua(lua) .into_lua(lua)
}), }),
(wrap_v, { (wrap_v, skip_set, get={
wrapping_mode_to_str(this.wrap_v) wrapping_mode_to_str(this.wrap_v)
.into_lua(lua) .into_lua(lua)
}), }),
(wrap_w, { (wrap_w, skip_set, get={
wrapping_mode_to_str(this.wrap_w) wrapping_mode_to_str(this.wrap_w)
.into_lua(lua) .into_lua(lua)
}), }),
@ -160,31 +161,29 @@ wrap_lua_struct!(lyra_resource::TextureSampler,
wrap_lua_struct!(lyra_resource::gltf::PbrGlossiness, wrap_lua_struct!(lyra_resource::gltf::PbrGlossiness,
// this can be safely skipped since it wont be a component or resource. // this can be safely skipped since it wont be a component or resource.
skip(lua_reflect), skip(lua_reflect),
field_getters={ fields={
(diffuse_color, wrapper=crate::lua::wrappers::LuaVec4), (diffuse_color: wrap(crate::lua::wrappers::LuaVec4), skip_set),
(specular, wrapper=crate::lua::wrappers::LuaVec3), (specular: wrap(crate::lua::wrappers::LuaVec3), skip_set),
glossiness, (glossiness, skip_set),
} }
); );
wrap_lua_struct!(lyra_resource::gltf::Specular, wrap_lua_struct!(lyra_resource::gltf::Specular,
// this can be safely skipped since it wont be a component or resource. // this can be safely skipped since it wont be a component or resource.
skip(lua_reflect), skip(lua_reflect),
field_getters={ fields={
factor, (factor, skip_set),
(color_factor, wrapper=crate::lua::wrappers::LuaVec3), (color_factor: wrap(crate::lua::wrappers::LuaVec3), skip_set),
}, (texture, skip_set, get={
extra_fields={
fields.add_field_method_get("texture", |lua, this| {
this.texture.clone() this.texture.clone()
.map(|t| LuaTextureHandle(t)) .map(|t| LuaTextureHandle(t))
.into_lua(lua) .into_lua(lua)
}); }),
fields.add_field_method_get("color_texture", |lua, this| { (color_texture, skip_set, get={
this.color_texture.clone() this.color_texture.clone()
.map(|t| LuaTextureHandle(t)) .map(|t| LuaTextureHandle(t))
.into_lua(lua) .into_lua(lua)
}); })
} }
); );

View File

@ -0,0 +1,46 @@
use crate::{
lua::{
wrappers::{LuaAngle, LuaTransform},
LuaWrapper
},
ScriptBorrow,
};
use lyra_game::{render::camera::CameraProjectionMode, scene::CameraComponent};
use lyra_reflect::Enum;
use lyra_scripting_derive::to_lua_convert;
fn projection_mode_from_str(s: &str) -> Option<CameraProjectionMode> {
match s {
"perspective" => Some(CameraProjectionMode::Perspective),
"orthographic" => Some(CameraProjectionMode::Orthographic),
_ => None,
}
}
to_lua_convert!(
// Struct that is being wrapped[]
CameraComponent,
// Name of wrapping struct
name=LuaCamera,
// The name of the type in Lua
lua_name="Camera",
// Reflection type, can be 'component' or 'resource'
reflect=component,
fields={
transform: wrap(LuaTransform),
fov: wrap(LuaAngle),
(
mode,
// Get the table from the value, result must be `CameraProjectionMode`
get={
let mode: String = table.get("mode")?;
projection_mode_from_str(&mode).unwrap()
},
// Get the value from self, result must be the type in Lua, here its `String`.
set={
self.mode.variant_name().to_lowercase()
}
),
debug: bool
}
);

View File

@ -34,7 +34,15 @@ impl mlua::IntoLua for LuaDeltaTime {
} }
impl LuaWrapper for LuaDeltaTime { impl LuaWrapper for LuaDeltaTime {
type Wrap = DeltaTime;
#[inline(always)]
fn wrapped_type_id() -> std::any::TypeId { fn wrapped_type_id() -> std::any::TypeId {
TypeId::of::<DeltaTime>() TypeId::of::<DeltaTime>()
} }
#[inline(always)]
fn into_wrapped(self) -> Self::Wrap {
self.0
}
} }

View File

@ -173,9 +173,15 @@ impl mlua::FromLua for LuaActionHandler {
} }
impl LuaWrapper for LuaActionHandler { impl LuaWrapper for LuaActionHandler {
type Wrap = ActionHandler;
fn wrapped_type_id() -> std::any::TypeId { fn wrapped_type_id() -> std::any::TypeId {
std::any::TypeId::of::<ActionHandler>() std::any::TypeId::of::<ActionHandler>()
} }
fn into_wrapped(self) -> Self::Wrap {
self.handler
}
} }
fn process_keyboard_string(key_name: &str) -> Option<ActionSource> { fn process_keyboard_string(key_name: &str) -> Option<ActionSource> {

View File

@ -11,7 +11,7 @@ wrap_lua_struct!(
math::Vec2, math::Vec2,
derives(PartialEq, Copy), derives(PartialEq, Copy),
new, new,
fields(x, y), fields={x: f32, y: f32},
metamethods( metamethods(
Add(LuaVec2, f32), Add(LuaVec2, f32),
Sub(LuaVec2, f32), Sub(LuaVec2, f32),
@ -50,7 +50,7 @@ wrap_lua_struct!(
math::Vec3, math::Vec3,
derives(PartialEq, Copy), derives(PartialEq, Copy),
new, new,
fields(x, y, z), fields={x: f32, y: f32, z: f32},
metamethods( metamethods(
Add(LuaVec3, f32), Add(LuaVec3, f32),
Sub(LuaVec3, f32), Sub(LuaVec3, f32),
@ -90,7 +90,7 @@ wrap_lua_struct!(
math::Vec4, math::Vec4,
derives(PartialEq, Copy), derives(PartialEq, Copy),
new, new,
fields(x, y, z, w), fields={x: f32, y: f32, z: f32, w: f32},
metamethods( metamethods(
Add(LuaVec4, f32), Add(LuaVec4, f32),
Sub(LuaVec4, f32), Sub(LuaVec4, f32),
@ -108,7 +108,7 @@ wrap_lua_struct!(
math::Quat, math::Quat,
derives(PartialEq, Copy), derives(PartialEq, Copy),
//new, //new,
fields(x, y, z, w), fields={x: f32, y: f32, z: f32, w: f32},
metamethods( metamethods(
Eq, Eq,
Add, Add,
@ -360,4 +360,27 @@ wrap_lua_struct!(
Ok(t) Ok(t)
}); });
} }
);
wrap_lua_struct!(
math::Angle,
derives(Copy),
skip(lua_reflect),
extra_methods = {
methods.add_function("new_degrees", |_, deg: f32| {
Ok(Self(math::Angle::Degrees(deg)))
});
methods.add_function("new_radians", |_, rad: f32| {
Ok(Self(math::Angle::Radians(rad)))
});
methods.add_method("radians", |_, this, ()| {
Ok(this.to_radians())
});
methods.add_method("degrees", |_, this, ()| {
Ok(this.to_degrees())
});
},
); );

View File

@ -11,4 +11,7 @@ mod delta_time;
pub use delta_time::*; pub use delta_time::*;
mod window; mod window;
pub use window::*; pub use window::*;
mod camera;
pub use camera::*;

View File

@ -1,7 +1,6 @@
use lyra_scripting_derive::wrap_lua_struct; use lyra_scripting_derive::wrap_lua_struct;
use lyra_game::winit::{WindowOptions, FullscreenMode, Theme, CursorGrabMode, WindowLevel}; use lyra_game::winit::{WindowOptions, FullscreenMode, Theme, CursorGrabMode, WindowLevel};
use mlua::IntoLua;
use crate::lyra_engine; use crate::lyra_engine;
use crate as lyra_scripting; use crate as lyra_scripting;
@ -15,7 +14,9 @@ wrap_lua_struct!(
WindowOptions, WindowOptions,
//derives(Clone), //derives(Clone),
name=LuaWindow, name=LuaWindow,
fields(focused), fields={
(focused, skip_set)
},
extra_fields={ extra_fields={
fields.add_field_method_get("window_mode", |lua, this| { fields.add_field_method_get("window_mode", |lua, this| {
let s = match &this.fullscreen_mode { let s = match &this.fullscreen_mode {