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
9 changed files with 131 additions and 29 deletions
Showing only changes of commit d001e136d0 - Show all commits

View File

@ -93,13 +93,15 @@ 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) ---@param wt WorldTransform
function (t, wt)
print("Entity is at: " .. tostring(wt))
t:translate(0, 0.15 * dt, 0) t:translate(0, 0.15 * dt, 0)
return t return t
end, Transform end, Transform, WorldTransform
) ]] )
--[[ world:view( --[[ world:view(
---@param c Camera ---@param c Camera

View File

@ -1,18 +1,10 @@
use lyra_engine::{ use lyra_engine::{
assets::{gltf::Gltf, ResourceManager}, assets::{gltf::Gltf, ResourceManager}, game::App, input::{
game::App,
input::{
Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource, Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource,
InputActionPlugin, KeyCode, LayoutId, MouseAxis, MouseInput, InputActionPlugin, KeyCode, LayoutId, MouseAxis, MouseInput,
}, }, math::{self, Transform, Vec3}, render::light::directional::DirectionalLight, scene::{
script::{lua::{LuaScript, LuaScriptingPlugin}, Script, ScriptList}, self, CameraComponent, FreeFlyCamera, FreeFlyCameraPlugin, WorldTransform, ACTLBL_LOOK_LEFT_RIGHT, ACTLBL_LOOK_ROLL, ACTLBL_LOOK_UP_DOWN, ACTLBL_MOVE_FORWARD_BACKWARD, ACTLBL_MOVE_LEFT_RIGHT, ACTLBL_MOVE_UP_DOWN
math::{self, Transform, Vec3}, }, script::{lua::{LuaScript, LuaScriptingPlugin}, Script, ScriptList}
render::light::directional::DirectionalLight,
scene::{
CameraComponent, FreeFlyCamera, FreeFlyCameraPlugin, WorldTransform,
ACTLBL_LOOK_LEFT_RIGHT, ACTLBL_LOOK_ROLL, ACTLBL_LOOK_UP_DOWN,
ACTLBL_MOVE_FORWARD_BACKWARD, ACTLBL_MOVE_LEFT_RIGHT, ACTLBL_MOVE_UP_DOWN,
},
}; };
#[async_std::main] #[async_std::main]
@ -91,6 +83,11 @@ async fn main() {
app.with_plugin(setup_scene_plugin); app.with_plugin(setup_scene_plugin);
app.with_plugin(action_handler_plugin); app.with_plugin(action_handler_plugin);
app.with_plugin(setup_script_plugin); app.with_plugin(setup_script_plugin);
app.with_system(
"update_world_transforms",
scene::system_update_world_transforms,
&[],
);
//app.with_plugin(camera_debug_plugin); //app.with_plugin(camera_debug_plugin);
app.with_plugin(FreeFlyCameraPlugin); app.with_plugin(FreeFlyCameraPlugin);
app.run(); app.run();

View File

@ -2,6 +2,7 @@ use std::ops::Deref;
use lyra_ecs::{query::{filter::{Has, Not}, Entities, View}, relation::{ChildOf, RelationOriginComponent}, Component, Entity, World}; use lyra_ecs::{query::{filter::{Has, Not}, Entities, View}, relation::{ChildOf, RelationOriginComponent}, Component, Entity, World};
use lyra_math::Transform; use lyra_math::Transform;
use lyra_reflect::Reflect;
use crate::lyra_engine; use crate::lyra_engine;
/// The world transform of an entity. /// The world transform of an entity.
@ -11,7 +12,7 @@ use crate::lyra_engine;
/// you should use its [`Transform`]. You cannot mutate [`WorldTransform`] as its managed completey /// you should use its [`Transform`]. You cannot mutate [`WorldTransform`] as its managed completey
/// by the [`system_update_world_transforms`] system. For the WorldTransform to work properly, you /// by the [`system_update_world_transforms`] system. For the WorldTransform to work properly, you
/// must have both a [`Transform`] and [`WorldTransform`] on the entities in the scene. /// must have both a [`Transform`] and [`WorldTransform`] on the entities in the scene.
#[derive(Debug, Copy, Clone, PartialEq, Default, Component)] #[derive(Debug, Copy, Clone, PartialEq, Default, Component, Reflect)]
pub struct WorldTransform(pub(crate) Transform); pub struct WorldTransform(pub(crate) Transform);
impl Deref for WorldTransform { impl Deref for WorldTransform {

View File

@ -47,7 +47,7 @@ fn field_table_getter(field: &Field) -> proc_macro2::TokenStream {
} }
} }
fn wrapper_creation(wrapper: &syn::Ident, type_path: &syn::Path, create: Option<&syn::Block>, fields: &Vec<Field>) -> proc_macro2::TokenStream { fn wrapper_creation(wrapper: &syn::Ident, type_path: &syn::Path, struct_type: StructType, create: Option<&syn::Block>, fields: &Vec<Field>) -> proc_macro2::TokenStream {
match create { match create {
Some(b) => quote!(#b), Some(b) => quote!(#b),
@ -59,19 +59,28 @@ fn wrapper_creation(wrapper: &syn::Ident, type_path: &syn::Path, create: Option<
}); */ }); */
let field_iter = fields.iter().map(|f| { let field_iter = fields.iter().map(|f| {
let ident = &f.field; let ident = &f.field;
if f.field_ty.is_wrapped() { if f.field_ty.is_wrapped() && struct_type == StructType::Fields {
quote!(#ident: (*#ident).clone()) quote!(#ident: (*#ident).clone())
} else { } else {
quote!(#ident) quote!(#ident)
} }
}); });
quote! { match struct_type {
#wrapper(#type_path { StructType::Fields => {
#( quote! {
#field_iter #wrapper(#type_path {
),* #(
}) #field_iter
),*
})
}
},
StructType::Tuple => {
quote! {
#wrapper(#type_path( #(#field_iter),* ))
}
},
} }
} }
} }
@ -104,8 +113,17 @@ enum ReflectType {
Resource, Resource,
} }
/// The type of the wrapping struct
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
enum StructType {
#[default]
Fields,
Tuple,
}
struct IntoLuaUsage { struct IntoLuaUsage {
type_path: syn::Path, type_path: syn::Path,
struct_type: StructType,
override_name: Option<syn::Ident>, override_name: Option<syn::Ident>,
table_name: String, table_name: String,
derives: Vec<syn::Ident>, derives: Vec<syn::Ident>,
@ -126,6 +144,7 @@ impl syn::parse::Parse for IntoLuaUsage {
let mut s = Self { let mut s = Self {
type_path, type_path,
struct_type: StructType::Fields,
override_name: None, override_name: None,
table_name: lua_name, table_name: lua_name,
derives: vec![], derives: vec![],
@ -149,6 +168,23 @@ impl syn::parse::Parse for IntoLuaUsage {
let name: syn::Ident = input.parse()?; let name: syn::Ident = input.parse()?;
s.override_name = Some(name); s.override_name = Some(name);
}, },
"struct_type" => {
let _eq: Token![=] = input.parse()?;
let st_token = input.parse::<syn::LitStr>()?;
let st_str = st_token.value().to_lowercase();
let st_str = st_str.as_str();
let st = match st_str {
"fields" => StructType::Fields,
"tuple" => StructType::Tuple,
_ => return Err(syn::Error::new_spanned(
st_token,
format!("unknown struct type: '{}', expected 'fields', or `tuple`", st_str),
)),
};
s.struct_type = st;
},
"lua_name" => { "lua_name" => {
let _eq: Token![=] = input.parse()?; let _eq: Token![=] = input.parse()?;
s.table_name = input.parse::<syn::LitStr>()?.value(); s.table_name = input.parse::<syn::LitStr>()?.value();
@ -242,7 +278,7 @@ pub fn to_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::TokenSt
let lua_name = &input.table_name; let lua_name = &input.table_name;
let field_getters_iter = input.fields.iter().map(|f| field_table_getter(f)); 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 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 struct_creator = wrapper_creation(&wrapper, type_path, input.struct_type, input.create.as_ref(), &input.fields);
let reflect_fn = get_reflect_lua_functions(reflect_type, &input.type_path, true); 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); let reflect_type_fn = get_reflect_lua_functions(reflect_type, &input.type_path, false);

View File

@ -1,3 +1,5 @@
---@meta
---@class Window: userdata ---@class Window: userdata
Window = { Window = {
---Gets or sets the window's focus. ---Gets or sets the window's focus.

View File

@ -0,0 +1,9 @@
---@meta
--- A Transform represents the relative position of the entity to its parent entity, while
--- a world transform is the position relative to the World. When wanting to move an entity,
--- you should use its [`Transform`]. You cannot mutate [`WorldTransform`] as its managed completey
--- by the [`system_update_world_transforms`] system. For the WorldTransform to work properly, you
--- must have both a [`Transform`] and [`WorldTransform`] on the entities in the scene.
---@alias WorldTransform Transform The world transform of an entity.
WorldTransform = {}

View File

@ -19,6 +19,7 @@ impl ScriptApiProvider for LyraEcsApiProvider {
world.register_lua_convert_component::<LuaCamera>("Camera"); world.register_lua_convert_component::<LuaCamera>("Camera");
world.register_lua_convert_component::<LuaFreeFlyCamera>("FreeFlyCamera"); world.register_lua_convert_component::<LuaFreeFlyCamera>("FreeFlyCamera");
world.register_lua_convert_component::<LuaWorldTransform>("WorldTransform");
world.register_lua_wrapper::<LuaDeviceId>(); world.register_lua_wrapper::<LuaDeviceId>();
world.register_lua_convert::<LuaDeviceEventRaw>(); world.register_lua_convert::<LuaDeviceEventRaw>();
@ -44,6 +45,7 @@ impl ScriptApiProvider for LyraEcsApiProvider {
expose_comp_table_wrapper::<LuaCamera>(&ctx, &globals, "Camera")?; expose_comp_table_wrapper::<LuaCamera>(&ctx, &globals, "Camera")?;
expose_comp_table_wrapper::<LuaFreeFlyCamera>(&ctx, &globals, "FreeFlyCamera")?; expose_comp_table_wrapper::<LuaFreeFlyCamera>(&ctx, &globals, "FreeFlyCamera")?;
expose_comp_table_wrapper::<LuaWorldTransform>(&ctx, &globals, "WorldTransform")?;
expose_table_wrapper::<LuaDeviceEvent>(&ctx, &globals, "DeviceEvent")?; expose_table_wrapper::<LuaDeviceEvent>(&ctx, &globals, "DeviceEvent")?;
let dt_table = create_reflect_table::<lyra_game::DeltaTime>(&ctx)?; let dt_table = create_reflect_table::<lyra_game::DeltaTime>(&ctx)?;
@ -86,11 +88,12 @@ where
table.set(mlua::MetaMethod::Type.name(), name)?; table.set(mlua::MetaMethod::Type.name(), name)?;
Ok(table) Ok(table)
} }
/// Expose a wrapper that converts to/from a lua type. /// Expose a wrapper of a component that converts to/from a lua type.
///
/// This type of wrapper could convert to/from a Lua table, or number, string, etc., any Lua type.
/// ///
/// This creates the reflection functions on a table specified in globals. /// This creates the reflection functions on a table specified in globals.
/// The table name is set to `name`, which is also how the script will use the table. /// The table name is set to `name`, which is also how the script will use the table.

View File

@ -20,4 +20,7 @@ mod free_fly_camera;
pub use free_fly_camera::*; pub use free_fly_camera::*;
mod events; mod events;
pub use events::*; pub use events::*;
mod world_transform;
pub use world_transform::*;

View File

@ -0,0 +1,49 @@
use crate::lua::{wrappers::LuaTransform, LuaWrapper};
use crate::lyra_engine;
use lyra_game::scene::WorldTransform;
use lyra_reflect::Reflect;
#[derive(Clone, Default, Reflect)]
pub struct LuaWorldTransform(pub(crate) WorldTransform);
impl std::ops::Deref for LuaWorldTransform {
type Target = WorldTransform;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl std::ops::DerefMut for LuaWorldTransform {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl mlua::FromLua for LuaWorldTransform {
fn from_lua(v: mlua::Value, lua: &mlua::Lua) -> mlua::Result<Self> {
let t = LuaTransform::from_lua(v, lua)?;
Ok(Self(WorldTransform::from(*t)))
}
}
impl mlua::IntoLua for LuaWorldTransform {
fn into_lua(self, lua: &mlua::Lua) -> mlua::Result<mlua::Value> {
let t = LuaTransform(*self.0);
t.into_lua(lua)
}
}
impl LuaWrapper for LuaWorldTransform {
type Wrap = WorldTransform;
#[inline(always)]
fn into_wrapped(self) -> Self::Wrap {
self.0
}
#[inline(always)]
fn from_wrapped(wrap: Self::Wrap) -> Option<Self> {
Some(Self(wrap))
}
}