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:
PROMETHIA-27 2022-05-09 16:32:15 +00:00
parent 38a940dbbe
commit aced6aff04
4 changed files with 470 additions and 50 deletions

View file

@ -8,6 +8,7 @@ pub fn impl_struct(
bevy_reflect_path: &Path,
active_fields: &[(&Field, usize)],
ignored_fields: &[(&Field, usize)],
custom_constructor: Option<proc_macro2::TokenStream>,
) -> TokenStream {
let field_names = active_fields
.iter()
@ -60,20 +61,35 @@ pub fn impl_struct(
#(#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! {
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> {
use #bevy_reflect_path::Struct;
if let #bevy_reflect_path::ReflectRef::Struct(ref_struct) = reflect.reflect_ref() {
Some(
Self{
#(#field_idents: {
<#field_types as #bevy_reflect_path::FromReflect>::from_reflect(ref_struct.field(#field_names)?)?
},)*
#(#ignored_field_idents: Default::default(),)*
}
)
#constructor
} else {
None
}

View file

@ -287,8 +287,7 @@ fn impl_struct(
#[inline]
fn clone_value(&self) -> Box<dyn #bevy_reflect_path::Reflect> {
use #bevy_reflect_path::Struct;
Box::new(self.clone_dynamic())
Box::new(#bevy_reflect_path::Struct::clone_dynamic(self))
}
#[inline]
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]
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() {
for (i, value) in struct_value.iter_fields().enumerate() {
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 {
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)]
struct ReflectAttrs {
reflect_hash: TraitImpl,
@ -862,6 +1058,7 @@ pub fn derive_from_reflect(input: TokenStream) -> TokenStream {
&bevy_reflect_path,
&active_fields,
&ignored_fields,
None,
),
DeriveType::TupleStruct => from_reflect::impl_tuple_struct(
type_name,

View file

@ -1,44 +1,160 @@
use crate as bevy_reflect;
use crate::prelude::ReflectDefault;
use crate::reflect::Reflect;
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::*;
impl_reflect_value!(IVec2(PartialEq, Serialize, Deserialize));
impl_reflect_value!(IVec3(PartialEq, Serialize, Deserialize));
impl_reflect_value!(IVec4(PartialEq, Serialize, Deserialize));
impl_reflect_value!(UVec2(PartialEq, Serialize, Deserialize));
impl_reflect_value!(UVec3(PartialEq, Serialize, Deserialize));
impl_reflect_value!(UVec4(PartialEq, Serialize, Deserialize));
impl_reflect_value!(Vec2(PartialEq, Serialize, Deserialize));
impl_reflect_value!(Vec3(PartialEq, Serialize, Deserialize));
impl_reflect_value!(Vec3A(PartialEq, Serialize, Deserialize));
impl_reflect_value!(Vec4(PartialEq, Serialize, Deserialize));
impl_reflect_value!(DVec2(PartialEq, Serialize, Deserialize));
impl_reflect_value!(DVec3(PartialEq, Serialize, Deserialize));
impl_reflect_value!(DVec4(PartialEq, Serialize, Deserialize));
impl_reflect_value!(Mat3(PartialEq, Serialize, Deserialize));
impl_reflect_value!(Mat4(PartialEq, Serialize, Deserialize));
impl_reflect_value!(Quat(PartialEq, Serialize, Deserialize));
impl_reflect_value!(DMat3(PartialEq, Serialize, Deserialize));
impl_reflect_value!(DMat4(PartialEq, Serialize, Deserialize));
impl_reflect_value!(DQuat(PartialEq, Serialize, Deserialize));
impl_reflect_struct!(
#[reflect(PartialEq, Serialize, Deserialize, Default)]
struct IVec2 {
x: i32,
y: i32,
}
);
impl_reflect_struct!(
#[reflect(PartialEq, Serialize, Deserialize, Default)]
struct IVec3 {
x: i32,
y: i32,
z: i32,
}
);
impl_reflect_struct!(
#[reflect(PartialEq, Serialize, Deserialize, Default)]
struct IVec4 {
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!(DMat3);
impl_from_reflect_value!(DMat4);
impl_from_reflect_value!(DQuat);

View file

@ -82,7 +82,10 @@ pub mod __macro_exports {
#[cfg(test)]
#[allow(clippy::blacklisted_name, clippy::approx_constant)]
mod tests {
#[cfg(feature = "glam")]
use ::glam::{vec3, Vec3};
use ::serde::de::DeserializeSeed;
use ::serde::Serialize;
use bevy_utils::HashMap;
use ron::{
ser::{to_string_pretty, PrettyConfig},
@ -471,4 +474,92 @@ mod tests {
// Should compile:
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, &registry);
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(&registry);
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));
}
}
}