Skip to content

Commit

Permalink
add #[reflect(Default)] to create default value for reflected types (
Browse files Browse the repository at this point in the history
…bevyengine#3733)

### Problem
It currently isn't possible to construct the default value of a reflected type. Because of that, it isn't possible to use `add_component` of `ReflectComponent` to add a new component to an entity because you can't know what the initial value should be.

### Solution

1. add `ReflectDefault` type
```rust
#[derive(Clone)]
pub struct ReflectDefault {
    default: fn() -> Box<dyn Reflect>,
}

impl ReflectDefault {
    pub fn default(&self) -> Box<dyn Reflect> {
        (self.default)()
    }
}

impl<T: Reflect + Default> FromType<T> for ReflectDefault {
    fn from_type() -> Self {
        ReflectDefault {
            default: || Box::new(T::default()),
        }
    }
}
```

2. add `#[reflect(Default)]` to all component types that implement `Default` and are user facing (so not `ComputedSize`, `CubemapVisibleEntities` etc.)



This makes it possible to add the default value of a component to an entity without any compile-time information:

```rust
fn main() {
    let mut app = App::new();
    app.register_type::<Camera>();

    let type_registry = app.world.get_resource::<TypeRegistry>().unwrap();
    let type_registry = type_registry.read();

    let camera_registration = type_registry.get(std::any::TypeId::of::<Camera>()).unwrap();
    let reflect_default = camera_registration.data::<ReflectDefault>().unwrap();
    let reflect_component = camera_registration
        .data::<ReflectComponent>()
        .unwrap()
        .clone();

    let default = reflect_default.default();

    drop(type_registry);

    let entity = app.world.spawn().id();
    reflect_component.add_component(&mut app.world, entity, &*default);

    let camera = app.world.entity(entity).get::<Camera>().unwrap();
    dbg!(&camera);
}
```

### Open questions
- should we have `ReflectDefault` or `ReflectFromWorld` or both?
  • Loading branch information
jakobhellermann authored and exjam committed May 22, 2022
1 parent c523466 commit 23f21b0
Show file tree
Hide file tree
Showing 17 changed files with 65 additions and 26 deletions.
3 changes: 2 additions & 1 deletion crates/bevy_core/src/name.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use bevy_ecs::{component::Component, reflect::ReflectComponent};
use bevy_reflect::std_traits::ReflectDefault;
use bevy_reflect::Reflect;
use bevy_utils::AHasher;
use std::{
Expand All @@ -14,7 +15,7 @@ use std::{
/// as multiple entities can have the same name. [`bevy_ecs::entity::Entity`] should be
/// used instead as the default unique identifier.
#[derive(Component, Debug, Clone, Reflect)]
#[reflect(Component)]
#[reflect(Component, Default)]
pub struct Name {
hash: u64, // TODO: Shouldn't be serialized
name: Cow<'static, str>,
Expand Down
2 changes: 2 additions & 0 deletions crates/bevy_core/src/time/stopwatch.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use bevy_reflect::prelude::*;
use bevy_reflect::Reflect;
use bevy_utils::Duration;

Expand All @@ -23,6 +24,7 @@ use bevy_utils::Duration;
/// assert_eq!(stopwatch.elapsed_secs(), 0.0);
/// ```
#[derive(Clone, Debug, Default, Reflect)]
#[reflect(Default)]
pub struct Stopwatch {
elapsed: Duration,
paused: bool,
Expand Down
3 changes: 2 additions & 1 deletion crates/bevy_core/src/time/timer.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::Stopwatch;
use bevy_reflect::Reflect;
use bevy_reflect::prelude::*;
use bevy_utils::Duration;

/// Tracks elapsed time. Enters the finished state once `duration` is reached.
Expand All @@ -10,6 +10,7 @@ use bevy_utils::Duration;
///
/// Paused timers will not have elapsed time increased.
#[derive(Clone, Debug, Default, Reflect)]
#[reflect(Default)]
pub struct Timer {
stopwatch: Stopwatch,
duration: Duration,
Expand Down
3 changes: 2 additions & 1 deletion crates/bevy_pbr/src/alpha.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use bevy_ecs::{component::Component, reflect::ReflectComponent};
use bevy_reflect::std_traits::ReflectDefault;
use bevy_reflect::Reflect;

// FIXME: This should probably be part of bevy_render2!
/// Alpha mode
#[derive(Component, Debug, Reflect, Copy, Clone, PartialEq)]
#[reflect(Component)]
#[reflect(Component, Default)]
pub enum AlphaMode {
Opaque,
/// An alpha cutoff must be supplied where alpha values >= the cutoff
Expand Down
12 changes: 7 additions & 5 deletions crates/bevy_pbr/src/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::collections::HashSet;
use bevy_asset::Assets;
use bevy_ecs::prelude::*;
use bevy_math::{Mat4, UVec2, UVec3, Vec2, Vec3, Vec3A, Vec3Swizzles, Vec4, Vec4Swizzles};
use bevy_reflect::Reflect;
use bevy_reflect::prelude::*;
use bevy_render::{
camera::{Camera, CameraProjection, OrthographicProjection},
color::Color,
Expand Down Expand Up @@ -41,7 +41,7 @@ use crate::{
///
/// Source: [Wikipedia](https://en.wikipedia.org/wiki/Lumen_(unit)#Lighting)
#[derive(Component, Debug, Clone, Copy, Reflect)]
#[reflect(Component)]
#[reflect(Component, Default)]
pub struct PointLight {
pub color: Color,
pub intensity: f32,
Expand Down Expand Up @@ -113,7 +113,7 @@ impl Default for PointLightShadowMap {
///
/// Source: [Wikipedia](https://en.wikipedia.org/wiki/Lux)
#[derive(Component, Debug, Clone, Reflect)]
#[reflect(Component)]
#[reflect(Component, Default)]
pub struct DirectionalLight {
pub color: Color,
/// Illuminance in lux
Expand Down Expand Up @@ -185,10 +185,12 @@ impl Default for AmbientLight {
}

/// Add this component to make a [`Mesh`](bevy_render::mesh::Mesh) not cast shadows.
#[derive(Component)]
#[derive(Component, Reflect, Default)]
#[reflect(Component, Default)]
pub struct NotShadowCaster;
/// Add this component to make a [`Mesh`](bevy_render::mesh::Mesh) not receive shadows.
#[derive(Component)]
#[derive(Component, Reflect, Default)]
#[reflect(Component, Default)]
pub struct NotShadowReceiver;

#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemLabel)]
Expand Down
3 changes: 2 additions & 1 deletion crates/bevy_pbr/src/wireframe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use bevy_app::Plugin;
use bevy_asset::{load_internal_asset, Handle, HandleUntyped};
use bevy_core_pipeline::Opaque3d;
use bevy_ecs::{prelude::*, reflect::ReflectComponent};
use bevy_reflect::std_traits::ReflectDefault;
use bevy_reflect::{Reflect, TypeUuid};
use bevy_render::{
mesh::{Mesh, MeshVertexBufferLayout},
Expand Down Expand Up @@ -61,7 +62,7 @@ fn extract_wireframes(mut commands: Commands, query: Query<Entity, With<Wirefram

/// Controls whether an entity should rendered in wireframe-mode if the [`WireframePlugin`] is enabled
#[derive(Component, Debug, Clone, Default, Reflect)]
#[reflect(Component)]
#[reflect(Component, Default)]
pub struct Wireframe;

#[derive(Debug, Clone, Default)]
Expand Down
3 changes: 3 additions & 0 deletions crates/bevy_reflect/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ mod impls {
}

pub mod serde;
pub mod std_traits;

pub mod prelude {
pub use crate::std_traits::*;
#[doc(hidden)]
pub use crate::{
reflect_trait, GetField, GetTupleStructField, Reflect, ReflectDeserialize, Struct,
Expand Down
23 changes: 23 additions & 0 deletions crates/bevy_reflect/src/std_traits.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use crate::{FromType, Reflect};

/// A struct used to provide the default value of a type.
///
/// A [`ReflectDefault`] for type `T` can be obtained via [`FromType::from_type`].
#[derive(Clone)]
pub struct ReflectDefault {
default: fn() -> Box<dyn Reflect>,
}

impl ReflectDefault {
pub fn default(&self) -> Box<dyn Reflect> {
(self.default)()
}
}

impl<T: Reflect + Default> FromType<T> for ReflectDefault {
fn from_type() -> Self {
ReflectDefault {
default: || Box::new(T::default()),
}
}
}
4 changes: 2 additions & 2 deletions crates/bevy_render/src/camera/camera.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ use bevy_ecs::{
system::{Commands, ParamSet, Query, Res, ResMut},
};
use bevy_math::{Mat4, UVec2, Vec2, Vec3};
use bevy_reflect::{Reflect, ReflectDeserialize};
use bevy_reflect::prelude::*;
use bevy_transform::components::GlobalTransform;
use bevy_utils::HashSet;
use bevy_window::{WindowCreated, WindowId, WindowResized, Windows};
use serde::{Deserialize, Serialize};
use wgpu::Extent3d;

#[derive(Component, Default, Debug, Reflect, Clone)]
#[reflect(Component)]
#[reflect(Component, Default)]
pub struct Camera {
pub projection_matrix: Mat4,
#[reflect(ignore)]
Expand Down
5 changes: 3 additions & 2 deletions crates/bevy_render/src/camera/projection.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use super::DepthCalculation;
use bevy_ecs::{component::Component, reflect::ReflectComponent};
use bevy_math::Mat4;
use bevy_reflect::std_traits::ReflectDefault;
use bevy_reflect::{Reflect, ReflectDeserialize};
use serde::{Deserialize, Serialize};

Expand All @@ -12,7 +13,7 @@ pub trait CameraProjection {
}

#[derive(Component, Debug, Clone, Reflect)]
#[reflect(Component)]
#[reflect(Component, Default)]
pub struct PerspectiveProjection {
pub fov: f32,
pub aspect_ratio: f32,
Expand Down Expand Up @@ -72,7 +73,7 @@ pub enum ScalingMode {
}

#[derive(Component, Debug, Clone, Reflect)]
#[reflect(Component)]
#[reflect(Component, Default)]
pub struct OrthographicProjection {
pub left: f32,
pub right: f32,
Expand Down
3 changes: 2 additions & 1 deletion crates/bevy_render/src/view/visibility/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub use render_layers::*;
use bevy_app::{CoreStage, Plugin};
use bevy_asset::{Assets, Handle};
use bevy_ecs::prelude::*;
use bevy_reflect::std_traits::ReflectDefault;
use bevy_reflect::Reflect;
use bevy_transform::components::GlobalTransform;
use bevy_transform::TransformSystem;
Expand All @@ -18,7 +19,7 @@ use crate::{

/// User indication of whether an entity is visible
#[derive(Component, Clone, Reflect, Debug)]
#[reflect(Component)]
#[reflect(Component, Default)]
pub struct Visibility {
pub is_visible: bool,
}
Expand Down
3 changes: 2 additions & 1 deletion crates/bevy_render/src/view/visibility/render_layers.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use bevy_ecs::prelude::{Component, ReflectComponent};
use bevy_reflect::std_traits::ReflectDefault;
use bevy_reflect::Reflect;

type LayerMask = u32;
Expand All @@ -20,7 +21,7 @@ pub type Layer = u8;
///
/// Entities without this component belong to layer `0`.
#[derive(Component, Copy, Clone, Reflect, PartialEq, Eq, PartialOrd, Ord)]
#[reflect(Component, PartialEq)]
#[reflect(Component, Default, PartialEq)]
pub struct RenderLayers(LayerMask);

impl std::fmt::Debug for RenderLayers {
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_text/src/text.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use bevy_asset::Handle;
use bevy_ecs::{prelude::Component, reflect::ReflectComponent};
use bevy_reflect::{FromReflect, Reflect, ReflectDeserialize};
use bevy_reflect::{prelude::*, FromReflect};
use bevy_render::color::Color;
use serde::{Deserialize, Serialize};

use crate::Font;

#[derive(Component, Debug, Default, Clone, Reflect)]
#[reflect(Component)]
#[reflect(Component, Default)]
pub struct Text {
pub sections: Vec<TextSection>,
pub alignment: TextAlignment,
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_transform/src/components/global_transform.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::Transform;
use bevy_ecs::{component::Component, reflect::ReflectComponent};
use bevy_math::{const_vec3, Affine3A, Mat3, Mat4, Quat, Vec3};
use bevy_reflect::Reflect;
use bevy_reflect::prelude::*;
use std::ops::Mul;

/// Describe the position of an entity relative to the reference frame.
Expand All @@ -25,7 +25,7 @@ use std::ops::Mul;
/// update the[`Transform`] of an entity in this stage or after, you will notice a 1 frame lag
/// before the [`GlobalTransform`] is updated.
#[derive(Component, Debug, PartialEq, Clone, Copy, Reflect)]
#[reflect(Component, PartialEq)]
#[reflect(Component, Default, PartialEq)]
pub struct GlobalTransform {
/// The position of the global transform
pub translation: Vec3,
Expand Down
3 changes: 2 additions & 1 deletion crates/bevy_transform/src/components/transform.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use super::GlobalTransform;
use bevy_ecs::{component::Component, reflect::ReflectComponent};
use bevy_math::{const_vec3, Mat3, Mat4, Quat, Vec3};
use bevy_reflect::prelude::*;
use bevy_reflect::Reflect;
use std::ops::Mul;

Expand All @@ -26,7 +27,7 @@ use std::ops::Mul;
/// update the[`Transform`] of an entity in this stage or after, you will notice a 1 frame lag
/// before the [`GlobalTransform`] is updated.
#[derive(Component, Debug, PartialEq, Clone, Copy, Reflect)]
#[reflect(Component, PartialEq)]
#[reflect(Component, Default, PartialEq)]
pub struct Transform {
/// Position of the entity. In 2d, the last value of the `Vec3` is used for z-ordering.
pub translation: Vec3,
Expand Down
10 changes: 5 additions & 5 deletions crates/bevy_ui/src/ui_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::{Size, UiRect};
use bevy_asset::Handle;
use bevy_ecs::{prelude::Component, reflect::ReflectComponent};
use bevy_math::Vec2;
use bevy_reflect::{Reflect, ReflectDeserialize};
use bevy_reflect::prelude::*;
use bevy_render::{
color::Color,
texture::{Image, DEFAULT_IMAGE_HANDLE},
Expand All @@ -12,7 +12,7 @@ use std::ops::{Add, AddAssign};

/// Describes the size of a UI node
#[derive(Component, Debug, Clone, Default, Reflect)]
#[reflect(Component)]
#[reflect(Component, Default)]
pub struct Node {
/// The size of the node as width and height in pixels
pub size: Vec2,
Expand Down Expand Up @@ -68,7 +68,7 @@ impl AddAssign<f32> for Val {
/// **Note:** Bevy's UI is upside down compared to how Flexbox normally works, to stay consistent with engine paradigms about layouting from
/// the upper left corner of the display
#[derive(Component, Clone, PartialEq, Debug, Reflect)]
#[reflect(Component, PartialEq)]
#[reflect(Component, Default, PartialEq)]
pub struct Style {
/// Whether to arrange this node and its children with flexbox layout
pub display: Display,
Expand Down Expand Up @@ -359,7 +359,7 @@ pub struct CalculatedSize {

/// The color of the node
#[derive(Component, Default, Copy, Clone, Debug, Reflect)]
#[reflect(Component)]
#[reflect(Component, Default)]
pub struct UiColor(pub Color);

impl From<Color> for UiColor {
Expand All @@ -370,7 +370,7 @@ impl From<Color> for UiColor {

/// The image of the node
#[derive(Component, Clone, Debug, Reflect)]
#[reflect(Component)]
#[reflect(Component, Default)]
pub struct UiImage(pub Handle<Image>);

impl Default for UiImage {
Expand Down
3 changes: 2 additions & 1 deletion crates/bevy_ui/src/widget/button.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use bevy_ecs::prelude::Component;
use bevy_ecs::reflect::ReflectComponent;
use bevy_reflect::std_traits::ReflectDefault;
use bevy_reflect::Reflect;

/// Marker struct for buttons
#[derive(Component, Debug, Default, Clone, Copy, Reflect)]
#[reflect(Component)]
#[reflect(Component, Default)]
pub struct Button;

0 comments on commit 23f21b0

Please sign in to comment.