diff --git a/CREDITS.md b/CREDITS.md index 3a286f4234..efb6ce691f 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -2,10 +2,11 @@ ## Adapted Code +* legion * legion_transform * wgpu-rs examples -## Insipration +## Inspiration * amethyst * coffee \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 7353a75ac6..631478951b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" [features] default = ["headless", "wgpu", "winit"] -headless = ["asset", "core", "derive", "diagnostic", "gltf", "input", "pbr", "render", "serialization", "text", "transform", "ui", "window"] +headless = ["asset", "core", "derive", "diagnostic", "gltf", "input", "pbr", "props", "render", "scene", "text", "transform", "ui", "window"] asset = ["bevy_asset"] core = ["bevy_core"] derive = ["bevy_derive"] @@ -14,8 +14,9 @@ diagnostic = ["bevy_diagnostic"] gltf = ["bevy_gltf"] input = ["bevy_input"] pbr = ["bevy_pbr"] +props = ["bevy_props"] render = ["bevy_render"] -serialization = ["bevy_serialization"] +scene = ["bevy_scene"] text = ["bevy_text"] transform = ["bevy_transform"] ui = ["bevy_ui"] @@ -40,8 +41,9 @@ bevy_diagnostic = { path = "crates/bevy_diagnostic", optional = true } bevy_gltf = { path = "crates/bevy_gltf", optional = true } bevy_input = { path = "crates/bevy_input", optional = true } bevy_pbr = { path = "crates/bevy_pbr", optional = true } +bevy_props = { path = "crates/bevy_props", optional = true } bevy_render = { path = "crates/bevy_render", optional = true } -bevy_serialization = { path = "crates/bevy_serialization", optional = true } +bevy_scene = { path = "crates/bevy_scene", optional = true } bevy_transform = { path = "crates/bevy_transform", optional = true } bevy_text = { path = "crates/bevy_text", optional = true } bevy_ui = { path = "crates/bevy_ui", optional = true } @@ -80,8 +82,8 @@ name = "parenting" path = "examples/3d/parenting.rs" [[example]] -name = "scene" -path = "examples/3d/scene.rs" +name = "3d_scene" +path = "examples/3d/3d_scene.rs" [[example]] name = "spawner" @@ -148,8 +150,8 @@ name = "input_keyboard" path = "examples/input/input_keyboard.rs" [[example]] -name = "serializing" -path = "examples/serializing/serializing.rs" +name = "load_scene" +path = "examples/scene/load_scene.rs" [[example]] name = "shader_custom_material" diff --git a/assets/scene/load_scene_example.scn b/assets/scene/load_scene_example.scn new file mode 100644 index 0000000000..e31440ba3e --- /dev/null +++ b/assets/scene/load_scene_example.scn @@ -0,0 +1,32 @@ +[ + ( + id: 2309003120, + components: [ + ( + type: "Test", + data: ( + x: 3, + y: 4, + ), + ), + ( + type: "Foo", + data: ( + value: "hi", + ), + ), + ], + ), + ( + id: 4238063392, + components: [ + ( + type: "Test", + data: ( + x: 3, + y: 4, + ), + ), + ], + ), +] \ No newline at end of file diff --git a/crates/bevy_app/Cargo.toml b/crates/bevy_app/Cargo.toml index 18b8b17d00..293bc040ed 100644 --- a/crates/bevy_app/Cargo.toml +++ b/crates/bevy_app/Cargo.toml @@ -7,4 +7,6 @@ edition = "2018" [dependencies] legion = { path = "../bevy_legion", features = ["serialize"] } libloading = "0.5.2" -log = { version = "0.4", features = ["release_max_level_info"] } \ No newline at end of file +log = { version = "0.4", features = ["release_max_level_info"] } +serde = { version = "1.0", features = ["derive"]} +erased-serde = "0.3" \ No newline at end of file diff --git a/crates/bevy_app/src/lib.rs b/crates/bevy_app/src/lib.rs index c1420883d1..d15717daf0 100644 --- a/crates/bevy_app/src/lib.rs +++ b/crates/bevy_app/src/lib.rs @@ -5,10 +5,10 @@ mod entity_archetype; mod event; mod plugin; mod resources; +mod system; pub mod schedule_plan; pub mod schedule_runner; pub mod stage; -mod system; pub use app::*; pub use app_builder::*; diff --git a/crates/bevy_asset/src/assets.rs b/crates/bevy_asset/src/assets.rs index 458b4369be..cdb6878c48 100644 --- a/crates/bevy_asset/src/assets.rs +++ b/crates/bevy_asset/src/assets.rs @@ -2,7 +2,7 @@ use crate::{ update_asset_storage_system, AssetChannel, AssetLoader, AssetServer, ChannelAssetHandler, Handle, HandleId, }; -use bevy_app::{AppBuilder, Events}; +use bevy_app::{AppBuilder, Events, FromResources}; use bevy_core::bytes::GetBytes; use legion::prelude::*; use std::{ @@ -134,9 +134,9 @@ pub trait AddAsset { fn add_asset(&mut self) -> &mut Self where T: Send + Sync + 'static; - fn add_asset_loader(&mut self, loader: TLoader) -> &mut Self + fn add_asset_loader(&mut self) -> &mut Self where - TLoader: AssetLoader + Clone, + TLoader: AssetLoader + FromResources, TAsset: Send + Sync + 'static; } @@ -153,9 +153,9 @@ impl AddAsset for AppBuilder { .add_event::>() } - fn add_asset_loader(&mut self, loader: TLoader) -> &mut Self + fn add_asset_loader(&mut self) -> &mut Self where - TLoader: AssetLoader + Clone, + TLoader: AssetLoader + FromResources, TAsset: Send + Sync + 'static, { { @@ -174,8 +174,8 @@ impl AddAsset for AppBuilder { .resources() .get_mut::() .expect("AssetServer does not exist. Consider adding it as a resource."); - asset_server.add_loader(loader.clone()); - let handler = ChannelAssetHandler::new(loader, asset_channel.sender.clone()); + asset_server.add_loader(TLoader::from_resources(self.resources())); + let handler = ChannelAssetHandler::new(TLoader::from_resources(self.resources()), asset_channel.sender.clone()); asset_server.add_handler(handler); } self diff --git a/crates/bevy_gltf/src/lib.rs b/crates/bevy_gltf/src/lib.rs index 1a14c76669..5728e4b9a2 100644 --- a/crates/bevy_gltf/src/lib.rs +++ b/crates/bevy_gltf/src/lib.rs @@ -3,12 +3,13 @@ pub use loader::*; use bevy_app::{AppBuilder, AppPlugin}; use bevy_asset::AddAsset; +use bevy_render::mesh::Mesh; #[derive(Default)] pub struct GltfPlugin; impl AppPlugin for GltfPlugin { fn build(&self, app: &mut AppBuilder) { - app.add_asset_loader(GltfLoader); + app.add_asset_loader::(); } } diff --git a/crates/bevy_gltf/src/loader.rs b/crates/bevy_gltf/src/loader.rs index bf03d6a4ec..14c1bed04f 100644 --- a/crates/bevy_gltf/src/loader.rs +++ b/crates/bevy_gltf/src/loader.rs @@ -9,7 +9,7 @@ use gltf::{buffer::Source, iter, mesh::Mode}; use std::{fs, io, path::Path}; use thiserror::Error; -#[derive(Clone)] +#[derive(Default)] pub struct GltfLoader; impl AssetLoader for GltfLoader { diff --git a/crates/bevy_legion/legion_core/src/entity.rs b/crates/bevy_legion/legion_core/src/entity.rs index 171101c5dc..4d066399b4 100644 --- a/crates/bevy_legion/legion_core/src/entity.rs +++ b/crates/bevy_legion/legion_core/src/entity.rs @@ -264,11 +264,12 @@ impl GuidEntityAllocator { /// Allocates a new unused `Entity` ID. pub fn create_entity(&self) -> Entity { - if !self.next_ids.read().is_empty() { - return self.next_ids.write().pop().unwrap(); - } + let entity = if !self.next_ids.read().is_empty() { + self.next_ids.write().pop().unwrap() + } else { + Entity::new(rand::random::(), Wrapping(1)) + }; - let entity = Entity::new(rand::random::(), Wrapping(1)); self.entities.write().insert(entity); entity } diff --git a/crates/bevy_legion/legion_core/src/world.rs b/crates/bevy_legion/legion_core/src/world.rs index c396a1788a..c215709146 100644 --- a/crates/bevy_legion/legion_core/src/world.rs +++ b/crates/bevy_legion/legion_core/src/world.rs @@ -98,7 +98,7 @@ impl WorldId { pub struct World { id: WorldId, storage: UnsafeCell, - pub(crate) entity_allocator: Arc, + pub entity_allocator: Arc, entity_locations: Locations, defrag_progress: usize, command_buffer_size: usize, diff --git a/crates/bevy_props/Cargo.toml b/crates/bevy_props/Cargo.toml new file mode 100644 index 0000000000..e1567d7064 --- /dev/null +++ b/crates/bevy_props/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "bevy_props" +version = "0.1.0" +authors = ["Carter Anderson "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +serde = "1" +erased-serde = "0.3" \ No newline at end of file diff --git a/crates/bevy_props/src/lib.rs b/crates/bevy_props/src/lib.rs new file mode 100644 index 0000000000..e0f815bbbc --- /dev/null +++ b/crates/bevy_props/src/lib.rs @@ -0,0 +1,143 @@ +use serde::{ser::SerializeMap, Deserialize, Serialize}; +use std::{any::Any, collections::HashMap}; + +pub struct Test { + a: usize, + b: String, +} + +impl Props for Test { + fn prop(&self, name: &str) -> Option<&dyn Prop> { + match name { + "a" => Some(&self.a), + "b" => Some(&self.b), + _ => None, + } + } + fn prop_mut(&mut self, name: &str) -> Option<&mut dyn Prop> { + match name { + "a" => Some(&mut self.a), + "b" => Some(&mut self.b), + _ => None, + } + } + fn prop_names(&self) -> Vec<&str> { + static NAMES: &[&str] = &["a", "b"]; + NAMES.to_vec() + } +} + +#[derive(Default)] +pub struct DynamicProps { + pub props: HashMap>, +} + +impl DynamicProps { + pub fn set(&mut self, name: &str, prop: T) { + self.props.insert(name.to_string(), Box::new(prop)); + } +} + +impl Props for DynamicProps { + fn prop(&self, name: &str) -> Option<&dyn Prop> { + self.props.get(name).map(|p| &**p) + } + fn prop_mut(&mut self, name: &str) -> Option<&mut dyn Prop> { + self.props.get_mut(name).map(|p| &mut **p) + } + fn prop_names(&self) -> Vec<&str> { + self.props.keys().map(|k| k.as_str()).collect::>() + } +} + +impl Serialize for DynamicProps { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_map(Some(self.props.len()))?; + for prop_name in self.prop_names() { + let prop = self.prop(prop_name).unwrap(); + state.serialize_entry(prop_name, prop)?; + } + state.end() + // let mut state = serializer.serialize_struct("dyn", self.props.len())?; + // { + // for prop_name in self.prop_names() { + // let prop = self.prop(prop_name).unwrap(); + // state.serialize_field(strrr, prop)?; + // } + // } + // state.end() + } +} + +pub trait Props { + fn prop(&self, name: &str) -> Option<&dyn Prop>; + fn prop_mut(&mut self, name: &str) -> Option<&mut dyn Prop>; + fn prop_names(&self) -> Vec<&str>; + fn apply(&mut self, props: &dyn Props) { + for prop_name in props.prop_names() { + self.prop_mut(prop_name) + .unwrap() + .set_prop_val(props.prop(prop_name).unwrap().clone()); + } + } +} + +pub trait Prop: erased_serde::Serialize + Send + Sync + Any + 'static { + fn any(&self) -> &dyn Any; + fn any_mut(&mut self) -> &mut dyn Any; + fn clone(&self) -> Box; + fn type_name(&self) -> &str { + std::any::type_name::() + } +} + +erased_serde::serialize_trait_object!(Prop); + +pub trait PropVal { + fn prop_val(&self) -> Option<&T>; + fn set_prop_val(&mut self, value: T); + fn set_prop_val_boxed(&mut self, value: Box); +} + +impl PropVal for dyn Prop { + fn prop_val(&self) -> Option<&T> { + self.any().downcast_ref::() + } + fn set_prop_val(&mut self, value: T) { + if let Some(prop) = self.any_mut().downcast_mut::() { + *prop = value; + } + } + fn set_prop_val_boxed(&mut self, value: Box) { + if let Some(prop) = self.any_mut().downcast_mut::() { + *prop = *value.downcast::().unwrap(); + } + } +} + +impl<'a> Deserialize<'a> for DynamicProps { + fn deserialize(_deserializer: D) -> Result + where + D: serde::Deserializer<'a>, + { + Ok(DynamicProps::default()) + } +} + +impl Prop for T +where + T: Clone + Serialize + Send + Sync + Any + 'static, +{ + fn any(&self) -> &dyn Any { + self + } + fn any_mut(&mut self) -> &mut dyn Any { + self + } + fn clone(&self) -> Box { + Box::new(self.clone()) + } +} diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index a9d3039cd6..b8342ba68d 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -72,7 +72,7 @@ impl AppPlugin for RenderPlugin { .add_asset::() .add_asset::() .add_asset::() - .add_asset_loader(PngTextureLoader::default()) + .add_asset_loader::() .init_resource::() .init_resource::() .init_resource::() diff --git a/crates/bevy_serialization/Cargo.toml b/crates/bevy_scene/Cargo.toml similarity index 61% rename from crates/bevy_serialization/Cargo.toml rename to crates/bevy_scene/Cargo.toml index 025b82eb9c..b477675814 100644 --- a/crates/bevy_serialization/Cargo.toml +++ b/crates/bevy_scene/Cargo.toml @@ -1,13 +1,15 @@ [package] -name = "bevy_serialization" +name = "bevy_scene" version = "0.1.0" authors = ["Carter Anderson "] edition = "2018" [dependencies] +bevy_app = { path = "../bevy_app" } +bevy_asset = { path = "../bevy_asset" } legion = { path = "../bevy_legion", features = ["serialize"] } serde = { version = "1.0", features = ["derive"]} erased-serde = "0.3" -type-uuid = "0.1" ron = "0.5.1" -uuid = { version = "0.8", features = ["v4", "serde"] } \ No newline at end of file +uuid = { version = "0.8", features = ["v4", "serde"] } +anyhow = "1.0" \ No newline at end of file diff --git a/crates/bevy_scene/src/component_registry.rs b/crates/bevy_scene/src/component_registry.rs new file mode 100644 index 0000000000..350ef5d561 --- /dev/null +++ b/crates/bevy_scene/src/component_registry.rs @@ -0,0 +1,124 @@ +use bevy_app::AppBuilder; +use legion::{ + prelude::{Entity, World}, + storage::{ArchetypeDescription, ComponentResourceSet, ComponentTypeId}, +}; +use serde::{de::DeserializeSeed, ser::Serialize, Deserialize}; +use std::{collections::HashMap, marker::PhantomData, ptr::NonNull, sync::{RwLock, Arc}}; +use crate::world::ComponentSeqDeserializer; + +#[derive(Default)] +pub struct ComponentRegistryContext { + pub value: Arc>, +} + +#[derive(Default)] +pub struct ComponentRegistry { + pub registrations: HashMap, + pub short_names: HashMap, + pub full_names: HashMap, +} + +impl ComponentRegistry { + pub fn register(&mut self) + where + T: Send + Sync + 'static + Serialize + for<'de> Deserialize<'de>, + { + let registration = ComponentRegistration::of::(); + self.short_names + .insert(registration.short_name.to_string(), registration.ty); + self.full_names + .insert(registration.ty.0.to_string(), registration.ty); + self.registrations.insert(registration.ty, registration); + } + + pub fn get(&self, type_id: &ComponentTypeId) -> Option<&ComponentRegistration> { + self.registrations.get(type_id) + } + + pub fn get_with_full_name(&self, full_name: &str) -> Option<&ComponentRegistration> { + self.full_names + .get(full_name) + .and_then(|id| self.registrations.get(id)) + } + + pub fn get_with_short_name(&self, short_name: &str) -> Option<&ComponentRegistration> { + self.short_names + .get(short_name) + .and_then(|id| self.registrations.get(id)) + } +} + +#[derive(Clone)] +pub struct ComponentRegistration { + pub ty: ComponentTypeId, + pub comp_serialize_fn: fn(&ComponentResourceSet, &mut dyn FnMut(&dyn erased_serde::Serialize)), + pub individual_comp_serialize_fn: + fn(&ComponentResourceSet, usize, &mut dyn FnMut(&dyn erased_serde::Serialize)), + pub comp_deserialize_fn: fn( + deserializer: &mut dyn erased_serde::Deserializer, + get_next_storage_fn: &mut dyn FnMut() -> Option<(NonNull, usize)>, + ) -> Result<(), erased_serde::Error>, + pub individual_comp_deserialize_fn: fn( + deserializer: &mut dyn erased_serde::Deserializer, + &mut World, + Entity, + ) -> Result<(), erased_serde::Error>, + pub register_comp_fn: fn(&mut ArchetypeDescription), + pub short_name: &'static str, +} + +impl ComponentRegistration { + pub fn of Deserialize<'de> + Send + Sync + 'static>() -> Self { + let ty = ComponentTypeId::of::(); + Self { + ty, + comp_serialize_fn: |comp_storage, serialize_fn| { + // it's safe because we know this is the correct type due to lookup + let slice = unsafe { comp_storage.data_slice::() }; + serialize_fn(&*slice); + }, + individual_comp_serialize_fn: |comp_storage, index: usize, serialize_fn| { + // it's safe because we know this is the correct type due to lookup + let slice = unsafe { comp_storage.data_slice::() }; + serialize_fn(&slice[index]); + }, + comp_deserialize_fn: |deserializer, get_next_storage_fn| { + let comp_seq_deser = ComponentSeqDeserializer:: { + get_next_storage_fn, + _marker: PhantomData, + }; + comp_seq_deser.deserialize(deserializer)?; + Ok(()) + }, + individual_comp_deserialize_fn: |deserializer, world, entity| { + let component = erased_serde::deserialize::(deserializer)?; + world.add_component(entity, component).unwrap(); + Ok(()) + }, + register_comp_fn: |desc| { + desc.register_component::(); + }, + short_name: ty.0.split("::").last().unwrap(), + } + } +} + +pub trait RegisterComponent { + fn register_component(&mut self) -> &mut Self + where + T: Send + Sync + 'static + Serialize + for<'de> Deserialize<'de>; +} + +impl RegisterComponent for AppBuilder { + fn register_component(&mut self) -> &mut Self + where + T: Send + Sync + 'static + Serialize + for<'de> Deserialize<'de>, + { + { + let registry_context = self.resources().get_mut::().unwrap(); + registry_context.value.write().unwrap().register::(); + } + self + } +} diff --git a/crates/bevy_scene/src/lib.rs b/crates/bevy_scene/src/lib.rs new file mode 100644 index 0000000000..6dda3ba2fe --- /dev/null +++ b/crates/bevy_scene/src/lib.rs @@ -0,0 +1,28 @@ +mod component_registry; +mod scene; +mod serde; +pub use crate::serde::*; +pub use component_registry::*; +pub use scene::*; + +use bevy_app::{AppBuilder, AppPlugin}; +use bevy_asset::AddAsset; + +#[derive(Default)] +pub struct ComponentRegistryPlugin; + +impl AppPlugin for ComponentRegistryPlugin { + fn build(&self, app: &mut AppBuilder) { + app.init_resource::(); + } +} + +#[derive(Default)] +pub struct ScenePlugin; + +impl AppPlugin for ScenePlugin { + fn build(&self, app: &mut AppBuilder) { + app.add_asset::() + .add_asset_loader::(); + } +} diff --git a/crates/bevy_scene/src/scene.rs b/crates/bevy_scene/src/scene.rs new file mode 100644 index 0000000000..48cb462809 --- /dev/null +++ b/crates/bevy_scene/src/scene.rs @@ -0,0 +1,49 @@ +use crate::{ComponentRegistry, ComponentRegistryContext, SceneDeserializer}; +use anyhow::Result; +use bevy_app::FromResources; +use bevy_asset::AssetLoader; +use legion::prelude::{Resources, World}; +use serde::de::DeserializeSeed; +use std::{ + path::Path, + sync::{Arc, RwLock}, +}; + +#[derive(Default)] +pub struct Scene { + pub world: World, +} + +pub struct SceneLoader { + component_registry: Arc>, +} + +impl FromResources for SceneLoader { + fn from_resources(resources: &Resources) -> Self { + let component_registry = resources + .get::() + .expect("SceneLoader requires the ComponentRegistry resource."); + SceneLoader { + component_registry: component_registry.value.clone(), + } + } +} + +impl AssetLoader for SceneLoader { + fn from_bytes(&self, _asset_path: &Path, bytes: Vec) -> Result { + let mut deserializer = ron::de::Deserializer::from_bytes(&bytes).unwrap(); + let mut scene = Scene::default(); + let scene_deserializer = SceneDeserializer { + component_registry: &self.component_registry.read().unwrap(), + scene: &mut scene, + }; + + scene_deserializer.deserialize(&mut deserializer).unwrap(); + + Ok(scene) + } + fn extensions(&self) -> &[&str] { + static EXTENSIONS: &[&str] = &["scn"]; + EXTENSIONS + } +} diff --git a/crates/bevy_scene/src/serde/mod.rs b/crates/bevy_scene/src/serde/mod.rs new file mode 100644 index 0000000000..775bbd2964 --- /dev/null +++ b/crates/bevy_scene/src/serde/mod.rs @@ -0,0 +1,6 @@ +mod scene_de; +mod scene_ser; +pub mod world; + +pub use scene_de::*; +pub use scene_ser::*; \ No newline at end of file diff --git a/crates/bevy_scene/src/serde/scene_de.rs b/crates/bevy_scene/src/serde/scene_de.rs new file mode 100644 index 0000000000..578d8aae44 --- /dev/null +++ b/crates/bevy_scene/src/serde/scene_de.rs @@ -0,0 +1,288 @@ +use crate::{ComponentRegistration, ComponentRegistry, Scene}; +use legion::prelude::{Entity, World}; +use serde::{ + de::{DeserializeSeed, Error, MapAccess, SeqAccess, Visitor}, + Deserialize, +}; +use std::num::Wrapping; + +pub struct SceneDeserializer<'a> { + pub component_registry: &'a ComponentRegistry, + pub scene: &'a mut Scene, +} + +impl<'de> DeserializeSeed<'de> for SceneDeserializer<'de> { + type Value = (); + fn deserialize(self, deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_seq(EntitySeqVisiter { + world: &mut self.scene.world, + component_registry: &self.component_registry, + })?; + + Ok(()) + } +} + +struct EntitySeqVisiter<'a> { + pub component_registry: &'a ComponentRegistry, + pub world: &'a mut World, +} + +impl<'a, 'de> Visitor<'de> for EntitySeqVisiter<'a> { + type Value = (); + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("list of entities") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + while let Some(()) = seq.next_element_seed(EntityDeserializer { + world: self.world, + component_registry: self.component_registry, + })? {} + + Ok(()) + } +} + +struct EntityDeserializer<'a> { + pub component_registry: &'a ComponentRegistry, + pub world: &'a mut World, +} + + +pub const ENTITY_FIELD_ID: &str = "id"; +pub const ENTITY_FIELD_COMPONENTS: &str = "components"; + +impl<'a, 'de> DeserializeSeed<'de> for EntityDeserializer<'a> { + type Value = (); + fn deserialize(self, deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_struct( + "Entity", + &[ENTITY_FIELD_ID, ENTITY_FIELD_COMPONENTS], + EntityVisiter { + world: self.world, + component_registry: self.component_registry, + }, + ) + } +} + +struct EntityVisiter<'a> { + pub component_registry: &'a ComponentRegistry, + pub world: &'a mut World, +} + +impl<'a, 'de> Visitor<'de> for EntityVisiter<'a> { + type Value = (); + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("entity struct") + } + + fn visit_map(self, mut map: V) -> Result<(), V::Error> + where + V: MapAccess<'de>, + { + let mut entity = None; + let mut components = false; + while let Some(key) = map.next_key()? { + match key { + EntityField::Id => { + if entity.is_some() { + return Err(Error::duplicate_field(ENTITY_FIELD_ID)); + } + let id = map.next_value()?; + self.world + .entity_allocator + .push_next_ids((&[Entity::new(id, Wrapping(1))]).iter().map(|e| (*e))); + entity = Some(self.world.insert((), vec![()])[0]); + } + EntityField::Components => { + if components { + return Err(Error::duplicate_field(ENTITY_FIELD_COMPONENTS)); + } + + let entity = entity.ok_or_else(|| Error::missing_field(ENTITY_FIELD_ID))?; + // this is just a placeholder value to protect against duplicates + components = true; + map.next_value_seed(ComponentSeqDeserializer { + entity, + world: self.world, + component_registry: self.component_registry, + })?; + } + } + } + Ok(()) + } +} + +#[derive(Deserialize)] +#[serde(field_identifier, rename_all = "lowercase")] +enum EntityField { + Id, + Components, +} + +struct ComponentSeqDeserializer<'a> { + pub component_registry: &'a ComponentRegistry, + pub world: &'a mut World, + pub entity: Entity, +} + +impl<'a, 'de> DeserializeSeed<'de> for ComponentSeqDeserializer<'a> { + type Value = (); + fn deserialize(self, deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_seq(ComponentSeqVisiter { + entity: self.entity, + world: self.world, + component_registry: self.component_registry, + }) + } +} + +struct ComponentSeqVisiter<'a> { + pub component_registry: &'a ComponentRegistry, + pub world: &'a mut World, + pub entity: Entity, +} + +impl<'a, 'de> Visitor<'de> for ComponentSeqVisiter<'a> { + type Value = (); + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("list of components") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + while let Some(()) = seq.next_element_seed(ComponentDeserializer { + entity: self.entity, + world: self.world, + component_registry: self.component_registry, + })? {} + + Ok(()) + } +} +struct ComponentDeserializer<'a> { + pub component_registry: &'a ComponentRegistry, + pub world: &'a mut World, + pub entity: Entity, +} + +impl<'a, 'de> DeserializeSeed<'de> for ComponentDeserializer<'a> { + type Value = (); + fn deserialize(self, deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_struct( + "Component", + &[COMPONENT_FIELD_TYPE, COMPONENT_FIELD_DATA], + ComponentVisiter { + entity: self.entity, + world: self.world, + component_registry: self.component_registry, + }, + ) + } +} + +#[derive(Deserialize)] +#[serde(field_identifier, rename_all = "lowercase")] +enum ComponentField { + Type, + Data, +} + +pub const COMPONENT_FIELD_TYPE: &str = "type"; +pub const COMPONENT_FIELD_DATA: &str = "data"; + +struct ComponentVisiter<'a> { + pub component_registry: &'a ComponentRegistry, + pub world: &'a mut World, + pub entity: Entity, +} + +impl<'a, 'de> Visitor<'de> for ComponentVisiter<'a> { + type Value = (); + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("component") + } + + fn visit_map(self, mut map: V) -> Result<(), V::Error> + where + V: MapAccess<'de>, + { + let mut component_type = None; + let mut component_data = false; + while let Some(key) = map.next_key()? { + match key { + ComponentField::Type => { + if component_type.is_some() { + return Err(Error::duplicate_field(COMPONENT_FIELD_TYPE)); + } + component_type = Some(map.next_value::()?); + } + ComponentField::Data => { + if component_data { + return Err(Error::duplicate_field(COMPONENT_FIELD_DATA)); + } + + let component_type = component_type + .as_ref() + .ok_or_else(|| Error::missing_field(COMPONENT_FIELD_TYPE))?; + let component_registration = self + .component_registry + .get_with_short_name(component_type) + .ok_or_else(|| Error::custom(format!("Component '{}' has not been registered. Consider registering it with AppBuilder::register_component::<{}>()", component_type, component_type)))?; + // this is just a placeholder value to protect against duplicates + component_data = true; + map.next_value_seed(ComponentDataDeserializer { + entity: self.entity, + world: self.world, + component_registration, + })?; + } + } + } + Ok(()) + } +} + +struct ComponentDataDeserializer<'a> { + pub component_registration: &'a ComponentRegistration, + pub world: &'a mut World, + pub entity: Entity, +} + +impl<'a, 'de> DeserializeSeed<'de> for ComponentDataDeserializer<'a> { + type Value = (); + fn deserialize(self, deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + if let Err(err) = (self.component_registration.individual_comp_deserialize_fn)( + &mut erased_serde::Deserializer::erase(deserializer), + self.world, + self.entity, + ) { + return Err(Error::custom(err.to_string())); + } + + Ok(()) + } +} diff --git a/crates/bevy_serialization/src/scene.rs b/crates/bevy_scene/src/serde/scene_ser.rs similarity index 74% rename from crates/bevy_serialization/src/scene.rs rename to crates/bevy_scene/src/serde/scene_ser.rs index 5104b28f90..ce611afc76 100644 --- a/crates/bevy_serialization/src/scene.rs +++ b/crates/bevy_scene/src/serde/scene_ser.rs @@ -1,37 +1,10 @@ -use crate::ComponentRegistration; +use crate::{ComponentRegistry, Scene, ComponentRegistration}; use legion::{ - prelude::{Entity, World}, - storage::{ComponentMeta, ComponentStorage, ComponentTypeId, ComponentResourceSet}, + prelude::Entity, + storage::{ComponentMeta, ComponentResourceSet, ComponentStorage, ComponentTypeId}, }; -use serde::{ - ser::{Serialize, SerializeSeq, SerializeStruct}, - Deserialize, -}; -use std::{cell::RefCell, collections::HashMap}; - -#[derive(Default)] -pub struct Scene { - pub world: World, -} - -#[derive(Default)] -pub struct ComponentRegistry { - pub registrations: HashMap, -} - -impl ComponentRegistry { - pub fn register(&mut self) - where - T: Send + Sync + 'static + Serialize + for<'de> Deserialize<'de>, - { - let registration = ComponentRegistration::of::(); - self.registrations.insert(registration.ty, registration); - } - - pub fn get(&self, type_id: ComponentTypeId) -> Option<&ComponentRegistration> { - self.registrations.get(&type_id) - } -} +use serde::ser::{Serialize, SerializeSeq, SerializeStruct}; +use std::cell::RefCell; pub struct SerializableScene<'a> { pub scene: &'a Scene, @@ -68,13 +41,6 @@ impl<'a> Serialize for SerializableScene<'a> { } } } - // for entity in self.scene.world.iter_entities() { - // seq.serialize_element(&WorldEntity { - // world: &self.scene.world, - // component_registry: &self.component_registry, - // entity, - // })?; - // } seq.end() } @@ -125,7 +91,7 @@ impl<'a> Serialize for EntityComponents<'a> { seq.serialize_element(&EntityComponent { index: self.index, component_resource_set: self.component_storage.components(*component_type).unwrap(), - component_registration: self.component_registry.get(*component_type).unwrap(), + component_registration: self.component_registry.get(component_type).unwrap(), })?; } seq.end() @@ -139,6 +105,44 @@ struct EntityComponent<'a> { } impl<'a> Serialize for EntityComponent<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("Component", 2)?; + state.serialize_field( + "type", + &ComponentName(self.component_registration.short_name), + )?; + state.serialize_field( + "data", + &ComponentData { + index: self.index, + component_registration: self.component_registration, + component_resource_set: self.component_resource_set, + }, + )?; + state.end() + } +} + +struct ComponentName(&'static str); + +impl<'a> Serialize for ComponentName { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(self.0) + } +} +struct ComponentData<'a> { + index: usize, + component_resource_set: &'a ComponentResourceSet, + component_registration: &'a ComponentRegistration, +} + +impl<'a> Serialize for ComponentData<'a> { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, diff --git a/crates/bevy_serialization/src/world.rs b/crates/bevy_scene/src/serde/world.rs similarity index 70% rename from crates/bevy_serialization/src/world.rs rename to crates/bevy_scene/src/serde/world.rs index 0b93096c9a..4d0b48048e 100644 --- a/crates/bevy_serialization/src/world.rs +++ b/crates/bevy_scene/src/serde/world.rs @@ -1,5 +1,6 @@ // adapted from https://github.com/TomGillen/legion/blob/master/examples/serde.rs +use crate::ComponentRegistry; use legion::{ entity::{EntityIndex, GuidEntityAllocator}, prelude::*, @@ -12,10 +13,7 @@ use serde::{ de::{self, DeserializeSeed, IgnoredAny, Visitor}, Deserialize, Deserializer, Serialize, Serializer, }; -use std::{ - cell::RefCell, collections::HashMap, iter::FromIterator, marker::PhantomData, num::Wrapping, - ptr::NonNull, -}; +use std::{cell::RefCell, marker::PhantomData, num::Wrapping, ptr::NonNull}; struct ComponentDeserializer<'de, T: Deserialize<'de>> { ptr: *mut T, @@ -36,9 +34,9 @@ impl<'de, T: Deserialize<'de> + 'static> DeserializeSeed<'de> for ComponentDeser } } -struct ComponentSeqDeserializer<'a, T> { - get_next_storage_fn: &'a mut dyn FnMut() -> Option<(NonNull, usize)>, - _marker: PhantomData, +pub(crate) struct ComponentSeqDeserializer<'a, T> { + pub get_next_storage_fn: &'a mut dyn FnMut() -> Option<(NonNull, usize)>, + pub _marker: PhantomData, } impl<'de, 'a, T: for<'b> Deserialize<'b> + 'static> DeserializeSeed<'de> @@ -97,76 +95,18 @@ impl<'de, 'a, T: for<'b> Deserialize<'b> + 'static> Visitor<'de> } } -#[derive(Clone)] -pub struct ComponentRegistration { - pub ty: ComponentTypeId, - pub comp_serialize_fn: fn(&ComponentResourceSet, &mut dyn FnMut(&dyn erased_serde::Serialize)), - pub individual_comp_serialize_fn: - fn(&ComponentResourceSet, usize, &mut dyn FnMut(&dyn erased_serde::Serialize)), - pub comp_deserialize_fn: fn( - deserializer: &mut dyn erased_serde::Deserializer, - get_next_storage_fn: &mut dyn FnMut() -> Option<(NonNull, usize)>, - ) -> Result<(), erased_serde::Error>, - pub register_comp_fn: fn(&mut ArchetypeDescription), -} - -impl ComponentRegistration { - pub fn of Deserialize<'de> + Send + Sync + 'static>() -> Self { - Self { - ty: ComponentTypeId::of::(), - comp_serialize_fn: |comp_storage, serialize_fn| { - // it's safe because we know this is the correct type due to lookup - let slice = unsafe { comp_storage.data_slice::() }; - serialize_fn(&*slice); - }, - individual_comp_serialize_fn: |comp_storage, index: usize, serialize_fn| { - // it's safe because we know this is the correct type due to lookup - let slice = unsafe { comp_storage.data_slice::() }; - serialize_fn(&slice[index]); - }, - comp_deserialize_fn: |deserializer, get_next_storage_fn| { - let comp_seq_deser = ComponentSeqDeserializer:: { - get_next_storage_fn, - _marker: PhantomData, - }; - comp_seq_deser.deserialize(deserializer)?; - Ok(()) - }, - register_comp_fn: |desc| { - desc.register_component::(); - }, - } - } -} - #[derive(Serialize, Deserialize)] struct SerializedArchetypeDescription { tag_types: Vec, component_types: Vec, } -pub struct SerializeImpl { - pub comp_types: HashMap, -} - -impl SerializeImpl { - pub fn new(component_registrations: &[ComponentRegistration]) -> Self { - SerializeImpl { - comp_types: HashMap::from_iter( - component_registrations - .iter() - .map(|reg| (reg.ty.0.to_string(), reg.clone())), - ), - } - } -} - -impl legion::serialize::ser::WorldSerializer for SerializeImpl { +impl legion::serialize::ser::WorldSerializer for ComponentRegistry { fn can_serialize_tag(&self, _ty: &TagTypeId, _meta: &TagMeta) -> bool { false } fn can_serialize_component(&self, ty: &ComponentTypeId, _meta: &ComponentMeta) -> bool { - self.comp_types.get(ty.0).is_some() + self.get(ty).is_some() } fn serialize_archetype_description( &self, @@ -196,7 +136,7 @@ impl legion::serialize::ser::WorldSerializer for SerializeImpl { _component_meta: &ComponentMeta, components: &ComponentResourceSet, ) -> Result { - if let Some(reg) = self.comp_types.get(component_type.0) { + if let Some(reg) = self.get(component_type) { let result = RefCell::new(None); let serializer = RefCell::new(Some(serializer)); { @@ -236,19 +176,7 @@ impl legion::serialize::ser::WorldSerializer for SerializeImpl { } } -pub struct DeserializeImpl<'a> { - pub comp_types: &'a HashMap, -} - -impl<'a> DeserializeImpl<'a> { - pub fn new(component_types: &'a HashMap) -> Self { - DeserializeImpl { - comp_types: component_types, - } - } -} - -impl<'a> legion::serialize::de::WorldDeserializer for DeserializeImpl<'a> { +impl<'a> legion::serialize::de::WorldDeserializer for ComponentRegistry { fn deserialize_archetype_description<'de, D: Deserializer<'de>>( &self, deserializer: D, @@ -258,7 +186,7 @@ impl<'a> legion::serialize::de::WorldDeserializer for DeserializeImpl<'a> { let mut desc = ArchetypeDescription::default(); for comp in serialized_desc.component_types { - if let Some(reg) = self.comp_types.get(&comp) { + if let Some(reg) = self.get_with_full_name(&comp) { (reg.register_comp_fn)(&mut desc); } } @@ -271,7 +199,7 @@ impl<'a> legion::serialize::de::WorldDeserializer for DeserializeImpl<'a> { _component_meta: &ComponentMeta, get_next_storage_fn: &mut dyn FnMut() -> Option<(NonNull, usize)>, ) -> Result<(), >::Error> { - if let Some(reg) = self.comp_types.get(component_type.0) { + if let Some(reg) = self.get(component_type) { let mut erased = erased_serde::Deserializer::erase(deserializer); (reg.comp_deserialize_fn)(&mut erased, get_next_storage_fn) .map_err(<>::Error as serde::de::Error>::custom)?; diff --git a/crates/bevy_serialization/src/lib.rs b/crates/bevy_serialization/src/lib.rs deleted file mode 100644 index b729e16d43..0000000000 --- a/crates/bevy_serialization/src/lib.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod world; -mod scene; -pub use world::*; -pub use scene::*; diff --git a/crates/bevy_text/src/font_loader.rs b/crates/bevy_text/src/font_loader.rs index 79542c63ab..985e6995cf 100644 --- a/crates/bevy_text/src/font_loader.rs +++ b/crates/bevy_text/src/font_loader.rs @@ -3,7 +3,7 @@ use anyhow::Result; use bevy_asset::AssetLoader; use std::path::Path; -#[derive(Clone)] +#[derive(Default)] pub struct FontLoader; impl AssetLoader for FontLoader { diff --git a/crates/bevy_text/src/lib.rs b/crates/bevy_text/src/lib.rs index 4313d55fce..dcb05fee3b 100644 --- a/crates/bevy_text/src/lib.rs +++ b/crates/bevy_text/src/lib.rs @@ -13,6 +13,7 @@ pub struct TextPlugin; impl AppPlugin for TextPlugin { fn build(&self, app: &mut AppBuilder) { - app.add_asset::().add_asset_loader(FontLoader); + app.add_asset::() + .add_asset_loader::(); } } diff --git a/examples/3d/scene.rs b/examples/3d/3d_scene.rs similarity index 100% rename from examples/3d/scene.rs rename to examples/3d/3d_scene.rs diff --git a/examples/scene/load_scene.rs b/examples/scene/load_scene.rs new file mode 100644 index 0000000000..7bd4be7207 --- /dev/null +++ b/examples/scene/load_scene.rs @@ -0,0 +1,34 @@ +use bevy::prelude::*; +use serde::{Deserialize, Serialize}; + +fn main() { + App::build() + .add_default_plugins() + // Registering components informs Bevy that they exist. This allows them to be used when loading/saving scenes + .register_component::() + .register_component::() + .add_startup_system(load_scene) + .run(); +} + +#[derive(Serialize, Deserialize)] +struct Test { + pub x: f32, + pub y: f32, +} + +#[derive(Serialize, Deserialize)] +struct Foo { + pub value: String, +} + +fn load_scene(_world: &mut World, resources: &mut Resources) { + let asset_server = resources.get::().unwrap(); + let mut scenes = resources.get_mut::>().unwrap(); + + let scene_handle: Handle = asset_server + .load_sync(&mut scenes, "assets/scene/load_scene_example.scn") + .unwrap(); + let _scene= scenes.get(&scene_handle).unwrap(); + // world.merge(scene) +} \ No newline at end of file diff --git a/examples/serializing/serializing.rs b/examples/serializing/serializing.rs deleted file mode 100644 index a01e10b889..0000000000 --- a/examples/serializing/serializing.rs +++ /dev/null @@ -1,88 +0,0 @@ -use bevy::{prelude::*, serialization::*}; -use legion::serialize::{de::deserialize, ser::serializable_world}; -use serde::{Deserialize, Serialize}; - -fn main() { - App::build() - .add_plugin(ScheduleRunnerPlugin::run_once()) - // .add_startup_system(setup) - .add_startup_system(setup_scene.system()) - .run(); -} - -#[derive(Serialize, Deserialize)] -struct Test { - pub x: f32, - pub y: f32, -} - -#[derive(Serialize, Deserialize)] -struct Foo { - pub value: String, -} - -fn setup_scene() { - let mut component_registry = ComponentRegistry::default(); - component_registry.register::(); - component_registry.register::(); - - let mut scene = Scene::default(); - scene.world.insert((), vec![(Test { x: 3.0, y: 4.0 }, Foo { value: "hi".to_string()}),]); - scene.world.insert((), vec![(Test { x: 3.0, y: 4.0 },)]); - - let serializable_scene = SerializableScene::new(&scene, &component_registry); - - let mut serializer = ron::ser::Serializer::new(Some(ron::ser::PrettyConfig::default()), true); - serializable_scene.serialize(&mut serializer).unwrap(); - println!("{}", serializer.into_output_string()); -} - -fn _setup(world: &mut World, resources: &mut Resources) { - world.insert((), vec![(Test { x: 3.0, y: 4.0 },)]); - - let comp_registrations = [ComponentRegistration::of::()]; - - let ser_helper = SerializeImpl::new(&comp_registrations); - let serializable = serializable_world(world, &ser_helper); - println!("JSON"); - let serialized_data = serde_json::to_string(&serializable).unwrap(); - println!("{}", serialized_data); - println!(); - - println!("RON"); - let pretty = ron::ser::PrettyConfig { - depth_limit: 2, - separate_tuple_members: true, - enumerate_arrays: true, - ..Default::default() - }; - let s = ron::ser::to_string_pretty(&serializable, pretty.clone()).expect("Serialization failed"); - println!("{}", s); - println!(); - - - let universe = resources.get_mut::().unwrap(); - println!("JSON (Round Trip)"); - let de_helper = DeserializeImpl::new(&ser_helper.comp_types); - let mut new_world = universe.create_world(); - let mut deserializer = serde_json::Deserializer::from_str(&serialized_data); - deserialize(&mut new_world, &de_helper, &mut deserializer).unwrap(); - let round_trip_ser_helper = - SerializeImpl::new(&comp_registrations); - let serializable = serializable_world(&new_world, &round_trip_ser_helper); - let roundtrip_data = serde_json::to_string(&serializable).unwrap(); - println!("{}", roundtrip_data); - assert_eq!(roundtrip_data, serialized_data); - - println!("RON (Round Trip)"); - let de_helper = DeserializeImpl::new(&ser_helper.comp_types); - let mut new_world = universe.create_world(); - let mut deserializer = ron::de::Deserializer::from_str(&s).unwrap(); - deserialize(&mut new_world, &de_helper, &mut deserializer).unwrap(); - let round_trip_ser_helper = - SerializeImpl::new(&comp_registrations); - let serializable = serializable_world(&new_world, &round_trip_ser_helper); - let roundtrip_data = ron::ser::to_string_pretty(&serializable, pretty).expect("Serialization failed"); - println!("{}", roundtrip_data); - assert_eq!(roundtrip_data, s); -} diff --git a/src/add_default_plugins.rs b/src/add_default_plugins.rs index 3d406d1ed1..0eab6eacf9 100644 --- a/src/add_default_plugins.rs +++ b/src/add_default_plugins.rs @@ -12,6 +12,9 @@ impl AddDefaultPlugins for AppBuilder { #[cfg(feature = "diagnostic")] self.add_plugin(bevy_diagnostic::DiagnosticsPlugin::default()); + #[cfg(feature = "scene")] + self.add_plugin(bevy_scene::ComponentRegistryPlugin::default()); + #[cfg(feature = "input")] self.add_plugin(bevy_input::InputPlugin::default()); @@ -21,6 +24,9 @@ impl AddDefaultPlugins for AppBuilder { #[cfg(feature = "asset")] self.add_plugin(bevy_asset::AssetPlugin::default()); + #[cfg(feature = "scene")] + self.add_plugin(bevy_scene::ScenePlugin::default()); + #[cfg(feature = "render")] self.add_plugin(bevy_render::RenderPlugin::default()); diff --git a/src/lib.rs b/src/lib.rs index c00bd4a415..7a155da088 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -61,10 +61,12 @@ pub use bevy_gltf as gltf; pub use bevy_input as input; #[cfg(feature = "pbr")] pub use bevy_pbr as pbr; +#[cfg(feature = "props")] +pub use bevy_props as props; #[cfg(feature = "render")] pub use bevy_render as render; -#[cfg(feature = "serialization")] -pub use bevy_serialization as serialization; +#[cfg(feature = "scene")] +pub use bevy_scene as scene; #[cfg(feature = "text")] pub use bevy_text as text; #[cfg(feature = "transform")] diff --git a/src/prelude.rs b/src/prelude.rs index 02110a2d26..d416a759b8 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -29,6 +29,8 @@ pub use crate::render::{ texture::{Texture, TextureType}, ActiveCamera, ActiveCamera2d, Camera, CameraType, Color, ColorSource, Renderable, }; +#[cfg(feature = "scene")] +pub use crate::scene::{Scene, RegisterComponent}; #[cfg(feature = "text")] pub use crate::text::Font; #[cfg(feature = "transform")]