Expose structs to Lua and write Lua type annotations #28
|
@ -69,12 +69,23 @@ function on_update()
|
|||
---@type number
|
||||
local dt = world:resource(DeltaTime)
|
||||
|
||||
world:view(
|
||||
--[[ world:view(
|
||||
---@param t Transform
|
||||
function (t)
|
||||
t:translate(0, 0.15 * dt, 0)
|
||||
return t
|
||||
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
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use lyra_reflect::Reflect;
|
||||
use winit::dpi::PhysicalSize;
|
||||
|
||||
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 {
|
||||
/// 3d camera projection
|
||||
Perspective,
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use lyra_ecs::Component;
|
||||
use lyra_reflect::Reflect;
|
||||
|
||||
use crate::{math::{Angle, Transform}, render::camera::CameraProjectionMode};
|
||||
|
||||
#[derive(Clone, Component)]
|
||||
#[derive(Clone, Component, Reflect)]
|
||||
pub struct CameraComponent {
|
||||
pub transform: Transform,
|
||||
pub fov: Angle,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use lyra_math::Angle;
|
||||
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::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 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
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
|
@ -247,9 +247,15 @@ pub(crate) fn lua_wrap_handle_impl(input: proc_macro::TokenStream) -> proc_macro
|
|||
}
|
||||
|
||||
impl LuaWrapper for #wrapper_name {
|
||||
type Wrap = ResHandle<#handle_path>;
|
||||
|
||||
fn wrapped_type_id() -> std::any::TypeId {
|
||||
TypeId::of::<ResHandle<#handle_path>>()
|
||||
}
|
||||
|
||||
fn into_wrapped(self) -> Self::Wrap {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
}.into()
|
||||
}
|
|
@ -5,9 +5,12 @@ use syn::{parse_macro_input, Token};
|
|||
|
||||
mod mat_wrapper;
|
||||
mod vec_wrapper;
|
||||
use to_lua_macro::to_lua_struct_impl;
|
||||
use vec_wrapper::VecWrapper;
|
||||
|
||||
mod lua_macro;
|
||||
mod to_lua_macro;
|
||||
mod field;
|
||||
|
||||
mod handle_macro;
|
||||
|
||||
|
@ -24,6 +27,11 @@ pub fn lua_wrap_handle(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
|
|||
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 {
|
||||
#[allow(dead_code)]
|
||||
pub type_path: syn::Path,
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
use proc_macro2::Span;
|
||||
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)]
|
||||
enum SkipType {
|
||||
/// Skips implementing
|
||||
LuaReflect
|
||||
LuaReflect,
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for SkipType {
|
||||
|
@ -17,9 +19,7 @@ impl syn::parse::Parse for SkipType {
|
|||
|
||||
match name_str.as_str() {
|
||||
"lua_reflect" => Ok(Self::LuaReflect),
|
||||
_ => {
|
||||
Err(syn::Error::new_spanned(name, "unknown skip type"))
|
||||
}
|
||||
_ => Err(syn::Error::new_spanned(name, "unknown skip type")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -43,12 +43,8 @@ impl MetaMethod {
|
|||
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
|
||||
},
|
||||
| "BXor" => true,
|
||||
"Unm" | "BNot" | "ToString" => false,
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
@ -58,7 +54,10 @@ impl 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 {
|
||||
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 {
|
||||
|
@ -75,17 +74,17 @@ impl MetaMethod {
|
|||
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))
|
||||
|
@ -95,41 +94,47 @@ impl MetaMethod {
|
|||
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
|
||||
}
|
||||
_ => 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(),
|
||||
}
|
||||
_ => {
|
||||
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) {
|
||||
// Lua wrappers must be dereferenced
|
||||
quote! {
|
||||
|
@ -167,7 +172,6 @@ impl MetaMethod {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
} 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));
|
||||
|
@ -184,26 +188,30 @@ impl MetaMethod {
|
|||
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,
|
||||
"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));
|
||||
let body =
|
||||
Self::get_body_for_arg(&self.name, num_ident, quote!(n as #num_ident));
|
||||
|
||||
quote! {
|
||||
mlua::Value::Number(n) => {
|
||||
#body
|
||||
},
|
||||
}
|
||||
} else { quote!() }
|
||||
} else {
|
||||
quote!()
|
||||
}
|
||||
};
|
||||
|
||||
let userdata_arm = {
|
||||
let wrappers: Vec<&Ident> = self.arg.iter()
|
||||
let wrappers: Vec<&Ident> = self
|
||||
.arg
|
||||
.iter()
|
||||
.filter(|i| Self::is_arg_wrapper(i))
|
||||
.collect();
|
||||
|
||||
|
@ -215,7 +223,6 @@ impl MetaMethod {
|
|||
#body
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
quote! {
|
||||
|
@ -274,17 +281,15 @@ 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![],
|
||||
};
|
||||
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![,])?;
|
||||
let arg: Punctuated<Ident, Token![,]> =
|
||||
content.parse_terminated(Ident::parse, Token![,])?;
|
||||
s.arg = arg.into_iter().collect(); // convert to Vec<Ident>
|
||||
}
|
||||
|
||||
|
@ -296,8 +301,7 @@ struct WrapUsage {
|
|||
type_path: syn::Path,
|
||||
/// The extra derives of the type.
|
||||
override_name: Option<Ident>,
|
||||
auto_fields: Vec<Ident>,
|
||||
manual_field_getters: Vec<FieldGetter>,
|
||||
auto_fields: Vec<Field>,
|
||||
auto_derives: Vec<Ident>,
|
||||
auto_new: bool,
|
||||
meta_methods: Vec<MetaMethod>,
|
||||
|
@ -315,7 +319,6 @@ impl syn::parse::Parse for WrapUsage {
|
|||
override_name: None,
|
||||
auto_fields: vec![],
|
||||
auto_derives: vec![],
|
||||
manual_field_getters: vec![],
|
||||
extra_fields: None,
|
||||
extra_methods: None,
|
||||
auto_new: false,
|
||||
|
@ -339,73 +342,72 @@ impl syn::parse::Parse for WrapUsage {
|
|||
|
||||
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![,])?;
|
||||
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();
|
||||
}
|
||||
},
|
||||
"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()?;
|
||||
|
||||
if input.peek(token::Brace) {
|
||||
let content;
|
||||
let _braced: token::Brace = braced!(content in input);
|
||||
|
||||
let terminated = content.parse_terminated(FieldGetter::parse, Token![,])?;
|
||||
s.manual_field_getters = terminated.into_iter().collect();
|
||||
let terminated = content.parse_terminated(Field::parse, Token![,])?;
|
||||
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() {
|
||||
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)
|
||||
|
@ -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 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!")
|
||||
.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()));
|
||||
|
||||
let derive_idents_iter = input.auto_derives.iter();
|
||||
let extra_fields = input.extra_fields;
|
||||
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
|
||||
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 arg_names = input
|
||||
.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_types_clone = arg_types.clone();
|
||||
|
||||
quote! {
|
||||
// arguments for function are not specified since they can be implied from the call
|
||||
// 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),* )))
|
||||
});
|
||||
}
|
||||
|
||||
} else { quote!() };
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
|
||||
let meta_methods_tokens = {
|
||||
let method_tokens = input.meta_methods.iter().map(|mm| {
|
||||
mm.to_tokens(&wrapper_typename)
|
||||
});
|
||||
let method_tokens = input
|
||||
.meta_methods
|
||||
.iter()
|
||||
.map(|mm| mm.to_tokens(&wrapper_typename));
|
||||
|
||||
quote! {
|
||||
#(#method_tokens)*
|
||||
|
@ -482,28 +483,74 @@ pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::Token
|
|||
}
|
||||
};
|
||||
|
||||
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_creator = match &g.wrapper_type {
|
||||
Some(wrap) => {
|
||||
quote!(#wrap(this.#field).into_lua(lua))
|
||||
},
|
||||
None => match &g.body {
|
||||
let field_getter = match &g.field_ty {
|
||||
FieldType::Wrapped(wrap) => {
|
||||
quote!(#wrap(this.#field.clone()).into_lua(lua))
|
||||
}
|
||||
_ => match &g.getter {
|
||||
Some(body) => {
|
||||
quote!(#body)
|
||||
},
|
||||
}
|
||||
None => {
|
||||
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! {
|
||||
fields.add_field_method_get(stringify!($field), |lua, this| {
|
||||
#field_creator
|
||||
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! {
|
||||
fields.add_field_method_get(stringify!(#field), |lua, this| {
|
||||
#field_getter
|
||||
});
|
||||
#field_setter
|
||||
}
|
||||
});
|
||||
|
||||
proc_macro::TokenStream::from(quote! {
|
||||
|
@ -535,13 +582,16 @@ pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::Token
|
|||
|
||||
impl mlua::UserData for #wrapper_typename {
|
||||
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
|
||||
#(#field_get_set_pairs)*
|
||||
#(#custom_getters)*
|
||||
use mlua::IntoLua;
|
||||
|
||||
#(#fields)*
|
||||
|
||||
#extra_fields
|
||||
}
|
||||
|
||||
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
|
||||
use mlua::IntoLua;
|
||||
|
||||
#lua_reflects
|
||||
|
||||
#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 {
|
||||
type Wrap = #path;
|
||||
|
||||
fn wrapped_type_id() -> std::any::TypeId {
|
||||
std::any::TypeId::of::<#path>()
|
||||
}
|
||||
|
||||
fn into_wrapped(self) -> Self::Wrap {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -61,3 +61,9 @@ WrappingMode = {
|
|||
MIRRORED_REPEAT = "mirrored_repeat",
|
||||
REPEAT = "repeat",
|
||||
}
|
||||
|
||||
---@enum CameraProjectionMode
|
||||
CameraProjectionMode = {
|
||||
PERSPECTIVE = "perspective",
|
||||
ORTHOGRAPHIC = "orthographic",
|
||||
}
|
|
@ -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,
|
||||
}
|
|
@ -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
|
|
@ -131,6 +131,11 @@ pub trait RegisterLuaType {
|
|||
fn register_lua_convert<T>(&mut self)
|
||||
where
|
||||
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 {
|
||||
|
@ -165,6 +170,18 @@ impl RegisterLuaType for World {
|
|||
let reg_type = registry.get_type_or_default(T::wrapped_type_id());
|
||||
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 {
|
||||
|
|
|
@ -4,7 +4,9 @@ use lyra_ecs::ResourceObject;
|
|||
use lyra_reflect::{Reflect, TypeRegistry};
|
||||
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)]
|
||||
pub struct LyraEcsApiProvider;
|
||||
|
@ -17,6 +19,12 @@ impl ScriptApiProvider for LyraEcsApiProvider {
|
|||
world.register_lua_wrapper::<LuaSceneHandle>();
|
||||
world.register_lua_wrapper::<LuaActionHandler>();
|
||||
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();
|
||||
|
||||
|
@ -33,8 +41,6 @@ impl ScriptApiProvider for LyraEcsApiProvider {
|
|||
}
|
||||
);
|
||||
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> {
|
||||
|
@ -51,6 +57,9 @@ impl ScriptApiProvider for LyraEcsApiProvider {
|
|||
globals.set("ActionHandler", ctx.create_proxy::<LuaActionHandler>()?)?;
|
||||
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)?;
|
||||
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(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)
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
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::lua::RegisterLuaType;
|
||||
|
||||
|
@ -16,6 +16,7 @@ impl ScriptApiProvider for LyraMathApiProvider {
|
|||
world.register_lua_wrapper::<LuaVec3>();
|
||||
world.register_lua_wrapper::<LuaQuat>();
|
||||
world.register_lua_wrapper::<LuaTransform>();
|
||||
world.register_lua_wrapper::<LuaAngle>();
|
||||
}
|
||||
|
||||
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("Quat", ctx.create_proxy::<LuaQuat>()?)?;
|
||||
globals.set("Transform", ctx.create_proxy::<LuaTransform>()?)?;
|
||||
globals.set("Angle", ctx.create_proxy::<LuaAngle>()?)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -9,8 +9,11 @@ use crate::{ScriptBorrow, ScriptDynamicBundle};
|
|||
use super::{Error, FN_NAME_INTERNAL_REFLECT};
|
||||
|
||||
pub trait LuaWrapper {
|
||||
type Wrap: Reflect + 'static;
|
||||
|
||||
/// The type id of the wrapped type.
|
||||
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.
|
||||
|
|
|
@ -129,28 +129,29 @@ fn wrapping_mode_to_str(wm: WrappingMode) -> &'static str {
|
|||
wrap_lua_struct!(lyra_resource::TextureSampler,
|
||||
// this can be safely skipped since it wont be a component or resource.
|
||||
skip(lua_reflect),
|
||||
field_getters={
|
||||
(mag_filter, {
|
||||
fields={
|
||||
// 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))
|
||||
.into_lua(lua)
|
||||
}),
|
||||
(min_filter, {
|
||||
(min_filter, skip_set, get={
|
||||
this.min_filter.map(|f| filter_mode_to_str(f))
|
||||
.into_lua(lua)
|
||||
}),
|
||||
(mipmap_filter, {
|
||||
(mipmap_filter, skip_set, get={
|
||||
this.mipmap_filter.map(|f| filter_mode_to_str(f))
|
||||
.into_lua(lua)
|
||||
}),
|
||||
(wrap_u, {
|
||||
(wrap_u, skip_set, get={
|
||||
wrapping_mode_to_str(this.wrap_u)
|
||||
.into_lua(lua)
|
||||
}),
|
||||
(wrap_v, {
|
||||
(wrap_v, skip_set, get={
|
||||
wrapping_mode_to_str(this.wrap_v)
|
||||
.into_lua(lua)
|
||||
}),
|
||||
(wrap_w, {
|
||||
(wrap_w, skip_set, get={
|
||||
wrapping_mode_to_str(this.wrap_w)
|
||||
.into_lua(lua)
|
||||
}),
|
||||
|
@ -160,31 +161,29 @@ wrap_lua_struct!(lyra_resource::TextureSampler,
|
|||
wrap_lua_struct!(lyra_resource::gltf::PbrGlossiness,
|
||||
// this can be safely skipped since it wont be a component or resource.
|
||||
skip(lua_reflect),
|
||||
field_getters={
|
||||
(diffuse_color, wrapper=crate::lua::wrappers::LuaVec4),
|
||||
(specular, wrapper=crate::lua::wrappers::LuaVec3),
|
||||
glossiness,
|
||||
fields={
|
||||
(diffuse_color: wrap(crate::lua::wrappers::LuaVec4), skip_set),
|
||||
(specular: wrap(crate::lua::wrappers::LuaVec3), skip_set),
|
||||
(glossiness, skip_set),
|
||||
}
|
||||
);
|
||||
|
||||
wrap_lua_struct!(lyra_resource::gltf::Specular,
|
||||
// this can be safely skipped since it wont be a component or resource.
|
||||
skip(lua_reflect),
|
||||
field_getters={
|
||||
factor,
|
||||
(color_factor, wrapper=crate::lua::wrappers::LuaVec3),
|
||||
},
|
||||
extra_fields={
|
||||
fields.add_field_method_get("texture", |lua, this| {
|
||||
fields={
|
||||
(factor, skip_set),
|
||||
(color_factor: wrap(crate::lua::wrappers::LuaVec3), skip_set),
|
||||
(texture, skip_set, get={
|
||||
this.texture.clone()
|
||||
.map(|t| LuaTextureHandle(t))
|
||||
.into_lua(lua)
|
||||
});
|
||||
fields.add_field_method_get("color_texture", |lua, this| {
|
||||
}),
|
||||
(color_texture, skip_set, get={
|
||||
this.color_texture.clone()
|
||||
.map(|t| LuaTextureHandle(t))
|
||||
.into_lua(lua)
|
||||
});
|
||||
})
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
);
|
|
@ -34,7 +34,15 @@ impl mlua::IntoLua for LuaDeltaTime {
|
|||
}
|
||||
|
||||
impl LuaWrapper for LuaDeltaTime {
|
||||
type Wrap = DeltaTime;
|
||||
|
||||
#[inline(always)]
|
||||
fn wrapped_type_id() -> std::any::TypeId {
|
||||
TypeId::of::<DeltaTime>()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn into_wrapped(self) -> Self::Wrap {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
|
|
@ -173,9 +173,15 @@ impl mlua::FromLua for LuaActionHandler {
|
|||
}
|
||||
|
||||
impl LuaWrapper for LuaActionHandler {
|
||||
type Wrap = ActionHandler;
|
||||
|
||||
fn wrapped_type_id() -> std::any::TypeId {
|
||||
std::any::TypeId::of::<ActionHandler>()
|
||||
}
|
||||
|
||||
fn into_wrapped(self) -> Self::Wrap {
|
||||
self.handler
|
||||
}
|
||||
}
|
||||
|
||||
fn process_keyboard_string(key_name: &str) -> Option<ActionSource> {
|
||||
|
|
|
@ -11,7 +11,7 @@ wrap_lua_struct!(
|
|||
math::Vec2,
|
||||
derives(PartialEq, Copy),
|
||||
new,
|
||||
fields(x, y),
|
||||
fields={x: f32, y: f32},
|
||||
metamethods(
|
||||
Add(LuaVec2, f32),
|
||||
Sub(LuaVec2, f32),
|
||||
|
@ -50,7 +50,7 @@ wrap_lua_struct!(
|
|||
math::Vec3,
|
||||
derives(PartialEq, Copy),
|
||||
new,
|
||||
fields(x, y, z),
|
||||
fields={x: f32, y: f32, z: f32},
|
||||
metamethods(
|
||||
Add(LuaVec3, f32),
|
||||
Sub(LuaVec3, f32),
|
||||
|
@ -90,7 +90,7 @@ wrap_lua_struct!(
|
|||
math::Vec4,
|
||||
derives(PartialEq, Copy),
|
||||
new,
|
||||
fields(x, y, z, w),
|
||||
fields={x: f32, y: f32, z: f32, w: f32},
|
||||
metamethods(
|
||||
Add(LuaVec4, f32),
|
||||
Sub(LuaVec4, f32),
|
||||
|
@ -108,7 +108,7 @@ wrap_lua_struct!(
|
|||
math::Quat,
|
||||
derives(PartialEq, Copy),
|
||||
//new,
|
||||
fields(x, y, z, w),
|
||||
fields={x: f32, y: f32, z: f32, w: f32},
|
||||
metamethods(
|
||||
Eq,
|
||||
Add,
|
||||
|
@ -361,3 +361,26 @@ wrap_lua_struct!(
|
|||
});
|
||||
}
|
||||
);
|
||||
|
||||
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())
|
||||
});
|
||||
},
|
||||
);
|
|
@ -12,3 +12,6 @@ pub use delta_time::*;
|
|||
|
||||
mod window;
|
||||
pub use window::*;
|
||||
|
||||
mod camera;
|
||||
pub use camera::*;
|
|
@ -1,7 +1,6 @@
|
|||
use lyra_scripting_derive::wrap_lua_struct;
|
||||
|
||||
use lyra_game::winit::{WindowOptions, FullscreenMode, Theme, CursorGrabMode, WindowLevel};
|
||||
use mlua::IntoLua;
|
||||
|
||||
use crate::lyra_engine;
|
||||
use crate as lyra_scripting;
|
||||
|
@ -15,7 +14,9 @@ wrap_lua_struct!(
|
|||
WindowOptions,
|
||||
//derives(Clone),
|
||||
name=LuaWindow,
|
||||
fields(focused),
|
||||
fields={
|
||||
(focused, skip_set)
|
||||
},
|
||||
extra_fields={
|
||||
fields.add_field_method_get("window_mode", |lua, this| {
|
||||
let s = match &this.fullscreen_mode {
|
||||
|
|
Loading…
Reference in New Issue