From 534cad611dc1fff3476be661196b2a0d599e9832 Mon Sep 17 00:00:00 2001 From: Hennadii Chernyshchyk Date: Mon, 4 Jul 2022 13:04:20 +0000 Subject: [PATCH] Add reflection for resources (#5175) # Objective We don't have reflection for resources. ## Solution Introduce reflection for resources. Continues #3580 (by @Davier), related to #3576. --- ## Changelog ### Added * Reflection on a resource type (by adding `ReflectResource`): ```rust #[derive(Reflect)] #[reflect(Resource)] struct MyResourse; ``` ### Changed * Rename `ReflectComponent::add_component` into `ReflectComponent::insert_component` for consistency. ## Migration Guide * Rename `ReflectComponent::add_component` into `ReflectComponent::insert_component`. --- crates/bevy_core_pipeline/src/clear_color.rs | 3 +- crates/bevy_core_pipeline/src/lib.rs | 3 +- crates/bevy_ecs/src/change_detection.rs | 2 +- crates/bevy_ecs/src/lib.rs | 2 +- crates/bevy_ecs/src/reflect.rs | 131 ++++++++++++++++++- crates/bevy_pbr/src/lib.rs | 3 + crates/bevy_pbr/src/light.rs | 9 +- crates/bevy_pbr/src/wireframe.rs | 6 +- crates/bevy_render/src/view/mod.rs | 7 +- 9 files changed, 154 insertions(+), 12 deletions(-) diff --git a/crates/bevy_core_pipeline/src/clear_color.rs b/crates/bevy_core_pipeline/src/clear_color.rs index 1c8b6f1035400..6160f2256f039 100644 --- a/crates/bevy_core_pipeline/src/clear_color.rs +++ b/crates/bevy_core_pipeline/src/clear_color.rs @@ -17,7 +17,8 @@ pub enum ClearColorConfig { /// /// This color appears as the "background" color for simple apps, when /// there are portions of the screen with nothing rendered. -#[derive(Component, Clone, Debug, Deref, DerefMut, ExtractResource)] +#[derive(Component, Clone, Debug, Deref, DerefMut, ExtractResource, Reflect)] +#[reflect(Resource)] pub struct ClearColor(pub Color); impl Default for ClearColor { diff --git a/crates/bevy_core_pipeline/src/lib.rs b/crates/bevy_core_pipeline/src/lib.rs index f8f5db70a3d8c..28304608e7661 100644 --- a/crates/bevy_core_pipeline/src/lib.rs +++ b/crates/bevy_core_pipeline/src/lib.rs @@ -20,7 +20,8 @@ pub struct CorePipelinePlugin; impl Plugin for CorePipelinePlugin { fn build(&self, app: &mut App) { - app.init_resource::() + app.register_type::() + .init_resource::() .add_plugin(ExtractResourcePlugin::::default()) .add_plugin(Core2dPlugin) .add_plugin(Core3dPlugin); diff --git a/crates/bevy_ecs/src/change_detection.rs b/crates/bevy_ecs/src/change_detection.rs index 2b5ae2bd2f5cb..b96f4e48c16c9 100644 --- a/crates/bevy_ecs/src/change_detection.rs +++ b/crates/bevy_ecs/src/change_detection.rs @@ -216,7 +216,7 @@ change_detection_impl!(Mut<'a, T>, T,); impl_into_inner!(Mut<'a, T>, T,); impl_debug!(Mut<'a, T>,); -/// Unique mutable borrow of a Reflected component +/// Unique mutable borrow of a reflected component or resource #[cfg(feature = "bevy_reflect")] pub struct ReflectMut<'a> { pub(crate) value: &'a mut dyn Reflect, diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 27c2d44e85702..30ec5aa8e72c3 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -23,7 +23,7 @@ pub use bevy_ptr as ptr; pub mod prelude { #[doc(hidden)] #[cfg(feature = "bevy_reflect")] - pub use crate::reflect::ReflectComponent; + pub use crate::reflect::{ReflectComponent, ReflectResource}; #[doc(hidden)] pub use crate::{ bundle::Bundle, diff --git a/crates/bevy_ecs/src/reflect.rs b/crates/bevy_ecs/src/reflect.rs index bcd41971c7c9e..46871b59a1906 100644 --- a/crates/bevy_ecs/src/reflect.rs +++ b/crates/bevy_ecs/src/reflect.rs @@ -4,6 +4,7 @@ pub use crate::change_detection::ReflectMut; use crate::{ component::Component, entity::{Entity, EntityMap, MapEntities, MapEntitiesError}, + system::Resource, world::{FromWorld, World}, }; use bevy_reflect::{ @@ -11,6 +12,10 @@ use bevy_reflect::{ ReflectSerialize, }; +/// A struct used to operate on reflected [`Component`] of a type. +/// +/// A [`ReflectComponent`] for type `T` can be obtained via +/// [`bevy_reflect::TypeRegistration::data`]. #[derive(Clone)] pub struct ReflectComponent { add_component: fn(&mut World, Entity, &dyn Reflect), @@ -22,18 +27,34 @@ pub struct ReflectComponent { } impl ReflectComponent { + /// Insert a reflected [`Component`] into the entity like [`insert()`](crate::world::EntityMut::insert). + /// + /// # Panics + /// + /// Panics if there is no such entity. pub fn add_component(&self, world: &mut World, entity: Entity, component: &dyn Reflect) { (self.add_component)(world, entity, component); } + /// Uses reflection to set the value of this [`Component`] type in the entity to the given value. + /// + /// # Panics + /// + /// Panics if there is no [`Component`] of the given type or the `entity` does not exist. pub fn apply_component(&self, world: &mut World, entity: Entity, component: &dyn Reflect) { (self.apply_component)(world, entity, component); } + /// Removes this [`Component`] type from the entity. Does nothing if it doesn't exist. + /// + /// # Panics + /// + /// Panics if there is no [`Component`] of the given type or the `entity` does not exist. pub fn remove_component(&self, world: &mut World, entity: Entity) { (self.remove_component)(world, entity); } + /// Gets the value of this [`Component`] type from the entity as a reflected reference. pub fn reflect_component<'a>( &self, world: &'a World, @@ -42,6 +63,7 @@ impl ReflectComponent { (self.reflect_component)(world, entity) } + /// Gets the value of this [`Component`] type from the entity as a mutable reflected reference. pub fn reflect_component_mut<'a>( &self, world: &'a mut World, @@ -56,7 +78,7 @@ impl ReflectComponent { /// violating Rust's aliasing rules. To avoid this: /// * Only call this method in an exclusive system to avoid sharing across threads (or use a /// scheduler that enforces safe memory access). - /// * Don't call this method more than once in the same scope for a given component. + /// * Don't call this method more than once in the same scope for a given [`Component`]. pub unsafe fn reflect_component_unchecked_mut<'a>( &self, world: &'a World, @@ -65,6 +87,11 @@ impl ReflectComponent { (self.reflect_component_mut)(world, entity) } + /// Gets the value of this [`Component`] type from entity from `source_world` and [applies](Self::apply_component()) it to the value of this [`Component`] type in entity in `destination_world`. + /// + /// # Panics + /// + /// Panics if there is no [`Component`] of the given type or either entity does not exist. pub fn copy_component( &self, source_world: &World, @@ -123,6 +150,108 @@ impl FromType for ReflectComponent { } } +/// A struct used to operate on reflected [`Resource`] of a type. +/// +/// A [`ReflectResource`] for type `T` can be obtained via +/// [`bevy_reflect::TypeRegistration::data`]. +#[derive(Clone)] +pub struct ReflectResource { + insert_resource: fn(&mut World, &dyn Reflect), + apply_resource: fn(&mut World, &dyn Reflect), + remove_resource: fn(&mut World), + reflect_resource: fn(&World) -> Option<&dyn Reflect>, + reflect_resource_unchecked_mut: unsafe fn(&World) -> Option, + copy_resource: fn(&World, &mut World), +} + +impl ReflectResource { + /// Insert a reflected [`Resource`] into the world like [`insert_resource()`](World::insert_resource). + pub fn insert_resource(&self, world: &mut World, resource: &dyn Reflect) { + (self.insert_resource)(world, resource); + } + + /// Uses reflection to set the value of this [`Resource`] type in the world to the given value. + /// + /// # Panics + /// + /// Panics if there is no [`Resource`] of the given type. + pub fn apply_resource(&self, world: &mut World, resource: &dyn Reflect) { + (self.apply_resource)(world, resource); + } + + /// Removes this [`Resource`] type from the world. Does nothing if it doesn't exist. + pub fn remove_resource(&self, world: &mut World) { + (self.remove_resource)(world); + } + + /// Gets the value of this [`Resource`] type from the world as a reflected reference. + pub fn reflect_resource<'a>(&self, world: &'a World) -> Option<&'a dyn Reflect> { + (self.reflect_resource)(world) + } + + /// Gets the value of this [`Resource`] type from the world as a mutable reflected reference. + pub fn reflect_resource_mut<'a>(&self, world: &'a mut World) -> Option> { + // SAFE: unique world access + unsafe { (self.reflect_resource_unchecked_mut)(world) } + } + + /// # Safety + /// This method does not prevent you from having two mutable pointers to the same data, + /// violating Rust's aliasing rules. To avoid this: + /// * Only call this method in an exclusive system to avoid sharing across threads (or use a + /// scheduler that enforces safe memory access). + /// * Don't call this method more than once in the same scope for a given [`Resource`]. + pub unsafe fn reflect_resource_unckecked_mut<'a>( + &self, + world: &'a World, + ) -> Option> { + (self.reflect_resource_unchecked_mut)(world) + } + + /// Gets the value of this [`Resource`] type from `source_world` and [applies](Self::apply_resource()) it to the value of this [`Resource`] type in `destination_world`. + /// + /// # Panics + /// + /// Panics if there is no [`Resource`] of the given type. + pub fn copy_resource(&self, source_world: &World, destination_world: &mut World) { + (self.copy_resource)(source_world, destination_world); + } +} + +impl FromType for ReflectResource { + fn from_type() -> Self { + ReflectResource { + insert_resource: |world, reflected_resource| { + let mut resource = C::from_world(world); + resource.apply(reflected_resource); + world.insert_resource(resource); + }, + apply_resource: |world, reflected_resource| { + let mut resource = world.resource_mut::(); + resource.apply(reflected_resource); + }, + remove_resource: |world| { + world.remove_resource::(); + }, + reflect_resource: |world| world.get_resource::().map(|res| res as &dyn Reflect), + reflect_resource_unchecked_mut: |world| unsafe { + world + .get_resource_unchecked_mut::() + .map(|res| ReflectMut { + value: res.value as &mut dyn Reflect, + ticks: res.ticks, + }) + }, + copy_resource: |source_world, destination_world| { + let source_resource = source_world.resource::(); + let mut destination_resource = C::from_world(destination_world); + destination_resource.apply(source_resource); + destination_world.insert_resource(destination_resource); + }, + } + } +} + impl_reflect_value!(Entity(Hash, PartialEq, Serialize, Deserialize)); impl_from_reflect_value!(Entity); diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index e3ac3914512cb..35c2cc9a6a428 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -125,6 +125,9 @@ impl Plugin for PbrPlugin { .register_type::() .add_plugin(MeshRenderPlugin) .add_plugin(MaterialPlugin::::default()) + .register_type::() + .register_type::() + .register_type::() .init_resource::() .init_resource::() .init_resource::() diff --git a/crates/bevy_pbr/src/light.rs b/crates/bevy_pbr/src/light.rs index e5eb283c7db05..ae03ebe665f60 100644 --- a/crates/bevy_pbr/src/light.rs +++ b/crates/bevy_pbr/src/light.rs @@ -73,7 +73,8 @@ impl PointLight { pub const DEFAULT_SHADOW_NORMAL_BIAS: f32 = 0.6; } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Reflect)] +#[reflect(Resource)] pub struct PointLightShadowMap { pub size: usize, } @@ -151,7 +152,8 @@ impl DirectionalLight { pub const DEFAULT_SHADOW_NORMAL_BIAS: f32 = 0.6; } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Reflect)] +#[reflect(Resource)] pub struct DirectionalLightShadowMap { pub size: usize, } @@ -166,7 +168,8 @@ impl Default for DirectionalLightShadowMap { } /// An ambient light, which lights the entire scene equally. -#[derive(Clone, Debug, ExtractResource)] +#[derive(Clone, Debug, ExtractResource, Reflect)] +#[reflect(Resource)] pub struct AmbientLight { pub color: Color, /// A direct scale factor multiplied with `color` before being passed to the shader. diff --git a/crates/bevy_pbr/src/wireframe.rs b/crates/bevy_pbr/src/wireframe.rs index 6a7a4c3c297c2..c16e396f8ea6f 100644 --- a/crates/bevy_pbr/src/wireframe.rs +++ b/crates/bevy_pbr/src/wireframe.rs @@ -35,7 +35,8 @@ impl Plugin for WireframePlugin { Shader::from_wgsl ); - app.init_resource::() + app.register_type::() + .init_resource::() .add_plugin(ExtractResourcePlugin::::default()); if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { @@ -60,7 +61,8 @@ fn extract_wireframes(mut commands: Commands, query: Query() + app.register_type::() + .init_resource::() // NOTE: windows.is_changed() handles cases where a window was resized .add_plugin(ExtractResourcePlugin::::default()) .add_plugin(VisibilityPlugin); @@ -45,7 +47,6 @@ impl Plugin for ViewPlugin { } } -#[derive(Clone, ExtractResource)] /// Configuration resource for [Multi-Sample Anti-Aliasing](https://en.wikipedia.org/wiki/Multisample_anti-aliasing). /// /// # Example @@ -56,6 +57,8 @@ impl Plugin for ViewPlugin { /// .insert_resource(Msaa { samples: 4 }) /// .run(); /// ``` +#[derive(Clone, ExtractResource, Reflect)] +#[reflect(Resource)] pub struct Msaa { /// The number of samples to run for Multi-Sample Anti-Aliasing. Higher numbers result in /// smoother edges.