and with that import/export kinda just works...

main
Elijah Voigt 2 years ago
parent a82126cc6b
commit 27522e2209

240
Cargo.lock generated

@ -709,19 +709,6 @@ version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15702dff420fac72c2ab92428a8600e079ae89c5845401c4e39b843665a3d2d0" checksum = "15702dff420fac72c2ab92428a8600e079ae89c5845401c4e39b843665a3d2d0"
[[package]]
name = "bevy_rapier3d"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12402872b857ba490f1040ab6212915bd9bf25f8584b31f2c43cef41b33f3be4"
dependencies = [
"bevy",
"bitflags 1.3.2",
"log",
"nalgebra",
"rapier3d",
]
[[package]] [[package]]
name = "bevy_reflect" name = "bevy_reflect"
version = "0.11.2" version = "0.11.2"
@ -1361,20 +1348,6 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "crossbeam"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c"
dependencies = [
"cfg-if",
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-epoch",
"crossbeam-queue",
"crossbeam-utils",
]
[[package]] [[package]]
name = "crossbeam-channel" name = "crossbeam-channel"
version = "0.5.8" version = "0.5.8"
@ -1385,40 +1358,6 @@ dependencies = [
"crossbeam-utils", "crossbeam-utils",
] ]
[[package]]
name = "crossbeam-deque"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
dependencies = [
"cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-queue"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add"
dependencies = [
"cfg-if",
"crossbeam-utils",
]
[[package]] [[package]]
name = "crossbeam-utils" name = "crossbeam-utils"
version = "0.8.16" version = "0.8.16"
@ -1463,12 +1402,6 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]] [[package]]
name = "encase" name = "encase"
version = "0.6.1" version = "0.6.1"
@ -2108,12 +2041,6 @@ dependencies = [
"windows-sys 0.48.0", "windows-sys 0.48.0",
] ]
[[package]]
name = "libm"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4"
[[package]] [[package]]
name = "libudev-sys" name = "libudev-sys"
version = "0.1.4" version = "0.1.4"
@ -2167,31 +2094,12 @@ dependencies = [
"regex-automata 0.1.10", "regex-automata 0.1.10",
] ]
[[package]]
name = "matrixmultiply"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "090126dc04f95dc0d1c1c91f61bdd474b3930ca064c1edc8a849da2c6cbe1e77"
dependencies = [
"autocfg",
"rawpointer",
]
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.6.2" version = "2.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5486aed0026218e61b8a01d5fbd5a0a134649abb71a0e53b7bc088529dced86e" checksum = "5486aed0026218e61b8a01d5fbd5a0a134649abb71a0e53b7bc088529dced86e"
[[package]]
name = "memoffset"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
dependencies = [
"autocfg",
]
[[package]] [[package]]
name = "metal" name = "metal"
version = "0.24.0" version = "0.24.0"
@ -2239,7 +2147,6 @@ name = "monologue-trees"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"bevy", "bevy",
"bevy_rapier3d",
"serde", "serde",
] ]
@ -2284,34 +2191,6 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "nalgebra"
version = "0.32.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "307ed9b18cc2423f29e83f84fd23a8e73628727990181f18641a8b5dc2ab1caa"
dependencies = [
"approx",
"glam",
"matrixmultiply",
"nalgebra-macros",
"num-complex",
"num-rational",
"num-traits",
"simba",
"typenum",
]
[[package]]
name = "nalgebra-macros"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91761aed67d03ad966ef783ae962ef9bbaca728d2dd7ceb7939ec110fffad998"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]] [[package]]
name = "ndk" name = "ndk"
version = "0.7.0" version = "0.7.0"
@ -2411,15 +2290,6 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "num-complex"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214"
dependencies = [
"num-traits",
]
[[package]] [[package]]
name = "num-derive" name = "num-derive"
version = "0.3.3" version = "0.3.3"
@ -2459,7 +2329,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"libm",
] ]
[[package]] [[package]]
@ -2596,12 +2465,6 @@ version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "optional"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978aa494585d3ca4ad74929863093e87cac9790d81fe7aba2b3dc2890643a0fc"
[[package]] [[package]]
name = "orbclient" name = "orbclient"
version = "0.3.46" version = "0.3.46"
@ -2655,27 +2518,6 @@ dependencies = [
"windows-targets 0.48.5", "windows-targets 0.48.5",
] ]
[[package]]
name = "parry3d"
version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b55dc0e6db79bddbc5fd583569f7356cdcc63e1e9b2b93a9ab70dd8e717160e0"
dependencies = [
"approx",
"arrayvec",
"bitflags 1.3.2",
"downcast-rs",
"either",
"nalgebra",
"num-derive",
"num-traits",
"rustc-hash",
"simba",
"slab",
"smallvec",
"spade",
]
[[package]] [[package]]
name = "paste" name = "paste"
version = "1.0.14" version = "1.0.14"
@ -2784,38 +2626,12 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8a99fddc9f0ba0a85884b8d14e3592853e787d581ca1816c91349b10e4eeab" checksum = "9c8a99fddc9f0ba0a85884b8d14e3592853e787d581ca1816c91349b10e4eeab"
[[package]]
name = "rapier3d"
version = "0.17.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62a8a0bd9d3135f7b4eb45d0796540e7bab47b6b7c974f90567ccc5a0454f42b"
dependencies = [
"approx",
"arrayvec",
"bit-vec",
"bitflags 1.3.2",
"crossbeam",
"downcast-rs",
"nalgebra",
"num-derive",
"num-traits",
"parry3d",
"rustc-hash",
"simba",
]
[[package]] [[package]]
name = "raw-window-handle" name = "raw-window-handle"
version = "0.5.2" version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9"
[[package]]
name = "rawpointer"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
[[package]] [[package]]
name = "rectangle-pack" name = "rectangle-pack"
version = "0.4.2" version = "0.4.2"
@ -2881,12 +2697,6 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "216080ab382b992234dda86873c18d4c48358f5cfcb70fd693d7f6f2131b628b" checksum = "216080ab382b992234dda86873c18d4c48358f5cfcb70fd693d7f6f2131b628b"
[[package]]
name = "robust"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5864e7ef1a6b7bcf1d6ca3f655e65e724ed3b52546a0d0a663c991522f552ea"
[[package]] [[package]]
name = "rodio" name = "rodio"
version = "0.17.1" version = "0.17.1"
@ -2938,15 +2748,6 @@ version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
[[package]]
name = "safe_arch"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f398075ce1e6a179b46f51bd88d0598b92b00d3551f1a2d4ac49e771b56ac354"
dependencies = [
"bytemuck",
]
[[package]] [[package]]
name = "same-file" name = "same-file"
version = "1.0.6" version = "1.0.6"
@ -3008,19 +2809,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3"
[[package]]
name = "simba"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "061507c94fc6ab4ba1c9a0305018408e312e17c041eb63bef8aa726fa33aceae"
dependencies = [
"approx",
"num-complex",
"num-traits",
"paste",
"wide",
]
[[package]] [[package]]
name = "simd-adler32" name = "simd-adler32"
version = "0.3.7" version = "0.3.7"
@ -3063,18 +2851,6 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "spade"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88e65803986868d2372c582007c39ba89936a36ea5f236bf7a7728dc258f04f9"
dependencies = [
"num-traits",
"optional",
"robust",
"smallvec",
]
[[package]] [[package]]
name = "spirv" name = "spirv"
version = "0.2.0+1.5.4" version = "0.2.0+1.5.4"
@ -3325,12 +3101,6 @@ dependencies = [
"static_assertions", "static_assertions",
] ]
[[package]]
name = "typenum"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.11" version = "1.0.11"
@ -3586,16 +3356,6 @@ dependencies = [
"web-sys", "web-sys",
] ]
[[package]]
name = "wide"
version = "0.7.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa469ffa65ef7e0ba0f164183697b89b854253fd31aeb92358b7b6155177d62f"
dependencies = [
"bytemuck",
"safe_arch",
]
[[package]] [[package]]
name = "widestring" name = "widestring"
version = "1.0.2" version = "1.0.2"

@ -46,7 +46,7 @@ path = "bin/serialize-wtf.rs"
[dependencies] [dependencies]
bevy = "0.11" bevy = "0.11"
bevy_rapier3d = "*" # bevy_rapier3d = "*"
serde = "1" serde = "1"
# From rapier docs # From rapier docs

BIN
assets/foo/bar/baz/inspect.glb (Stored with Git LFS)

Binary file not shown.

@ -1,8 +1,9 @@
( (
resources: {}, resources: {},
entities: { entities: {
19: ( 1: (
components: { components: {
"bevy_render::view::visibility::Visibility": Inherited,
"bevy_transform::components::transform::Transform": ( "bevy_transform::components::transform::Transform": (
translation: ( translation: (
x: 0.0, x: 0.0,
@ -40,23 +41,15 @@
z: 0.0, z: 0.0,
), ),
)), )),
"bevy_render::view::visibility::Visibility": Inherited, "editor::LevelRoot": (),
"bevy_hierarchy::components::children::Children": ([ "bevy_hierarchy::components::children::Children": ([
20, 198,
21,
]), ]),
}, },
), ),
20: ( 198: (
components: {
"bevy_hierarchy::components::parent::Parent": (19),
"bevy_asset::handle::Handle<bevy_audio::audio_source::AudioSource>": (
id: AssetPathId(((5718828327369153735), (8823233378563626002))),
),
},
),
21: (
components: { components: {
"bevy_render::view::visibility::Visibility": Inherited,
"bevy_transform::components::transform::Transform": ( "bevy_transform::components::transform::Transform": (
translation: ( translation: (
x: 0.0, x: 0.0,
@ -94,13 +87,12 @@
z: 0.0, z: 0.0,
), ),
)), )),
"bevy_render::view::visibility::Visibility": Inherited, "bevy_hierarchy::components::parent::Parent": (1),
"bevy_hierarchy::components::parent::Parent": (19),
"bevy_hierarchy::components::children::Children": ([ "bevy_hierarchy::components::children::Children": ([
22, 199,
]), ]),
"bevy_asset::handle::Handle<bevy_scene::scene::Scene>": ( "bevy_asset::handle::Handle<bevy_scene::scene::Scene>": (
id: AssetPathId(((8329447081204006281), (13531267934229906755))), id: AssetPathId(((13241355290950327508), (2370051114748836591))),
), ),
}, },
), ),

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -1,10 +0,0 @@
Voluptatem culpa quo quia alias minima amet. Consequatur fugit et vitae qui dolor. Aut ea dolorum dicta quas ex et recusandae et. Nostrum eos quia quis est consequuntur.
Ratione facilis aliquid et. Dolores expedita magni suscipit minima. Voluptatem in pariatur vitae laboriosam aliquam non ducimus. Laudantium illum provident et libero assumenda et sunt similique. Corporis tenetur repellat enim perferendis ut minus omnis.
Blanditiis vitae quae ut ipsum consequatur. Ratione dignissimos exercitationem autem accusamus. Qui molestiae ipsam pariatur quis amet quia voluptate sunt. In voluptate dolorum in quia. Sit ut non voluptatem qui placeat quis. Ducimus ipsa adipisci eligendi dolor id.
Ex totam nam laudantium quis. Omnis saepe mollitia eligendi unde rerum. Odit voluptatum repellat rem est iure neque saepe.
Nulla est consequatur sint amet nesciunt quam. Qui fuga excepturi veritatis quia. Saepe natus et enim eveniet voluptates velit quod sint. Dolores reprehenderit eligendi aut. Et voluptate ea aliquam.

@ -1 +0,0 @@
This is a placeholder monologue

@ -5,17 +5,12 @@
// BUGS: // BUGS:
// //
// TODO: // TODO:
// * re-load assets dir when new asset is copied
// * copy-and-reload drag-and-drop dirs
// * font resource for monologues
// * edit textbox with actions // * edit textbox with actions
// * run_if logic for actions where possible
// * make min/max/close buttons into actions not selects
// * (medium) Select Font -> "Default Font" Resource
// * (medium) Pre-compute animation target entities
// * (hard) Harden Active Camera
// * (brutal) export level // * (brutal) export level
// * (hard) import level // * (hard) import level
// * (hard) Harden Active Camera
// * (medium) Pre-compute animation target entities
// * make min/max/close buttons into actions not selects
// * (???) Better handle hide/close monologue // * (???) Better handle hide/close monologue
use bevy::{ use bevy::{
@ -60,10 +55,9 @@ fn main() {
enable_alerts: true, enable_alerts: true,
}, },
)) ))
.register_type::<LevelRoot>()
.init_resource::<AssetRegistry>() .init_resource::<AssetRegistry>()
.insert_resource(AssetsDir::new("assets/scratch"))
.init_resource::<FontInfo>() .init_resource::<FontInfo>()
.add_event::<ImportAsset>()
.add_asset::<Monologue>() .add_asset::<Monologue>()
.init_asset_loader::<MonologueLoader>() .init_asset_loader::<MonologueLoader>()
.add_systems(Startup, (initialize_ui, init_texts_ui, welcome_message)) .add_systems(Startup, (initialize_ui, init_texts_ui, welcome_message))
@ -98,11 +92,7 @@ fn main() {
.add_systems( .add_systems(
Update, Update,
( (
init_assets_dir, reload_assets.run_if(ui::activated::<ReloadAssets>),
import_assets,
import_file,
import_folder,
reload_assets.run_if(ui::event::<ImportAsset>),
clear_assets.run_if(ui::activated::<ClearAssets>), clear_assets.run_if(ui::activated::<ClearAssets>),
), ),
) )
@ -115,6 +105,15 @@ fn main() {
), ),
) )
.add_systems(Update, clear_level) .add_systems(Update, clear_level)
.add_systems(
Update,
(
level_ui,
load_level,
export_level.run_if(ui::activated::<ExportLevel>),
rehydrate_level,
),
)
.run(); .run();
} }
@ -124,7 +123,8 @@ pub struct AssetRegistry(Vec<HandleUntyped>);
#[derive(Debug, Component)] #[derive(Debug, Component)]
pub struct TabRoot; pub struct TabRoot;
#[derive(Debug, Component)] #[derive(Debug, Component, Reflect, Default)]
#[reflect(Component)]
pub struct LevelRoot; pub struct LevelRoot;
#[derive(Debug, Component)] #[derive(Debug, Component)]
@ -132,7 +132,7 @@ pub struct EditorCamera;
fn initialize_ui(mut commands: Commands) { fn initialize_ui(mut commands: Commands) {
// Empty entity for populating the level being edited // Empty entity for populating the level being edited
commands.spawn((SpatialBundle { ..default() }, LevelRoot)); commands.spawn((SpatialBundle { ..default() }, LevelRoot::default()));
commands.spawn(( commands.spawn((
Camera2dBundle { ..default() }, Camera2dBundle { ..default() },
@ -230,6 +230,11 @@ fn initialize_ui(mut commands: Commands) {
parent, parent,
ui::Select::Multi, ui::Select::Multi,
)); ));
content_containers.push(spawn_tab_container::<LevelWidget>(
"Level",
parent,
ui::Select::Single,
));
content_containers.push(spawn_tab_container::<GltfWidget>( content_containers.push(spawn_tab_container::<GltfWidget>(
"Gltf", "Gltf",
parent, parent,
@ -337,17 +342,35 @@ fn initialize_ui(mut commands: Commands) {
.with_children(|parent| { .with_children(|parent| {
parent.spawn(( parent.spawn((
simple_button.clone(), simple_button.clone(),
ClearAssets, ReloadAssets,
ui::Sorting(1), ui::Sorting(1),
ui::Title {
text: "Reload Assets".into(),
..default()
},
));
parent.spawn((
simple_button.clone(),
ClearAssets,
ui::Sorting(2),
ui::Title { ui::Title {
text: "Clear Assets".into(), text: "Clear Assets".into(),
..default() ..default()
}, },
)); ));
parent.spawn((
simple_button.clone(),
ExportLevel,
ui::Sorting(3),
ui::Title {
text: "Export Level".into(),
..default()
},
));
parent.spawn(( parent.spawn((
simple_button.clone(), simple_button.clone(),
ClearLevel, ClearLevel,
ui::Sorting(2), ui::Sorting(3),
ui::Title { ui::Title {
text: "Reset Level".into(), text: "Reset Level".into(),
..default() ..default()
@ -497,146 +520,23 @@ mod audio {
use assets::*; use assets::*;
mod assets { mod assets {
use std::{
fs::DirBuilder,
fs::{copy, create_dir_all, read_dir},
path::PathBuf,
};
use bevy::tasks::IoTaskPool;
use super::*; use super::*;
#[derive(Debug, Resource)] #[derive(Debug, Component)]
pub struct AssetsDir { pub struct ReloadAssets;
pub root: PathBuf,
}
impl AssetsDir {
pub fn new(root: &str) -> Self {
AssetsDir {
root: PathBuf::from(root)
.canonicalize()
.expect("Setting assets dir"),
}
}
}
pub fn init_assets_dir(assets_dir: Res<AssetsDir>) {
if assets_dir.is_added() || assets_dir.is_changed() {
let d = assets_dir.root.clone();
IoTaskPool::get()
.spawn(async move {
match DirBuilder::new().create(d) {
Ok(_) => info!("Created assets directory"),
Err(e) => warn!("Error creating assets directory {:?}", e),
}
})
.detach();
}
}
#[derive(Debug, Event)]
pub enum ImportAsset {
File(PathBuf),
Dir(PathBuf),
}
pub fn import_assets(
mut events: EventReader<FileDragAndDrop>,
mut writer: EventWriter<ImportAsset>,
) {
events.iter().for_each(|event| match event {
FileDragAndDrop::DroppedFile { path_buf, .. } => {
if path_buf.is_file() {
writer.send(ImportAsset::File(path_buf.clone()));
} else if path_buf.is_dir() {
writer.send(ImportAsset::Dir(path_buf.clone()));
} else {
warn!("Could not identify filetype for {:?}", path_buf)
}
}
_ => (),
})
}
pub fn import_file(mut events: EventReader<ImportAsset>, assets_dir: Res<AssetsDir>) {
events
.iter()
.filter_map(|event| match event {
ImportAsset::File(path) => Some(path.clone()),
_ => None,
})
.for_each(|path| {
let fname = path.file_name().expect("PathBuf should have file name");
let newpath = PathBuf::from(assets_dir.root.clone()).join(fname);
IoTaskPool::get()
.spawn(async move {
if path == newpath {
info!("Skipping copying {:?} to itself {:?}", path, newpath);
} else {
match copy(path, newpath) {
Ok(_) => info!("Created assets directory"),
Err(e) => warn!("Error creating assets directory {:?}", e),
}
}
})
.detach();
});
}
fn copy_dir(from: PathBuf, to: PathBuf) -> std::io::Result<()> {
if let Ok(entries) = read_dir(from) {
for entry in entries.filter_map(|e| e.ok()) {
if entry.path().is_file() {
let from_file = entry.path();
let to_file = to.join(entry.file_name());
copy(from_file, to_file).map(|_| ())?;
} else if entry.path().is_dir() {
info!("Recursive");
let target = to.join(entry.file_name());
create_dir_all(target.clone())?;
copy_dir(entry.path(), target.clone())?;
}
}
}
Ok(())
}
pub fn import_folder(mut events: EventReader<ImportAsset>, assets_dir: Res<AssetsDir>) {
events
.iter()
.filter_map(|event| match event {
ImportAsset::Dir(path) => Some(path.clone()),
_ => None,
})
.for_each(|path| {
let fname = path.file_name().expect("PathBuf should have file name");
let newpath = PathBuf::from(assets_dir.root.clone()).join(fname);
info!("copying folder {:?} to {:?}", path.clone(), newpath);
IoTaskPool::get()
.spawn(async move {
if path.clone() == newpath {
info!("Skipping copying {:?} to itself {:?}", path, newpath);
} else {
match copy_dir(path.clone(), newpath) {
Ok(_) => info!("Created assets directory"),
Err(e) => warn!("Error creating assets directory {:?}", e),
}
}
})
.detach();
});
}
pub fn reload_assets( pub fn reload_assets(
server: Res<AssetServer>, server: Res<AssetServer>,
mut registry: ResMut<AssetRegistry>, mut registry: ResMut<AssetRegistry>,
assets_dir: Res<AssetsDir>, mut writer: EventWriter<ui::Alert>,
) { ) {
registry.0 = server match server.load_folder(".") {
.load_folder(assets_dir.root.clone()) Ok(handles) => registry.0 = handles,
.expect("Reload assets folder"); Err(e) => writer.send(ui::Alert::Warn(format!(
"Could not find `assets` folder!\n{:?}",
e
))),
}
} }
pub fn get_asset_name<T: Asset>(server: &AssetServer, handle: Handle<T>) -> String { pub fn get_asset_name<T: Asset>(server: &AssetServer, handle: Handle<T>) -> String {
@ -1508,3 +1408,155 @@ mod reset {
registry.0.clear(); registry.0.clear();
} }
} }
pub use level::*;
mod level {
use std::fs::File;
use std::io::Write;
use bevy::tasks::IoTaskPool;
use super::*;
type Level = DynamicScene;
#[derive(Debug, Component, Default)]
pub struct LevelWidget;
#[derive(Debug, Component)]
pub struct ExportLevel;
pub fn level_ui(
mut events: EventReader<AssetEvent<Level>>,
mut commands: Commands,
widget: Query<Entity, With<LevelWidget>>,
current: Query<(Entity, &ui::TargetAsset<Level>)>,
server: Res<AssetServer>,
) {
events.iter().for_each(|event| match event {
AssetEvent::Created { handle } => {
info!("Asset created! {:?}", event);
create_asset_button(
&widget,
&mut commands,
ui::TargetAsset {
handle: handle.clone(),
},
get_asset_name(&server, handle.clone()),
None,
);
}
AssetEvent::Removed { handle } => {
info!("Asset removed! {:?}", event);
destroy_asset_button(
&current,
&mut commands,
&ui::TargetAsset {
handle: handle.clone(),
},
);
}
AssetEvent::Modified { handle } => {
info!("Asset modified! {:?}", event);
destroy_asset_button(
&current,
&mut commands,
&ui::TargetAsset {
handle: handle.clone(),
},
);
create_asset_button(
&widget,
&mut commands,
ui::TargetAsset {
handle: handle.clone(),
},
get_asset_name(&server, handle.clone()),
None,
);
}
});
}
pub fn load_level(
events: Query<
&ui::TargetAsset<DynamicScene>,
(Added<ui::Active>, With<ui::TargetAsset<DynamicScene>>),
>,
root: Query<Entity, With<LevelRoot>>,
mut commands: Commands,
) {
events.iter().for_each(|ui::TargetAsset { handle }| {
commands.entity(root.single()).despawn_recursive();
commands.spawn(DynamicSceneBundle {
scene: handle.clone(),
..default()
});
});
}
pub fn export_level(
root: Query<Entity, With<LevelRoot>>,
children: Query<&Children>,
world: &World,
) {
let app_type_registry = world.resource::<AppTypeRegistry>().clone();
let mut builder = DynamicSceneBuilder::from_world(world.clone());
builder.deny_all_resources();
// builder.allow_all();
builder.deny::<ComputedVisibility>();
// Level administrivia
builder.allow::<LevelRoot>();
// Scene components
builder.allow::<Handle<Scene>>();
builder.allow::<Visibility>();
builder.allow::<Transform>();
builder.allow::<GlobalTransform>();
// Audio components
builder.allow::<Handle<AudioSource>>();
builder.allow::<PlaybackSettings>();
root.iter().for_each(|r| {
// Extract the level root
builder.extract_entity(r);
match children.get(r) {
Ok(cs) => {
builder.extract_entities(cs.iter().map(|&entity| entity));
}
Err(e) => warn!("Empty level! {:?}", e),
}
});
let scene = builder.build();
let serialized = scene
.serialize_ron(&app_type_registry)
.expect("Serialize scene");
IoTaskPool::get()
.spawn(async move {
// Write the scene RON data to file
File::create(format!("assets/output.scn.ron"))
.and_then(|mut file| file.write(serialized.as_bytes()))
.expect("Error while writing scene to file");
})
.detach();
}
pub fn rehydrate_level(
events: Query<Entity, (Added<Visibility>, Without<ComputedVisibility>)>,
mut commands: Commands,
) {
events.iter().for_each(|entity| {
commands
.entity(entity)
.insert(ComputedVisibility::default());
});
}
}

@ -5,24 +5,13 @@
// * Copy assets into local folder in leue of importing them // * Copy assets into local folder in leue of importing them
// * Check portability by moving binary + folder to new location // * Check portability by moving binary + folder to new location
use std::{ use std::time::Duration;
fs::{DirBuilder, File},
io::Write,
time::Duration,
};
use bevy::{ use bevy::{asset::ChangeWatcher, prelude::*};
asset::{Asset, ChangeWatcher},
audio::PlaybackMode,
gltf::Gltf,
prelude::*,
tasks::IoTaskPool,
};
use monologue_trees::{debug::*, ui}; use monologue_trees::{debug::*, ui};
fn main() { fn main() {
App::new() App::new()
.init_resource::<AssetRegistry>()
.add_plugins(( .add_plugins((
DefaultPlugins DefaultPlugins
.set(WindowPlugin { .set(WindowPlugin {
@ -42,386 +31,75 @@ fn main() {
DebugInfoPlugin, DebugInfoPlugin,
ui::GameUiPlugin { ..default() }, ui::GameUiPlugin { ..default() },
)) ))
.add_systems(Startup, (init, init_assets_dir)) .register_type::<LevelRoot>()
.add_systems( .register_type::<Other>()
Update, .register_type::<Choice>()
( .add_systems(Startup, init)
export.run_if(interaction_condition::<ExportAction>), .add_systems(Update, serialize_debug.run_if(pending))
clear.run_if(interaction_condition::<ClearAction>),
import.run_if(interaction_condition::<ImportAction>),
inspect.run_if(interaction_condition::<InspectAction>),
load.run_if(interaction_condition::<LoadAssetsAction>),
unload.run_if(interaction_condition::<UnloadAssetsAction>),
spawn_level.run_if(interaction_condition::<SpawnLevelAction>),
asset_inspector::<AudioSource>,
asset_inspector::<Scene>,
),
)
.add_systems(
PostUpdate,
(
rehydrate::<Visibility, ComputedVisibility>,
rehydrate::<Handle<AudioSource>, PlaybackSettings>,
fallback_camera.run_if(fallback_camera_condition),
),
)
.run(); .run();
} }
#[derive(Debug, Component, Reflect, Default)] #[derive(Debug, Component, Default, Reflect)]
#[reflect(Component)] #[reflect(Component)]
struct LevelRoot; struct LevelRoot;
#[derive(Debug, Component)] #[derive(Debug, Component, Default, Reflect)]
struct ExportAction; #[reflect(Component)]
struct Other {
#[derive(Debug, Component)] inner: String,
struct ClearAction;
#[derive(Debug, Component)]
struct ImportAction;
#[derive(Debug, Component)]
struct InspectAction;
#[derive(Debug, Component)]
struct LoadAssetsAction;
#[derive(Debug, Component)]
struct UnloadAssetsAction;
#[derive(Debug, Component)]
struct SpawnLevelAction;
#[derive(Debug, Resource, Default)]
struct AssetRegistry {
handles: Vec<HandleUntyped>,
} }
fn init_assets_dir() { #[derive(Debug, Component, Default, Reflect)]
IoTaskPool::get() #[reflect(Component)]
.spawn(async move { enum Choice {
match DirBuilder::new().create("assets") { #[reflect(default)]
Ok(_) => info!("Created assets directory"), #[default]
Err(e) => warn!("Error creating assets directory", e), One,
} Two,
}) Three,
.detach();
} }
fn init(mut commands: Commands) { fn init(mut commands: Commands) {
commands.spawn(( commands.spawn((
Camera2dBundle { ..default() }, SpatialBundle { ..default() },
UiCameraConfig { show_ui: true }, LevelRoot::default(),
)); Name::new("Root Entity"),
Other::default(),
commands Choice::default(),
.spawn((
NodeBundle {
style: Style {
top: Val::Px(0.0),
right: Val::Px(0.0),
margin: UiRect::all(Val::Px(5.0)),
padding: UiRect::all(Val::Px(5.0)),
border: UiRect::all(Val::Px(1.0)),
position_type: PositionType::Absolute,
..default()
},
background_color: Color::WHITE.into(),
border_color: Color::BLACK.into(),
..default()
},
ui::Select::Action,
))
.with_children(|parent| {
parent.spawn((
ButtonBundle {
style: Style {
margin: UiRect::all(Val::Px(5.0)),
padding: UiRect::all(Val::Px(5.0)),
border: UiRect::all(Val::Px(1.0)),
..default()
},
border_color: Color::BLACK.into(),
..default()
},
ui::Title {
text: "Load Assets".into(),
..default()
},
LoadAssetsAction,
));
parent.spawn((
ButtonBundle {
style: Style {
margin: UiRect::all(Val::Px(5.0)),
padding: UiRect::all(Val::Px(5.0)),
border: UiRect::all(Val::Px(1.0)),
..default()
},
border_color: Color::BLACK.into(),
..default()
},
ui::Title {
text: "Dump Assets".into(),
..default()
},
UnloadAssetsAction,
)); ));
parent.spawn((
ButtonBundle {
style: Style {
margin: UiRect::all(Val::Px(5.0)),
padding: UiRect::all(Val::Px(5.0)),
border: UiRect::all(Val::Px(1.0)),
..default()
},
border_color: Color::BLACK.into(),
..default()
},
ui::Title {
text: "Spawn Level".into(),
..default()
},
SpawnLevelAction,
));
parent.spawn((
ButtonBundle {
style: Style {
margin: UiRect::all(Val::Px(5.0)),
padding: UiRect::all(Val::Px(5.0)),
border: UiRect::all(Val::Px(1.0)),
..default()
},
border_color: Color::BLACK.into(),
..default()
},
ui::Title {
text: "Export".into(),
..default()
},
ExportAction,
));
parent.spawn((
ButtonBundle {
style: Style {
margin: UiRect::all(Val::Px(5.0)),
padding: UiRect::all(Val::Px(5.0)),
border: UiRect::all(Val::Px(1.0)),
..default()
},
border_color: Color::BLACK.into(),
..default()
},
ui::Title {
text: "Clear".into(),
..default()
},
ClearAction,
));
parent.spawn((
ButtonBundle {
style: Style {
margin: UiRect::all(Val::Px(5.0)),
padding: UiRect::all(Val::Px(5.0)),
border: UiRect::all(Val::Px(1.0)),
..default()
},
border_color: Color::BLACK.into(),
..default()
},
ui::Title {
text: "Import".into(),
..default()
},
ImportAction,
));
parent.spawn((
ButtonBundle {
style: Style {
margin: UiRect::all(Val::Px(5.0)),
padding: UiRect::all(Val::Px(5.0)),
border: UiRect::all(Val::Px(1.0)),
..default()
},
border_color: Color::BLACK.into(),
..default()
},
ui::Title {
text: "Inspect".into(),
..default()
},
InspectAction,
));
});
} }
fn interaction_condition<T: Component>( fn serialize_debug(query: Query<Entity, With<LevelRoot>>, world: &World) {
events: Query<&Interaction, (Changed<Interaction>, With<T>)>, let entities = query.iter().collect();
) -> bool { print!("{}", ser(entities, world));
events
.iter()
.find(|&interaction| *interaction == Interaction::Pressed)
.is_some()
} }
fn rehydrate<W: Component, WO: Component + Default + std::fmt::Debug>( fn pending(query: Query<Entity, With<LevelRoot>>, mut done: Local<bool>) -> bool {
events: Query<Entity, (Added<W>, Without<WO>)>, if !*done {
mut commands: Commands, if query.iter().len() > 0 {
) { *done = true;
events.iter().for_each(|entity| { } else {
info!("Rehydrating {:?}", WO::default()); *done = false;
commands.entity(entity).insert(WO::default()); }
}); return *done;
}
return false;
} }
fn ser( fn ser(entities: Vec<Entity>, world: &World) -> String {
root: &Query<Entity, With<LevelRoot>>,
children: &Query<&Children>,
world: &World,
) -> String {
let app_type_registry = world.resource::<AppTypeRegistry>().clone(); let app_type_registry = world.resource::<AppTypeRegistry>().clone();
let mut builder = DynamicSceneBuilder::from_world(world.clone());
builder.deny_all_resources();
// builder.allow_all();
builder.deny::<ComputedVisibility>();
// Level administrivia
builder.allow::<LevelRoot>();
// Scene components
builder.allow::<Handle<Scene>>();
builder.allow::<Visibility>();
builder.allow::<Transform>();
builder.allow::<GlobalTransform>();
// Audio components
builder.allow::<Handle<AudioSource>>();
builder.allow::<PlaybackSettings>();
root.iter().for_each(|r| { let scene = {
// Extract the level root let mut builder = DynamicSceneBuilder::from_world(world.clone());
builder.extract_entity(r); builder
.allow_all_resources()
// Extract all level root children .allow_all()
builder.extract_entities( .deny::<ComputedVisibility>()
children .extract_entities(entities.into_iter());
.get(r) builder.build()
.expect("Root has children") };
.iter()
.map(|&entity| entity),
);
});
let scene = builder.build();
scene scene
.serialize_ron(&app_type_registry) .serialize_ron(&app_type_registry)
.expect("Serialize scene") .expect("Serialize scene")
} }
fn export(root: Query<Entity, With<LevelRoot>>, children: Query<&Children>, world: &World) {
info!("Export level");
let serialized = ser(&root, &children, world);
IoTaskPool::get()
.spawn(async move {
// Write the scene RON data to file
File::create(format!("assets/output.scn.ron"))
.and_then(|mut file| file.write(serialized.as_bytes()))
.expect("Error while writing scene to file");
})
.detach();
}
fn inspect(root: Query<Entity, With<LevelRoot>>, children: Query<&Children>, world: &World) {
info!("Inexpect level");
let serialized = ser(&root, &children, world);
print!("{}", serialized);
}
fn clear(root: Query<Entity, With<LevelRoot>>, mut commands: Commands) {
info!("Clearing level");
root.iter().for_each(|entity| {
commands.entity(entity).despawn_recursive();
});
}
// TODO: Figure out how to import the same asset from a differnt source
// How do the plugins do it??
fn import(mut commands: Commands, server: Res<AssetServer>) {
info!("Importing level");
let scene_handle: Handle<DynamicScene> = server.load("output.scn.ron");
commands.spawn((
LevelRoot,
DynamicSceneBundle {
scene: scene_handle.clone(),
..default()
},
));
}
fn fallback_camera_condition(
added: Query<Entity, Added<Camera>>,
mut removed: RemovedComponents<Camera>,
) -> bool {
added.iter().chain(removed.iter()).count() > 0
}
fn fallback_camera(
mut ui_camera: Query<&mut Camera, With<Camera2d>>,
cameras: Query<&mut Camera, Without<Camera2d>>,
) {
ui_camera.single_mut().is_active = cameras.iter().len() <= 0;
}
fn asset_inspector<T: Asset>(mut events: EventReader<AssetEvent<T>>) {
events.iter().for_each(|event| match event {
AssetEvent::Created { handle } => info!("Asset Created {:?}", handle),
AssetEvent::Modified { handle } => info!("Asset Modified {:?}", handle),
AssetEvent::Removed { handle } => info!("Asset Removed {:?}", handle),
});
}
// OK seems like `load_folder` does not automatically pick up added files
fn load(mut registry: ResMut<AssetRegistry>, server: Res<AssetServer>) {
info!("Loading assets");
registry.handles = server.load_folder("./dynamic").unwrap();
info!("Current files: {:?}", registry.handles);
}
fn unload(mut registry: ResMut<AssetRegistry>, mut gltfs: ResMut<Assets<Gltf>>) {
info!("Unloading asstes");
registry.handles.clear();
// This is required to clear scenes from asset cache
gltfs.clear();
}
fn spawn_level(mut commands: Commands, server: Res<AssetServer>) {
commands
.spawn((SpatialBundle { ..default() }, LevelRoot))
.with_children(|parent| {
parent.spawn(AudioSourceBundle {
source: server.load::<AudioSource, &str>("dynamic/Lake Sound 1.ogg"),
settings: PlaybackSettings {
mode: PlaybackMode::Loop,
paused: false,
..default()
},
});
parent.spawn((SceneBundle {
scene: server.load("dynamic/materials.glb#Scene0"),
..default()
},));
});
}

Loading…
Cancel
Save