Get some math types and some ecs stuff exposed to lua

This commit is contained in:
SeanOMik 2024-01-12 14:11:33 -05:00
parent acfd238274
commit d14abcc3e5
Signed by: SeanOMik
GPG Key ID: FEC9E2FC15235964
22 changed files with 1784 additions and 21 deletions

View File

@ -5,8 +5,8 @@ edition = "2021"
[dependencies] [dependencies]
lyra-resource = { path = "../lyra-resource" } lyra-resource = { path = "../lyra-resource" }
lyra-ecs = { path = "../lyra-ecs" } lyra-ecs = { path = "../lyra-ecs", features = [ "math" ] }
lyra-reflect = { path = "../lyra-reflect" } lyra-reflect = { path = "../lyra-reflect", features = [ "math" ] }
winit = "0.28.1" winit = "0.28.1"
tracing = "0.1.37" tracing = "0.1.37"

View File

@ -342,7 +342,7 @@ impl Game {
.with(fmt::layer().with_writer(stdout_layer)) .with(fmt::layer().with_writer(stdout_layer))
.with(filter::Targets::new() .with(filter::Targets::new()
// done by prefix, so it includes all lyra subpackages // done by prefix, so it includes all lyra subpackages
.with_target("lyra", Level::TRACE) .with_target("lyra", Level::DEBUG)
.with_target("wgpu", Level::WARN) .with_target("wgpu", Level::WARN)
.with_default(Level::INFO)) .with_default(Level::INFO))
.init(); .init();

View File

@ -89,8 +89,7 @@ impl Transform {
/// Performs a linear interpolation between `self` and `rhs` based on the value `alpha`. /// Performs a linear interpolation between `self` and `rhs` based on the value `alpha`.
/// ///
/// When `alpha` is `0.0`, the result will be equal to `self`. When `alpha` is `1.0`, the result /// When `alpha` is `0.0`, the result will be equal to `self`. When `alpha` is `1.0`, the result
/// will be equal to `rhs`. When `alpha` is outside of range `[0, 1]`, the result is linearly /// will be equal to `rhs`.
/// extrapolated.
pub fn lerp(&self, rhs: Transform, alpha: f32) -> Self { pub fn lerp(&self, rhs: Transform, alpha: f32) -> Self {
if alpha.is_finite() { if alpha.is_finite() {

View File

@ -10,8 +10,9 @@ default = ["lua"]
lua = ["dep:mlua"] lua = ["dep:mlua"]
[dependencies] [dependencies]
lyra-ecs = { path = "../lyra-ecs" } lyra-scripting-derive = { path = "lyra-scripting-derive" }
lyra-reflect = { path = "../lyra-reflect" } lyra-ecs = { path = "../lyra-ecs", features = [ "math" ] }
lyra-reflect = { path = "../lyra-reflect", features = [ "math" ] }
lyra-resource = { path = "../lyra-resource" } lyra-resource = { path = "../lyra-resource" }
lyra-game = { path = "../lyra-game" } lyra-game = { path = "../lyra-game" }
thiserror = "1.0.50" thiserror = "1.0.50"

View File

@ -0,0 +1,14 @@
[package]
name = "lyra-scripting-derive"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
proc-macro = true
[dependencies]
proc-macro2 = "1.0.70"
quote = "1.0.33"
syn = "2.0.41"

View File

@ -0,0 +1,606 @@
use proc_macro2::{Ident, Span};
use quote::quote;
use syn::{parse_macro_input, Path, Token, token, parenthesized, punctuated::Punctuated, braced, bracketed};
mod mat_wrapper;
use mat_wrapper::MatWrapper;
const FN_NAME_INTERNAL_REFLECT_TYPE: &str = "__lyra_internal_reflect_type";
const FN_NAME_INTERNAL_REFLECT: &str = "__lyra_internal_reflect";
pub(crate) struct MetaMethod {
pub ident: Ident,
pub mods: Vec<Ident>,
}
impl syn::parse::Parse for MetaMethod {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let ident: Ident = input.parse()?;
let mods = if input.peek(token::Paren) {
let content;
let _parens: token::Paren = parenthesized!(content in input);
content.parse_terminated(Ident::parse, Token![,])?
.into_iter().collect()
} else { vec![] };
Ok(Self {
ident,
mods,
})
}
}
pub(crate) struct VecWrapper {
}
impl VecWrapper {
fn vec_size(&self, wrapper_ident: &Ident) -> usize {
let name = wrapper_ident.to_string();
name[name.len() - 1..].parse::<usize>()
.or_else(|_| name[name.len() - 2.. name.len() - 1].parse::<usize>())
.expect("Failure to grab Vec size from ident name")
}
/// Returns the token stream of the type of the axis of the vec (Vec2 vs IVec2 vs I64Vec2, etc.)
fn vec_axis_type(&self, wrapper_ident: &Ident) -> &'static str {
let name = wrapper_ident.to_string();
let start = name.find("Vec").unwrap();
let before = &name[start - 1.. start];
match before {
"D" => return "f64",
"I" => return "i32",
"U" => return "u32",
"B" => return "bool",
_ => {},
}
//println!("before is {before}");
let three_before = &name[start - 3.. start];
match three_before {
"I64" => return "i64",
"U64" => return "u64",
_ => {},
}
//println!("three before is {three_before}");
"f32"
}
pub fn to_field_tokens(&self, wrapped_path: &Path, wrapper_ident: &Ident) -> proc_macro2::TokenStream {
let mut consts = vec![quote!(ZERO), quote!(ONE), quote!(X),
quote!(Y), ]; // , quote!(AXES)
let vec_size = self.vec_size(wrapper_ident);
let axis_type_name = self.vec_axis_type(wrapper_ident);
if axis_type_name.contains("b") {
return quote! {
fields.add_field("FALSE", #wrapper_ident(#wrapped_path::FALSE));
fields.add_field("TRUE", #wrapper_ident(#wrapped_path::TRUE));
};
}
if vec_size >= 3 {
consts.push(quote!(Z));
// no negative numbers for unsigned vecs
if !axis_type_name.contains("u") {
consts.push(quote!(NEG_Z));
}
}
if vec_size == 4 {
consts.push(quote!(W));
// no negative numbers for unsigned vecs
if !axis_type_name.contains("u") {
consts.push(quote!(NEG_W));
}
}
// no negative numbers for unsigned vecs
if !axis_type_name.contains("u") {
consts.push(quote!(NEG_X));
consts.push(quote!(NEG_Y));
consts.push(quote!(NEG_ONE));
}
if axis_type_name.contains("f") {
consts.push(quote!(NAN))
}
let const_tokens = consts.iter().map(|cnst| {
let const_name = cnst.to_string();
quote! {
fields.add_field(#const_name, #wrapper_ident(#wrapped_path::#cnst));
}
});
quote! {
#(#const_tokens)*
}
}
pub fn to_method_tokens(&self, wrapped_path: &Path, wrapper_ident: &Ident) -> proc_macro2::TokenStream {
let vec_size = self.vec_size(wrapper_ident);
let axis_type_name = self.vec_axis_type(wrapper_ident);
// methods that only some vecs have
let mut optional_methods = vec![];
// boolean vectors dont have much :(
if axis_type_name.contains("b") {
return quote!(); // TODO: all, any, bitmask, splat
}
if axis_type_name.contains("f") {
let type_id = Ident::new(axis_type_name, Span::call_site());
optional_methods.push(
quote! {
methods.add_method("clamp_length",
|_, this, (min, max): (#type_id, #type_id)| {
Ok(#wrapper_ident(this.clamp_length(min, max)))
});
methods.add_method("abs_diff_eq",
|_, this, (rhs, max_abs_diff): (#wrapper_ident, #type_id)| {
Ok(this.abs_diff_eq(rhs.0, max_abs_diff))
});
methods.add_method("ceil",
|_, this, (): ()| {
Ok(#wrapper_ident(this.ceil()))
});
}
);
if vec_size != 4 {
optional_methods.push(
quote! {
methods.add_method("angle_between",
|_, this, (rhs,): (#wrapper_ident,)| {
Ok(this.angle_between(rhs.0))
});
}
)
}
}
if !axis_type_name.contains("u") {
optional_methods.push(
quote! {
methods.add_method("abs",
|_, this, (): ()| {
Ok(#wrapper_ident(this.abs()))
});
}
)
}
let optional_methods_iter = optional_methods.iter();
quote! {
methods.add_method("clamp",
|_, this, (min, max): (#wrapper_ident, #wrapper_ident)| {
Ok(#wrapper_ident(this.clamp(min.0, max.0)))
});
// TODO: Not all Vecs have this
/* methods.add_method("clamp_length",
|_, this, (min, max): (f32, f32)| {
Ok(#wrapper_ident(this.clamp_length(min, max)))
}); */
methods.add_method("to_array",
|_, this, (): ()| {
Ok(this.to_array())
});
#(#optional_methods_iter)*
}
}
}
pub(crate) struct WrapUsage {
pub type_path: Path,
/// The extra derives of the type.
pub derive_idents: Punctuated<Ident, Token![,]>,
/// The field idents of the type that will be exposed with gets and sets
pub field_idents: Punctuated<Ident, Token![,]>,
pub skip_new_fn: bool,
/// The identifiers that are taken as parameters in the types 'new' function
pub new_fn_idents: Punctuated<Ident, Token![,]>,
pub meta_method_idents: Punctuated<MetaMethod, Token![,]>,
pub matrix: Option<MatWrapper>,
pub vec: Option<VecWrapper>,
}
impl syn::parse::Parse for WrapUsage {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let type_path: Path = input.parse()?;
let mut s = Self {
type_path,
derive_idents: Punctuated::default(),
field_idents: Punctuated::default(),
skip_new_fn: false,
new_fn_idents: Punctuated::default(),
meta_method_idents: Punctuated::default(),
matrix: None,
vec: None,
};
/* let mut derive_idents = None;
let mut field_idents = None;
let mut new_fn_idents = None; */
while input.peek(Token![,]) {
let _: Token![,] = input.parse()?;
//println!("Peeked a , ({:?})", input);
if input.peek(syn::Ident) {
let ident: Ident = input.parse()?;
let ident_str = ident.to_string();
let ident_str = ident_str.as_str();
match ident_str {
"derives" => {
if input.peek(token::Paren) {
let content;
let _parens: token::Paren = parenthesized!(content in input);
let derives: Punctuated<Ident, Token![,]> = content.parse_terminated(Ident::parse, Token![,])?;
s.derive_idents = derives;
//println!("read derives: {:?}", s.derive_idents);
}
},
"fields" => {
if input.peek(token::Paren) {
let content;
let _parens: token::Paren = parenthesized!(content in input);
let fields: Punctuated<Ident, Token![,]> = content.parse_terminated(Ident::parse, Token![,])?;
s.field_idents = fields;
//println!("read fields: {:?}", s.field_idents);
}
},
"new" => {
if input.peek(token::Paren) {
let content;
let _parens: token::Paren = parenthesized!(content in input);
let fields: Punctuated<Ident, Token![,]> = content.parse_terminated(Ident::parse, Token![,])?;
s.new_fn_idents = fields;
//println!("read fields: {:?}", s.new_fn_idents);
}
},
"no_new" => {
s.skip_new_fn = true;
},
"matrix" => {
if input.peek(token::Brace) {
let content;
let _braces = braced!(content in input);
s.matrix = Some(content.parse()?);
}
},
"metamethods" => {
if input.peek(token::Paren) {
let content;
let _bracket: token::Paren = parenthesized!(content in input);
let meta_methods: Punctuated<MetaMethod, Token![,]> = content.parse_terminated(MetaMethod::parse, Token![,])?;
s.meta_method_idents = meta_methods;
}
},
_ => {
return Err(syn::Error::new_spanned(ident, "unknown wrapper command"));
}
}
}
}
Ok(s)
}
}
/// Creates a wrapper type for a VecN from the engine math library.
#[proc_macro]
pub fn wrap_math_vec_copy(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as WrapUsage);
let path: Path = input.type_path;
let type_name = &path.segments.last()
.expect("Failure to find typename in macro usage!")
.ident;
let wrapper_typename = Ident::new(&format!("Lua{}", type_name), Span::call_site());
let vec_wrapper = {
let name_str = type_name.to_string();
if name_str.contains("Vec") {
Some(VecWrapper {})
} else {
None
}
};
/* let vec_wrapper_fields = vec_wrapper.as_ref().map(|vec|
vec.to_field_tokens(&path, &wrapper_typename)); */
let vec_wrapper_fields: Option<proc_macro2::TokenStream> = None;
let vec_wrapper_methods = vec_wrapper.as_ref().map(|vec|
vec.to_method_tokens(&path, &wrapper_typename));
let derive_idents_iter = input.derive_idents.iter();
let field_get_set_pairs = input.field_idents.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(())
});
}
});
let new_fn_idents = {
let idents = if input.new_fn_idents.is_empty() {
input.field_idents.iter()
} else {
input.new_fn_idents.iter()
};
let idents_c = idents.clone();
if !input.skip_new_fn {
quote! {
methods.add_function("new", |_, ( #(#idents_c),* )| {
Ok(#wrapper_typename(#path::new( #(#idents),* )))
});
}
} else { quote!() }
};
let matrix_wrapper_methods = input.matrix.as_ref().map(|m|
m.to_method_tokens(&path, &wrapper_typename));
let matrix_wrapper_fields = input.matrix.as_ref().map(|m|
m.to_field_tokens(&path, &wrapper_typename));
let meta_method_idents = {
let idents = input.meta_method_idents.iter().map(|metamethod| {
let metamethod_ident = &metamethod.ident;
let mm_str = metamethod.ident.to_string();
let mm_str = mm_str.as_str();
match mm_str {
"Add" | "Sub" | "Div" | "Mul" | "Mod" => {
let symbol = match mm_str {
"Add" => quote!(+),
"Sub" => quote!(-),
"Div" => quote!(/),
"Mul" => quote!(*),
"Mod" => quote!(%),
_ => unreachable!(),
};
// create a temporary vec to chain with metamethod.mods. If no parameters
// were provided, add the wrapper to the list of parameters.
let t = if metamethod.mods.is_empty() {
vec![wrapper_typename.clone()]
} else { vec![] };
let mods = metamethod.mods.iter().chain(t.iter()).map(|param| {
let other = if param.to_string().starts_with("Lua") {
quote!(other.0)
} else {
quote!(other)
};
quote! {
methods.add_meta_method(mlua::MetaMethod::#metamethod_ident,
|_, this, (other,): (#param,)| {
Ok(#wrapper_typename(this.0 #symbol #other))
});
}
});
quote! {
#(#mods)*
}
},
"Unm" => {
quote! {
methods.add_meta_method(mlua::MetaMethod::#metamethod_ident, |_, this, ()| {
Ok(#wrapper_typename(-this.0))
});
}
},
// Eq meta method has a different implementation than the above methods.
"Eq" => {
quote! {
methods.add_meta_method(mlua::MetaMethod::#metamethod_ident,
|_, this, (other,): (#wrapper_typename,)| {
Ok(this.0 == other.0)
});
}
},
"Shl" => {
// create a temporary vec to chain with metamethod.mods. If no parameters
// were provided, add the wrapper to the list of parameters.
let t = if metamethod.mods.is_empty() {
vec![wrapper_typename.clone()]
} else { vec![] };
let mods = metamethod.mods.iter().chain(t.iter()).map(|param| {
let other = if param.to_string().starts_with("Lua") {
quote!(other.0)
} else {
quote!(other)
};
quote! {
methods.add_meta_method(mlua::MetaMethod::#metamethod_ident,
|_, this, (other,): (#param,)| {
Ok(#wrapper_typename(this.0 << #other))
});
}
});
quote! {
#(#mods)*
}
}
"Shr" => {
// create a temporary vec to chain with metamethod.mods. If no parameters
// were provided, add the wrapper to the list of parameters.
let t = if metamethod.mods.is_empty() {
vec![wrapper_typename.clone()]
} else { vec![] };
let mods = metamethod.mods.iter().chain(t.iter()).map(|param| {
let other = if param.to_string().starts_with("Lua") {
quote!(other.0)
} else {
quote!(other)
};
quote! {
methods.add_meta_method(mlua::MetaMethod::#metamethod_ident,
|_, this, (other,): (#param,)| {
Ok(#wrapper_typename(this.0 >> #other))
});
}
});
quote! {
#(#mods)*
}
},
"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
};
// create a temporary vec to chain with metamethod.mods. If no parameters
// were provided, add the wrapper to the list of parameters.
let t = if metamethod.mods.is_empty() {
vec![wrapper_typename.clone()]
} else { vec![] };
let mods = metamethod.mods.iter().chain(t.iter()).map(|param| {
let other = if param.to_string().starts_with("Lua") {
quote!(other.0)
} else {
quote!(other)
};
quote! {
methods.add_meta_method(mlua::MetaMethod::#metamethod_ident,
|_, this, (other,): (#param,)| {
Ok(#wrapper_typename(this.0 #symbol #other))
});
}
});
quote! {
#(#mods)*
}
},
"BNot" => {
quote! {
methods.add_meta_method(mlua::MetaMethod::#metamethod_ident, |_, this, ()| {
Ok(#wrapper_typename(!this.0))
});
}
},
"ToString" => {
quote! {
methods.add_meta_method(mlua::MetaMethod::ToString, |_, this, ()| {
Ok(format!("{:?}", this.0))
});
}
},
_ => syn::Error::new_spanned(metamethod_ident,
"unsupported auto implementation of metamethod").to_compile_error(),
}
});
quote! {
#(#idents)*
}
};
proc_macro::TokenStream::from(quote! {
#[derive(Clone, Copy, lyra_reflect::Reflect, #(#derive_idents_iter),*)]
pub struct #wrapper_typename(#[reflect(skip)] pub(crate) #path);
impl std::ops::Deref for #wrapper_typename {
type Target = #path;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl std::ops::DerefMut for #wrapper_typename {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<'lua> mlua::FromLua<'lua> for #wrapper_typename {
fn from_lua(value: mlua::Value<'lua>, _lua: &'lua mlua::Lua) -> mlua::Result<Self> {
match value {
mlua::Value::UserData(ud) => Ok(*ud.borrow::<Self>()?),
_ => unreachable!(),
}
}
}
impl mlua::UserData for #wrapper_typename {
fn add_fields<'lua, F: mlua::prelude::LuaUserDataFields<'lua, Self>>(fields: &mut F) {
#(#field_get_set_pairs)*
#matrix_wrapper_fields
#vec_wrapper_fields
}
fn add_methods<'lua, M: mlua::prelude::LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
#new_fn_idents
methods.add_method(#FN_NAME_INTERNAL_REFLECT, |_, this, ()| {
Ok(crate::ScriptBorrow::from_component::<#path>(Some(this.0.clone())))
});
methods.add_function(#FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| {
Ok(crate::ScriptBorrow::from_component::<#path>(None))
});
#meta_method_idents
#matrix_wrapper_methods
#vec_wrapper_methods
}
}
impl lyra_scripting::lua::LuaWrapper for #wrapper_typename {
fn wrapped_type_id() -> std::any::TypeId {
std::any::TypeId::of::<#path>()
}
}
})
}

View File

@ -0,0 +1,142 @@
use proc_macro2::Ident;
use quote::quote;
use syn::{Path, Token};
pub(crate) struct MatWrapper {
pub column_type: Ident,
}
impl MatWrapper {
pub fn to_field_tokens(&self, wrapped_path: &Path, wrapper_ident: &Ident) -> proc_macro2::TokenStream {
quote! {
fields.add_field("ZERO", #wrapper_ident(#wrapped_path::ZERO));
fields.add_field("IDENTITY", #wrapper_ident(#wrapped_path::IDENTITY));
fields.add_field("NAN", #wrapper_ident(#wrapped_path::NAN));
}
}
pub fn to_method_tokens(&self, wrapped_path: &Path, wrapper_ident: &Ident) -> proc_macro2::TokenStream {
let column_type = &self.column_type;
let column_size = {
let ty_str = column_type.to_string();
ty_str[ty_str.len() - 1..].parse::<usize>()
.expect("Failure to parse number from token type")
};
let column_size_xtwo = column_size * 2;
let element_ty = quote!(f32);
quote! {
methods.add_function("from_cols",
|_, (x_axis, y_axis): (#column_type, #column_type)| {
Ok(#wrapper_ident(#wrapped_path::from_cols(x_axis.0, y_axis.0)))
});
methods.add_function("from_cols_array",
|_, (arr,): ([#element_ty; #column_size_xtwo],)| {
Ok(#wrapper_ident(#wrapped_path::from_cols_array(&arr)))
});
methods.add_function("from_cols_array_2d",
|_, (arr,): ([[#element_ty; #column_size]; #column_size],)| {
Ok(#wrapper_ident(#wrapped_path::from_cols_array_2d(&arr)))
});
methods.add_function("from_diagonal",
|_, (diag,): (#column_type,)| {
Ok(#wrapper_ident(#wrapped_path::from_diagonal(diag.0)))
});
methods.add_method("col",
|_, this, (idx,): (usize,)| {
Ok(#column_type(this.col(idx)))
});
methods.add_method("row",
|_, this, (idx,): (usize,)| {
Ok(#column_type(this.row(idx)))
});
methods.add_method_mut("set_col",
|_, this, (idx, newc): (usize, #column_type)| {
let col = this.col_mut(idx);
*col = newc.0;
Ok(())
});
methods.add_method("is_finite",
|_, this, (): ()| {
Ok(this.is_finite())
});
methods.add_method("is_nan",
|_, this, (): ()| {
Ok(this.is_nan())
});
methods.add_method("transpose",
|_, this, (): ()| {
Ok(#wrapper_ident(this.0.transpose()))
});
methods.add_method("determinant",
|_, this, (): ()| {
Ok(this.determinant())
});
methods.add_method("inverse",
|_, this, (): ()| {
Ok(#wrapper_ident(this.inverse()))
});
methods.add_method("abs_diff_eq",
|_, this, (rhs, max_abs_diff): (#wrapper_ident, f32)| {
Ok(this.abs_diff_eq(rhs.0, max_abs_diff))
});
// TODO: After all DMat's are implemented
/* methods.add_method("as_dmat",
|_, this, (rhs, max_abs_diff): (#wrapper_ident, f32)| {
Ok(D#wrapper_ident(this.as_dmat))
}); */
}
}
}
impl syn::parse::Parse for MatWrapper {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let mut column_type = None;
// cba to remove the use of this bool
let mut first = true;
while input.peek(Token![,]) || first {
if !first {
let _: Token![,] = input.parse()?;
}
if input.peek(syn::Ident) {
let ident: Ident = input.parse()?;
let ident_str = ident.to_string();
let ident_str = ident_str.as_str();
match ident_str {
"col_type" => {
let _eq: Token![=] = input.parse()?;
column_type = Some(input.parse()?);
},
_ => return Err(syn::Error::new_spanned(ident, "unknown matrix wrapper command")),
}
}
first = false;
}
Ok(Self {
column_type: column_type.ok_or_else(|| syn::Error::new(input.span(),
"expected `col_type`"))?,
})
}
}

View File

View File

@ -0,0 +1,177 @@
Quat = { x = 0.0, y = 0.0, z = 0.0, w = 0.0 }
Quat.__index = Quat
Quat.__name = "Quat"
--- Constructs a new Quaternion from x, y, z, and w.
---@param x number
---@param y number
---@param z number
---@param w number
---@return Quat
function Quat:new(x, y, z, w)
local q = {}
setmetatable(q, Quat)
q.x = x
q.y = y
q.z = z
q.w = w
return q
end
Quat.IDENTITY = Quat:new(0, 0, 0, 1)
function Quat:copy()
return Quat:new(self.x, self.y, self.z, self.w)
end
--- Creates a quaternion from the angle, in radians, around the x axis.
--- @param rad number
--- @return Quat
function Quat:from_rotation_x(rad)
local sin = math.sin(rad * 0.5)
local cos = math.cos(rad * 0.5)
return Quat:new(sin, 0, 0, cos)
end
--- Creates a quaternion from the angle, in radians, around the y axis.
--- @param rad number
--- @return Quat
function Quat:from_rotation_y(rad)
local sin = math.sin(rad * 0.5)
local cos = math.cos(rad * 0.5)
return Quat:new(0, sin, 0, cos)
end
--- Creates a quaternion from the angle, in radians, around the z axis.
--- @param rad number
--- @return Quat
function Quat:from_rotation_z(rad)
local sin = math.sin(rad * 0.5)
local cos = math.cos(rad * 0.5)
return Quat:new(0, 0, sin, cos)
end
--- Computes the dot product of `self`.
---@param rhs Quat
---@return number
function Quat:dot(rhs)
return (self.x * rhs.x) + (self.y * rhs.y) + (self.z * rhs.z) + (self.w * rhs.w)
end
--- Computes the length of `self`.
---@return number
function Quat:length()
return math.sqrt(self:dot(self))
end
--- Compute the length of `self` squared.
---@return number
function Quat:length_squared()
return self:length() ^ 2
end
--- Normalizes `self` and returns the new Quat
---@return unknown
function Quat:normalize()
local length = self:length()
return self / length
end
--- Multiplies two Quaternions together. Keep in mind that Quaternion multiplication is NOT
--- commutative so the order in which you multiply the quaternions matters.
---@param rhs Quat
---@return Quat
function Quat:mult_quat(rhs)
local x1, y1, z1, w1 = self.x, self.y, self.z, self.w
local x2, y2, z2, w2 = rhs.x, rhs.y, rhs.z, rhs.w
local x = w1 * x2 + x1 * w2 + y1 * z2 - z1 * y2
local y = w1 * y2 - x1 * z2 + y1 * w2 + z1 * x2
local z = w1 * z2 + x1 * y2 - y1 * x2 + z1 * w2
local w = w1 * w2 - x1 * x2 - y1 * y2 - z1 * x2
return Quat:new(x, y, z, w)
end
--- Multiplies `self` by a Vec3, returning the rotated Vec3
---@param vec Vec3
---@return Vec3
function Quat:mult_vec3(vec)
local vec_quat = Quat:new(vec.x, vec.y, vec.z, 0)
local quat = self:mult_quat(vec_quat)
return Vec3:new(quat.x, quat.y, quat.z)
end
--- Calculates the linear iterpolation between `self` and `rhs` based on the `alpha`.
--- When `alpha` is `0`, the result will be equal to `self`. When `s` is `1`, the result
--- will be equal to `rhs`
--- @param rhs Quat
--- @param alpha number
--- @return Quat
function Quat:lerp(rhs, alpha)
-- ensure alpha is [0, 1]
local alpha = math.max(0, math.min(1, alpha))
local x1, y1, z1, w1 = self.x, self.y, self.z, self.w
local x2, y2, z2, w2 = rhs.x, rhs.y, rhs.z, rhs.w
local x = (1 - alpha) * x1 + alpha * x2
local y = (1 - alpha) * y1 + alpha * y2
local z = (1 - alpha) * z1 + alpha * z2
local w = (1 - alpha) * w1 + alpha * w2
return Quat:new(x, y, z, w):normalize()
end
function Quat:__add(rhs)
return Quat:new(self.x + rhs.x, self.y + rhs.y, self.z + rhs.z, self.w + rhs.w)
end
function Quat:__sub(rhs)
return Quat:new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z, self.w - rhs.w)
end
function Quat:__mul(rhs)
if type(rhs) == "number" then
return Quat:new(self.x * rhs, self.y * rhs, self.z * rhs, self.w * rhs)
elseif type(rhs) == "table" then
local name = rhs.__name
if name == "Vec3" then
return self:mult_vec3(rhs)
elseif name == "Quat" then
return self:mult_quat(rhs)
else
assert(false, "Unknown usertype of rhs" .. name)
end
else
assert(false, "Unknown type of rhs" .. type(rhs))
end
end
function Quat:__div(rhs)
if type(rhs) == "number" then
return Quat:new(self.x / rhs, self.y / rhs, self.z / rhs, self.w / rhs)
else
assert(rhs.__name == "Quat", "Attempted to divide Quat by unknown type " .. rhs.__name)
return Quat:new(self.x / rhs.x, self.y / rhs.y, self.z / rhs.z, self.w / rhs.w)
end
end
function Quat:__eq(rhs)
return self.x == rhs.x and self.y == rhs.y and self.z == rhs.z and self.w == rhs.w
end
function Quat:__lt(rhs)
return self.x < rhs.x and self.y < rhs.y and self.z < rhs.z and self.w < rhs.w
end
function Quat:__le(rhs)
return self.x <= rhs.x and self.y <= rhs.y and self.z <= rhs.z and self.w <= rhs.w
end
function Quat:__tostring()
return "Quat(" .. self.x .. ", " .. self.y .. ", " .. self.z .. ", " .. self.w .. ")"
end

View File

@ -0,0 +1,94 @@
--require("math.quat")
--require("math.vec3")
Transform = { translation = Vec3.ZERO, rotation = Quat.IDENTITY, scale = Vec3.ONE }
Transform.__index = Transform
Transform.__name = "Transform"
function Transform:new(translation, rotation, scale)
local t = {}
setmetatable(t, Transform)
t.translation = translation
t.rotation = rotation
t.scale = scale
return t
end
function Transform:copy()
return Transform:new(self.translation:copy(), self.rotation:copy(), self.scale:copy())
end
--- Creates a new Transform with the translation at the vec3
--- @param pos Vec3
function Transform:from_vec3(pos)
local t = Transform:copy() -- copy of default transform
t.translation = pos
return t
end
function Transform:from_xyz(x, y, z)
Transform:from_vec3(Vec3:new(x, y, z))
end
--- Calculates the forward vector of the Transform.
--- @return Vec3
function Transform:forward()
return (self.rotation * Vec3.NEG_Z):normalize()
end
--- Calculates the left vector of the Transform.
--- @return Vec3
function Transform:left()
return (self.rotation * Vec3.X):normalize()
end
--- Calculates the up vector of the Transform.
--- @return Vec3
function Transform:up()
return (self.rotation * Vec3.Y):normalize()
end
--- Rotates `self` using a Quaternion
--- @param quat Quat
function Transform:rotate(quat)
self.rotation = (quat * self.rotation):normalize()
end
--- Rotates `self` around the x-axis
--- @param rad number
function Transform:rotate_x(rad)
self:rotate(Quat:from_rotation_x(rad))
end
--- Rotates `self` around the y-axis
--- @param rad number
function Transform:rotate_y(rad)
self:rotate(Quat:from_rotation_y(rad))
end
--- Rotates `self` around the z-axis
--- @param rad number
function Transform:rotate_z(rad)
self:rotate(Quat:from_rotation_z(rad))
end
--- Calculates the linear iterpolation between `self` and `rhs` based on the `alpha`.
--- When `alpha` is `0`, the result will be equal to `self`. When `s` is `1`, the result
--- will be equal to `rhs`
--- @param rhs Transform
--- @param alpha number
--- @return Transform
function Transform:lerp(rhs, alpha)
local res = self:copy()
res.translation = self.translation:lerp(rhs.translation, alpha)
res.rotation = self.rotation:lerp(rhs.rotation, alpha)
res.scale = self.scale:lerp(rhs.scale, alpha)
return res
end
function Transform:__tostring()
return "Transform(pos=" .. tostring(self.translation) .. ", rot="
.. tostring(self.rotation) .. ", scale=" .. tostring(self.scale) .. ")"
end

View File

@ -0,0 +1,161 @@
Vec3 = { x = 0.0, y = 0.0, z = 0.0 }
Vec3.__index = Vec3
Vec3.__name = "Vec3"
--- Constructs a new vector
---@param x number
---@param y number
---@param z number
---@return Vec3
function Vec3:new(x, y, z)
local v = {}
setmetatable(v, Vec3)
v.x = x
v.y = y
v.z = z
return v
end
function Vec3:copy()
return Vec3:new(self.x, self.y, self.z)
end
--- Constructs a vector with all elements as parameter `x`.
---@param x number
---@return Vec3
function Vec3:all(x)
return Vec3:new(x, x, x)
end
--- A unit-length vector pointing alongside the positive X axis.
Vec3.X = Vec3:new(1, 0, 0)
--- A unit-length vector pointing alongside the positive Y axis.
Vec3.Y = Vec3:new(0, 1, 0)
--- A unit-length vector pointing alongside the positive Z axis.
Vec3.Z = Vec3:new(0, 0, 1)
--- A unit-length vector pointing alongside the negative X axis.
Vec3.NEG_X = Vec3:new(-1, 0, 0)
--- A unit-length vector pointing alongside the negative Y axis.
Vec3.NEG_Y = Vec3:new(0, -1, 0)
--- A unit-length vector pointing alongside the negative Z axis.
Vec3.NEG_Z = Vec3:new(0, 0, -1)
--- A vector of all zeros
Vec3.ZERO = Vec3:new(0, 0, 0)
--- A vector of all ones
Vec3.ONE = Vec3:new(1, 1, 1)
--- Computes the absolute value of `self`.
---@return Vec3
function Vec3:abs()
return Vec3:new(math.abs(self.x), math.abs(self.y), math.abs(self.z))
end
--- Computes the length of `self`.
---@return number
function Vec3:length()
return math.sqrt(self:dot(self))
end
--- Computes the dot product of `self` and `rhs`.
---@param rhs Vec3
---@return number
function Vec3:dot(rhs)
assert(rhs.__name == "Vec3")
return (self.x * rhs.x) + (self.y * rhs.y) + (self.z * rhs.z)
end
--- Returns a vector that has the minimum value of each element of `self` and `rhs`
---@param rhs Vec3
---@return Vec3
function Vec3:min(rhs)
local x = math.min(self.x, rhs.x)
local y = math.min(self.y, rhs.y)
local z = math.min(self.z, rhs.z)
return Vec3:new(x, y, z)
end
--- Returns `self` normalized to a length 1.
---@return unknown
function Vec3:normalize()
local len_recip = 1.0 / self:length()
return self * len_recip
end
--- Calculates the linear iterpolation between `self` and `rhs` based on the `alpha`.
--- When `alpha` is `0`, the result will be equal to `self`. When `s` is `1`, the result
--- will be equal to `rhs`
--- @param rhs Vec3
--- @param alpha number
--- @return Vec3
function Vec3:lerp(rhs, alpha)
-- ensure alpha is [0, 1]
local alpha = math.max(0, math.min(1, alpha))
local res = self:copy()
res = res + ((rhs - res) * alpha)
return res
end
function Vec3:__add(rhs)
return Vec3:new(self.x + rhs.x, self.y + rhs.y, self.z + rhs.z)
end
function Vec3:__sub(rhs)
return Vec3:new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z)
end
function Vec3:__mul(rhs)
if type(rhs) == "number" then
return Vec3:new(self.x * rhs, self.y * rhs, self.z * rhs)
else
return Vec3:new(self.x * rhs.x, self.y * rhs.y, self.z * rhs.z)
end
end
function Vec3:__div(rhs)
if type(rhs) == "number" then
return Vec3:new(self.x / rhs, self.y / rhs, self.z / rhs)
else
return Vec3:new(self.x / rhs.x, self.y / rhs.y, self.z / rhs.z)
end
end
function Vec3:__idiv(rhs)
if type(rhs) == "number" then
return Vec3:new(self.x // rhs, self.y // rhs, self.z // rhs)
else
return Vec3:new(self.x // rhs.x, self.y // rhs.y, self.z // rhs.z)
end
end
function Vec3:__unm()
return Vec3:new(-self.x, -self.y, -self.z)
end
function Vec3:__pow(rhs)
if type(rhs) == "number" then
return Vec3:new(self.x ^ rhs, self.y ^ rhs, self.z ^ rhs)
end
end
function Vec3:__eq(rhs)
return self.x == rhs.x and self.y == rhs.y and self.z == rhs.z
end
function Vec3:__lt(rhs)
return self.x < rhs.x and self.y < rhs.y and self.z < rhs.z
end
function Vec3:__le(rhs)
return self.x <= rhs.x and self.y <= rhs.y and self.z <= rhs.z
end
function Vec3:__tostring()
return "Vec3(" .. self.x .. ", " .. self.y .. ", " .. self.z .. ")"
end

View File

@ -1,6 +1,6 @@
use std::collections::HashMap; use std::collections::HashMap;
use lyra_ecs::{ResourceObject, Entity}; use lyra_ecs::{ResourceObject, Entity, World};
use crate::ScriptWorldPtr; use crate::ScriptWorldPtr;
@ -42,6 +42,9 @@ pub trait ScriptApiProvider {
/// The type used as the script's context. /// The type used as the script's context.
type ScriptContext; type ScriptContext;
/// Prepare the ECS world for this api. Things like registering types with the type registry happen here.
fn prepare_world(&mut self, world: &mut World) {}
/// Exposes an API in the provided script context. /// Exposes an API in the provided script context.
fn expose_api(&mut self, ctx: &mut Self::ScriptContext) -> Result<(), ScriptError>; fn expose_api(&mut self, ctx: &mut Self::ScriptContext) -> Result<(), ScriptError>;

View File

@ -2,6 +2,7 @@
pub mod lua; pub mod lua;
pub mod world; pub mod world;
use lyra_ecs::Component;
pub use world::*; pub use world::*;
pub mod wrap; pub mod wrap;
@ -18,9 +19,10 @@ use lyra_game::game::Game;
#[allow(unused_imports)] #[allow(unused_imports)]
pub(crate) mod lyra_engine { pub(crate) mod lyra_engine {
pub use lyra_ecs as ecs; pub use lyra_ecs as ecs;
pub use lyra_reflect as reflect;
} }
use lyra_reflect::{ReflectedComponent, Reflect}; use lyra_reflect::{ReflectedComponent, Reflect, FromType};
#[derive(Clone)] #[derive(Clone)]
pub enum ReflectBranch { pub enum ReflectBranch {
@ -60,6 +62,20 @@ impl Clone for ScriptBorrow {
} }
} }
impl ScriptBorrow {
pub fn from_component<T>(data: Option<T>) -> Self
where
T: Reflect + Component + Default + 'static
{
let data = data.map(|d| Box::new(d) as Box<(dyn Reflect + 'static)>);
Self {
reflect_branch: ReflectBranch::Component(<ReflectedComponent as FromType<T>>::from_type()),
data,
}
}
}
/// An extension trait that adds some helpful methods that makes it easier to do scripting things /// An extension trait that adds some helpful methods that makes it easier to do scripting things
pub trait GameScriptExt { pub trait GameScriptExt {
fn add_script_api_provider<T, P>(&mut self, provider: P) fn add_script_api_provider<T, P>(&mut self, provider: P)
@ -69,12 +85,13 @@ pub trait GameScriptExt {
} }
impl GameScriptExt for Game { impl GameScriptExt for Game {
fn add_script_api_provider<T, P>(&mut self, provider: P) fn add_script_api_provider<T, P>(&mut self, mut provider: P)
where where
T: ScriptHost, T: ScriptHost,
P: ScriptApiProvider<ScriptContext = T::ScriptContext> + 'static P: ScriptApiProvider<ScriptContext = T::ScriptContext> + 'static
{ {
let world = self.world(); let world = self.world();
provider.prepare_world(world);
let mut providers = world.get_resource_mut::<ScriptApiProviders<T>>(); let mut providers = world.get_resource_mut::<ScriptApiProviders<T>>();
providers.add_provider(provider); providers.add_provider(provider);
} }

View File

@ -149,7 +149,7 @@ impl ReflectedIterator {
unsafe { self.reflected_components.as_ref().unwrap().as_ref() }; unsafe { self.reflected_components.as_ref().unwrap().as_ref() };
let reg_type = reflected_components.get_type(id) let reg_type = reflected_components.get_type(id)
.expect("Could not find type for dynamic view!"); .expect("Requested type was not found in TypeRegistry");
let proxy = reg_type.get_data::<ReflectLuaProxy>() let proxy = reg_type.get_data::<ReflectLuaProxy>()
.expect("Type does not have ReflectLuaProxy as a TypeData"); .expect("Type does not have ReflectLuaProxy as a TypeData");

View File

@ -4,7 +4,7 @@ pub use dynamic_iter::*;
pub mod world; pub mod world;
use lyra_game::{plugin::Plugin, game::GameStages}; use lyra_game::{plugin::Plugin, game::GameStages};
use lyra_resource::ResourceManager; use lyra_resource::ResourceManager;
use tracing::{debug, error}; use tracing::{debug, error, trace};
pub use world::*; pub use world::*;
pub mod script; pub mod script;
@ -13,16 +13,16 @@ pub use script::*;
pub mod loader; pub mod loader;
pub use loader::*; pub use loader::*;
pub mod modules; pub mod providers;
pub use modules::*; pub mod wrappers;
#[cfg(test)] #[cfg(test)]
mod test; mod test;
use std::{ptr::NonNull, sync::Mutex}; use std::{ptr::NonNull, sync::Mutex, any::TypeId};
use lyra_ecs::{DynamicBundle, World, query::{ResMut, View, Entities}}; use lyra_ecs::{DynamicBundle, World, query::{ResMut, View, Entities}};
use lyra_reflect::{Reflect, FromType}; use lyra_reflect::{Reflect, FromType, RegisteredType, TypeRegistry};
use mlua::{Lua, AnyUserDataExt}; use mlua::{Lua, AnyUserDataExt};
@ -33,6 +33,35 @@ pub const FN_NAME_INTERNAL_REFLECT: &str = "__lyra_internal_reflect";
use crate::{ScriptBorrow, ScriptDynamicBundle, ScriptApiProviders, ScriptContexts, ScriptWorldPtr, ScriptList, ScriptData, ScriptHost, ScriptError, GameScriptExt}; use crate::{ScriptBorrow, ScriptDynamicBundle, ScriptApiProviders, ScriptContexts, ScriptWorldPtr, ScriptList, ScriptData, ScriptHost, ScriptError, GameScriptExt};
use self::providers::{UtilityApiProvider, LyraMathApiProvider, LyraEcsApiProvider};
pub trait RegisterLuaType {
/// Register a lua type that **is not wrapped**.
fn register_lua_type<'a, T: Reflect + LuaProxy + Clone + mlua::FromLua<'a> + mlua::UserData>(&mut self);
/// Registers a wrapped lua type.
/// You provide the wrapper as `W`, and the type that the wrapper wraps, as `T`.
fn register_lua_wrapper<'a, W: Reflect + LuaProxy + LuaWrapper + Clone + mlua::FromLua<'a> + mlua::UserData>(&mut self);
}
impl RegisterLuaType for World {
fn register_lua_type<'a, T: Reflect + LuaProxy + Clone + mlua::FromLua<'a> + mlua::UserData>(&mut self) {
let mut registry = self.get_resource_mut::<TypeRegistry>();
let type_id = TypeId::of::<T>();
let reg_type = registry.get_type_or_default(type_id);
reg_type.add_data(<ReflectLuaProxy as FromType<T>>::from_type());
//reg_type.add_data(<ReflectedComponent as FromType<T>>::from_type());
}
fn register_lua_wrapper<'a, W: Reflect + LuaProxy + LuaWrapper + Clone + mlua::FromLua<'a> + mlua::UserData>(&mut self) {
let mut registry = self.get_resource_mut::<TypeRegistry>();
let reg_type = registry.get_type_or_default(W::wrapped_type_id());
reg_type.add_data(<ReflectLuaProxy as FromType<W>>::from_type());
}
}
impl<'lua> mlua::FromLua<'lua> for ScriptBorrow { impl<'lua> mlua::FromLua<'lua> for ScriptBorrow {
fn from_lua(value: mlua::Value<'lua>, _lua: &'lua Lua) -> mlua::Result<Self> { fn from_lua(value: mlua::Value<'lua>, _lua: &'lua Lua) -> mlua::Result<Self> {
match value { match value {
@ -49,6 +78,11 @@ pub fn reflect_user_data(ud: &mlua::AnyUserData) -> ScriptBorrow {
.expect("Type does not implement '__internal_reflect' properly") .expect("Type does not implement '__internal_reflect' properly")
} }
pub trait LuaWrapper {
/// The type id of the wrapped type.
fn wrapped_type_id() -> TypeId;
}
pub trait LuaProxy { pub trait LuaProxy {
fn as_lua_value<'lua>(lua: &'lua mlua::Lua, this: &dyn Reflect) -> mlua::Result<mlua::AnyUserData<'lua>>; fn as_lua_value<'lua>(lua: &'lua mlua::Lua, this: &dyn Reflect) -> mlua::Result<mlua::AnyUserData<'lua>>;
fn apply(lua: &mlua::Lua, this: &mut dyn Reflect, apply: &mlua::AnyUserData) -> mlua::Result<()>; fn apply(lua: &mlua::Lua, this: &mut dyn Reflect, apply: &mlua::AnyUserData) -> mlua::Result<()>;
@ -176,7 +210,7 @@ fn lua_call_script_function(world: &mut World, stage_name: &str) -> anyhow::Resu
}; };
if let Some(ctx) = contexts.get_context_mut(script.id()) { if let Some(ctx) = contexts.get_context_mut(script.id()) {
debug!("Running '{}' function in script '{}'", stage_name, script.name()); trace!("Running '{}' function in script '{}'", stage_name, script.name());
match host.call_script(world_ptr.clone(), &script_data, ctx, &mut providers, stage_name) { match host.call_script(world_ptr.clone(), &script_data, ctx, &mut providers, stage_name) {
Ok(()) => {}, Ok(()) => {},
Err(e) => match e { Err(e) => match e {
@ -230,6 +264,8 @@ impl Plugin for LuaScriptingPlugin {
fn setup(&self, game: &mut lyra_game::game::Game) { fn setup(&self, game: &mut lyra_game::game::Game) {
let world = game.world(); let world = game.world();
world.add_resource_default::<TypeRegistry>();
world.add_resource_default::<LuaHost>(); world.add_resource_default::<LuaHost>();
world.add_resource_default::<ScriptApiProviders<LuaHost>>(); world.add_resource_default::<ScriptApiProviders<LuaHost>>();
world.add_resource_default::<ScriptContexts<LuaContext>>(); world.add_resource_default::<ScriptContexts<LuaContext>>();
@ -240,6 +276,8 @@ impl Plugin for LuaScriptingPlugin {
drop(loader); drop(loader);
game.add_script_api_provider::<LuaHost, _>(UtilityApiProvider); game.add_script_api_provider::<LuaHost, _>(UtilityApiProvider);
game.add_script_api_provider::<LuaHost, _>(LyraEcsApiProvider);
game.add_script_api_provider::<LuaHost, _>(LyraMathApiProvider);
game game
.add_system_to_stage(GameStages::First, "lua_create_contexts", lua_scripts_create_contexts, &[]) .add_system_to_stage(GameStages::First, "lua_create_contexts", lua_scripts_create_contexts, &[])

View File

@ -0,0 +1,26 @@
use crate::{lua::LuaContext, ScriptApiProvider, ScriptWorldPtr, ScriptDynamicBundle};
#[derive(Default)]
pub struct LyraEcsApiProvider;
impl ScriptApiProvider for LyraEcsApiProvider {
type ScriptContext = LuaContext;
fn expose_api(&mut self, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> {
let ctx = ctx.lock().unwrap();
let globals = ctx.globals();
globals.set("World", ctx.create_proxy::<ScriptWorldPtr>()?)?;
globals.set("DynamicBundle", ctx.create_proxy::<ScriptDynamicBundle>()?)?;
Ok(())
}
fn setup_script(&mut self, data: &crate::ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> {
Ok(())
}
fn update_script_environment(&mut self, world: crate::ScriptWorldPtr, data: &crate::ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> {
Ok(())
}
}

View File

@ -0,0 +1,108 @@
use lyra_ecs::World;
use lyra_game::math;
use crate::lua::RegisterLuaType;
use crate::lua::wrappers::LuaVec3;
use crate::{ScriptApiProvider, lua::LuaContext};
#[derive(Default)]
pub struct LyraMathApiProvider;
impl ScriptApiProvider for LyraMathApiProvider {
type ScriptContext = LuaContext;
fn prepare_world(&mut self, world: &mut World) {
world.register_lua_wrapper::<LuaVec3>();
}
fn expose_api(&mut self, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> {
let ctx = ctx.lock().unwrap();
/* let bytes = include_bytes!("../../../scripts/lua/math/vec3.lua");
ctx.load(bytes.to_vec()).exec()?;
let bytes = include_bytes!("../../../scripts/lua/math/quat.lua");
ctx.load(bytes.to_vec()).exec()?;
let bytes = include_bytes!("../../../scripts/lua/math/transform.lua");
ctx.load(bytes.to_vec()).exec()?; */
let globals = ctx.globals();
globals.set("Vec3", ctx.create_proxy::<LuaVec3>()?)?;
//globals.set("Vec3", LuaVec3(math::Vec3::ZERO).into_lua(&ctx)?)?;
Ok(())
}
fn setup_script(&mut self, data: &crate::ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> {
Ok(())
}
fn update_script_environment(&mut self, world: crate::ScriptWorldPtr, data: &crate::ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> {
Ok(())
}
}
/* #[derive(Clone, Copy, PartialEq, Debug, lyra_reflect::Reflect)]
pub struct LuaVec3(#[reflect(skip)] math::Vec3);
impl From<math::Vec3> for LuaVec3 {
fn from(value: math::Vec3) -> Self {
Self(value)
}
}
impl<'lua> mlua::IntoLua<'lua> for LuaVec3 {
fn into_lua(self, lua: &'lua mlua::prelude::Lua) -> mlua::prelude::LuaResult<mlua::prelude::LuaValue<'lua>> {
let globals = lua.globals();
let v3 = globals.get::<_, mlua::Table>("Vec3")?;
let v3_new = v3.get::<_, mlua::Function>("new")?;
v3_new.call::<_, mlua::Table>((v3, self.0.x, self.0.y, self.0.z))
.and_then(|t| t.into_lua(lua))
}
}
#[derive(Clone, Copy, PartialEq, Debug, lyra_reflect::Reflect)]
pub struct LuaQuat(#[reflect(skip)] math::Quat);
impl From<math::Quat> for LuaQuat {
fn from(value: math::Quat) -> Self {
Self(value)
}
}
impl<'lua> mlua::IntoLua<'lua> for LuaQuat {
fn into_lua(self, lua: &'lua mlua::prelude::Lua) -> mlua::prelude::LuaResult<mlua::prelude::LuaValue<'lua>> {
let globals = lua.globals();
let q = globals.get::<_, mlua::Table>("Quat")?;
let q_new = q.get::<_, mlua::Function>("new")?;
q_new.call::<_, mlua::Table>((q, self.0.x, self.0.y, self.0.z, self.0.w))
.and_then(|t| t.into_lua(lua))
}
}
#[derive(Clone, Copy, Debug, lyra_reflect::Reflect)]
pub struct LuaTransform(#[reflect(skip)] math::Transform);
impl From<math::Transform> for LuaTransform {
fn from(value: math::Transform) -> Self {
Self(value)
}
}
impl<'lua> mlua::IntoLua<'lua> for LuaTransform {
fn into_lua(self, lua: &'lua mlua::prelude::Lua) -> mlua::prelude::LuaResult<mlua::prelude::LuaValue<'lua>> {
let globals = lua.globals();
let translation = LuaVec3(self.0.translation).into_lua(lua)?;
let rot = LuaQuat(self.0.rotation).into_lua(lua)?;
let scale = LuaVec3(self.0.scale).into_lua(lua)?;
let transf = globals.get::<_, mlua::Table>("Transform")?;
let transf_new = transf.get::<_, mlua::Function>("new")?;
transf_new.call::<_, mlua::Table>((transf, translation, rot, scale))
.and_then(|t| t.into_lua(lua))
}
} */

View File

@ -0,0 +1,8 @@
pub mod util;
pub use util::*;
pub mod math;
pub use math::*;
pub mod ecs;
pub use ecs::*;

View File

@ -1,7 +1,7 @@
use std::sync::Mutex; use std::sync::Mutex;
use mlua::IntoLua; use mlua::IntoLua;
use tracing::debug; use tracing::{debug, trace};
use crate::{ScriptHost, ScriptError, ScriptWorldPtr, ScriptEntity}; use crate::{ScriptHost, ScriptError, ScriptWorldPtr, ScriptEntity};
@ -17,7 +17,7 @@ fn try_call_lua_function(lua: &mlua::Lua, fn_name: &str) -> Result<(), ScriptErr
.map_err(ScriptError::MluaError)?; .map_err(ScriptError::MluaError)?;
}, },
Err(mlua::Error::FromLuaConversionError { from: "nil", to: "function", message: None }) => { Err(mlua::Error::FromLuaConversionError { from: "nil", to: "function", message: None }) => {
debug!("Function '{}' was not found, ignoring...", fn_name) trace!("Function '{}' was not found, ignoring...", fn_name)
// ignore // ignore
}, },
Err(e) => { Err(e) => {

View File

@ -1,3 +1,5 @@
use std::sync::Arc;
use lyra_ecs::query::dynamic::QueryDynamicType; use lyra_ecs::query::dynamic::QueryDynamicType;
use lyra_reflect::TypeRegistry; use lyra_reflect::TypeRegistry;
use mlua::{AnyUserDataExt, IntoLua, IntoLuaMulti}; use mlua::{AnyUserDataExt, IntoLua, IntoLuaMulti};
@ -73,7 +75,9 @@ impl mlua::UserData for ScriptWorldPtr {
methods.add_method("view", |lua, this, (system, queries): (mlua::Function, mlua::Variadic<mlua::AnyUserData>)| { methods.add_method("view", |lua, this, (system, queries): (mlua::Function, mlua::Variadic<mlua::AnyUserData>)| {
if queries.is_empty() { if queries.is_empty() {
panic!("No components were provided!"); return Err(mlua::Error::BadArgument { to: Some("world:view".to_string()), pos: 2, name: Some("...".to_string()), cause:
Arc::new(mlua::Error::external(WorldError::LuaInvalidUsage("no component types provided".to_string())))
});
} }
let world = unsafe { this.inner.as_ref() }; let world = unsafe { this.inner.as_ref() };

View File

@ -0,0 +1,365 @@
use lyra_game::math;
use lyra_scripting_derive::wrap_math_vec_copy;
use crate::lyra_engine;
use crate as lyra_scripting;
// f32 types
/* wrap_math_vec_copy!(
math::Vec2,
derives(PartialEq),
fields(x, y),
metamethods(
Add(LuaVec2, f32),
Sub(LuaVec2, f32),
Div(LuaVec2, f32),
Mul(LuaVec2, f32),
Mod(LuaVec2, f32),
Eq, Unm
)
); */
wrap_math_vec_copy!(
math::Vec3,
derives(PartialEq),
fields(x, y, z),
metamethods(
ToString,
Eq, Unm
)
/* metamethods(
Add(LuaVec3, f32),
Sub(LuaVec3, f32),
Div(LuaVec3, f32),
Mul(LuaVec3, f32),
Mod(LuaVec3, f32),
Eq, Unm
) */
);
/* wrap_math_vec_copy!(
math::Vec3A,
derives(PartialEq),
fields(x, y, z),
metamethods(
Add(LuaVec3A, f32),
Sub(LuaVec3A, f32),
Div(LuaVec3A, f32),
Mul(LuaVec3A, f32),
Mod(LuaVec3A, f32),
Eq, Unm
)
);
wrap_math_vec_copy!(
math::Vec4,
derives(PartialEq),
fields(w, x, y, z),
metamethods(
Add(LuaVec4, f32),
Sub(LuaVec4, f32),
Div(LuaVec4, f32),
Mul(LuaVec4, f32),
Mod(LuaVec4, f32),
Eq, Unm
)
);
// f64 types
wrap_math_vec_copy!(
math::DVec2,
derives(PartialEq),
fields(x, y),
metamethods(
Add(LuaDVec2, f64),
Sub(LuaDVec2, f64),
Div(LuaDVec2, f64),
Mul(LuaDVec2, f64),
Mod(LuaDVec2, f64),
Eq, Unm
)
);
wrap_math_vec_copy!(
math::DVec3,
derives(PartialEq),
fields(x, y, z),
metamethods(
Add(LuaDVec3, f64),
Sub(LuaDVec3, f64),
Div(LuaDVec3, f64),
Mul(LuaDVec3, f64),
Mod(LuaDVec3, f64),
Eq, Unm
)
);
wrap_math_vec_copy!(
math::DVec4,
derives(PartialEq),
fields(w, x, y, z),
metamethods(
Add(LuaDVec4, f64),
Sub(LuaDVec4, f64),
Div(LuaDVec4, f64),
Mul(LuaDVec4, f64),
Mod(LuaDVec4, f64),
Eq, Unm
)
);
// i32 types
wrap_math_vec_copy!(
math::IVec2,
derives(PartialEq, Eq, Hash),
fields(x, y),
metamethods(
Add(LuaIVec2, i32),
Sub(LuaIVec2, i32),
Div(LuaIVec2, i32),
Mul(LuaIVec2, i32),
Mod(LuaIVec2, i32),
Shl(LuaIVec2, LuaUVec2, i32),
Shr(LuaIVec2, LuaUVec2, i32),
BAnd(LuaIVec2, i32),
BOr(LuaIVec2, i32),
BXor(LuaIVec2, i32),
Eq, Unm, BNot
)
);
wrap_math_vec_copy!(
math::IVec3,
derives(PartialEq, Eq, Hash),
fields(x, y, z),
metamethods(
Add(LuaIVec3, i32),
Sub(LuaIVec3, i32),
Div(LuaIVec3, i32),
Mul(LuaIVec3, i32),
Mod(LuaIVec3, i32),
Shl(LuaIVec3, LuaUVec3, i32),
Shr(LuaIVec3, LuaUVec3, i32),
BAnd(LuaIVec3, i32),
BOr(LuaIVec3, i32),
BXor(LuaIVec3, i32),
Eq, Unm, BNot
)
);
wrap_math_vec_copy!(
math::IVec4,
derives(PartialEq, Eq, Hash),
fields(w, x, y, z),
metamethods(
Add(LuaIVec4, i32),
Sub(LuaIVec4, i32),
Div(LuaIVec4, i32),
Mul(LuaIVec4, i32),
Mod(LuaIVec4, i32),
Shl(LuaIVec4, LuaUVec4, i32),
Shr(LuaIVec4, LuaUVec4, i32),
BAnd(LuaIVec4, i32),
BOr(LuaIVec4, i32),
BXor(LuaIVec4, i32),
Eq, Unm, BNot
)
);
// u32 types
wrap_math_vec_copy!(
math::UVec2,
derives(PartialEq, Eq, Hash),
fields(x, y),
metamethods(
Add(LuaUVec2, u32),
Sub(LuaUVec2, u32),
Div(LuaUVec2, u32),
Mul(LuaUVec2, u32),
Mod(LuaUVec2, u32),
Shl(LuaUVec2, LuaIVec2, i32),
Shr(LuaUVec2, LuaIVec2, i32),
BAnd(LuaUVec2, u32),
BOr(LuaUVec2, u32),
BXor(LuaUVec2, u32),
Eq, BNot
)
);
wrap_math_vec_copy!(
math::UVec3,
derives(PartialEq, Eq, Hash),
fields(x, y, z),
metamethods(
Add(LuaUVec3, u32),
Sub(LuaUVec3, u32),
Div(LuaUVec3, u32),
Mul(LuaUVec3, u32),
Mod(LuaUVec3, u32),
Shl(LuaUVec3, LuaIVec3, i32),
Shr(LuaUVec3, LuaIVec3, i32),
BAnd(LuaUVec3, u32),
BOr(LuaUVec3, u32),
BXor(LuaUVec3, u32),
Eq, BNot
)
);
wrap_math_vec_copy!(
math::UVec4,
derives(PartialEq, Eq, Hash),
fields(w, x, y, z),
metamethods(
Add(LuaUVec4, u32),
Sub(LuaUVec4, u32),
Div(LuaUVec4, u32),
Mul(LuaUVec4, u32),
Mod(LuaUVec4, u32),
Shl(LuaUVec4, LuaIVec4, i32),
Shr(LuaUVec4, LuaIVec4, i32),
BAnd(LuaUVec4, u32),
BOr(LuaUVec4, u32),
BXor(LuaUVec4, u32),
Eq, BNot
)
);
// i64 types
wrap_math_vec_copy!(
math::I64Vec2,
derives(PartialEq, Eq, Hash),
fields(x, y),
metamethods(
Add(LuaI64Vec2, i64),
Sub(LuaI64Vec2, i64),
Div(LuaI64Vec2, i64),
Mul(LuaI64Vec2, i64),
Mod(LuaI64Vec2, i64),
Shl(i64),
Shr(i64),
BAnd(LuaI64Vec2, i64),
BOr(LuaI64Vec2, i64),
BXor(LuaI64Vec2, i64),
Eq, BNot
)
);
wrap_math_vec_copy!(
math::I64Vec3,
derives(PartialEq, Eq, Hash),
fields(x, y, z),
metamethods(
Add(LuaI64Vec3, i64),
Sub(LuaI64Vec3, i64),
Div(LuaI64Vec3, i64),
Mul(LuaI64Vec3, i64),
Mod(LuaI64Vec3, i64),
Shl(i64),
Shr(i64),
BAnd(LuaI64Vec3, i64),
BOr(LuaI64Vec3, i64),
BXor(LuaI64Vec3, i64),
Eq, BNot
)
);
wrap_math_vec_copy!(
math::I64Vec4,
derives(PartialEq, Eq, Hash),
fields(w, x, y, z),
metamethods(
Add(LuaI64Vec4, i64),
Sub(LuaI64Vec4, i64),
Div(LuaI64Vec4, i64),
Mul(LuaI64Vec4, i64),
Mod(LuaI64Vec4, i64),
Shl(i64),
Shr(i64),
BAnd(LuaI64Vec4, i64),
BOr(LuaI64Vec4, i64),
BXor(LuaI64Vec4, i64),
Eq, BNot
)
);
// u64 types
wrap_math_vec_copy!(
math::U64Vec2,
derives(PartialEq, Eq, Hash),
fields(x, y),
metamethods(
Add(LuaU64Vec2, u64),
Sub(LuaU64Vec2, u64),
Div(LuaU64Vec2, u64),
Mul(LuaU64Vec2, u64),
Mod(LuaU64Vec2, u64),
Shl(i64),
Shr(i64),
BAnd(LuaU64Vec2, u64),
BOr(LuaU64Vec2, u64),
BXor(LuaU64Vec2, u64),
Eq, BNot
)
);
wrap_math_vec_copy!(
math::U64Vec3,
derives(PartialEq, Eq, Hash),
fields(x, y, z),
metamethods(
Add(LuaU64Vec3, u64),
Sub(LuaU64Vec3, u64),
Div(LuaU64Vec3, u64),
Mul(LuaU64Vec3, u64),
Mod(LuaU64Vec3, u64),
Shl(i64),
Shr(i64),
BAnd(LuaU64Vec3, u64),
BOr(LuaU64Vec3, u64),
BXor(LuaU64Vec3, u64),
Eq, BNot
)
);
wrap_math_vec_copy!(
math::U64Vec4,
derives(PartialEq, Eq, Hash),
fields(w, x, y, z),
metamethods(
Add(LuaU64Vec4, u64),
Sub(LuaU64Vec4, u64),
Div(LuaU64Vec4, u64),
Mul(LuaU64Vec4, u64),
Mod(LuaU64Vec4, u64),
Shl(i64),
Shr(i64),
BAnd(LuaU64Vec4, u64),
BOr(LuaU64Vec4, u64),
BXor(LuaU64Vec4, u64),
Eq, BNot
)
);
// bool types
wrap_math_vec_copy!(
math::BVec2,
derives(PartialEq, Eq, Hash),
fields(x, y),
metamethods(Eq, BAnd, BOr, BXor, BNot)
);
wrap_math_vec_copy!(
math::BVec3,
derives(PartialEq, Eq, Hash),
fields(x, y, z),
metamethods(Eq, BAnd, BOr, BXor, BNot)
);
wrap_math_vec_copy!(
math::BVec4,
derives(PartialEq, Eq, Hash),
fields(w, x, y, z),
metamethods(Eq, BAnd, BOr, BXor, BNot)
);
// mat2
wrap_math_vec_copy!(
math::Mat2,
derives(PartialEq),
no_new,
matrix {
col_type = LuaVec2
},
metamethods(
Eq,
Add,
Sub,
Mul(LuaMat2, f32),
Unm
)
); */