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
					
				| @ -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…
	
		Reference in New Issue
	
	Block a user
	 PROMETHIA-27
						PROMETHIA-27