scenes: deserialization and refactor

This commit is contained in:
Carter Anderson 2020-05-21 17:21:33 -07:00
parent 553b754492
commit d920100d35
30 changed files with 822 additions and 247 deletions

View file

@ -2,10 +2,11 @@
## Adapted Code
* legion
* legion_transform
* wgpu-rs examples
## Insipration
## Inspiration
* amethyst
* coffee

View file

@ -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"

View file

@ -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,
),
),
],
),
]

View file

@ -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"] }
log = { version = "0.4", features = ["release_max_level_info"] }
serde = { version = "1.0", features = ["derive"]}
erased-serde = "0.3"

View file

@ -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::*;

View file

@ -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<T>(&mut self) -> &mut Self
where
T: Send + Sync + 'static;
fn add_asset_loader<TLoader, TAsset>(&mut self, loader: TLoader) -> &mut Self
fn add_asset_loader<TAsset, TLoader>(&mut self) -> &mut Self
where
TLoader: AssetLoader<TAsset> + Clone,
TLoader: AssetLoader<TAsset> + FromResources,
TAsset: Send + Sync + 'static;
}
@ -153,9 +153,9 @@ impl AddAsset for AppBuilder {
.add_event::<AssetEvent<T>>()
}
fn add_asset_loader<TLoader, TAsset>(&mut self, loader: TLoader) -> &mut Self
fn add_asset_loader<TAsset, TLoader>(&mut self) -> &mut Self
where
TLoader: AssetLoader<TAsset> + Clone,
TLoader: AssetLoader<TAsset> + FromResources,
TAsset: Send + Sync + 'static,
{
{
@ -174,8 +174,8 @@ impl AddAsset for AppBuilder {
.resources()
.get_mut::<AssetServer>()
.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

View file

@ -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::<Mesh, GltfLoader>();
}
}

View file

@ -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<Mesh> for GltfLoader {

View file

@ -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::<u32>(), Wrapping(1))
};
let entity = Entity::new(rand::random::<u32>(), Wrapping(1));
self.entities.write().insert(entity);
entity
}

View file

@ -98,7 +98,7 @@ impl WorldId {
pub struct World {
id: WorldId,
storage: UnsafeCell<Storage>,
pub(crate) entity_allocator: Arc<GuidEntityAllocator>,
pub entity_allocator: Arc<GuidEntityAllocator>,
entity_locations: Locations,
defrag_progress: usize,
command_buffer_size: usize,

View file

@ -0,0 +1,11 @@
[package]
name = "bevy_props"
version = "0.1.0"
authors = ["Carter Anderson <mcanders1@gmail.com>"]
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"

View file

@ -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<String, Box<dyn Prop>>,
}
impl DynamicProps {
pub fn set<T: Prop>(&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::<Vec<&str>>()
}
}
impl Serialize for DynamicProps {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
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<dyn Any>;
fn type_name(&self) -> &str {
std::any::type_name::<Self>()
}
}
erased_serde::serialize_trait_object!(Prop);
pub trait PropVal {
fn prop_val<T: 'static>(&self) -> Option<&T>;
fn set_prop_val<T: 'static>(&mut self, value: T);
fn set_prop_val_boxed<T: 'static>(&mut self, value: Box<dyn Any>);
}
impl PropVal for dyn Prop {
fn prop_val<T: 'static>(&self) -> Option<&T> {
self.any().downcast_ref::<T>()
}
fn set_prop_val<T: 'static>(&mut self, value: T) {
if let Some(prop) = self.any_mut().downcast_mut::<T>() {
*prop = value;
}
}
fn set_prop_val_boxed<T: 'static>(&mut self, value: Box<dyn Any>) {
if let Some(prop) = self.any_mut().downcast_mut::<T>() {
*prop = *value.downcast::<T>().unwrap();
}
}
}
impl<'a> Deserialize<'a> for DynamicProps {
fn deserialize<D>(_deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'a>,
{
Ok(DynamicProps::default())
}
}
impl<T> 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<dyn Any> {
Box::new(self.clone())
}
}

View file

@ -72,7 +72,7 @@ impl AppPlugin for RenderPlugin {
.add_asset::<Texture>()
.add_asset::<Shader>()
.add_asset::<PipelineDescriptor>()
.add_asset_loader(PngTextureLoader::default())
.add_asset_loader::<Texture, PngTextureLoader>()
.init_resource::<RenderGraph>()
.init_resource::<PipelineAssignments>()
.init_resource::<PipelineCompiler>()

View file

@ -1,13 +1,15 @@
[package]
name = "bevy_serialization"
name = "bevy_scene"
version = "0.1.0"
authors = ["Carter Anderson <mcanders1@gmail.com>"]
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"] }
uuid = { version = "0.8", features = ["v4", "serde"] }
anyhow = "1.0"

View file

@ -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<RwLock<ComponentRegistry>>,
}
#[derive(Default)]
pub struct ComponentRegistry {
pub registrations: HashMap<ComponentTypeId, ComponentRegistration>,
pub short_names: HashMap<String, ComponentTypeId>,
pub full_names: HashMap<String, ComponentTypeId>,
}
impl ComponentRegistry {
pub fn register<T>(&mut self)
where
T: Send + Sync + 'static + Serialize + for<'de> Deserialize<'de>,
{
let registration = ComponentRegistration::of::<T>();
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<u8>, 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<T: Serialize + for<'de> Deserialize<'de> + Send + Sync + 'static>() -> Self {
let ty = ComponentTypeId::of::<T>();
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::<T>() };
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::<T>() };
serialize_fn(&slice[index]);
},
comp_deserialize_fn: |deserializer, get_next_storage_fn| {
let comp_seq_deser = ComponentSeqDeserializer::<T> {
get_next_storage_fn,
_marker: PhantomData,
};
comp_seq_deser.deserialize(deserializer)?;
Ok(())
},
individual_comp_deserialize_fn: |deserializer, world, entity| {
let component = erased_serde::deserialize::<T>(deserializer)?;
world.add_component(entity, component).unwrap();
Ok(())
},
register_comp_fn: |desc| {
desc.register_component::<T>();
},
short_name: ty.0.split("::").last().unwrap(),
}
}
}
pub trait RegisterComponent {
fn register_component<T>(&mut self) -> &mut Self
where
T: Send + Sync + 'static + Serialize + for<'de> Deserialize<'de>;
}
impl RegisterComponent for AppBuilder {
fn register_component<T>(&mut self) -> &mut Self
where
T: Send + Sync + 'static + Serialize + for<'de> Deserialize<'de>,
{
{
let registry_context = self.resources().get_mut::<ComponentRegistryContext>().unwrap();
registry_context.value.write().unwrap().register::<T>();
}
self
}
}

View file

@ -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::<ComponentRegistryContext>();
}
}
#[derive(Default)]
pub struct ScenePlugin;
impl AppPlugin for ScenePlugin {
fn build(&self, app: &mut AppBuilder) {
app.add_asset::<Scene>()
.add_asset_loader::<Scene, SceneLoader>();
}
}

View file

@ -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<RwLock<ComponentRegistry>>,
}
impl FromResources for SceneLoader {
fn from_resources(resources: &Resources) -> Self {
let component_registry = resources
.get::<ComponentRegistryContext>()
.expect("SceneLoader requires the ComponentRegistry resource.");
SceneLoader {
component_registry: component_registry.value.clone(),
}
}
}
impl AssetLoader<Scene> for SceneLoader {
fn from_bytes(&self, _asset_path: &Path, bytes: Vec<u8>) -> Result<Scene> {
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
}
}

View file

@ -0,0 +1,6 @@
mod scene_de;
mod scene_ser;
pub mod world;
pub use scene_de::*;
pub use scene_ser::*;

View file

@ -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<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
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<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
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<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
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<V>(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<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
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<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
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<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
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<V>(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::<String>()?);
}
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<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
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(())
}
}

View file

@ -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<ComponentTypeId, ComponentRegistration>,
}
impl ComponentRegistry {
pub fn register<T>(&mut self)
where
T: Send + Sync + 'static + Serialize + for<'de> Deserialize<'de>,
{
let registration = ComponentRegistration::of::<T>();
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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,

View file

@ -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<u8>, usize)>,
_marker: PhantomData<T>,
pub(crate) struct ComponentSeqDeserializer<'a, T> {
pub get_next_storage_fn: &'a mut dyn FnMut() -> Option<(NonNull<u8>, usize)>,
pub _marker: PhantomData<T>,
}
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<u8>, usize)>,
) -> Result<(), erased_serde::Error>,
pub register_comp_fn: fn(&mut ArchetypeDescription),
}
impl ComponentRegistration {
pub fn of<T: Serialize + for<'de> Deserialize<'de> + Send + Sync + 'static>() -> Self {
Self {
ty: ComponentTypeId::of::<T>(),
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::<T>() };
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::<T>() };
serialize_fn(&slice[index]);
},
comp_deserialize_fn: |deserializer, get_next_storage_fn| {
let comp_seq_deser = ComponentSeqDeserializer::<T> {
get_next_storage_fn,
_marker: PhantomData,
};
comp_seq_deser.deserialize(deserializer)?;
Ok(())
},
register_comp_fn: |desc| {
desc.register_component::<T>();
},
}
}
}
#[derive(Serialize, Deserialize)]
struct SerializedArchetypeDescription {
tag_types: Vec<String>,
component_types: Vec<String>,
}
pub struct SerializeImpl {
pub comp_types: HashMap<String, ComponentRegistration>,
}
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<S: Serializer>(
&self,
@ -196,7 +136,7 @@ impl legion::serialize::ser::WorldSerializer for SerializeImpl {
_component_meta: &ComponentMeta,
components: &ComponentResourceSet,
) -> Result<S::Ok, S::Error> {
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<String, ComponentRegistration>,
}
impl<'a> DeserializeImpl<'a> {
pub fn new(component_types: &'a HashMap<String, ComponentRegistration>) -> 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<u8>, usize)>,
) -> Result<(), <D as Deserializer<'de>>::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(<<D as serde::Deserializer<'de>>::Error as serde::de::Error>::custom)?;

View file

@ -1,4 +0,0 @@
mod world;
mod scene;
pub use world::*;
pub use scene::*;

View file

@ -3,7 +3,7 @@ use anyhow::Result;
use bevy_asset::AssetLoader;
use std::path::Path;
#[derive(Clone)]
#[derive(Default)]
pub struct FontLoader;
impl AssetLoader<Font> for FontLoader {

View file

@ -13,6 +13,7 @@ pub struct TextPlugin;
impl AppPlugin for TextPlugin {
fn build(&self, app: &mut AppBuilder) {
app.add_asset::<Font>().add_asset_loader(FontLoader);
app.add_asset::<Font>()
.add_asset_loader::<Font, FontLoader>();
}
}

View file

@ -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::<Test>()
.register_component::<Foo>()
.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::<AssetServer>().unwrap();
let mut scenes = resources.get_mut::<Assets<Scene>>().unwrap();
let scene_handle: Handle<Scene> = asset_server
.load_sync(&mut scenes, "assets/scene/load_scene_example.scn")
.unwrap();
let _scene= scenes.get(&scene_handle).unwrap();
// world.merge(scene)
}

View file

@ -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::<Test>();
component_registry.register::<Foo>();
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::<Test>()];
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::<Universe>().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);
}

View file

@ -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());

View file

@ -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")]

View file

@ -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")]