From a40958f0bb763ac90d155f00a4d280305b811e1b Mon Sep 17 00:00:00 2001 From: Elijah Voigt Date: Tue, 26 Sep 2023 07:11:54 -0700 Subject: [PATCH] Saving my place --- Cargo.lock | 1 + Cargo.toml | 3 +- src/editor/level.rs | 92 ++++++++++++++++++++++++++++++++++++++++-- src/editor/prelude.rs | 1 + src/editor/timeline.rs | 41 ++++++------------- 5 files changed, 105 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 639aefc..6aa7d71 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2147,6 +2147,7 @@ name = "monologue-trees" version = "0.1.0" dependencies = [ "bevy", + "ron", "serde", ] diff --git a/Cargo.toml b/Cargo.toml index 498c40b..549e570 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,8 @@ path = "bin/serialize-wtf.rs" [dependencies] bevy = "0.11" # bevy_rapier3d = "*" -serde = "1" +serde = { version = "1", features = ["derive"] } +ron = "0.8" # From rapier docs [profile.dev.package.bevy_rapier3d] diff --git a/src/editor/level.rs b/src/editor/level.rs index d3ba0e5..f9bbf54 100644 --- a/src/editor/level.rs +++ b/src/editor/level.rs @@ -1,5 +1,41 @@ +/// +/// General levevel structure is this: +/// Level: Vec +/// Epoch: Id, Handle +/// +/// You can index a Level by it's Epoch like +/// `/path/to/level.file#epoch-id` +/// +/// !!!TODO!!! +/// * read .trees.level files, loading Levels +/// * Similar to GLTFs, this contains nested assets +/// * Handle -> Vec> => Handle +/// * Review how GLTF loading works for this, very similar goal. +/// +/// Ex: +/// foo.trees.level: +/// [ +/// Epoch { +/// id: 1, +/// scene: [ +/// entities: [...] +/// resources: [...] +/// ], +/// }, +/// Epoch { +/// id: 2, +/// scene: [...] +/// }, etc +/// ] +/// use crate::editor::prelude::*; -use bevy::tasks::IoTaskPool; +use bevy::{ + asset::{AssetLoader, LoadContext, LoadedAsset}, + reflect::{TypePath, TypeUuid}, + scene::SceneLoader, + tasks::IoTaskPool, + utils::BoxedFuture, +}; #[derive(Debug, Default)] pub struct EditorLevelPlugin; @@ -7,8 +43,10 @@ pub struct EditorLevelPlugin; impl Plugin for EditorLevelPlugin { fn build(&self, app: &mut App) { app.register_type::() + .add_asset::() + .init_asset_loader::() .add_systems(Update, level_ui) - .add_systems(Update, load_level) + .add_systems(Update, set_level) .add_systems(Update, export_level.run_if(ui::activated::)) .add_systems(Update, rehydrate_level::) .add_systems( @@ -24,7 +62,16 @@ impl Plugin for EditorLevelPlugin { #[reflect(Component)] pub struct LevelRoot; -pub type Level = DynamicScene; +#[derive(Debug, TypeUuid, TypePath, Default, Deserialize)] +#[uuid = "eb5c5f73-076e-4476-83f2-d97cf077ec90"] +struct Level { + epochs: Vec, +} + +#[derive(Reflect)] +struct RawLevel { + epochs: Vec, +} #[derive(Debug, Component, Default)] pub struct LevelWidget; @@ -84,7 +131,7 @@ fn level_ui( }); } -fn load_level( +fn set_level( events: Query< &ui::TargetAsset, (Added, With>), @@ -224,3 +271,40 @@ fn clear_level( }); }) } + +#[derive(Default)] +pub struct LevelAssetLoader; + +impl AssetLoader for LevelAssetLoader { + fn load<'a>( + &'a self, + bytes: &'a [u8], + load_context: &'a mut LoadContext, + ) -> BoxedFuture<'a, Result<(), bevy::asset::Error>> { + Box::pin(async move { Ok(load_level(bytes, load_context).await?) }) + } + + fn extensions(&self) -> &[&str] { + &["trees.level"] + } +} + +// ref: https://github.com/bevyengine/bevy/blob/12c6fa7e591545afe8c7101b0f021121eea7272f/crates/bevy_gltf/src/loader.rs#L92 +pub async fn load_level<'a, 'b>( + bytes: &'a [u8], + load_context: &'a mut LoadContext<'b>, +) -> Handle { + let level: Level = ron::de::from_bytes(bytes).expect("Deserialize level"); + info!("Level: {:?}", level); + let scene = SceneLoader::from_world(&mut World::new()) + .load(bytes, load_context) + .await; + info!("Scene result: {:?}", scene); + let epochs: Vec = { + todo!("Load epochs"); + let id = todo!("get epoch ID"); + let scene = todo!("get dynamic scene"); + vec![] + }; + load_context.set_default_asset(LoadedAsset::new(Level { epochs })); +} diff --git a/src/editor/prelude.rs b/src/editor/prelude.rs index 97006d9..57464b4 100644 --- a/src/editor/prelude.rs +++ b/src/editor/prelude.rs @@ -6,3 +6,4 @@ pub use crate::{ ui, }; pub use bevy::{gltf::Gltf, prelude::*}; +pub use serde::{Deserialize, Serialize}; diff --git a/src/editor/timeline.rs b/src/editor/timeline.rs index 375d0b2..032524f 100644 --- a/src/editor/timeline.rs +++ b/src/editor/timeline.rs @@ -1,3 +1,5 @@ +use bevy::reflect::TypeUuid; + use crate::editor::prelude::*; #[derive(Debug, Default)] @@ -10,44 +12,27 @@ impl Plugin for EditorTimelinePlugin { } } +#[derive(Debug, Component, Resource, Clone, Default, Reflect, TypeUuid, Deserialize)] +#[uuid = "959f5f02-7c80-4b3d-ad02-9dc2e5d1b963"] +pub struct Epoch { + id: usize, + // scene: Handle, +} + /// Timeline widget marker #[derive(Debug, Component)] pub struct TimelineWidget; -#[derive(Debug, Resource)] -struct ActiveEpoch { - id: EpochId, - entity: Entity, -} - /// Add Epoch component, used on a button to trigger a new epoch addition #[derive(Debug, Component)] pub struct AddEpoch; -/// Epoch ID Component -#[derive(Debug, Reflect, Component, Clone)] -struct EpochId { - id: usize, -} - -#[derive(Component)] -struct EpochScene(DynamicScene); - fn control_active_epoch( - events: Query<(Entity, &EpochId), Added>, + events: Query<(Entity, &Epoch), Added>, mut commands: Commands, - level_root: Query>, - audio_root: Query>, - children: Query<&Children>, - world: &World, ) { - events.iter().for_each(|(entity, &ref id)| { - let scene = export_level_state(&level_root, &audio_root, &children, world); - commands.entity(entity).insert(EpochScene(scene)); - commands.insert_resource(ActiveEpoch { - id: id.clone(), - entity, - }); + events.iter().for_each(|(entity, epoch)| { + commands.insert_resource(epoch.clone()); }); } @@ -78,7 +63,7 @@ fn add_timeline_epoch( text: name, ..default() }, - EpochId { id }, + Epoch { id, ..default() }, )); }); });