Compare commits
5 Commits
e49d69dbc1
...
d14abcc3e5
Author | SHA1 | Date |
---|---|---|
SeanOMik | d14abcc3e5 | |
SeanOMik | acfd238274 | |
SeanOMik | 544aee4a31 | |
SeanOMik | eb44aba3dc | |
SeanOMik | 29467faf55 |
|
@ -5,10 +5,14 @@ edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[features]
|
||||||
|
math = ["dep:glam"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
lyra-ecs-derive = { path = "./lyra-ecs-derive" }
|
lyra-ecs-derive = { path = "./lyra-ecs-derive" }
|
||||||
anyhow = "1.0.75"
|
anyhow = "1.0.75"
|
||||||
thiserror = "1.0.50"
|
thiserror = "1.0.50"
|
||||||
|
glam = { version = "0.24.0", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rand = "0.8.5" # used for tests
|
rand = "0.8.5" # used for tests
|
||||||
|
|
|
@ -34,6 +34,10 @@ pub mod system;
|
||||||
pub mod tick;
|
pub mod tick;
|
||||||
pub use tick::*;
|
pub use tick::*;
|
||||||
|
|
||||||
|
/// Implements Component for glam math types
|
||||||
|
#[cfg(feature = "math")]
|
||||||
|
pub mod math;
|
||||||
|
|
||||||
pub use lyra_ecs_derive::*;
|
pub use lyra_ecs_derive::*;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
use crate::Component;
|
||||||
|
|
||||||
|
use glam::{Vec3, Quat, Vec2, Vec4, Mat4};
|
||||||
|
|
||||||
|
macro_rules! impl_component_math {
|
||||||
|
($type: ident) => {
|
||||||
|
impl Component for $type {
|
||||||
|
fn name() -> &'static str {
|
||||||
|
stringify!($type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_component_math!(Vec2);
|
||||||
|
impl_component_math!(Vec3);
|
||||||
|
impl_component_math!(Vec4);
|
||||||
|
impl_component_math!(Mat4);
|
||||||
|
impl_component_math!(Quat);
|
|
@ -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"
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -5,6 +5,10 @@ edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[features]
|
||||||
|
math = ["dep:glam"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
lyra-reflect-derive = { path = "lyra-reflect-derive" }
|
lyra-reflect-derive = { path = "lyra-reflect-derive" }
|
||||||
lyra-ecs = { path = "../lyra-ecs" }
|
lyra-ecs = { path = "../lyra-ecs" }
|
||||||
|
glam = { version = "0.24.0", optional = true }
|
|
@ -32,7 +32,7 @@ impl From<&Variant> for VariantType {
|
||||||
|
|
||||||
/// Generates the following different outputs:
|
/// Generates the following different outputs:
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```compile_fail
|
||||||
/// // for struct variants
|
/// // for struct variants
|
||||||
/// TestEnum::Error { msg, code }
|
/// TestEnum::Error { msg, code }
|
||||||
///
|
///
|
||||||
|
@ -98,7 +98,7 @@ fn gen_variant_if(enum_id: &proc_macro2::Ident, variant: &Variant, if_body: proc
|
||||||
|
|
||||||
/// Generates the following:
|
/// Generates the following:
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```compile_fail
|
||||||
/// /// generated one field here
|
/// /// generated one field here
|
||||||
/// if name == "msg" {
|
/// if name == "msg" {
|
||||||
/// return Some(msg);
|
/// return Some(msg);
|
||||||
|
@ -113,7 +113,7 @@ fn gen_variant_if(enum_id: &proc_macro2::Ident, variant: &Variant, if_body: proc
|
||||||
fn gen_if_field_names(variant: &Variant) -> proc_macro2::TokenStream {
|
fn gen_if_field_names(variant: &Variant) -> proc_macro2::TokenStream {
|
||||||
let field_ifs = variant.fields.iter().map(|field| {
|
let field_ifs = variant.fields.iter().map(|field| {
|
||||||
let id = field.ident.as_ref().unwrap();
|
let id = field.ident.as_ref().unwrap();
|
||||||
let id_str = id.span().source_text().unwrap();
|
let id_str = id.to_string();
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
if name == #id_str {
|
if name == #id_str {
|
||||||
|
@ -129,7 +129,7 @@ fn gen_if_field_names(variant: &Variant) -> proc_macro2::TokenStream {
|
||||||
|
|
||||||
/// Generates the following rust code:
|
/// Generates the following rust code:
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```compile_fail
|
||||||
/// match name {
|
/// match name {
|
||||||
/// "msg" | "code" => true,
|
/// "msg" | "code" => true,
|
||||||
/// _ => false,
|
/// _ => false,
|
||||||
|
@ -140,7 +140,7 @@ fn gen_match_names(variant: &Variant) -> proc_macro2::TokenStream {
|
||||||
let field_name_strs = variant.fields.iter().map(|field| {
|
let field_name_strs = variant.fields.iter().map(|field| {
|
||||||
let id = field.ident.as_ref()
|
let id = field.ident.as_ref()
|
||||||
.expect("Could not find identifier for enum field!");
|
.expect("Could not find identifier for enum field!");
|
||||||
id.span().source_text().unwrap()
|
id.to_string()
|
||||||
});
|
});
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
|
@ -153,7 +153,7 @@ fn gen_match_names(variant: &Variant) -> proc_macro2::TokenStream {
|
||||||
|
|
||||||
/// Generates the following:
|
/// Generates the following:
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```compile_fail
|
||||||
/// /// generated one field here
|
/// /// generated one field here
|
||||||
/// if idx == 0 {
|
/// if idx == 0 {
|
||||||
/// return Some(a);
|
/// return Some(a);
|
||||||
|
@ -190,7 +190,7 @@ fn gen_if_field_indices(variant: &Variant) -> proc_macro2::TokenStream {
|
||||||
|
|
||||||
/// Generates the following:
|
/// Generates the following:
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```compile_fail
|
||||||
/// /// generated one field here
|
/// /// generated one field here
|
||||||
/// if idx == 0 {
|
/// if idx == 0 {
|
||||||
/// return Some("a");
|
/// return Some("a");
|
||||||
|
@ -226,7 +226,7 @@ fn gen_if_field_indices_names(variant: &Variant) -> proc_macro2::TokenStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates the following:
|
/// Generates the following:
|
||||||
/// ```rust
|
/// ```compile_fail
|
||||||
/// /// when `by_index` is false:
|
/// /// when `by_index` is false:
|
||||||
///
|
///
|
||||||
/// if let TestEnum::Error{ msg, code} = self {
|
/// if let TestEnum::Error{ msg, code} = self {
|
||||||
|
@ -292,7 +292,6 @@ fn gen_enum_if_stmts(enum_id: &proc_macro2::Ident, data: &DataEnum, by_index: bo
|
||||||
_ => quote! { },
|
_ => quote! { },
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
println!("====");
|
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
#( #struct_vars )*
|
#( #struct_vars )*
|
||||||
|
@ -301,7 +300,7 @@ fn gen_enum_if_stmts(enum_id: &proc_macro2::Ident, data: &DataEnum, by_index: bo
|
||||||
|
|
||||||
/// Generates the following rust code:
|
/// Generates the following rust code:
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```compile_fail
|
||||||
/// if let TestEnum::Error { msg, code } = self {
|
/// if let TestEnum::Error { msg, code } = self {
|
||||||
/// return match name {
|
/// return match name {
|
||||||
/// // expands for continuing struct fields
|
/// // expands for continuing struct fields
|
||||||
|
@ -332,7 +331,7 @@ fn gen_enum_has_field(enum_id: &proc_macro2::Ident, data: &DataEnum) -> proc_mac
|
||||||
|
|
||||||
/// Generates the following code:
|
/// Generates the following code:
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```compile_fail
|
||||||
/// match self {
|
/// match self {
|
||||||
/// TestEnum::Start => 0,
|
/// TestEnum::Start => 0,
|
||||||
/// TestEnum::Middle(a, b) => 2,
|
/// TestEnum::Middle(a, b) => 2,
|
||||||
|
@ -359,7 +358,7 @@ fn gen_enum_fields_len(enum_id: &proc_macro2::Ident, data: &DataEnum) -> proc_ma
|
||||||
|
|
||||||
/// Generates the following code:
|
/// Generates the following code:
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```compile_fail
|
||||||
/// if let TestEnum::Error { msg, code } = self {
|
/// if let TestEnum::Error { msg, code } = self {
|
||||||
/// if idx == 0 {
|
/// if idx == 0 {
|
||||||
/// return Some("msg");
|
/// return Some("msg");
|
||||||
|
@ -390,7 +389,7 @@ fn gen_enum_field_name_at(enum_id: &proc_macro2::Ident, data: &DataEnum) -> proc
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates the following code:
|
/// Generates the following code:
|
||||||
/// ```rust
|
/// ```compile_fail
|
||||||
/// match self {
|
/// match self {
|
||||||
/// TestEnum::Start => 0,
|
/// TestEnum::Start => 0,
|
||||||
/// TestEnum::Middle(a, b) => 1,
|
/// TestEnum::Middle(a, b) => 1,
|
||||||
|
@ -428,7 +427,7 @@ fn gen_enum_variant_name(enum_id: &proc_macro2::Ident, data: &DataEnum, gen_inde
|
||||||
/// Generates a match statement that returns the types of the variants of the enum.
|
/// Generates a match statement that returns the types of the variants of the enum.
|
||||||
///
|
///
|
||||||
/// Example:
|
/// Example:
|
||||||
/// ```rust
|
/// ```compile_fail
|
||||||
/// match self {
|
/// match self {
|
||||||
/// TestEnum::Start => EnumType::Unit,
|
/// TestEnum::Start => EnumType::Unit,
|
||||||
/// TestEnum::Middle(a, b) => EnumType::Tuple,
|
/// TestEnum::Middle(a, b) => EnumType::Tuple,
|
||||||
|
@ -458,41 +457,35 @@ fn gen_enum_variant_type(enum_id: &proc_macro2::Ident, data: &DataEnum) -> proc_
|
||||||
/// Create a reflect implementation for an enum
|
/// Create a reflect implementation for an enum
|
||||||
pub fn derive_reflect_enum(input: &DeriveInput, data_enum: &DataEnum) -> proc_macro2::TokenStream {
|
pub fn derive_reflect_enum(input: &DeriveInput, data_enum: &DataEnum) -> proc_macro2::TokenStream {
|
||||||
|
|
||||||
let type_path = &input.ident;
|
let input_ident = &input.ident;
|
||||||
let name = type_path.span().source_text().unwrap();
|
let ident_str = input.ident.to_string();
|
||||||
//println!("Got type path: {}", type_path);
|
|
||||||
|
|
||||||
let variant_count = data_enum.variants.len();
|
let variant_count = data_enum.variants.len();
|
||||||
|
|
||||||
/* let mut variants_iter = data_enum.variants.iter();
|
let field_ifs = gen_enum_if_stmts(input_ident, data_enum, false);
|
||||||
|
let field_mut_ifs = gen_enum_if_stmts(input_ident, data_enum, false);
|
||||||
|
|
||||||
let variant = variants_iter.next().unwrap();
|
let field_at_ifs = gen_enum_if_stmts(input_ident, data_enum, true);
|
||||||
let variant_name = &variant.ident; */
|
let field_at_mut_ifs = gen_enum_if_stmts(input_ident, data_enum, true);
|
||||||
|
|
||||||
let field_ifs = gen_enum_if_stmts(type_path, data_enum, false);
|
let has_field = gen_enum_has_field(input_ident, data_enum);
|
||||||
let field_mut_ifs = gen_enum_if_stmts(type_path, data_enum, false);
|
let field_len = gen_enum_fields_len(input_ident, data_enum);
|
||||||
|
let field_name_at = gen_enum_field_name_at(input_ident, data_enum);
|
||||||
let field_at_ifs = gen_enum_if_stmts(type_path, data_enum, true);
|
let variant_name_match = gen_enum_variant_name(input_ident, data_enum, false);
|
||||||
let field_at_mut_ifs = gen_enum_if_stmts(type_path, data_enum, true);
|
let variant_idx_match = gen_enum_variant_name(input_ident, data_enum, true);
|
||||||
|
let variant_type = gen_enum_variant_type(input_ident, data_enum);
|
||||||
let has_field = gen_enum_has_field(type_path, data_enum);
|
|
||||||
let field_len = gen_enum_fields_len(type_path, data_enum);
|
|
||||||
let field_name_at = gen_enum_field_name_at(type_path, data_enum);
|
|
||||||
let variant_name_match = gen_enum_variant_name(type_path, data_enum, false);
|
|
||||||
let variant_idx_match = gen_enum_variant_name(type_path, data_enum, true);
|
|
||||||
let variant_type = gen_enum_variant_type(type_path, data_enum);
|
|
||||||
|
|
||||||
let generics = add_trait_bounds(input.generics.clone(), vec![parse_quote!(Reflect), parse_quote!(Clone)]);
|
let generics = add_trait_bounds(input.generics.clone(), vec![parse_quote!(Reflect), parse_quote!(Clone)]);
|
||||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
return proc_macro2::TokenStream::from(quote! {
|
return proc_macro2::TokenStream::from(quote! {
|
||||||
impl #impl_generics lyra_engine::reflect::Reflect for #type_path #ty_generics #where_clause {
|
impl #impl_generics lyra_engine::reflect::Reflect for #input_ident #ty_generics #where_clause {
|
||||||
fn name(&self) -> ::std::string::String {
|
fn name(&self) -> ::std::string::String {
|
||||||
#name.to_string()
|
#ident_str.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn type_id(&self) -> std::any::TypeId {
|
fn type_id(&self) -> std::any::TypeId {
|
||||||
std::any::TypeId::of::<#type_path #ty_generics>()
|
std::any::TypeId::of::<#input_ident #ty_generics>()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_any(&self) -> &dyn std::any::Any {
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
|
@ -530,7 +523,7 @@ pub fn derive_reflect_enum(input: &DeriveInput, data_enum: &DataEnum) -> proc_ma
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl #impl_generics lyra_engine::reflect::Enum for #type_path #ty_generics #where_clause {
|
impl #impl_generics lyra_engine::reflect::Enum for #input_ident #ty_generics #where_clause {
|
||||||
fn field(&self, name: &str) -> Option<&dyn lyra_engine::reflect::Reflect> {
|
fn field(&self, name: &str) -> Option<&dyn lyra_engine::reflect::Reflect> {
|
||||||
let name = name.to_lowercase();
|
let name = name.to_lowercase();
|
||||||
let name = name.as_str();
|
let name = name.as_str();
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
|
use proc_macro2::Ident;
|
||||||
use quote::{quote, ToTokens};
|
use quote::{quote, ToTokens};
|
||||||
use syn::{Generics, Path, Attribute, GenericParam, parse_macro_input, DeriveInput, TypeParamBound};
|
use syn::{Generics, Path, Attribute, GenericParam, parse_macro_input, DeriveInput, TypeParamBound};
|
||||||
|
|
||||||
|
@ -10,8 +11,55 @@ mod struct_derive;
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use struct_derive::*;
|
use struct_derive::*;
|
||||||
|
|
||||||
|
mod struct_macro;
|
||||||
|
|
||||||
|
/* #[proc_macro_attribute(attributes(reflect))]
|
||||||
|
pub fn reflect(_attr: proc_macro::TokenStream, item: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
|
item
|
||||||
|
} */
|
||||||
|
|
||||||
|
pub(crate) struct FieldAttributes(Vec<ReflectAttribute>);
|
||||||
|
|
||||||
|
impl FieldAttributes {
|
||||||
|
/// Searches for a usage of the 'reflect' attribute and returns a list of the
|
||||||
|
/// things used in the usage.
|
||||||
|
pub fn from_vec(v: &Vec<syn::Attribute>) -> Result<Self, syn::Error> {
|
||||||
|
let s: Result<Vec<ReflectAttribute>, _> = v.iter().filter_map(|att| match &att.meta {
|
||||||
|
syn::Meta::Path(_) => None,
|
||||||
|
syn::Meta::List(l) => {
|
||||||
|
Some(syn::parse::<ReflectAttribute>(l.tokens.clone().into()))
|
||||||
|
}
|
||||||
|
syn::Meta::NameValue(_) => None
|
||||||
|
}).collect();
|
||||||
|
|
||||||
|
Ok(Self(s?))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_skip(&self) -> bool {
|
||||||
|
self.0.iter().any(|a| *a == ReflectAttribute::Skip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub(crate) enum ReflectAttribute {
|
||||||
|
Skip
|
||||||
|
}
|
||||||
|
|
||||||
|
impl syn::parse::Parse for ReflectAttribute {
|
||||||
|
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||||
|
let ident: Ident = input.parse()?;
|
||||||
|
let ident_str = ident.to_string().to_lowercase();
|
||||||
|
|
||||||
|
match ident_str.as_str() {
|
||||||
|
"skip" => Ok(Self::Skip),
|
||||||
|
_ => Err(syn::Error::new(ident.span(), "Unknown reflect attribute flag"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) struct ReflectDef {
|
pub(crate) struct ReflectDef {
|
||||||
|
//pub ident: Ident,
|
||||||
pub type_path: Path,
|
pub type_path: Path,
|
||||||
pub generics: Generics,
|
pub generics: Generics,
|
||||||
pub attributes: Vec<Attribute>
|
pub attributes: Vec<Attribute>
|
||||||
|
@ -21,10 +69,12 @@ impl syn::parse::Parse for ReflectDef {
|
||||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||||
let attributes = input.call(Attribute::parse_outer)?;
|
let attributes = input.call(Attribute::parse_outer)?;
|
||||||
let type_path = Path::parse_mod_style(input)?;
|
let type_path = Path::parse_mod_style(input)?;
|
||||||
|
//let ident = type_path. //type_path.require_ident()?;
|
||||||
let mut generics = input.parse::<Generics>()?;
|
let mut generics = input.parse::<Generics>()?;
|
||||||
generics.where_clause = input.parse()?;
|
generics.where_clause = input.parse()?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
//ident: ident.clone(),
|
||||||
type_path,
|
type_path,
|
||||||
generics,
|
generics,
|
||||||
attributes,
|
attributes,
|
||||||
|
@ -32,7 +82,7 @@ impl syn::parse::Parse for ReflectDef {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[proc_macro_derive(Reflect)]
|
#[proc_macro_derive(Reflect, attributes(reflect))]
|
||||||
pub fn derive_reflect(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
pub fn derive_reflect(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
let input = parse_macro_input!(input as DeriveInput);
|
let input = parse_macro_input!(input as DeriveInput);
|
||||||
|
|
||||||
|
@ -55,18 +105,22 @@ pub fn derive_reflect(input: proc_macro::TokenStream) -> proc_macro::TokenStream
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn impl_reflect_trait_value(input: TokenStream) -> TokenStream {
|
pub fn impl_reflect_trait_value(input: TokenStream) -> TokenStream {
|
||||||
let reflect = syn::parse_macro_input!(input as ReflectDef);
|
let reflect = syn::parse_macro_input!(input as ReflectDef);
|
||||||
|
|
||||||
let type_path = reflect.type_path.clone().into_token_stream();
|
|
||||||
|
|
||||||
let name_id = &reflect.type_path.segments.last().unwrap().ident;
|
let type_path = reflect.type_path;
|
||||||
let name = name_id.span().source_text().unwrap();
|
// convert the type path to a string. This would not create a leading separator
|
||||||
|
let type_path_str = {
|
||||||
|
let idents: Vec<String> = type_path.segments.iter()
|
||||||
|
.map(|segment| segment.ident.to_string())
|
||||||
|
.collect();
|
||||||
|
idents.join("::")
|
||||||
|
};
|
||||||
|
|
||||||
let (impl_generics, ty_generics, where_clause) = reflect.generics.split_for_impl();
|
let (impl_generics, ty_generics, where_clause) = reflect.generics.split_for_impl();
|
||||||
|
|
||||||
TokenStream::from(quote! {
|
TokenStream::from(quote! {
|
||||||
impl #impl_generics lyra_engine::reflect::Reflect for #type_path #ty_generics #where_clause {
|
impl #impl_generics lyra_engine::reflect::Reflect for #type_path #ty_generics #where_clause {
|
||||||
fn name(&self) -> ::std::string::String {
|
fn name(&self) -> ::std::string::String {
|
||||||
#name.to_string()
|
#type_path_str.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn type_id(&self) -> std::any::TypeId {
|
fn type_id(&self) -> std::any::TypeId {
|
||||||
|
@ -119,4 +173,9 @@ pub(crate) fn add_trait_bounds(mut generics: Generics, add_bounds: Vec<TypeParam
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
generics
|
generics
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn impl_reflect_simple_struct(input: TokenStream) -> TokenStream {
|
||||||
|
struct_macro::impl_reflect_simple_struct(input)
|
||||||
}
|
}
|
|
@ -1,13 +1,34 @@
|
||||||
use quote::{quote, ToTokens};
|
use quote::{quote, ToTokens};
|
||||||
use syn::{DeriveInput, parse_quote, DataStruct};
|
use syn::{DeriveInput, parse_quote, DataStruct};
|
||||||
|
|
||||||
use crate::add_trait_bounds;
|
use crate::{add_trait_bounds, FieldAttributes};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
enum StructType {
|
||||||
|
Unit,
|
||||||
|
Named,
|
||||||
|
Tuple,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StructType {
|
||||||
|
pub fn new(data: &DataStruct) -> Self {
|
||||||
|
if let Some(first) = data.fields.iter().next() {
|
||||||
|
if first.ident.is_some() {
|
||||||
|
Self::Named
|
||||||
|
} else {
|
||||||
|
Self::Tuple
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Self::Unit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Generates code that matches a string with a struct's field name, and returns an Option that
|
/// Generates code that matches a string with a struct's field name, and returns an Option that
|
||||||
/// contains a borrow (mutable borrow if `is_mut` is true) to the matching struct field.
|
/// contains a borrow (mutable borrow if `is_mut` is true) to the matching struct field.
|
||||||
///
|
///
|
||||||
/// Example:
|
/// Example:
|
||||||
/// ```rust
|
/// ```compile_fail
|
||||||
/// // when `is_mut` = false
|
/// // when `is_mut` = false
|
||||||
/// match name {
|
/// match name {
|
||||||
/// "x" => Some(&self.x),
|
/// "x" => Some(&self.x),
|
||||||
|
@ -22,35 +43,49 @@ use crate::add_trait_bounds;
|
||||||
/// _ => None,
|
/// _ => None,
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// If the struct is a unit or tuple struct, None will always be returned
|
||||||
fn gen_struct_field_match(data: &DataStruct, is_mut: bool) -> proc_macro2::TokenStream {
|
fn gen_struct_field_match(data: &DataStruct, is_mut: bool) -> proc_macro2::TokenStream {
|
||||||
let mut_tkn = if is_mut {
|
let ty = StructType::new(data);
|
||||||
quote! {
|
|
||||||
mut
|
|
||||||
}
|
|
||||||
} else { quote!{} };
|
|
||||||
|
|
||||||
let field_arms = data.fields.iter().map(|field| {
|
|
||||||
let field_ident = field.ident.as_ref().expect("Struct is missing field ident!");
|
|
||||||
let field_name_str = field_ident.to_string();
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
#field_name_str => Some(&#mut_tkn self.#field_ident)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
quote! {
|
if ty == StructType::Named {
|
||||||
match name {
|
let mut_tkn = if is_mut {
|
||||||
#(#field_arms,)*
|
quote! {
|
||||||
_ => None,
|
mut
|
||||||
|
}
|
||||||
|
} else { quote!{} };
|
||||||
|
|
||||||
|
let field_arms = data.fields.iter().map(|field| {
|
||||||
|
let field_ident = field.ident.as_ref().expect("Struct is missing field ident!");
|
||||||
|
let field_name_str = field_ident.to_string();
|
||||||
|
|
||||||
|
let attrs = FieldAttributes::from_vec(&field.attrs)
|
||||||
|
.expect("Failure to parse reflect attributes");
|
||||||
|
if attrs.has_skip() {
|
||||||
|
quote! {
|
||||||
|
#field_name_str => None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! {
|
||||||
|
#field_name_str => Some(&#mut_tkn self.#field_ident)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
match name {
|
||||||
|
#(#field_arms,)*
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
} else { quote!(None) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates code that matches a string with a struct's field name, and sets that field value
|
/// Generates code that matches a string with a struct's field name, and sets that field value
|
||||||
/// with the provided `val`.
|
/// with the provided `val`.
|
||||||
///
|
///
|
||||||
/// Example:
|
/// Example:
|
||||||
/// ```rust
|
/// ```compile_fail
|
||||||
/// match name {
|
/// match name {
|
||||||
/// "x" => self.x = any_val.downcast_ref::<f32>()
|
/// "x" => self.x = any_val.downcast_ref::<f32>()
|
||||||
/// .expect(&format!("Cannot set struct's field of {} type to the provided type of {}", "f32", val.name()))
|
/// .expect(&format!("Cannot set struct's field of {} type to the provided type of {}", "f32", val.name()))
|
||||||
|
@ -64,34 +99,48 @@ fn gen_struct_field_match(data: &DataStruct, is_mut: bool) -> proc_macro2::Token
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
fn gen_struct_set_field_match(data: &DataStruct) -> proc_macro2::TokenStream {
|
fn gen_struct_set_field_match(data: &DataStruct) -> proc_macro2::TokenStream {
|
||||||
let field_arms = data.fields.iter().map(|field| {
|
let ty = StructType::new(data);
|
||||||
let field_ident = field.ident.as_ref().expect("Struct is missing field ident!");
|
|
||||||
let field_name_str = field_ident.to_string();
|
|
||||||
let field_ty = &field.ty;
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
#field_name_str => self.#field_ident = any_val.downcast_ref::<#field_ty>()
|
|
||||||
.expect(&format!("Cannot set struct's field of {} type to the provided type of {}", #field_name_str, val.name()))
|
|
||||||
.clone() //Some(&#mut_tkn self.#field_ident)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
quote! {
|
if ty == StructType::Named {
|
||||||
let any_val = val.as_any();
|
let field_arms = data.fields.iter().map(|field| {
|
||||||
match name {
|
let field_ident = field.ident.as_ref().expect("Struct is missing field ident!");
|
||||||
#(#field_arms,)*
|
let field_name_str = field_ident.to_string();
|
||||||
_ => {
|
let field_ty = &field.ty;
|
||||||
return false;
|
|
||||||
},
|
let attrs = FieldAttributes::from_vec(&field.attrs)
|
||||||
|
.expect("Failure to parse reflect attributes");
|
||||||
|
if attrs.has_skip() {
|
||||||
|
quote! {
|
||||||
|
#field_name_str => {}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! {
|
||||||
|
#field_name_str => self.#field_ident = any_val.downcast_ref::<#field_ty>()
|
||||||
|
.expect(&format!("Cannot set struct's field of {} type to the provided type of {}", #field_name_str, val.name()))
|
||||||
|
.clone() //Some(&#mut_tkn self.#field_ident)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
let any_val = val.as_any();
|
||||||
|
match name {
|
||||||
|
#(#field_arms,)*
|
||||||
|
_ => {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
}
|
}
|
||||||
}
|
} else { quote!(return false;) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates code that matches a string with a struct's field name, and returns a string that is
|
/// Generates code that matches a string with a struct's field name, and returns a string that is
|
||||||
/// the type of the field.
|
/// the type of the field.
|
||||||
///
|
///
|
||||||
/// Example:
|
/// Example:
|
||||||
/// ```rust
|
/// ```compile_fail
|
||||||
/// match name {
|
/// match name {
|
||||||
/// "x" => Some("f32"),
|
/// "x" => Some("f32"),
|
||||||
/// "y" => Some("f32"),
|
/// "y" => Some("f32"),
|
||||||
|
@ -99,32 +148,36 @@ fn gen_struct_set_field_match(data: &DataStruct) -> proc_macro2::TokenStream {
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
fn gen_struct_field_name_match(data: &DataStruct) -> proc_macro2::TokenStream {
|
fn gen_struct_field_name_match(data: &DataStruct) -> proc_macro2::TokenStream {
|
||||||
let field_arms = data.fields.iter().map(|field| {
|
let ty = StructType::new(data);
|
||||||
let field_ident = field.ident.as_ref().expect("Struct is missing field ident!");
|
|
||||||
let field_name_str = field_ident.to_string();
|
if ty == StructType::Named {
|
||||||
|
let field_arms = data.fields.iter().map(|field| {
|
||||||
|
let field_ident = field.ident.as_ref().expect("Struct is missing field ident!");
|
||||||
|
let field_name_str = field_ident.to_string();
|
||||||
|
|
||||||
|
let mut field_ty_stream = proc_macro2::TokenStream::new();
|
||||||
|
field.ty.to_tokens(&mut field_ty_stream);
|
||||||
|
let s = field_ty_stream.to_string();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#field_name_str => Some(#s)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let mut field_ty_stream = proc_macro2::TokenStream::new();
|
|
||||||
field.ty.to_tokens(&mut field_ty_stream);
|
|
||||||
let s = field_ty_stream.to_string();
|
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
#field_name_str => Some(#s)
|
match name {
|
||||||
|
#(#field_arms,)*
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
} else { quote!(None) }
|
||||||
|
|
||||||
quote! {
|
|
||||||
match name {
|
|
||||||
#(#field_arms,)*
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates code that matches a string with a struct's field name, and sets that field value
|
/// Generates code that matches a string with a struct's field name, and sets that field value
|
||||||
/// with the provided `val`.
|
/// with the provided `val`.
|
||||||
///
|
///
|
||||||
/// Example:
|
/// Example:
|
||||||
/// ```rust
|
/// ```compile_fail
|
||||||
/// match name {
|
/// match name {
|
||||||
/// 0 => self.x = any_val.downcast_ref::<f32>()
|
/// 0 => self.x = any_val.downcast_ref::<f32>()
|
||||||
/// .expect(&format!("Cannot set struct's field of {} type to the provided type of {}", "f32", val.name()))
|
/// .expect(&format!("Cannot set struct's field of {} type to the provided type of {}", "f32", val.name()))
|
||||||
|
@ -138,15 +191,38 @@ fn gen_struct_field_name_match(data: &DataStruct) -> proc_macro2::TokenStream {
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
fn gen_struct_set_field_match_idx(data: &DataStruct) -> proc_macro2::TokenStream {
|
fn gen_struct_set_field_match_idx(data: &DataStruct) -> proc_macro2::TokenStream {
|
||||||
|
let ty = StructType::new(data);
|
||||||
|
|
||||||
|
if ty == StructType::Unit {
|
||||||
|
return quote!( return false; );
|
||||||
|
}
|
||||||
|
|
||||||
let field_arms = data.fields.iter().enumerate().map(|(idx, field)| {
|
let field_arms = data.fields.iter().enumerate().map(|(idx, field)| {
|
||||||
let field_ident = field.ident.as_ref().expect("Struct is missing field ident!");
|
|
||||||
let field_name_str = field_ident.to_string();
|
|
||||||
let field_ty = &field.ty;
|
let field_ty = &field.ty;
|
||||||
|
|
||||||
quote! {
|
let attrs = FieldAttributes::from_vec(&field.attrs)
|
||||||
#idx => self.#field_ident = any_val.downcast_ref::<#field_ty>()
|
.expect("Failure to parse reflect attributes");
|
||||||
.expect(&format!("Cannot set struct's field of {} type to the provided type of {}", #field_name_str, val.name()))
|
if attrs.has_skip() {
|
||||||
.clone()
|
return quote! {
|
||||||
|
#idx => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(field_ident) = &field.ident {
|
||||||
|
let field_name_str = field_ident.to_string();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#idx => self.#field_ident = any_val.downcast_ref::<#field_ty>()
|
||||||
|
.expect(&format!("Cannot set struct's field of {} to the provided type of {}", #field_name_str, val.name()))
|
||||||
|
.clone()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let sidx = syn::Index::from(idx);
|
||||||
|
quote! {
|
||||||
|
#idx => self.#sidx = any_val.downcast_ref::<#field_ty>()
|
||||||
|
.expect(&format!("Cannot set struct's field at {} to the provided type of {}", #idx, val.name()))
|
||||||
|
.clone()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -158,6 +234,8 @@ fn gen_struct_set_field_match_idx(data: &DataStruct) -> proc_macro2::TokenStream
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,7 +243,7 @@ fn gen_struct_set_field_match_idx(data: &DataStruct) -> proc_macro2::TokenStream
|
||||||
/// type of the field.
|
/// type of the field.
|
||||||
///
|
///
|
||||||
/// Example:
|
/// Example:
|
||||||
/// ```rust
|
/// ```compile_fail
|
||||||
/// match name {
|
/// match name {
|
||||||
/// 0 => Some("f32"),
|
/// 0 => Some("f32"),
|
||||||
/// 1 => Some("f32"),
|
/// 1 => Some("f32"),
|
||||||
|
@ -196,7 +274,7 @@ fn gen_struct_field_name_match_idx(data: &DataStruct) -> proc_macro2::TokenStrea
|
||||||
/// to the matching struct field.
|
/// to the matching struct field.
|
||||||
///
|
///
|
||||||
/// Example:
|
/// Example:
|
||||||
/// ```rust
|
/// ```compile_fail
|
||||||
/// // when `is_mut` = false
|
/// // when `is_mut` = false
|
||||||
/// match idx {
|
/// match idx {
|
||||||
/// 0 => Some(&self.x),
|
/// 0 => Some(&self.x),
|
||||||
|
@ -212,6 +290,12 @@ fn gen_struct_field_name_match_idx(data: &DataStruct) -> proc_macro2::TokenStrea
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
fn gen_struct_field_match_idx(data: &DataStruct, is_mut: bool) -> proc_macro2::TokenStream {
|
fn gen_struct_field_match_idx(data: &DataStruct, is_mut: bool) -> proc_macro2::TokenStream {
|
||||||
|
let ty = StructType::new(data);
|
||||||
|
|
||||||
|
if ty == StructType::Unit {
|
||||||
|
return quote!(None);
|
||||||
|
}
|
||||||
|
|
||||||
let mut_tkn = if is_mut {
|
let mut_tkn = if is_mut {
|
||||||
quote! {
|
quote! {
|
||||||
mut
|
mut
|
||||||
|
@ -219,10 +303,23 @@ fn gen_struct_field_match_idx(data: &DataStruct, is_mut: bool) -> proc_macro2::T
|
||||||
} else { quote!{} };
|
} else { quote!{} };
|
||||||
|
|
||||||
let field_arms = data.fields.iter().enumerate().map(|(idx, field)| {
|
let field_arms = data.fields.iter().enumerate().map(|(idx, field)| {
|
||||||
let field_ident = field.ident.as_ref().expect("Struct is missing field ident!");
|
let attrs = FieldAttributes::from_vec(&field.attrs)
|
||||||
|
.expect("Failure to parse reflect attributes");
|
||||||
|
if attrs.has_skip() {
|
||||||
|
return quote! {
|
||||||
|
#idx => None
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
quote! {
|
if let Some(field_ident) = &field.ident {
|
||||||
#idx => Some(&#mut_tkn self.#field_ident)
|
quote! {
|
||||||
|
#idx => Some(&#mut_tkn self.#field_ident)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let sidx = syn::Index::from(idx);
|
||||||
|
quote! {
|
||||||
|
#idx => Some(&#mut_tkn self.#sidx)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -238,7 +335,7 @@ fn gen_struct_field_match_idx(data: &DataStruct, is_mut: bool) -> proc_macro2::T
|
||||||
/// and returns an Option that contains the name of the field.
|
/// and returns an Option that contains the name of the field.
|
||||||
///
|
///
|
||||||
/// Example:
|
/// Example:
|
||||||
/// ```rust
|
/// ```compile_fail
|
||||||
/// match idx {
|
/// match idx {
|
||||||
/// 0 => Some("x"),
|
/// 0 => Some("x"),
|
||||||
/// 1 => Some("y"),
|
/// 1 => Some("y"),
|
||||||
|
@ -246,27 +343,34 @@ fn gen_struct_field_match_idx(data: &DataStruct, is_mut: bool) -> proc_macro2::T
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
fn gen_struct_field_name_idx(data: &DataStruct) -> proc_macro2::TokenStream {
|
fn gen_struct_field_name_idx(data: &DataStruct) -> proc_macro2::TokenStream {
|
||||||
let field_arms = data.fields.iter().enumerate().map(|(idx, field)| {
|
let ty = StructType::new(data);
|
||||||
let field_ident = field.ident.as_ref().expect("Struct is missing field ident!");
|
|
||||||
let field_name_str = field_ident.to_string();
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
#idx => Some(#field_name_str)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
quote! {
|
if ty == StructType::Named {
|
||||||
match idx {
|
let field_arms = data.fields.iter().enumerate().map(|(idx, field)| {
|
||||||
#(#field_arms,)*
|
let field_ident = field.ident.as_ref().expect("Struct is missing field ident!");
|
||||||
_ => None,
|
let field_name_str = field_ident.to_string();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#idx => Some(#field_name_str)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
match idx {
|
||||||
|
#(#field_arms,)*
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
quote!(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates a token stream that implements Reflect and Struct for the provided struct
|
/// Generates a token stream that implements Reflect and Struct for the provided struct
|
||||||
pub fn derive_reflect_struct(input: &DeriveInput, data_struct: &DataStruct) -> proc_macro2::TokenStream {
|
pub fn derive_reflect_struct(input: &DeriveInput, data_struct: &DataStruct) -> proc_macro2::TokenStream {
|
||||||
let type_path = &input.ident;
|
let type_path = &input.ident;
|
||||||
let name = type_path.span().source_text().unwrap();
|
let name = type_path.to_string();
|
||||||
|
//let name = type_path.span().source_text().unwrap();
|
||||||
|
|
||||||
let field_len = data_struct.fields.len();
|
let field_len = data_struct.fields.len();
|
||||||
let get_field_match = gen_struct_field_match(data_struct, false);
|
let get_field_match = gen_struct_field_match(data_struct, false);
|
||||||
|
@ -360,12 +464,10 @@ pub fn derive_reflect_struct(input: &DeriveInput, data_struct: &DataStruct) -> p
|
||||||
|
|
||||||
fn set_field(&mut self, name: &str, val: &dyn lyra_engine::reflect::Reflect) -> bool {
|
fn set_field(&mut self, name: &str, val: &dyn lyra_engine::reflect::Reflect) -> bool {
|
||||||
#set_field_named
|
#set_field_named
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_field_at(&mut self, idx: usize, val: &dyn lyra_engine::reflect::Reflect) -> bool {
|
fn set_field_at(&mut self, idx: usize, val: &dyn lyra_engine::reflect::Reflect) -> bool {
|
||||||
#set_field_idx
|
#set_field_idx
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn field_type(&self, name: &str) -> Option<&'static str> {
|
fn field_type(&self, name: &str) -> Option<&'static str> {
|
||||||
|
|
|
@ -0,0 +1,270 @@
|
||||||
|
use quote::quote;
|
||||||
|
use syn::{Token, parenthesized, punctuated::Punctuated};
|
||||||
|
|
||||||
|
struct Field {
|
||||||
|
name: syn::Ident,
|
||||||
|
_eq: Token![=],
|
||||||
|
ty: syn::Ident,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl syn::parse::Parse for Field {
|
||||||
|
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||||
|
Ok(Self {
|
||||||
|
name: input.parse()?,
|
||||||
|
_eq: input.parse()?,
|
||||||
|
ty: input.parse()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SimpleStruct {
|
||||||
|
type_path: syn::Path,
|
||||||
|
pub generics: syn::Generics,
|
||||||
|
fields: Vec<Field>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl syn::parse::Parse for SimpleStruct {
|
||||||
|
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||||
|
//let type_path = syn::Path::parse_mod_style(input)?;
|
||||||
|
let type_path: syn::Path = input.parse()?;
|
||||||
|
/* let mut generics = input.parse::<syn::Generics>()?;
|
||||||
|
generics.where_clause = input.parse()?; */
|
||||||
|
|
||||||
|
let mut fields = vec![];
|
||||||
|
|
||||||
|
// parse fields if a comma is found
|
||||||
|
if input.peek(Token![,]) {
|
||||||
|
let _: Token![,] = input.parse()?;
|
||||||
|
let ident: syn::Ident = input.parse()?;
|
||||||
|
let ident_str = ident.to_string();
|
||||||
|
|
||||||
|
match ident_str.as_str() {
|
||||||
|
"fields" => {
|
||||||
|
let content;
|
||||||
|
let _parens: syn::token::Paren = parenthesized!(content in input);
|
||||||
|
|
||||||
|
let f: Punctuated<Field, Token![,]> = content.parse_terminated(Field::parse, Token![,])?;
|
||||||
|
fields = f.into_iter().collect();
|
||||||
|
},
|
||||||
|
_ => return Err(syn::Error::new(ident.span(), "Unknown macro command, expected `fields`")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
type_path,
|
||||||
|
generics: syn::Generics::default(),
|
||||||
|
fields,
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn gen_match_field_name_arm(simple: &SimpleStruct, default: &proc_macro2::TokenStream, arm_gen: fn(field: &Field) -> proc_macro2::TokenStream) -> proc_macro2::TokenStream {
|
||||||
|
let field_arms_iter = simple.fields.iter().map(|f| {
|
||||||
|
let fname = &f.name;
|
||||||
|
let arm = arm_gen(f);
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
stringify!(#fname) => #arm
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
match name {
|
||||||
|
#(#field_arms_iter,)*
|
||||||
|
_ => #default,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gen_match_field_index_arm(simple: &SimpleStruct, default: &proc_macro2::TokenStream, arm_gen: fn(field: &Field) -> proc_macro2::TokenStream) -> proc_macro2::TokenStream {
|
||||||
|
let field_arms_iter = simple.fields.iter().enumerate().map(|(idx, f)| {
|
||||||
|
let arm = arm_gen(f);
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#idx => #arm
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
match idx {
|
||||||
|
#(#field_arms_iter,)*
|
||||||
|
_ => #default,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn impl_reflect_simple_struct(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
|
let simple = syn::parse_macro_input!(input as SimpleStruct);
|
||||||
|
|
||||||
|
let type_path = &simple.type_path;
|
||||||
|
let (impl_generics, ty_generics, where_clause) = simple.generics.split_for_impl();
|
||||||
|
// convert the type path to a string. This would not create a leading separator
|
||||||
|
/* let type_path_str = {
|
||||||
|
let idents: Vec<String> = type_path.segments.iter()
|
||||||
|
.map(|segment| segment.ident.to_string())
|
||||||
|
.collect();
|
||||||
|
idents.join("::")
|
||||||
|
}; */
|
||||||
|
|
||||||
|
let borrow_fn = |field: &Field| {
|
||||||
|
let name = &field.name;
|
||||||
|
quote! {
|
||||||
|
Some(&self.#name)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let borrow_mut_fn = |field: &Field| {
|
||||||
|
let name = &field.name;
|
||||||
|
quote! {
|
||||||
|
Some(&mut self.#name)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let none_default = quote!(None);
|
||||||
|
let false_return_default = quote!( { return false; });
|
||||||
|
|
||||||
|
let field_count = simple.fields.len();
|
||||||
|
|
||||||
|
let field_fn = gen_match_field_name_arm(&simple, &none_default, borrow_fn);
|
||||||
|
let field_mut_fn = gen_match_field_name_arm(&simple, &none_default, borrow_mut_fn);
|
||||||
|
let field_at_fn = gen_match_field_index_arm(&simple, &none_default, borrow_fn);
|
||||||
|
let field_at_mut_fn = gen_match_field_index_arm(&simple, &none_default, borrow_mut_fn);
|
||||||
|
let field_name_at_fn = gen_match_field_index_arm(&simple, &none_default, |f| {
|
||||||
|
let name = &f.name;
|
||||||
|
quote! {
|
||||||
|
Some(stringify!(#name))
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let set_field_arm = |f: &Field| {
|
||||||
|
let name = &f.name;
|
||||||
|
let ty = &f.ty;
|
||||||
|
quote! {
|
||||||
|
self.#name = any_val.downcast_ref::<#ty>()
|
||||||
|
.expect(&format!("Cannot set struct's field of {} type to the provided type of {}", stringify!(#name), val.name()))
|
||||||
|
.clone() //Some(&#mut_tkn self.#field_ident)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let set_field_fn = gen_match_field_name_arm(&simple, &false_return_default, set_field_arm);
|
||||||
|
let set_field_at_fn = gen_match_field_index_arm(&simple, &false_return_default, set_field_arm);
|
||||||
|
|
||||||
|
let get_field_ty_arm = |f: &Field| {
|
||||||
|
let fty = &f.ty;
|
||||||
|
quote! {
|
||||||
|
Some(stringify!(#fty))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let field_type_fn = gen_match_field_name_arm(&simple, &none_default, get_field_ty_arm);
|
||||||
|
let field_type_at_fn = gen_match_field_index_arm(&simple, &none_default, get_field_ty_arm);
|
||||||
|
|
||||||
|
proc_macro::TokenStream::from(quote! {
|
||||||
|
impl #impl_generics lyra_engine::reflect::Reflect for #type_path #ty_generics #where_clause {
|
||||||
|
fn name(&self) -> ::std::string::String {
|
||||||
|
stringify!(#type_path).to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn type_id(&self) -> std::any::TypeId {
|
||||||
|
std::any::TypeId::of::<#type_path #ty_generics>()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply(&mut self, val: &dyn lyra_engine::reflect::Reflect) {
|
||||||
|
let val = val.as_any().downcast_ref::<Self>()
|
||||||
|
.expect("The type of `val` is not the same as `self`");
|
||||||
|
*self = val.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clone_inner(&self) -> Box<dyn lyra_engine::reflect::Reflect> {
|
||||||
|
Box::new(self.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reflect_ref(&self) -> lyra_engine::reflect::ReflectRef {
|
||||||
|
lyra_engine::reflect::ReflectRef::Value(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reflect_mut(&mut self) -> lyra_engine::reflect::ReflectMut {
|
||||||
|
lyra_engine::reflect::ReflectMut::Value(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reflect_val(&self) -> &dyn lyra_engine::reflect::Reflect {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reflect_val_mut(&mut self) -> &mut dyn lyra_engine::reflect::Reflect {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl #impl_generics lyra_engine::reflect::Struct for #type_path #ty_generics #where_clause {
|
||||||
|
fn field(&self, name: &str) -> Option<&dyn Reflect> {
|
||||||
|
#field_fn
|
||||||
|
}
|
||||||
|
|
||||||
|
fn field_mut(&mut self, name: &str) -> Option<&mut dyn Reflect> {
|
||||||
|
#field_mut_fn
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fields_len(&self) -> usize {
|
||||||
|
#field_count
|
||||||
|
}
|
||||||
|
|
||||||
|
fn field_at(&self, idx: usize) -> Option<&dyn Reflect> {
|
||||||
|
#field_at_fn
|
||||||
|
}
|
||||||
|
|
||||||
|
fn field_at_mut(&mut self, idx: usize) -> Option<&mut dyn Reflect> {
|
||||||
|
#field_at_mut_fn
|
||||||
|
}
|
||||||
|
|
||||||
|
fn field_name_at(&self, idx: usize) -> Option<&str> {
|
||||||
|
#field_name_at_fn
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_field(&mut self, name: &str, val: &dyn Reflect) -> bool {
|
||||||
|
let any_val = val.as_any();
|
||||||
|
|
||||||
|
#set_field_fn
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_field_at(&mut self, idx: usize, val: &dyn Reflect) -> bool {
|
||||||
|
let any_val = val.as_any();
|
||||||
|
|
||||||
|
#set_field_at_fn
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn field_type(&self, name: &str) -> Option<&'static str> {
|
||||||
|
#field_type_fn
|
||||||
|
}
|
||||||
|
|
||||||
|
fn field_type_at(&self, idx: usize) -> Option<&'static str> {
|
||||||
|
#field_type_at_fn
|
||||||
|
}
|
||||||
|
|
||||||
|
fn method(&self, name: &str) -> Option<&Method> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn method_at(&self, idx: usize) -> Option<&Method> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn methods_len(&self) -> usize {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
use lyra_reflect_derive::{impl_reflect_simple_struct, impl_reflect_trait_value};
|
||||||
|
|
||||||
|
use crate::{lyra_engine, Reflect, Method};
|
||||||
|
|
||||||
|
impl_reflect_simple_struct!(glam::Vec2, fields(x=f32, y=f32));
|
||||||
|
impl_reflect_simple_struct!(glam::Vec3, fields(x=f32, y=f32, z=f32));
|
||||||
|
impl_reflect_simple_struct!(glam::Vec4, fields(x=f32, y=f32, z=f32, w=f32));
|
||||||
|
impl_reflect_simple_struct!(glam::Quat, fields(x=f32, y=f32, z=f32, w=f32));
|
||||||
|
|
||||||
|
impl_reflect_trait_value!(glam::Mat4);
|
|
@ -4,7 +4,7 @@ use crate::List;
|
||||||
|
|
||||||
use crate::lyra_engine;
|
use crate::lyra_engine;
|
||||||
|
|
||||||
use super::{Reflect, ReflectRef, ReflectMut, util};
|
use crate::{Reflect, ReflectRef, ReflectMut, util};
|
||||||
|
|
||||||
impl_reflect_trait_value!(bool);
|
impl_reflect_trait_value!(bool);
|
||||||
impl_reflect_trait_value!(char);
|
impl_reflect_trait_value!(char);
|
|
@ -0,0 +1,4 @@
|
||||||
|
pub mod impl_std;
|
||||||
|
|
||||||
|
#[cfg(feature="math")]
|
||||||
|
pub mod impl_math;
|
|
@ -6,6 +6,8 @@ use lyra_ecs::{world::World, DynamicBundle, Component, Entity, ComponentInfo};
|
||||||
|
|
||||||
extern crate self as lyra_reflect;
|
extern crate self as lyra_reflect;
|
||||||
|
|
||||||
|
pub use lyra_reflect_derive::*;
|
||||||
|
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
pub(crate) mod lyra_engine {
|
pub(crate) mod lyra_engine {
|
||||||
pub(crate) mod reflect {
|
pub(crate) mod reflect {
|
||||||
|
@ -42,7 +44,7 @@ pub use method::*;
|
||||||
pub mod registry;
|
pub mod registry;
|
||||||
pub use registry::*;
|
pub use registry::*;
|
||||||
|
|
||||||
pub mod impl_std;
|
pub mod impls;
|
||||||
|
|
||||||
pub trait Reflect: Any {
|
pub trait Reflect: Any {
|
||||||
fn name(&self) -> String;
|
fn name(&self) -> String;
|
||||||
|
|
|
@ -52,7 +52,6 @@ pub trait Enum: Reflect {
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use lyra_reflect_derive::Reflect;
|
|
||||||
|
|
||||||
use super::EnumType;
|
use super::EnumType;
|
||||||
use crate::{lyra_engine, Reflect, ReflectRef};
|
use crate::{lyra_engine, Reflect, ReflectRef};
|
||||||
|
|
|
@ -41,7 +41,6 @@ pub trait Struct: Reflect {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use lyra_reflect_derive::Reflect;
|
|
||||||
use crate::{Reflect, ReflectRef, ReflectMut};
|
use crate::{Reflect, ReflectRef, ReflectMut};
|
||||||
use crate::lyra_engine;
|
use crate::lyra_engine;
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,12 @@ impl TypeRegistry {
|
||||||
self.inner.get_mut(&type_id)
|
self.inner.get_mut(&type_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a registered type, or register a new type and return it.
|
||||||
|
pub fn get_type_or_default(&mut self, type_id: TypeId) -> &mut RegisteredType {
|
||||||
|
self.inner.entry(type_id)
|
||||||
|
.or_insert(RegisteredType::default())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn register_type<T>(&mut self)
|
pub fn register_type<T>(&mut self)
|
||||||
where
|
where
|
||||||
T: AsRegisteredType + 'static
|
T: AsRegisteredType + 'static
|
||||||
|
|
|
@ -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"
|
||||||
|
@ -23,4 +24,4 @@ mlua = { version = "0.9.2", features = ["lua54"], optional = true } # luajit may
|
||||||
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tracing-subscriber = { version = "0.3.16", features = [ "tracing-log" ] }
|
tracing-subscriber = { version = "0.3.16", features = [ "tracing-log" ] }
|
||||||
|
|
|
@ -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"
|
|
@ -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>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
|
@ -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`"))?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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>;
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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");
|
||||||
|
|
||||||
|
|
|
@ -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, &[])
|
||||||
|
|
|
@ -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(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -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))
|
||||||
|
}
|
||||||
|
} */
|
|
@ -0,0 +1,8 @@
|
||||||
|
pub mod util;
|
||||||
|
pub use util::*;
|
||||||
|
|
||||||
|
pub mod math;
|
||||||
|
pub use math::*;
|
||||||
|
|
||||||
|
pub mod ecs;
|
||||||
|
pub use ecs::*;
|
|
@ -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) => {
|
||||||
|
|
|
@ -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() };
|
||||||
|
|
|
@ -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
|
||||||
|
)
|
||||||
|
); */
|
Loading…
Reference in New Issue