mirror of
https://github.com/bevyengine/bevy
synced 2025-02-16 22:18:33 +00:00
Add macro to implement reflect for struct types and migrate glam types (#4540)
# Objective Relevant issue: #4474 Currently glam types implement Reflect as a value, which is problematic for reflection, making scripting/editor work much more difficult. This PR re-implements them as structs. ## Solution Added a new proc macro, `impl_reflect_struct`, which replaces `impl_reflect_value` and `impl_from_reflect_value` for glam types. This macro could also be used for other types, but I don't know of any that would require it. It's specifically useful for foreign types that cannot derive Reflect normally. --- ## Changelog ### Added - `impl_reflect_struct` proc macro ### Changed - Glam reflect impls have been replaced with `impl_reflect_struct` - from_reflect's `impl_struct` altered to take an optional custom constructor, allowing non-default non-constructible foreign types to use it - Calls to `impl_struct` altered to conform to new signature - Altered glam types (All vec/mat combinations) have a different serialization structure, as they are reflected differently now. ## Migration Guide This will break altered glam types serialized to RON scenes, as they will expect to be serialized/deserialized as structs rather than values now. A future PR to add custom serialization for non-value types is likely on the way to restore previous behavior. Additionally, calls to `impl_struct` must add a `None` parameter to the end of the call to restore previous behavior. Co-authored-by: PROMETHIA-27 <42193387+PROMETHIA-27@users.noreply.github.com>
This commit is contained in:
parent
38a940dbbe
commit
aced6aff04
4 changed files with 470 additions and 50 deletions
|
@ -8,6 +8,7 @@ pub fn impl_struct(
|
||||||
bevy_reflect_path: &Path,
|
bevy_reflect_path: &Path,
|
||||||
active_fields: &[(&Field, usize)],
|
active_fields: &[(&Field, usize)],
|
||||||
ignored_fields: &[(&Field, usize)],
|
ignored_fields: &[(&Field, usize)],
|
||||||
|
custom_constructor: Option<proc_macro2::TokenStream>,
|
||||||
) -> TokenStream {
|
) -> TokenStream {
|
||||||
let field_names = active_fields
|
let field_names = active_fields
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -60,20 +61,35 @@ pub fn impl_struct(
|
||||||
#(#field_types: #bevy_reflect_path::FromReflect,)*
|
#(#field_types: #bevy_reflect_path::FromReflect,)*
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let constructor = if let Some(constructor) = custom_constructor {
|
||||||
|
quote!(
|
||||||
|
let mut value: Self = #constructor;
|
||||||
|
#(
|
||||||
|
value.#field_idents = {
|
||||||
|
<#field_types as #bevy_reflect_path::FromReflect>::from_reflect(#bevy_reflect_path::Struct::field(ref_struct, #field_names)?)?
|
||||||
|
};
|
||||||
|
)*
|
||||||
|
Some(value)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
quote!(
|
||||||
|
Some(
|
||||||
|
Self {
|
||||||
|
#(#field_idents: {
|
||||||
|
<#field_types as #bevy_reflect_path::FromReflect>::from_reflect(#bevy_reflect_path::Struct::field(ref_struct, #field_names)?)?
|
||||||
|
},)*
|
||||||
|
#(#ignored_field_idents: Default::default(),)*
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
TokenStream::from(quote! {
|
TokenStream::from(quote! {
|
||||||
impl #impl_generics #bevy_reflect_path::FromReflect for #struct_name #ty_generics #where_from_reflect_clause
|
impl #impl_generics #bevy_reflect_path::FromReflect for #struct_name #ty_generics #where_from_reflect_clause
|
||||||
{
|
{
|
||||||
fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> Option<Self> {
|
fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> Option<Self> {
|
||||||
use #bevy_reflect_path::Struct;
|
|
||||||
if let #bevy_reflect_path::ReflectRef::Struct(ref_struct) = reflect.reflect_ref() {
|
if let #bevy_reflect_path::ReflectRef::Struct(ref_struct) = reflect.reflect_ref() {
|
||||||
Some(
|
#constructor
|
||||||
Self{
|
|
||||||
#(#field_idents: {
|
|
||||||
<#field_types as #bevy_reflect_path::FromReflect>::from_reflect(ref_struct.field(#field_names)?)?
|
|
||||||
},)*
|
|
||||||
#(#ignored_field_idents: Default::default(),)*
|
|
||||||
}
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
|
@ -287,8 +287,7 @@ fn impl_struct(
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn clone_value(&self) -> Box<dyn #bevy_reflect_path::Reflect> {
|
fn clone_value(&self) -> Box<dyn #bevy_reflect_path::Reflect> {
|
||||||
use #bevy_reflect_path::Struct;
|
Box::new(#bevy_reflect_path::Struct::clone_dynamic(self))
|
||||||
Box::new(self.clone_dynamic())
|
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
fn set(&mut self, value: Box<dyn #bevy_reflect_path::Reflect>) -> Result<(), Box<dyn #bevy_reflect_path::Reflect>> {
|
fn set(&mut self, value: Box<dyn #bevy_reflect_path::Reflect>) -> Result<(), Box<dyn #bevy_reflect_path::Reflect>> {
|
||||||
|
@ -298,11 +297,10 @@ fn impl_struct(
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) {
|
fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) {
|
||||||
use #bevy_reflect_path::Struct;
|
|
||||||
if let #bevy_reflect_path::ReflectRef::Struct(struct_value) = value.reflect_ref() {
|
if let #bevy_reflect_path::ReflectRef::Struct(struct_value) = value.reflect_ref() {
|
||||||
for (i, value) in struct_value.iter_fields().enumerate() {
|
for (i, value) in struct_value.iter_fields().enumerate() {
|
||||||
let name = struct_value.name_at(i).unwrap();
|
let name = struct_value.name_at(i).unwrap();
|
||||||
self.field_mut(name).map(|v| v.apply(value));
|
#bevy_reflect_path::Struct::field_mut(self, name).map(|v| v.apply(value));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
panic!("Attempted to apply non-struct type to struct type.");
|
panic!("Attempted to apply non-struct type to struct type.");
|
||||||
|
@ -607,6 +605,204 @@ pub fn impl_reflect_value(input: TokenStream) -> TokenStream {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents the information needed to implement a type as a Reflect Struct.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```ignore
|
||||||
|
/// impl_reflect_struct!(
|
||||||
|
/// // attrs
|
||||||
|
/// // |----------------------------------------|
|
||||||
|
/// #[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||||
|
/// // type_name generics
|
||||||
|
/// // |-------------------||----------|
|
||||||
|
/// struct ThingThatImReflecting<T1, T2, T3> {
|
||||||
|
/// x: T1, // |
|
||||||
|
/// y: T2, // |- fields
|
||||||
|
/// z: T3 // |
|
||||||
|
/// }
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
struct ReflectStructDef {
|
||||||
|
type_name: Ident,
|
||||||
|
generics: Generics,
|
||||||
|
attrs: ReflectAttrs,
|
||||||
|
fields: Fields,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for ReflectStructDef {
|
||||||
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
|
let ast = input.parse::<DeriveInput>()?;
|
||||||
|
|
||||||
|
let type_name = ast.ident;
|
||||||
|
let generics = ast.generics;
|
||||||
|
let fields = match ast.data {
|
||||||
|
Data::Struct(data) => data.fields,
|
||||||
|
Data::Enum(data) => {
|
||||||
|
return Err(syn::Error::new_spanned(
|
||||||
|
data.enum_token,
|
||||||
|
"Enums are not currently supported for reflection",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
Data::Union(data) => {
|
||||||
|
return Err(syn::Error::new_spanned(
|
||||||
|
data.union_token,
|
||||||
|
"Unions are not supported for reflection",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut attrs = ReflectAttrs::default();
|
||||||
|
for attribute in ast.attrs.iter().filter_map(|attr| attr.parse_meta().ok()) {
|
||||||
|
let meta_list = if let Meta::List(meta_list) = attribute {
|
||||||
|
meta_list
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(ident) = meta_list.path.get_ident() {
|
||||||
|
if ident == REFLECT_ATTRIBUTE_NAME || ident == REFLECT_VALUE_ATTRIBUTE_NAME {
|
||||||
|
attrs = ReflectAttrs::from_nested_metas(&meta_list.nested);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
type_name,
|
||||||
|
generics,
|
||||||
|
attrs,
|
||||||
|
fields,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A replacement for `#[derive(Reflect)]` to be used with foreign types which
|
||||||
|
/// the definitions of cannot be altered.
|
||||||
|
///
|
||||||
|
/// This macro is an alternative to [`impl_reflect_value!`] and [`impl_from_reflect_value!`]
|
||||||
|
/// which implement foreign types as Value types. Note that there is no `impl_from_reflect_struct`,
|
||||||
|
/// as this macro will do the job of both. This macro implements them as `Struct` types,
|
||||||
|
/// which have greater functionality. The type being reflected must be in scope, as you cannot
|
||||||
|
/// qualify it in the macro as e.g. `bevy::prelude::Vec3`.
|
||||||
|
///
|
||||||
|
/// It may be necessary to add `#[reflect(Default)]` for some types, specifically non-constructible
|
||||||
|
/// foreign types. Without `Default` reflected for such types, you will usually get an arcane
|
||||||
|
/// error message and fail to compile. If the type does not implement `Default`, it may not
|
||||||
|
/// be possible to reflect without extending the macro.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// Implementing `Reflect` for `bevy::prelude::Vec3` as a struct type:
|
||||||
|
/// ```ignore
|
||||||
|
/// use bevy::prelude::Vec3;
|
||||||
|
///
|
||||||
|
/// impl_reflect_struct!(
|
||||||
|
/// #[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||||
|
/// struct Vec3 {
|
||||||
|
/// x: f32,
|
||||||
|
/// y: f32,
|
||||||
|
/// z: f32
|
||||||
|
/// }
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn impl_reflect_struct(input: TokenStream) -> TokenStream {
|
||||||
|
let ReflectStructDef {
|
||||||
|
type_name,
|
||||||
|
generics,
|
||||||
|
attrs,
|
||||||
|
fields,
|
||||||
|
} = parse_macro_input!(input as ReflectStructDef);
|
||||||
|
|
||||||
|
let bevy_reflect_path = BevyManifest::default().get_path("bevy_reflect");
|
||||||
|
|
||||||
|
let fields_and_args = fields
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, f)| {
|
||||||
|
(
|
||||||
|
f,
|
||||||
|
f.attrs
|
||||||
|
.iter()
|
||||||
|
.find(|a| *a.path.get_ident().as_ref().unwrap() == REFLECT_ATTRIBUTE_NAME)
|
||||||
|
.map(|a| {
|
||||||
|
syn::custom_keyword!(ignore);
|
||||||
|
let mut attribute_args = PropAttributeArgs { ignore: None };
|
||||||
|
a.parse_args_with(|input: ParseStream| {
|
||||||
|
if input.parse::<Option<ignore>>()?.is_some() {
|
||||||
|
attribute_args.ignore = Some(true);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.expect("Invalid 'property' attribute format.");
|
||||||
|
|
||||||
|
attribute_args
|
||||||
|
}),
|
||||||
|
i,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<Vec<(&Field, Option<PropAttributeArgs>, usize)>>();
|
||||||
|
let active_fields = fields_and_args
|
||||||
|
.iter()
|
||||||
|
.filter(|(_field, attrs, _i)| {
|
||||||
|
attrs.is_none()
|
||||||
|
|| match attrs.as_ref().unwrap().ignore {
|
||||||
|
Some(ignore) => !ignore,
|
||||||
|
None => true,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(|(f, _attr, i)| (*f, *i))
|
||||||
|
.collect::<Vec<(&Field, usize)>>();
|
||||||
|
let ignored_fields = fields_and_args
|
||||||
|
.iter()
|
||||||
|
.filter(|(_field, attrs, _i)| {
|
||||||
|
attrs
|
||||||
|
.as_ref()
|
||||||
|
.map(|attrs| attrs.ignore.unwrap_or(false))
|
||||||
|
.unwrap_or(false)
|
||||||
|
})
|
||||||
|
.map(|(f, _attr, i)| (*f, *i))
|
||||||
|
.collect::<Vec<(&Field, usize)>>();
|
||||||
|
|
||||||
|
let constructor = if attrs
|
||||||
|
.data
|
||||||
|
.contains(&Ident::new("ReflectDefault", Span::call_site()))
|
||||||
|
{
|
||||||
|
Some(quote! { Default::default() })
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let registration_data = &attrs.data;
|
||||||
|
let get_type_registration_impl =
|
||||||
|
impl_get_type_registration(&type_name, &bevy_reflect_path, registration_data, &generics);
|
||||||
|
|
||||||
|
let impl_struct: proc_macro2::TokenStream = impl_struct(
|
||||||
|
&type_name,
|
||||||
|
&generics,
|
||||||
|
&get_type_registration_impl,
|
||||||
|
&bevy_reflect_path,
|
||||||
|
&attrs,
|
||||||
|
&active_fields,
|
||||||
|
)
|
||||||
|
.into();
|
||||||
|
|
||||||
|
let impl_from_struct: proc_macro2::TokenStream = from_reflect::impl_struct(
|
||||||
|
&type_name,
|
||||||
|
&generics,
|
||||||
|
&bevy_reflect_path,
|
||||||
|
&active_fields,
|
||||||
|
&ignored_fields,
|
||||||
|
constructor,
|
||||||
|
)
|
||||||
|
.into();
|
||||||
|
|
||||||
|
TokenStream::from(quote! {
|
||||||
|
#impl_struct
|
||||||
|
|
||||||
|
#impl_from_struct
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct ReflectAttrs {
|
struct ReflectAttrs {
|
||||||
reflect_hash: TraitImpl,
|
reflect_hash: TraitImpl,
|
||||||
|
@ -862,6 +1058,7 @@ pub fn derive_from_reflect(input: TokenStream) -> TokenStream {
|
||||||
&bevy_reflect_path,
|
&bevy_reflect_path,
|
||||||
&active_fields,
|
&active_fields,
|
||||||
&ignored_fields,
|
&ignored_fields,
|
||||||
|
None,
|
||||||
),
|
),
|
||||||
DeriveType::TupleStruct => from_reflect::impl_tuple_struct(
|
DeriveType::TupleStruct => from_reflect::impl_tuple_struct(
|
||||||
type_name,
|
type_name,
|
||||||
|
|
|
@ -1,44 +1,160 @@
|
||||||
use crate as bevy_reflect;
|
use crate as bevy_reflect;
|
||||||
|
use crate::prelude::ReflectDefault;
|
||||||
|
use crate::reflect::Reflect;
|
||||||
use crate::ReflectDeserialize;
|
use crate::ReflectDeserialize;
|
||||||
use bevy_reflect_derive::{impl_from_reflect_value, impl_reflect_value};
|
use bevy_reflect_derive::{impl_from_reflect_value, impl_reflect_struct, impl_reflect_value};
|
||||||
use glam::*;
|
use glam::*;
|
||||||
|
|
||||||
impl_reflect_value!(IVec2(PartialEq, Serialize, Deserialize));
|
impl_reflect_struct!(
|
||||||
impl_reflect_value!(IVec3(PartialEq, Serialize, Deserialize));
|
#[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||||
impl_reflect_value!(IVec4(PartialEq, Serialize, Deserialize));
|
struct IVec2 {
|
||||||
impl_reflect_value!(UVec2(PartialEq, Serialize, Deserialize));
|
x: i32,
|
||||||
impl_reflect_value!(UVec3(PartialEq, Serialize, Deserialize));
|
y: i32,
|
||||||
impl_reflect_value!(UVec4(PartialEq, Serialize, Deserialize));
|
}
|
||||||
impl_reflect_value!(Vec2(PartialEq, Serialize, Deserialize));
|
);
|
||||||
impl_reflect_value!(Vec3(PartialEq, Serialize, Deserialize));
|
impl_reflect_struct!(
|
||||||
impl_reflect_value!(Vec3A(PartialEq, Serialize, Deserialize));
|
#[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||||
impl_reflect_value!(Vec4(PartialEq, Serialize, Deserialize));
|
struct IVec3 {
|
||||||
impl_reflect_value!(DVec2(PartialEq, Serialize, Deserialize));
|
x: i32,
|
||||||
impl_reflect_value!(DVec3(PartialEq, Serialize, Deserialize));
|
y: i32,
|
||||||
impl_reflect_value!(DVec4(PartialEq, Serialize, Deserialize));
|
z: i32,
|
||||||
impl_reflect_value!(Mat3(PartialEq, Serialize, Deserialize));
|
}
|
||||||
impl_reflect_value!(Mat4(PartialEq, Serialize, Deserialize));
|
);
|
||||||
impl_reflect_value!(Quat(PartialEq, Serialize, Deserialize));
|
impl_reflect_struct!(
|
||||||
impl_reflect_value!(DMat3(PartialEq, Serialize, Deserialize));
|
#[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||||
impl_reflect_value!(DMat4(PartialEq, Serialize, Deserialize));
|
struct IVec4 {
|
||||||
impl_reflect_value!(DQuat(PartialEq, Serialize, Deserialize));
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
z: i32,
|
||||||
|
w: i32,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
impl_reflect_struct!(
|
||||||
|
#[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||||
|
struct UVec2 {
|
||||||
|
x: u32,
|
||||||
|
y: u32,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
impl_reflect_struct!(
|
||||||
|
#[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||||
|
struct UVec3 {
|
||||||
|
x: u32,
|
||||||
|
y: u32,
|
||||||
|
z: u32,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
impl_reflect_struct!(
|
||||||
|
#[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||||
|
struct UVec4 {
|
||||||
|
x: u32,
|
||||||
|
y: u32,
|
||||||
|
z: u32,
|
||||||
|
w: u32,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
impl_reflect_struct!(
|
||||||
|
#[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||||
|
struct Vec2 {
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
impl_reflect_struct!(
|
||||||
|
#[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||||
|
struct Vec3 {
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
z: f32,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
impl_reflect_struct!(
|
||||||
|
#[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||||
|
struct Vec3A {
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
z: f32,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
impl_reflect_struct!(
|
||||||
|
#[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||||
|
struct Vec4 {
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
z: f32,
|
||||||
|
w: f32,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
impl_reflect_struct!(
|
||||||
|
#[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||||
|
struct DVec2 {
|
||||||
|
x: f64,
|
||||||
|
y: f64,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
impl_reflect_struct!(
|
||||||
|
#[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||||
|
struct DVec3 {
|
||||||
|
x: f64,
|
||||||
|
y: f64,
|
||||||
|
z: f64,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
impl_reflect_struct!(
|
||||||
|
#[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||||
|
struct DVec4 {
|
||||||
|
x: f64,
|
||||||
|
y: f64,
|
||||||
|
z: f64,
|
||||||
|
w: f64,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
impl_reflect_struct!(
|
||||||
|
#[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||||
|
struct Mat3 {
|
||||||
|
x_axis: Vec3,
|
||||||
|
y_axis: Vec3,
|
||||||
|
z_axis: Vec3,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
impl_reflect_struct!(
|
||||||
|
#[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||||
|
struct Mat4 {
|
||||||
|
x_axis: Vec4,
|
||||||
|
y_axis: Vec4,
|
||||||
|
z_axis: Vec4,
|
||||||
|
w_axis: Vec4,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
impl_reflect_struct!(
|
||||||
|
#[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||||
|
struct DMat3 {
|
||||||
|
x_axis: DVec3,
|
||||||
|
y_axis: DVec3,
|
||||||
|
z_axis: DVec3,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
impl_reflect_struct!(
|
||||||
|
#[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||||
|
struct DMat4 {
|
||||||
|
x_axis: DVec4,
|
||||||
|
y_axis: DVec4,
|
||||||
|
z_axis: DVec4,
|
||||||
|
w_axis: DVec4,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Quat fields are read-only (as of now), and reflection is currently missing
|
||||||
|
// mechanisms for read-only fields. I doubt those mechanisms would be added,
|
||||||
|
// so for now quaternions will remain as values. They are represented identically
|
||||||
|
// to Vec4 and DVec4, so you may use those instead and convert between.
|
||||||
|
impl_reflect_value!(Quat(PartialEq, Serialize, Deserialize, Default));
|
||||||
|
impl_reflect_value!(DQuat(PartialEq, Serialize, Deserialize, Default));
|
||||||
|
|
||||||
impl_from_reflect_value!(IVec2);
|
|
||||||
impl_from_reflect_value!(IVec3);
|
|
||||||
impl_from_reflect_value!(IVec4);
|
|
||||||
impl_from_reflect_value!(UVec2);
|
|
||||||
impl_from_reflect_value!(UVec3);
|
|
||||||
impl_from_reflect_value!(UVec4);
|
|
||||||
impl_from_reflect_value!(Vec2);
|
|
||||||
impl_from_reflect_value!(Vec3);
|
|
||||||
impl_from_reflect_value!(Vec4);
|
|
||||||
impl_from_reflect_value!(Vec3A);
|
|
||||||
impl_from_reflect_value!(DVec2);
|
|
||||||
impl_from_reflect_value!(DVec3);
|
|
||||||
impl_from_reflect_value!(DVec4);
|
|
||||||
impl_from_reflect_value!(Mat3);
|
|
||||||
impl_from_reflect_value!(Mat4);
|
|
||||||
impl_from_reflect_value!(Quat);
|
impl_from_reflect_value!(Quat);
|
||||||
impl_from_reflect_value!(DMat3);
|
|
||||||
impl_from_reflect_value!(DMat4);
|
|
||||||
impl_from_reflect_value!(DQuat);
|
impl_from_reflect_value!(DQuat);
|
||||||
|
|
|
@ -82,7 +82,10 @@ pub mod __macro_exports {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::blacklisted_name, clippy::approx_constant)]
|
#[allow(clippy::blacklisted_name, clippy::approx_constant)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#[cfg(feature = "glam")]
|
||||||
|
use ::glam::{vec3, Vec3};
|
||||||
use ::serde::de::DeserializeSeed;
|
use ::serde::de::DeserializeSeed;
|
||||||
|
use ::serde::Serialize;
|
||||||
use bevy_utils::HashMap;
|
use bevy_utils::HashMap;
|
||||||
use ron::{
|
use ron::{
|
||||||
ser::{to_string_pretty, PrettyConfig},
|
ser::{to_string_pretty, PrettyConfig},
|
||||||
|
@ -471,4 +474,92 @@ mod tests {
|
||||||
// Should compile:
|
// Should compile:
|
||||||
let _ = trait_object.as_reflect();
|
let _ = trait_object.as_reflect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "glam")]
|
||||||
|
mod glam {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn vec3_serialization() {
|
||||||
|
let v = vec3(12.0, 3.0, -6.9);
|
||||||
|
|
||||||
|
let mut registry = TypeRegistry::default();
|
||||||
|
registry.add_registration(Vec3::get_type_registration());
|
||||||
|
|
||||||
|
let ser = ReflectSerializer::new(&v, ®istry);
|
||||||
|
|
||||||
|
let mut dest = vec![];
|
||||||
|
let mut serializer = ron::ser::Serializer::new(&mut dest, None, false)
|
||||||
|
.expect("Failed to acquire serializer");
|
||||||
|
|
||||||
|
ser.serialize(&mut serializer).expect("Failed to serialize");
|
||||||
|
|
||||||
|
let result = String::from_utf8(dest).expect("Failed to convert to string");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
result,
|
||||||
|
r#"{"type":"glam::vec3::Vec3","struct":{"x":{"type":"f32","value":12},"y":{"type":"f32","value":3},"z":{"type":"f32","value":-6.9}}}"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn vec3_deserialization() {
|
||||||
|
let data = r#"{"type":"glam::vec3::Vec3","struct":{"x":{"type":"f32","value":12},"y":{"type":"f32","value":3},"z":{"type":"f32","value":-6.9}}}"#;
|
||||||
|
|
||||||
|
let mut registry = TypeRegistry::default();
|
||||||
|
registry.add_registration(Vec3::get_type_registration());
|
||||||
|
registry.add_registration(f32::get_type_registration());
|
||||||
|
|
||||||
|
let de = ReflectDeserializer::new(®istry);
|
||||||
|
|
||||||
|
let mut deserializer =
|
||||||
|
ron::de::Deserializer::from_str(data).expect("Failed to acquire deserializer");
|
||||||
|
|
||||||
|
let dynamic_struct = de
|
||||||
|
.deserialize(&mut deserializer)
|
||||||
|
.expect("Failed to deserialize");
|
||||||
|
|
||||||
|
let mut result = Vec3::default();
|
||||||
|
|
||||||
|
result.apply(&*dynamic_struct);
|
||||||
|
|
||||||
|
assert_eq!(result, vec3(12.0, 3.0, -6.9));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn vec3_field_access() {
|
||||||
|
let mut v = vec3(1.0, 2.0, 3.0);
|
||||||
|
|
||||||
|
assert_eq!(*v.get_field::<f32>("x").unwrap(), 1.0);
|
||||||
|
|
||||||
|
*v.get_field_mut::<f32>("y").unwrap() = 6.0;
|
||||||
|
|
||||||
|
assert_eq!(v.y, 6.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn vec3_path_access() {
|
||||||
|
let mut v = vec3(1.0, 2.0, 3.0);
|
||||||
|
|
||||||
|
assert_eq!(*v.path("x").unwrap().downcast_ref::<f32>().unwrap(), 1.0);
|
||||||
|
|
||||||
|
*v.path_mut("y").unwrap().downcast_mut::<f32>().unwrap() = 6.0;
|
||||||
|
|
||||||
|
assert_eq!(v.y, 6.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn vec3_apply_dynamic() {
|
||||||
|
let mut v = vec3(3.0, 3.0, 3.0);
|
||||||
|
|
||||||
|
let mut d = DynamicStruct::default();
|
||||||
|
d.insert("x", 4.0f32);
|
||||||
|
d.insert("y", 2.0f32);
|
||||||
|
d.insert("z", 1.0f32);
|
||||||
|
|
||||||
|
v.apply(&d);
|
||||||
|
|
||||||
|
assert_eq!(v, vec3(4.0, 2.0, 1.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue