Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reflect and (de)serialize resources in scenes #3580

Closed
wants to merge 11 commits into from
115 changes: 59 additions & 56 deletions assets/scenes/load_scene_example.scn.ron
Original file line number Diff line number Diff line change
@@ -1,64 +1,67 @@
[
(
entity: 0,
components: [
{
"type": "bevy_transform::components::transform::Transform",
"struct": {
"translation": {
"type": "glam::vec3::Vec3",
"value": (0.0, 0.0, 0.0),
},
"rotation": {
"type": "glam::quat::Quat",
"value": (0.0, 0.0, 0.0, 1.0),
},
"scale": {
"type": "glam::vec3::Vec3",
"value": (1.0, 1.0, 1.0),
(
resources: [],
entities: [
(
entity: 0,
components: [
{
"type": "bevy_transform::components::transform::Transform",
"struct": {
"translation": {
"type": "glam::vec3::Vec3",
"value": (0.0, 0.0, 0.0),
},
"rotation": {
"type": "glam::quat::Quat",
"value": (0.0, 0.0, 0.0, 1.0),
},
"scale": {
"type": "glam::vec3::Vec3",
"value": (1.0, 1.0, 1.0),
},
},
},
},
{
"type": "scene::ComponentB",
"struct": {
"value": {
"type": "alloc::string::String",
"value": "hello",
{
"type": "scene::ComponentB",
"struct": {
"value": {
"type": "alloc::string::String",
"value": "hello",
},
},
},
},
{
"type": "scene::ComponentA",
"struct": {
"x": {
"type": "f32",
"value": 1.0,
},
"y": {
"type": "f32",
"value": 2.0,
{
"type": "scene::ComponentA",
"struct": {
"x": {
"type": "f32",
"value": 1.0,
},
"y": {
"type": "f32",
"value": 2.0,
},
},
},
},
],
),
(
entity: 1,
components: [
{
"type": "scene::ComponentA",
"struct": {
"x": {
"type": "f32",
"value": 3.0,
},
"y": {
"type": "f32",
"value": 4.0,
],
),
(
entity: 1,
components: [
{
"type": "scene::ComponentA",
"struct": {
"x": {
"type": "f32",
"value": 3.0,
},
"y": {
"type": "f32",
"value": 4.0,
},
},
},
},
],
),
]
],
),
],
)
2 changes: 1 addition & 1 deletion crates/bevy_core_pipeline/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ bevy_asset = { path = "../bevy_asset", version = "0.5.0" }
bevy_core = { path = "../bevy_core", version = "0.5.0" }
bevy_ecs = { path = "../bevy_ecs", version = "0.5.0" }
bevy_render = { path = "../bevy_render", version = "0.5.0" }

bevy_reflect = { path = "../bevy_reflect", version = "0.5.0" }
7 changes: 5 additions & 2 deletions crates/bevy_core_pipeline/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub use main_pass_driver::*;
use bevy_app::{App, Plugin};
use bevy_core::FloatOrd;
use bevy_ecs::prelude::*;
use bevy_reflect::Reflect;
use bevy_render::{
camera::{ActiveCameras, CameraPlugin},
color::Color,
Expand All @@ -34,7 +35,8 @@ use bevy_render::{
};

/// Resource that configures the clear color
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Reflect)]
#[reflect(Resource)]
pub struct ClearColor(pub Color);

impl Default for ClearColor {
Expand Down Expand Up @@ -86,7 +88,8 @@ pub struct CorePipelinePlugin;

impl Plugin for CorePipelinePlugin {
fn build(&self, app: &mut App) {
app.init_resource::<ClearColor>();
app.register_type::<ClearColor>()
.init_resource::<ClearColor>();

let render_app = app.sub_app_mut(RenderApp);
render_app
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/change_detection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,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,
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub mod world;
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,
Expand Down
93 changes: 89 additions & 4 deletions crates/bevy_ecs/src/reflect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand All @@ -16,7 +17,7 @@ pub struct ReflectComponent {
apply_component: fn(&mut World, Entity, &dyn Reflect),
remove_component: fn(&mut World, Entity),
reflect_component: fn(&World, Entity) -> Option<&dyn Reflect>,
reflect_component_mut: unsafe fn(&World, Entity) -> Option<ReflectMut>,
reflect_component_unchecked_mut: unsafe fn(&World, Entity) -> Option<ReflectMut>,
copy_component: fn(&World, &mut World, Entity, Entity),
}

Expand Down Expand Up @@ -47,7 +48,7 @@ impl ReflectComponent {
entity: Entity,
) -> Option<ReflectMut<'a>> {
// SAFE: unique world access
unsafe { (self.reflect_component_mut)(world, entity) }
unsafe { (self.reflect_component_unchecked_mut)(world, entity) }
}

/// # Safety
Expand All @@ -61,7 +62,7 @@ impl ReflectComponent {
world: &'a World,
entity: Entity,
) -> Option<ReflectMut<'a>> {
(self.reflect_component_mut)(world, entity)
(self.reflect_component_unchecked_mut)(world, entity)
}

pub fn copy_component(
Expand Down Expand Up @@ -109,7 +110,7 @@ impl<C: Component + Reflect + FromWorld> FromType<C> for ReflectComponent {
.get::<C>()
.map(|c| c as &dyn Reflect)
},
reflect_component_mut: |world, entity| unsafe {
reflect_component_unchecked_mut: |world, entity| unsafe {
world
.get_entity(entity)?
.get_unchecked_mut::<C>(world.last_change_tick(), world.read_change_tick())
Expand All @@ -122,6 +123,90 @@ impl<C: Component + Reflect + FromWorld> FromType<C> for ReflectComponent {
}
}

#[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<ReflectMut>,
copy_resource: fn(&World, &mut World),
}

impl ReflectResource {
pub fn insert_resource(&self, world: &mut World, resource: &dyn Reflect) {
(self.insert_resource)(world, resource);
}

pub fn apply_resource(&self, world: &mut World, resource: &dyn Reflect) {
(self.apply_resource)(world, resource);
}

pub fn remove_resource(&self, world: &mut World) {
(self.remove_resource)(world);
}

pub fn reflect_resource<'a>(&self, world: &'a World) -> Option<&'a dyn Reflect> {
(self.reflect_resource)(world)
}

pub fn reflect_resource_mut<'a>(&self, world: &'a mut World) -> Option<ReflectMut<'a>> {
// 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<ReflectMut<'a>> {
(self.reflect_resource_unchecked_mut)(world)
}

pub fn copy_resource(&self, source_world: &World, destination_world: &mut World) {
(self.copy_resource)(source_world, destination_world);
}
}

impl<C: Resource + Reflect + FromWorld> FromType<C> 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.get_resource_mut::<C>().unwrap();
resource.apply(reflected_resource);
},
remove_resource: |world| {
world.remove_resource::<C>();
},
reflect_resource: |world| world.get_resource::<C>().map(|res| res as &dyn Reflect),
reflect_resource_unchecked_mut: |world| unsafe {
world
.get_resource_unchecked_mut::<C>()
.map(|res| ReflectMut {
value: res.value as &mut dyn Reflect,
ticks: res.ticks,
})
},
copy_resource: |source_world, destination_world| {
let source_resource = source_world.get_resource::<C>().unwrap();
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);

Expand Down
25 changes: 18 additions & 7 deletions crates/bevy_ecs/src/world/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -644,16 +644,27 @@ impl World {
Some(unsafe { ptr.cast::<T>().read() })
}

/// Returns `true` if a resource of a given [`ComponentId`] exists. Otherwise returns `false`.
#[inline]
pub fn contains_resource_with_id(&self, component_id: ComponentId) -> bool {
self.get_populated_resource_column(component_id).is_some()
}

/// Returns `true` if a resource of a given [`TypeId`] exists. Otherwise returns `false`.
#[inline]
pub fn contains_resource_with_type(&self, type_id: TypeId) -> bool {
let component_id = if let Some(component_id) = self.components.get_resource_id(type_id) {
component_id
} else {
return false;
};
self.contains_resource_with_id(component_id)
}

/// Returns `true` if a resource of type `T` exists. Otherwise returns `false`.
#[inline]
pub fn contains_resource<T: Resource>(&self) -> bool {
let component_id =
if let Some(component_id) = self.components.get_resource_id(TypeId::of::<T>()) {
component_id
} else {
return false;
};
self.get_populated_resource_column(component_id).is_some()
self.contains_resource_with_type(TypeId::of::<T>())
}

/// Gets a reference to the resource of the given type, if it exists. Otherwise returns [None]
Expand Down
4 changes: 3 additions & 1 deletion crates/bevy_pbr/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,12 @@ impl Plugin for PbrPlugin {

app.add_plugin(MeshRenderPlugin)
.add_plugin(MaterialPlugin::<StandardMaterial>::default())
.register_type::<AmbientLight>()
.register_type::<DirectionalLightShadowMap>()
.register_type::<PointLightShadowMap>()
.init_resource::<AmbientLight>()
.init_resource::<DirectionalLightShadowMap>()
.init_resource::<PointLightShadowMap>()
.init_resource::<AmbientLight>()
.init_resource::<VisiblePointLights>()
.add_system_to_stage(
CoreStage::PostUpdate,
Expand Down
10 changes: 7 additions & 3 deletions crates/bevy_pbr/src/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::collections::HashSet;

use bevy_ecs::prelude::*;
use bevy_math::{Mat4, UVec2, UVec3, Vec2, Vec3, Vec3Swizzles, Vec4, Vec4Swizzles};
use bevy_reflect::Reflect;
use bevy_render::{
camera::{Camera, CameraProjection, OrthographicProjection},
color::Color,
Expand Down Expand Up @@ -67,7 +68,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,
}
Expand Down Expand Up @@ -144,7 +146,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,
}
Expand All @@ -159,7 +162,8 @@ impl Default for DirectionalLightShadowMap {
}

/// An ambient light, which lights the entire scene equally.
#[derive(Debug)]
#[derive(Debug, Reflect)]
#[reflect(Resource)]
pub struct AmbientLight {
pub color: Color,
/// A direct scale factor multiplied with `color` before being passed to the shader.
Expand Down
Loading