mirror of
https://github.com/bevyengine/bevy
synced 2024-11-22 12:43:34 +00:00
remove darling
This commit is contained in:
parent
3b68c7cc4a
commit
2508a59f68
16 changed files with 140 additions and 225 deletions
|
@ -1,16 +1,15 @@
|
|||
[package]
|
||||
name = "bevy_derive"
|
||||
version = "0.1.0"
|
||||
authors = ["Carter Anderson <mcanders1@gmail.com>"]
|
||||
edition = "2018"
|
||||
name = "bevy_derive"
|
||||
version = "0.1.0"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
syn = "1.0"
|
||||
Inflector = {version = "0.11.4", default-features = false}
|
||||
proc-macro-crate = "0.1.4"
|
||||
proc-macro2 = "1.0"
|
||||
quote = "1.0"
|
||||
Inflector = { version = "0.11.4", default-features = false }
|
||||
darling = "0.10.2"
|
||||
proc-macro-crate = "0.1.4"
|
||||
syn = "1.0"
|
||||
|
|
|
@ -1,20 +1,10 @@
|
|||
use crate::{
|
||||
attributes::get_field_attributes,
|
||||
modules::{get_modules, get_path},
|
||||
};
|
||||
use darling::FromMeta;
|
||||
use inflector::Inflector;
|
||||
use proc_macro::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{parse_macro_input, DeriveInput, Path};
|
||||
|
||||
#[derive(FromMeta, Debug, Default)]
|
||||
struct VertexAttributeArgs {
|
||||
#[darling(default)]
|
||||
pub ignore: Option<bool>,
|
||||
#[darling(default)]
|
||||
pub instance: Option<bool>,
|
||||
}
|
||||
use syn::{parse_macro_input, DeriveInput, Path, Data, DataStruct, Fields, Field, parse::ParseStream};
|
||||
|
||||
#[derive(Default)]
|
||||
struct VertexAttributes {
|
||||
|
@ -22,27 +12,44 @@ struct VertexAttributes {
|
|||
pub instance: bool,
|
||||
}
|
||||
|
||||
impl From<VertexAttributeArgs> for VertexAttributes {
|
||||
fn from(args: VertexAttributeArgs) -> Self {
|
||||
VertexAttributes {
|
||||
ignore: args.ignore.unwrap_or(false),
|
||||
instance: args.instance.unwrap_or(false),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static VERTEX_ATTRIBUTE_NAME: &'static str = "vertex";
|
||||
|
||||
pub fn derive_as_vertex_buffer_descriptor(input: TokenStream) -> TokenStream {
|
||||
let ast = parse_macro_input!(input as DeriveInput);
|
||||
let modules = get_modules(&ast);
|
||||
let modules = get_modules();
|
||||
|
||||
let bevy_render_path: Path = get_path(&modules.bevy_render);
|
||||
let fields = match &ast.data {
|
||||
Data::Struct(DataStruct {
|
||||
fields: Fields::Named(fields),
|
||||
..
|
||||
}) => &fields.named,
|
||||
_ => panic!("expected a struct with named fields"),
|
||||
};
|
||||
let field_attributes = fields
|
||||
.iter()
|
||||
.map(|field| {
|
||||
(
|
||||
field,
|
||||
field.attrs
|
||||
.iter()
|
||||
.find(|a| a.path.get_ident().as_ref().unwrap().to_string() == VERTEX_ATTRIBUTE_NAME)
|
||||
.map_or_else(|| VertexAttributes::default() ,|a| {
|
||||
syn::custom_keyword!(ignore);
|
||||
let mut vertex_attributes = VertexAttributes::default();
|
||||
a.parse_args_with(|input: ParseStream| {
|
||||
if let Some(_) = input.parse::<Option<ignore>>()? {
|
||||
vertex_attributes.ignore = true;
|
||||
return Ok(());
|
||||
}
|
||||
Ok(())
|
||||
}).expect("invalid 'vertex' attribute format");
|
||||
|
||||
let field_attributes = get_field_attributes::<VertexAttributes, VertexAttributeArgs>(
|
||||
VERTEX_ATTRIBUTE_NAME,
|
||||
&ast.data,
|
||||
);
|
||||
vertex_attributes
|
||||
}),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<(&Field, VertexAttributes)>>();
|
||||
|
||||
let struct_name = &ast.ident;
|
||||
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
use darling::FromMeta;
|
||||
use syn::{Attribute, Data, DataStruct, Field, Fields};
|
||||
|
||||
pub fn get_field_attributes<'a, T, TArgs>(
|
||||
attribute_name: &str,
|
||||
data: &'a Data,
|
||||
) -> Vec<(&'a Field, T)>
|
||||
where
|
||||
T: Default,
|
||||
TArgs: FromMeta + Into<T> + Default,
|
||||
{
|
||||
let fields = match data {
|
||||
Data::Struct(DataStruct {
|
||||
fields: Fields::Named(fields),
|
||||
..
|
||||
}) => &fields.named,
|
||||
_ => panic!("expected a struct with named fields"),
|
||||
};
|
||||
|
||||
fields
|
||||
.iter()
|
||||
.map(|f| {
|
||||
(
|
||||
f,
|
||||
f.attrs
|
||||
.iter()
|
||||
.find(|a| a.path.get_ident().as_ref().unwrap().to_string() == attribute_name)
|
||||
.map(|a| {
|
||||
TArgs::from_meta(&a.parse_meta().unwrap())
|
||||
.unwrap_or_else(|_err| TArgs::default())
|
||||
})
|
||||
.unwrap_or_else(|| TArgs::default())
|
||||
.into(),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<(&Field, T)>>()
|
||||
}
|
||||
|
||||
pub fn get_attributes<'a, T, TArgs>(attribute_name: &str, attrs: &[Attribute]) -> T
|
||||
where
|
||||
T: Default,
|
||||
TArgs: FromMeta + Into<T> + Default,
|
||||
{
|
||||
attrs
|
||||
.iter()
|
||||
.find(|a| a.path.get_ident().as_ref().unwrap().to_string() == attribute_name)
|
||||
.map(|a| TArgs::from_meta(&a.parse_meta().unwrap()).unwrap_or_else(|_err| TArgs::default()))
|
||||
.unwrap_or_else(|| TArgs::default())
|
||||
.into()
|
||||
}
|
|
@ -13,7 +13,7 @@ pub fn derive_bytes(input: TokenStream) -> TokenStream {
|
|||
_ => panic!("expected a struct with named fields"),
|
||||
};
|
||||
|
||||
let modules = get_modules(&ast);
|
||||
let modules = get_modules();
|
||||
let bevy_core_path = get_path(&modules.bevy_core);
|
||||
|
||||
let fields = fields
|
||||
|
|
|
@ -13,7 +13,7 @@ pub fn derive_component_set(input: TokenStream) -> TokenStream {
|
|||
_ => panic!("expected a struct with named fields"),
|
||||
};
|
||||
|
||||
let modules = get_modules(&ast);
|
||||
let modules = get_modules();
|
||||
let bevy_app_path = get_path(&modules.bevy_app);
|
||||
let legion_path = get_path(&modules.legion);
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ extern crate proc_macro;
|
|||
|
||||
mod app_plugin;
|
||||
mod as_vertex_buffer_descriptor;
|
||||
mod attributes;
|
||||
mod bytes;
|
||||
mod component_set;
|
||||
mod modules;
|
||||
|
|
|
@ -1,26 +1,6 @@
|
|||
use darling::FromMeta;
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro_crate::crate_name;
|
||||
use syn::{DeriveInput, Path};
|
||||
|
||||
#[derive(FromMeta, Debug, Default)]
|
||||
pub struct ModuleAttributeArgs {
|
||||
#[darling(default)]
|
||||
pub bevy_render: Option<String>,
|
||||
#[darling(default)]
|
||||
pub bevy_asset: Option<String>,
|
||||
#[darling(default)]
|
||||
pub bevy_core: Option<String>,
|
||||
#[darling(default)]
|
||||
pub bevy_app: Option<String>,
|
||||
#[darling(default)]
|
||||
pub legion: Option<String>,
|
||||
|
||||
/// If true, it will use the meta "bevy" crate for dependencies by default (ex: bevy:app). If this is set to false, the individual bevy crates
|
||||
/// will be used (ex: "bevy_app"). Defaults to "true" if the "bevy" crate is in your cargo.toml
|
||||
#[darling(default)]
|
||||
pub meta: Option<bool>,
|
||||
}
|
||||
use syn::Path;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Modules {
|
||||
|
@ -57,44 +37,12 @@ fn use_meta() -> bool {
|
|||
crate_name("bevy").is_ok()
|
||||
}
|
||||
|
||||
pub static MODULE_ATTRIBUTE_NAME: &'static str = "module";
|
||||
|
||||
pub fn get_modules(ast: &DeriveInput) -> Modules {
|
||||
let module_attribute_args = ast
|
||||
.attrs
|
||||
.iter()
|
||||
.find(|a| a.path.get_ident().as_ref().unwrap().to_string() == MODULE_ATTRIBUTE_NAME)
|
||||
.map_or_else(
|
||||
|| ModuleAttributeArgs::default(),
|
||||
|a| {
|
||||
ModuleAttributeArgs::from_meta(&a.parse_meta().unwrap())
|
||||
.unwrap_or_else(|_err| ModuleAttributeArgs::default())
|
||||
},
|
||||
);
|
||||
|
||||
let mut modules = if module_attribute_args.meta.unwrap_or_else(|| use_meta()) {
|
||||
pub fn get_modules() -> Modules {
|
||||
if use_meta() {
|
||||
Modules::meta()
|
||||
} else {
|
||||
Modules::external()
|
||||
};
|
||||
|
||||
if let Some(path) = module_attribute_args.bevy_asset {
|
||||
modules.bevy_asset = path;
|
||||
}
|
||||
|
||||
if let Some(path) = module_attribute_args.bevy_render {
|
||||
modules.bevy_render = path;
|
||||
}
|
||||
|
||||
if let Some(path) = module_attribute_args.bevy_core {
|
||||
modules.bevy_core = path;
|
||||
}
|
||||
|
||||
if let Some(path) = module_attribute_args.bevy_app {
|
||||
modules.bevy_app = path;
|
||||
}
|
||||
|
||||
modules
|
||||
}
|
||||
|
||||
pub fn get_path(path_str: &str) -> Path {
|
||||
|
|
|
@ -5,7 +5,7 @@ use syn::{parse_macro_input, DeriveInput, Path};
|
|||
|
||||
pub fn derive_render_resource(input: TokenStream) -> TokenStream {
|
||||
let ast = parse_macro_input!(input as DeriveInput);
|
||||
let modules = get_modules(&ast);
|
||||
let modules = get_modules();
|
||||
|
||||
let bevy_render_path: Path = get_path(&modules.bevy_render);
|
||||
let bevy_asset_path: Path = get_path(&modules.bevy_asset);
|
||||
|
|
|
@ -1,50 +1,50 @@
|
|||
use crate::{
|
||||
attributes::{get_attributes, get_field_attributes},
|
||||
modules::{get_modules, get_path},
|
||||
};
|
||||
use darling::FromMeta;
|
||||
use crate::modules::{get_modules, get_path};
|
||||
use proc_macro::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{parse_macro_input, DeriveInput, Path};
|
||||
use syn::{
|
||||
parse::ParseStream, parse_macro_input, Data, DataStruct, DeriveInput, Field, Fields, Path,
|
||||
};
|
||||
|
||||
#[derive(FromMeta, Debug, Default)]
|
||||
struct RenderResourceAttributeArgs {
|
||||
#[darling(default)]
|
||||
pub ignore: Option<bool>,
|
||||
#[darling(default)]
|
||||
pub buffer: Option<bool>,
|
||||
#[darling(default)]
|
||||
pub from_self: Option<bool>,
|
||||
#[derive(Default)]
|
||||
struct RenderResourceFieldAttributes {
|
||||
pub ignore: bool,
|
||||
pub buffer: bool,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct RenderResourceAttributes {
|
||||
pub ignore: bool,
|
||||
pub buffer: bool,
|
||||
pub from_self: bool,
|
||||
}
|
||||
|
||||
impl From<RenderResourceAttributeArgs> for RenderResourceAttributes {
|
||||
fn from(args: RenderResourceAttributeArgs) -> Self {
|
||||
Self {
|
||||
ignore: args.ignore.unwrap_or(false),
|
||||
buffer: args.buffer.unwrap_or(false),
|
||||
from_self: args.from_self.unwrap_or(false),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static RENDER_RESOURCE_ATTRIBUTE_NAME: &'static str = "render_resources";
|
||||
|
||||
pub fn derive_render_resources(input: TokenStream) -> TokenStream {
|
||||
let ast = parse_macro_input!(input as DeriveInput);
|
||||
let modules = get_modules(&ast);
|
||||
let modules = get_modules();
|
||||
|
||||
let bevy_render_path: Path = get_path(&modules.bevy_render);
|
||||
let attributes = get_attributes::<RenderResourceAttributes, RenderResourceAttributeArgs>(
|
||||
RENDER_RESOURCE_ATTRIBUTE_NAME,
|
||||
&ast.attrs,
|
||||
);
|
||||
let attributes = ast
|
||||
.attrs
|
||||
.iter()
|
||||
.find(|a| {
|
||||
a.path.get_ident().as_ref().unwrap().to_string() == RENDER_RESOURCE_ATTRIBUTE_NAME
|
||||
})
|
||||
.map_or_else(
|
||||
|| RenderResourceAttributes::default(),
|
||||
|a| {
|
||||
syn::custom_keyword!(from_self);
|
||||
let mut attributes = RenderResourceAttributes::default();
|
||||
a.parse_args_with(|input: ParseStream| {
|
||||
if let Some(_) = input.parse::<Option<from_self>>()? {
|
||||
attributes.from_self = true;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.expect("invalid 'render_resources' attribute format");
|
||||
|
||||
attributes
|
||||
},
|
||||
);
|
||||
let struct_name = &ast.ident;
|
||||
let struct_name_string = struct_name.to_string();
|
||||
|
||||
|
@ -77,11 +77,47 @@ pub fn derive_render_resources(input: TokenStream) -> TokenStream {
|
|||
}
|
||||
})
|
||||
} else {
|
||||
let field_attributes = get_field_attributes::<
|
||||
RenderResourceAttributes,
|
||||
RenderResourceAttributeArgs,
|
||||
>(RENDER_RESOURCE_ATTRIBUTE_NAME, &ast.data);
|
||||
let fields = match &ast.data {
|
||||
Data::Struct(DataStruct {
|
||||
fields: Fields::Named(fields),
|
||||
..
|
||||
}) => &fields.named,
|
||||
_ => panic!("expected a struct with named fields"),
|
||||
};
|
||||
let field_attributes = fields
|
||||
.iter()
|
||||
.map(|field| {
|
||||
(
|
||||
field,
|
||||
field
|
||||
.attrs
|
||||
.iter()
|
||||
.find(|a| {
|
||||
a.path.get_ident().as_ref().unwrap().to_string()
|
||||
== RENDER_RESOURCE_ATTRIBUTE_NAME
|
||||
})
|
||||
.map_or_else(
|
||||
|| RenderResourceFieldAttributes::default(),
|
||||
|a| {
|
||||
syn::custom_keyword!(ignore);
|
||||
syn::custom_keyword!(buffer);
|
||||
let mut attributes = RenderResourceFieldAttributes::default();
|
||||
a.parse_args_with(|input: ParseStream| {
|
||||
if let Some(_) = input.parse::<Option<ignore>>()? {
|
||||
attributes.ignore = true;
|
||||
} else if let Some(_) = input.parse::<Option<buffer>>()? {
|
||||
attributes.buffer = true;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.expect("invalid 'render_resources' attribute format");
|
||||
|
||||
attributes
|
||||
},
|
||||
),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<(&Field, RenderResourceFieldAttributes)>>();
|
||||
let mut render_resource_names = Vec::new();
|
||||
let mut render_resource_fields = Vec::new();
|
||||
let mut render_resource_hints = Vec::new();
|
||||
|
|
|
@ -13,7 +13,7 @@ pub fn derive_from_resources(input: TokenStream) -> TokenStream {
|
|||
_ => panic!("expected a struct with named fields"),
|
||||
};
|
||||
|
||||
let modules = get_modules(&ast);
|
||||
let modules = get_modules();
|
||||
let bevy_app_path = get_path(&modules.bevy_app);
|
||||
|
||||
let field_types = fields.iter().map(|field| &field.ty);
|
||||
|
|
|
@ -9,7 +9,7 @@ static SHADER_DEF_ATTRIBUTE_NAME: &'static str = "shader_def";
|
|||
|
||||
pub fn derive_shader_defs(input: TokenStream) -> TokenStream {
|
||||
let ast = parse_macro_input!(input as DeriveInput);
|
||||
let modules = get_modules(&ast);
|
||||
let modules = get_modules();
|
||||
let bevy_render_path: Path = get_path(&modules.bevy_render);
|
||||
|
||||
let fields = match &ast.data {
|
||||
|
|
|
@ -28,7 +28,6 @@ hecs-macros = { path = "macros", version = "0.3.0", optional = true }
|
|||
hashbrown = { version = "0.8.0", default-features = false, features = ["ahash", "inline-more"] }
|
||||
lazy_static = { version = "1.4.0", optional = true, features = ["spin_no_std"] }
|
||||
serde = { version = "1", features = ["derive"], optional = true}
|
||||
getrandom = "0.1"
|
||||
rand = "0.7.3"
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -11,5 +11,4 @@ proc-macro = true
|
|||
syn = "1.0"
|
||||
proc-macro2 = "1.0"
|
||||
quote = "1.0"
|
||||
darling = "0.10.2"
|
||||
proc-macro-crate = "0.1.4"
|
|
@ -2,22 +2,20 @@ extern crate proc_macro;
|
|||
|
||||
mod modules;
|
||||
|
||||
use darling::FromMeta;
|
||||
use modules::{get_modules, get_path};
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro::{TokenStream};
|
||||
use proc_macro_crate::crate_name;
|
||||
use quote::quote;
|
||||
use syn::{
|
||||
parse::Parse,
|
||||
parse::{ParseStream, Parse},
|
||||
parse_macro_input,
|
||||
punctuated::Punctuated,
|
||||
token::{Comma, Where},
|
||||
Data, DataStruct, DeriveInput, Field, Fields, Generics, Ident, Index, Member,
|
||||
};
|
||||
|
||||
#[derive(FromMeta, Debug, Default)]
|
||||
#[derive(Default)]
|
||||
struct PropAttributeArgs {
|
||||
#[darling(default)]
|
||||
pub ignore: Option<bool>,
|
||||
}
|
||||
|
||||
|
@ -50,12 +48,21 @@ pub fn derive_properties(input: TokenStream) -> TokenStream {
|
|||
f,
|
||||
f.attrs
|
||||
.iter()
|
||||
.find(|a| {
|
||||
a.path.get_ident().as_ref().unwrap().to_string() == PROP_ATTRIBUTE_NAME
|
||||
})
|
||||
.find(|a| a.path.get_ident().as_ref().unwrap().to_string() == PROP_ATTRIBUTE_NAME)
|
||||
.map(|a| {
|
||||
PropAttributeArgs::from_meta(&a.parse_meta().unwrap())
|
||||
.unwrap_or_else(|_err| PropAttributeArgs::default())
|
||||
syn::custom_keyword!(ignore);
|
||||
let mut attribute_args = PropAttributeArgs {
|
||||
ignore: None,
|
||||
};
|
||||
a.parse_args_with(|input: ParseStream| {
|
||||
if let Some(_) = input.parse::<Option<ignore>>()? {
|
||||
attribute_args.ignore = Some(true);
|
||||
return Ok(());
|
||||
}
|
||||
Ok(())
|
||||
}).expect("invalid 'property' attribute format");
|
||||
|
||||
attribute_args
|
||||
}),
|
||||
i,
|
||||
)
|
||||
|
@ -73,7 +80,7 @@ pub fn derive_properties(input: TokenStream) -> TokenStream {
|
|||
.map(|(f, _attr, i)| (*f, *i))
|
||||
.collect::<Vec<(&Field, usize)>>();
|
||||
|
||||
let modules = get_modules(&ast);
|
||||
let modules = get_modules();
|
||||
let bevy_property_path = get_path(&modules.bevy_property);
|
||||
|
||||
let field_names = active_fields
|
||||
|
@ -224,7 +231,7 @@ pub fn derive_properties(input: TokenStream) -> TokenStream {
|
|||
#[proc_macro_derive(Property)]
|
||||
pub fn derive_property(input: TokenStream) -> TokenStream {
|
||||
let ast = parse_macro_input!(input as DeriveInput);
|
||||
let modules = get_modules(&ast);
|
||||
let modules = get_modules();
|
||||
let bevy_property_path = get_path(&modules.bevy_property);
|
||||
|
||||
let generics = ast.generics;
|
||||
|
|
|
@ -1,16 +1,6 @@
|
|||
use darling::FromMeta;
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro_crate::crate_name;
|
||||
use syn::{DeriveInput, Path};
|
||||
|
||||
#[derive(FromMeta, Debug, Default)]
|
||||
pub struct ModuleAttributeArgs {
|
||||
pub bevy_property: Option<String>,
|
||||
/// If true, it will use the meta "bevy" crate for dependencies by default (ex: bevy:app). If this is set to false, the individual bevy crates
|
||||
/// will be used (ex: "bevy_app"). Defaults to "true" if the "bevy" crate is in your cargo.toml
|
||||
#[darling(default)]
|
||||
pub meta: Option<bool>,
|
||||
}
|
||||
use syn::Path;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Modules {
|
||||
|
@ -35,32 +25,12 @@ fn use_meta() -> bool {
|
|||
crate_name("bevy").is_ok()
|
||||
}
|
||||
|
||||
pub static MODULE_ATTRIBUTE_NAME: &'static str = "module";
|
||||
|
||||
pub fn get_modules(ast: &DeriveInput) -> Modules {
|
||||
let module_attribute_args = ast
|
||||
.attrs
|
||||
.iter()
|
||||
.find(|a| a.path.get_ident().as_ref().unwrap().to_string() == MODULE_ATTRIBUTE_NAME)
|
||||
.map_or_else(
|
||||
|| ModuleAttributeArgs::default(),
|
||||
|a| {
|
||||
ModuleAttributeArgs::from_meta(&a.parse_meta().unwrap())
|
||||
.unwrap_or_else(|_err| ModuleAttributeArgs::default())
|
||||
},
|
||||
);
|
||||
|
||||
let mut modules = if module_attribute_args.meta.unwrap_or_else(|| use_meta()) {
|
||||
pub fn get_modules() -> Modules {
|
||||
if use_meta() {
|
||||
Modules::meta()
|
||||
} else {
|
||||
Modules::external()
|
||||
};
|
||||
|
||||
if let Some(path) = module_attribute_args.bevy_property {
|
||||
modules.bevy_property = path;
|
||||
}
|
||||
|
||||
modules
|
||||
}
|
||||
|
||||
pub fn get_path(path_str: &str) -> Path {
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use crate::pipeline::AsVertexBufferDescriptor;
|
||||
use bevy_core::bytes::Byteable;
|
||||
|
||||
use crate as bevy_render;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, AsVertexBufferDescriptor)]
|
||||
#[module(bevy_render = "crate")]
|
||||
pub struct Vertex {
|
||||
pub position: [f32; 3],
|
||||
pub normal: [f32; 3],
|
||||
|
|
Loading…
Reference in a new issue