Expose structs to Lua and write Lua type annotations #28
|
@ -1,10 +1,10 @@
|
||||||
use quote::{format_ident, quote};
|
use quote::{format_ident, quote};
|
||||||
use syn::{braced, parenthesized, parse_macro_input, token, Ident, Path, Token};
|
use syn::{braced, parenthesized, parse_macro_input, token, Ident, Path, Token};
|
||||||
|
|
||||||
struct FieldGetter {
|
pub(crate) struct FieldGetter {
|
||||||
field: Ident,
|
pub field: Ident,
|
||||||
body: Option<syn::Block>,
|
pub body: Option<syn::Block>,
|
||||||
wrapper_type: Option<syn::Path>,
|
pub wrapper_type: Option<syn::Path>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FieldGetter {
|
impl FieldGetter {
|
||||||
|
@ -131,6 +131,7 @@ impl syn::parse::Parse for HandleWrapUsage {
|
||||||
pub(crate) fn lua_wrap_handle_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
pub(crate) fn lua_wrap_handle_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
let input = parse_macro_input!(input as HandleWrapUsage);
|
let input = parse_macro_input!(input as HandleWrapUsage);
|
||||||
|
|
||||||
|
let handle_path = &input.type_path;
|
||||||
let handle_name = &input.type_path.segments.last().unwrap().ident;
|
let handle_name = &input.type_path.segments.last().unwrap().ident;
|
||||||
|
|
||||||
let base_name = input.override_name.unwrap_or_else(|| handle_name.clone());
|
let base_name = input.override_name.unwrap_or_else(|| handle_name.clone());
|
||||||
|
@ -145,7 +146,7 @@ pub(crate) fn lua_wrap_handle_impl(input: proc_macro::TokenStream) -> proc_macro
|
||||||
|
|
||||||
let field_creator = match &g.wrapper_type {
|
let field_creator = match &g.wrapper_type {
|
||||||
Some(wrap) => {
|
Some(wrap) => {
|
||||||
quote!(#wrap(data.#field).into_lua(lua))
|
quote!(#wrap(data.#field.clone()).into_lua(lua))
|
||||||
},
|
},
|
||||||
None => match &g.body {
|
None => match &g.body {
|
||||||
Some(body) => {
|
Some(body) => {
|
||||||
|
@ -170,18 +171,18 @@ pub(crate) fn lua_wrap_handle_impl(input: proc_macro::TokenStream) -> proc_macro
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
#[derive(Clone, Reflect)]
|
#[derive(Clone, Reflect)]
|
||||||
pub struct #wrapper_name(pub ResHandle<#handle_name>);
|
pub struct #wrapper_name(pub ResHandle<#handle_path>);
|
||||||
|
|
||||||
impl Deref for #wrapper_name {
|
impl Deref for #wrapper_name {
|
||||||
type Target = ResHandle<#handle_name>;
|
type Target = ResHandle<#handle_path>;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ResHandle<#handle_name>> for #wrapper_name {
|
impl From<ResHandle<#handle_path>> for #wrapper_name {
|
||||||
fn from(value: ResHandle<#handle_name>) -> Self {
|
fn from(value: ResHandle<#handle_path>) -> Self {
|
||||||
#wrapper_name(value)
|
#wrapper_name(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -224,7 +225,7 @@ pub(crate) fn lua_wrap_handle_impl(input: proc_macro::TokenStream) -> proc_macro
|
||||||
});
|
});
|
||||||
|
|
||||||
methods.add_function(FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| {
|
methods.add_function(FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| {
|
||||||
Ok(ScriptBorrow::from_component::<ResHandle<#handle_name>>(None))
|
Ok(ScriptBorrow::from_component::<ResHandle<#handle_path>>(None))
|
||||||
});
|
});
|
||||||
methods.add_method(FN_NAME_INTERNAL_REFLECT, |_, this, ()| {
|
methods.add_method(FN_NAME_INTERNAL_REFLECT, |_, this, ()| {
|
||||||
Ok(ScriptBorrow::from_component(Some(this.0.clone())))
|
Ok(ScriptBorrow::from_component(Some(this.0.clone())))
|
||||||
|
@ -247,7 +248,7 @@ pub(crate) fn lua_wrap_handle_impl(input: proc_macro::TokenStream) -> proc_macro
|
||||||
|
|
||||||
impl LuaWrapper for #wrapper_name {
|
impl LuaWrapper for #wrapper_name {
|
||||||
fn wrapped_type_id() -> std::any::TypeId {
|
fn wrapped_type_id() -> std::any::TypeId {
|
||||||
TypeId::of::<ResHandle<#handle_name>>()
|
TypeId::of::<ResHandle<#handle_path>>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.into()
|
}.into()
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use proc_macro2::Span;
|
use proc_macro2::Span;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::{parenthesized, parse_macro_input, punctuated::Punctuated, token, Ident, Path, Token};
|
use syn::{braced, parenthesized, parse_macro_input, punctuated::Punctuated, token, Ident, Path, Token};
|
||||||
|
|
||||||
use crate::{FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE};
|
use crate::{handle_macro::FieldGetter, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
enum SkipType {
|
enum SkipType {
|
||||||
|
@ -297,6 +297,7 @@ struct WrapUsage {
|
||||||
/// The extra derives of the type.
|
/// The extra derives of the type.
|
||||||
override_name: Option<Ident>,
|
override_name: Option<Ident>,
|
||||||
auto_fields: Vec<Ident>,
|
auto_fields: Vec<Ident>,
|
||||||
|
manual_field_getters: Vec<FieldGetter>,
|
||||||
auto_derives: Vec<Ident>,
|
auto_derives: Vec<Ident>,
|
||||||
auto_new: bool,
|
auto_new: bool,
|
||||||
meta_methods: Vec<MetaMethod>,
|
meta_methods: Vec<MetaMethod>,
|
||||||
|
@ -314,6 +315,7 @@ impl syn::parse::Parse for WrapUsage {
|
||||||
override_name: None,
|
override_name: None,
|
||||||
auto_fields: vec![],
|
auto_fields: vec![],
|
||||||
auto_derives: vec![],
|
auto_derives: vec![],
|
||||||
|
manual_field_getters: vec![],
|
||||||
extra_fields: None,
|
extra_fields: None,
|
||||||
extra_methods: None,
|
extra_methods: None,
|
||||||
auto_new: false,
|
auto_new: false,
|
||||||
|
@ -384,6 +386,17 @@ impl syn::parse::Parse for WrapUsage {
|
||||||
let _eq: Token![=] = input.parse()?;
|
let _eq: Token![=] = input.parse()?;
|
||||||
s.extra_methods = Some(input.parse::<syn::Block>()?);
|
s.extra_methods = Some(input.parse::<syn::Block>()?);
|
||||||
},
|
},
|
||||||
|
"field_getters" => {
|
||||||
|
let _eq: Token![=] = input.parse()?;
|
||||||
|
|
||||||
|
if input.peek(token::Brace) {
|
||||||
|
let content;
|
||||||
|
let _braced: token::Brace = braced!(content in input);
|
||||||
|
|
||||||
|
let terminated = content.parse_terminated(FieldGetter::parse, Token![,])?;
|
||||||
|
s.manual_field_getters = terminated.into_iter().collect();
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(syn::Error::new_spanned(ident, format!("unknown wrapper command: '{}'", ident_str)));
|
return Err(syn::Error::new_spanned(ident, format!("unknown wrapper command: '{}'", ident_str)));
|
||||||
}
|
}
|
||||||
|
@ -469,6 +482,30 @@ pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::Token
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let custom_getters = input.manual_field_getters.iter().map(|g| {
|
||||||
|
let field = &g.field;
|
||||||
|
|
||||||
|
let field_creator = match &g.wrapper_type {
|
||||||
|
Some(wrap) => {
|
||||||
|
quote!(#wrap(this.#field).into_lua(lua))
|
||||||
|
},
|
||||||
|
None => match &g.body {
|
||||||
|
Some(body) => {
|
||||||
|
quote!(#body)
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
quote!(this.#field.clone().into_lua(lua))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
fields.add_field_method_get(stringify!($field), |lua, this| {
|
||||||
|
#field_creator
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
proc_macro::TokenStream::from(quote! {
|
proc_macro::TokenStream::from(quote! {
|
||||||
#[derive(Clone, lyra_reflect::Reflect, #(#derive_idents_iter),*)]
|
#[derive(Clone, lyra_reflect::Reflect, #(#derive_idents_iter),*)]
|
||||||
pub struct #wrapper_typename(#[reflect(skip)] pub(crate) #path);
|
pub struct #wrapper_typename(#[reflect(skip)] pub(crate) #path);
|
||||||
|
@ -499,6 +536,7 @@ pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::Token
|
||||||
impl mlua::UserData for #wrapper_typename {
|
impl mlua::UserData for #wrapper_typename {
|
||||||
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
|
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
|
||||||
#(#field_get_set_pairs)*
|
#(#field_get_set_pairs)*
|
||||||
|
#(#custom_getters)*
|
||||||
|
|
||||||
#extra_fields
|
#extra_fields
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
---@enum ActionState
|
|
||||||
ActionState = {
|
|
||||||
IDLE = "idle",
|
|
||||||
PRESSED = "pressed",
|
|
||||||
JUST_PRESSED = "just_pressed",
|
|
||||||
JUST_RELEASED = "just_released",
|
|
||||||
AXIS = "axis",
|
|
||||||
OTHER = "other",
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
---@enum HandleState
|
|
||||||
HandleState = {
|
|
||||||
LOADING = "loading",
|
|
||||||
READY = "ready",
|
|
||||||
ERROR = "error",
|
|
||||||
}
|
|
||||||
|
|
||||||
---@enum ActionKind
|
|
||||||
ActionKind = {
|
|
||||||
BUTTON = "button",
|
|
||||||
AXIS = "axis",
|
|
||||||
}
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
---@enum WindowMode
|
||||||
|
WindowMode = {
|
||||||
|
WNDOWED = "windowed",
|
||||||
|
BORDERLESS_FULLSCREEN = "borderless_fullscreen",
|
||||||
|
SIZED_FULLSCREEN = "sized_fullscreen",
|
||||||
|
FULLSCREEN = "fullscreen",
|
||||||
|
}
|
||||||
|
|
||||||
|
---@enum CursorGrabMode
|
||||||
|
CursorGrabMode = {
|
||||||
|
NONE = "none",
|
||||||
|
CONFINED = "confined",
|
||||||
|
LOCKED = "locked",
|
||||||
|
}
|
||||||
|
|
||||||
|
---@enum WindowTheme
|
||||||
|
WindowTheme = {
|
||||||
|
LIGHT = "light",
|
||||||
|
DARK = "dark",
|
||||||
|
}
|
||||||
|
|
||||||
|
---@enum WindowLevel
|
||||||
|
WindowLevel = {
|
||||||
|
ALWAYS_ON_BOTTOM = "always_on_bottom",
|
||||||
|
NORMAL = "normal",
|
||||||
|
ALWAYS_ON_TOP = "always_on_top",
|
||||||
|
}
|
||||||
|
|
||||||
|
---@enum HandleState
|
||||||
|
HandleState = {
|
||||||
|
LOADING = "loading",
|
||||||
|
READY = "ready",
|
||||||
|
ERROR = "error",
|
||||||
|
}
|
||||||
|
|
||||||
|
---@enum ActionKind
|
||||||
|
ActionKind = {
|
||||||
|
BUTTON = "button",
|
||||||
|
AXIS = "axis",
|
||||||
|
}
|
||||||
|
|
||||||
|
---@enum ActionState
|
||||||
|
ActionState = {
|
||||||
|
IDLE = "idle",
|
||||||
|
PRESSED = "pressed",
|
||||||
|
JUST_PRESSED = "just_pressed",
|
||||||
|
JUST_RELEASED = "just_released",
|
||||||
|
AXIS = "axis",
|
||||||
|
OTHER = "other",
|
||||||
|
}
|
||||||
|
|
||||||
|
---@enum FilterMode
|
||||||
|
FilterMode = {
|
||||||
|
NEAREST = "nearest",
|
||||||
|
LINEAR = "linear",
|
||||||
|
}
|
||||||
|
|
||||||
|
---@enum WrappingMode
|
||||||
|
WrappingMode = {
|
||||||
|
CLAMP_TO_EDGE = "clamp_to_edge",
|
||||||
|
MIRRORED_REPEAT = "mirrored_repeat",
|
||||||
|
REPEAT = "repeat",
|
||||||
|
}
|
|
@ -1,27 +0,0 @@
|
||||||
---@enum WindowMode
|
|
||||||
WindowMode = {
|
|
||||||
WNDOWED = "windowed",
|
|
||||||
BORDERLESS_FULLSCREEN = "borderless_fullscreen",
|
|
||||||
SIZED_FULLSCREEN = "sized_fullscreen",
|
|
||||||
FULLSCREEN = "fullscreen",
|
|
||||||
}
|
|
||||||
|
|
||||||
---@enum CursorGrabMode
|
|
||||||
CursorGrabMode = {
|
|
||||||
NONE = "none",
|
|
||||||
CONFINED = "confined",
|
|
||||||
LOCKED = "locked",
|
|
||||||
}
|
|
||||||
|
|
||||||
---@enum WindowTheme
|
|
||||||
WindowTheme = {
|
|
||||||
LIGHT = "light",
|
|
||||||
DARK = "dark",
|
|
||||||
}
|
|
||||||
|
|
||||||
---@enum WindowLevel
|
|
||||||
WindowLevel = {
|
|
||||||
ALWAYS_ON_BOTTOM = "always_on_bottom",
|
|
||||||
NORMAL = "normal",
|
|
||||||
ALWAYS_ON_TOP = "always_on_top",
|
|
||||||
}
|
|
|
@ -40,12 +40,8 @@ impl ScriptApiProvider for LyraEcsApiProvider {
|
||||||
fn expose_api(&mut self, _: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> {
|
fn expose_api(&mut self, _: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> {
|
||||||
let ctx = ctx.lock().unwrap();
|
let ctx = ctx.lock().unwrap();
|
||||||
|
|
||||||
// load window enums
|
// load enums
|
||||||
let bytes = include_str!("../../../scripts/lua/window.lua");
|
let bytes = include_str!("../../../scripts/lua/enums.lua");
|
||||||
ctx.load(bytes).exec().unwrap();
|
|
||||||
|
|
||||||
// load asset handle enums
|
|
||||||
let bytes = include_str!("../../../scripts/lua/asset_handle.lua");
|
|
||||||
ctx.load(bytes).exec().unwrap();
|
ctx.load(bytes).exec().unwrap();
|
||||||
|
|
||||||
let globals = ctx.globals();
|
let globals = ctx.globals();
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{any::TypeId, ops::Deref};
|
use std::{any::TypeId, ops::Deref};
|
||||||
//use mlua::{AnyUserData, IntoLua, FromLua, Lua, Value};
|
//use mlua::{AnyUserData, IntoLua, FromLua, Lua, Value};
|
||||||
use lyra_resource::{gltf::{Gltf, Material, Mesh}, Texture, ResHandle, UntypedResHandle};
|
use lyra_resource::{gltf::{Gltf, Material, Mesh}, FilterMode, ResHandle, Texture, UntypedResHandle, WrappingMode};
|
||||||
use lyra_game::scene::SceneGraph;
|
use lyra_game::scene::SceneGraph;
|
||||||
use lyra_reflect::{Reflect, TypeData};
|
use lyra_reflect::{Reflect, TypeData};
|
||||||
use lyra_scripting_derive::{lua_wrap_handle, wrap_lua_struct};
|
use lyra_scripting_derive::{lua_wrap_handle, wrap_lua_struct};
|
||||||
|
@ -111,15 +111,87 @@ impl mlua::FromLua for LuaResHandle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: fields
|
fn filter_mode_to_str(fm: FilterMode) -> &'static str {
|
||||||
wrap_lua_struct!(lyra_resource::gltf::PbrGlossiness, skip(lua_reflect)); // doesn't need internal lua reflection methods
|
match fm {
|
||||||
// TODO: fields
|
FilterMode::Nearest => "nearest",
|
||||||
wrap_lua_struct!(lyra_resource::gltf::Specular, skip(lua_reflect)); // doesn't need internal lua reflection methods
|
FilterMode::Linear => "linear",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wrapping_mode_to_str(wm: WrappingMode) -> &'static str {
|
||||||
|
match wm {
|
||||||
|
WrappingMode::ClampToEdge => "clamp_to_edge",
|
||||||
|
WrappingMode::MirroredRepeat => "mirrored_repeat",
|
||||||
|
WrappingMode::Repeat => "repeat",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wrap_lua_struct!(lyra_resource::TextureSampler,
|
||||||
|
// this can be safely skipped since it wont be a component or resource.
|
||||||
|
skip(lua_reflect),
|
||||||
|
field_getters={
|
||||||
|
(mag_filter, {
|
||||||
|
this.mag_filter.map(|f| filter_mode_to_str(f))
|
||||||
|
.into_lua(lua)
|
||||||
|
}),
|
||||||
|
(min_filter, {
|
||||||
|
this.min_filter.map(|f| filter_mode_to_str(f))
|
||||||
|
.into_lua(lua)
|
||||||
|
}),
|
||||||
|
(mipmap_filter, {
|
||||||
|
this.mipmap_filter.map(|f| filter_mode_to_str(f))
|
||||||
|
.into_lua(lua)
|
||||||
|
}),
|
||||||
|
(wrap_u, {
|
||||||
|
wrapping_mode_to_str(this.wrap_u)
|
||||||
|
.into_lua(lua)
|
||||||
|
}),
|
||||||
|
(wrap_v, {
|
||||||
|
wrapping_mode_to_str(this.wrap_v)
|
||||||
|
.into_lua(lua)
|
||||||
|
}),
|
||||||
|
(wrap_w, {
|
||||||
|
wrapping_mode_to_str(this.wrap_w)
|
||||||
|
.into_lua(lua)
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
wrap_lua_struct!(lyra_resource::gltf::PbrGlossiness,
|
||||||
|
// this can be safely skipped since it wont be a component or resource.
|
||||||
|
skip(lua_reflect),
|
||||||
|
field_getters={
|
||||||
|
(diffuse_color, wrapper=crate::lua::wrappers::LuaVec4),
|
||||||
|
(specular, wrapper=crate::lua::wrappers::LuaVec3),
|
||||||
|
glossiness,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
wrap_lua_struct!(lyra_resource::gltf::Specular,
|
||||||
|
// this can be safely skipped since it wont be a component or resource.
|
||||||
|
skip(lua_reflect),
|
||||||
|
field_getters={
|
||||||
|
factor,
|
||||||
|
(color_factor, wrapper=crate::lua::wrappers::LuaVec3),
|
||||||
|
},
|
||||||
|
extra_fields={
|
||||||
|
fields.add_field_method_get("texture", |lua, this| {
|
||||||
|
this.texture.clone()
|
||||||
|
.map(|t| LuaTextureHandle(t))
|
||||||
|
.into_lua(lua)
|
||||||
|
});
|
||||||
|
fields.add_field_method_get("color_texture", |lua, this| {
|
||||||
|
this.color_texture.clone()
|
||||||
|
.map(|t| LuaTextureHandle(t))
|
||||||
|
.into_lua(lua)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// TODO: fields
|
// TODO: fields
|
||||||
lua_wrap_handle!(SceneGraph, name=Scene, {});
|
lua_wrap_handle!(SceneGraph, name=Scene, {});
|
||||||
|
|
||||||
lua_wrap_handle!(Mesh,
|
lua_wrap_handle!(Mesh,
|
||||||
field_getters={
|
field_getters={
|
||||||
(material, {
|
(material, {
|
||||||
data.material.clone()
|
data.material.clone()
|
||||||
|
@ -154,10 +226,32 @@ lua_wrap_handle!(Mesh,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: attributes
|
// TODO: mesh attributes
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
lua_wrap_handle!(Texture, {});
|
|
||||||
|
lua_wrap_handle!(lyra_resource::Image,
|
||||||
|
field_getters={
|
||||||
|
(width, {
|
||||||
|
data.width().into_lua(lua)
|
||||||
|
}),
|
||||||
|
(height, {
|
||||||
|
data.height().into_lua(lua)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
lua_wrap_handle!(Texture,
|
||||||
|
field_getters={
|
||||||
|
(image, wrapper=LuaImageHandle),
|
||||||
|
(sampler, {
|
||||||
|
data.sampler.clone()
|
||||||
|
.map(|s| LuaTextureSampler(s))
|
||||||
|
.into_lua(lua)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
lua_wrap_handle!(Material,
|
lua_wrap_handle!(Material,
|
||||||
field_getters={
|
field_getters={
|
||||||
|
@ -185,7 +279,6 @@ lua_wrap_handle!(Material,
|
||||||
alpha_cutoff,
|
alpha_cutoff,
|
||||||
(alpha_mode, {
|
(alpha_mode, {
|
||||||
match data.alpha_mode {
|
match data.alpha_mode {
|
||||||
// TODO: Lua enums
|
|
||||||
lyra_resource::gltf::AlphaMode::Opaque => "opaque",
|
lyra_resource::gltf::AlphaMode::Opaque => "opaque",
|
||||||
lyra_resource::gltf::AlphaMode::Mask => "mask",
|
lyra_resource::gltf::AlphaMode::Mask => "mask",
|
||||||
lyra_resource::gltf::AlphaMode::Blend => "blend",
|
lyra_resource::gltf::AlphaMode::Blend => "blend",
|
||||||
|
|
Loading…
Reference in New Issue