From 5429bf4779c31b71e0f6173ed610c4d30b19c1e7 Mon Sep 17 00:00:00 2001 From: Mark Moissette Date: Wed, 10 Jan 2024 14:49:29 +0100 Subject: [PATCH] feat(Save & load): new crate bevy_gltf_save_load + lots of upgrades & improvements (#95) * feat(bevy_gltf_save_load): saving & loading implemented * created new crate for save & load features, uses & filters out blueprints for efficient loading * saving & loading, even with dynamically spawned nested hierarchies works * component filter , resource filter & save path root are configurable * for saving: added removal & cleanup logic for children component with children that have been filtered out: ie no more invalid children getting stored in the save files ! * added sending of event once saving is done * feat(examples/save-load): example for the new crate * loading level static & dynamic data as blueprints * added a bit of ui when entering saving & loading states & cleanup when exiting * feat(bevy_gltf_blueprints): significant rewrite of how the crate works * simplified spawning process, no more spawning children containing blueprints etc * simplified post process : instead of copying original entity into blueprint root we now copy blueprint root data (components & children) into the original entity ! fixes #96 * much simpler code wise * solves issue with needing to register components that we only use on the bevy side (not gltf) since we are not copying the bevy data into the blueprints data * added **copyComponents** helper to copy components from one entity to another, excluding existing components on the target entity, & some bells & whistles * **Name** is now optional when spawning a blueprint: closes #97 * **Transform** is now optional when spawning a blueprint: closes #98 * removed transform from bundle (BREAKING change) * added (optional) **NoInBlueprint** component to have finer control over whether to inject the **InBlueprint** component inside spawned blueprint entities * added (optional) **Library** component, so we can override library path when we want * added (optional) **AddToGameWorld** component for convenience to automatically add entities to the game world, if there is one * chore(bevy_gltf_components): removed verbose output, cleaned it up a bit * feat(tools/auto_export): added option to split out "dynamic" objects in main scenes * ie if a collection instance (or its original collection) has a "dynamic" (aka mutable, saveable etc) flag it can get exported to a seperate gltf file (essentially acting like an "initial save") * the rest of the levels (the "static" data) is exported without the dynamic objects and can be reused with save files ! --- Cargo.lock | 65 +- Cargo.toml | 5 +- README.md | 4 + crates/bevy_gltf_blueprints/Cargo.toml | 2 +- crates/bevy_gltf_blueprints/README.md | 78 +- crates/bevy_gltf_blueprints/src/aabb.rs | 19 +- .../bevy_gltf_blueprints/src/clone_entity.rs | 70 - .../src/copy_components.rs | 109 + crates/bevy_gltf_blueprints/src/lib.rs | 14 +- .../src/spawn_from_blueprints.rs | 125 +- .../src/spawn_post_process.rs | 176 +- crates/bevy_gltf_components/Cargo.toml | 2 +- .../src/gltf_to_components.rs | 4 +- crates/bevy_gltf_save_load/Cargo.toml | 18 + crates/bevy_gltf_save_load/LICENSE.md | 4 + crates/bevy_gltf_save_load/LICENSE_APACHE.md | 201 ++ crates/bevy_gltf_save_load/LICENSE_MIT.md | 21 + crates/bevy_gltf_save_load/README.md | 312 ++ .../bevy_gltf_save_load/src/gltf_out_test.rs | 216 ++ crates/bevy_gltf_save_load/src/lib.rs | 108 + crates/bevy_gltf_save_load/src/loading.rs | 156 + crates/bevy_gltf_save_load/src/saveable.rs | 6 + crates/bevy_gltf_save_load/src/saving.rs | 192 ++ .../animation/src/game/in_game.rs | 3 +- .../basic/src/core/mod.rs | 4 - .../basic/src/core/save_load/loading.rs | 218 -- .../basic/src/core/save_load/mod.rs | 70 - .../basic/src/core/save_load/old.rs | 137 - .../basic/src/core/save_load/saveable.rs | 14 - .../basic/src/core/save_load/saving.rs | 87 - .../basic/src/game/in_game.rs | 8 +- .../basic/src/game/mod.rs | 2 +- .../src/game/in_game.rs | 3 +- .../basic_wasm/src/assets/assets_game.rs | 22 +- .../basic_wasm/src/game/in_game.rs | 10 +- .../basic_wasm/src/game/mod.rs | 2 +- .../basic_xpbd_physics/src/game/in_game.rs | 3 +- .../materials/src/game/in_game.rs | 3 +- .../multiple_levels/src/core/mod.rs | 4 - .../src/core/save_load/loading.rs | 218 -- .../multiple_levels/src/core/save_load/mod.rs | 70 - .../multiple_levels/src/core/save_load/old.rs | 137 - .../src/core/save_load/saveable.rs | 14 - .../src/core/save_load/saving.rs | 87 - .../multiple_levels/src/game/in_game.rs | 3 +- .../src/core/mod.rs | 4 - .../src/core/save_load/loading.rs | 218 -- .../src/core/save_load/mod.rs | 70 - .../src/core/save_load/old.rs | 137 - .../src/core/save_load/saveable.rs | 14 - .../src/core/save_load/saving.rs | 87 - .../src/game/in_game.rs | 3 +- .../src/game/level_transitions.rs | 10 +- .../nested_blueprints/src/core/mod.rs | 4 - .../src/core/save_load/loading.rs | 218 -- .../src/core/save_load/mod.rs | 70 - .../src/core/save_load/old.rs | 137 - .../src/core/save_load/saveable.rs | 14 - .../src/core/save_load/saving.rs | 87 - .../nested_blueprints/src/game/in_game.rs | 3 +- examples/bevy_gltf_save_load/basic/Cargo.toml | 17 + examples/bevy_gltf_save_load/basic/README.md | 16 + .../basic/assets/assets_core.assets.ron | 1 + .../basic/assets/assets_game.assets.ron | 8 + .../basic/assets/basic.blend | Bin 0 -> 1597452 bytes .../basic/assets/models/World.glb | Bin 0 -> 1536 bytes .../basic/assets/models/World_dynamic.glb | Bin 0 -> 1080 bytes .../basic/assets/models/library/Container.glb | Bin 0 -> 6060 bytes .../basic/assets/models/library/Ground.glb | Bin 0 -> 1872 bytes .../assets/models/library/Health_Pickup.glb | Bin 0 -> 5572 bytes .../assets/models/library/MagicTeapot.glb | Bin 0 -> 24140 bytes .../basic/assets/models/library/Pillar.glb | Bin 0 -> 4104 bytes .../basic/assets/models/library/Player.glb | Bin 0 -> 28896 bytes .../basic/assets/models/library/Sphero.glb | Bin 0 -> 9668 bytes .../models/library/Unused_in_level_test.glb | Bin 0 -> 5676 bytes .../basic/assets/scenes/save.scn.ron | 2536 +++++++++++++++++ .../basic/src/assets/assets_core.rs | 5 + .../basic/src/assets/assets_game.rs | 16 + .../basic/src/assets/mod.rs | 35 + .../src/core/camera/camera_replace_proxies.rs | 24 + .../basic/src/core/camera/camera_tracking.rs | 58 + .../basic/src/core/camera/mod.rs | 24 + .../core/lighting/lighting_replace_proxies.rs | 25 + .../basic/src/core/lighting/mod.rs | 18 + .../bevy_gltf_save_load/basic/src/core/mod.rs | 62 + .../basic/src/core/physics/controls.rs | 26 + .../basic/src/core/physics/mod.rs | 38 + .../core/physics/physics_replace_proxies.rs | 101 + .../basic/src/core/physics/utils.rs | 175 ++ .../basic/src/core/physics/utils_old.rs | 75 + .../basic/src/core/relationships/mod.rs | 11 + ...lationships_insert_dependant_components.rs | 15 + .../basic/src/game/in_game.rs | 203 ++ .../basic/src/game/in_game_loading.rs | 47 + .../basic/src/game/in_game_saving.rs | 32 + .../basic/src/game/in_main_menu.rs | 116 + .../bevy_gltf_save_load/basic/src/game/mod.rs | 171 ++ .../basic/src/game/picking.rs | 34 + .../bevy_gltf_save_load/basic/src/main.rs | 33 + .../bevy_gltf_save_load/basic/src/state.rs | 57 + .../basic/src/test_components.rs | 80 + tools/gltf_auto_export/README.md | 13 +- tools/gltf_auto_export/__init__.py | 2 +- .../docs/blender_addon_use3.png | Bin 16966 -> 17186 bytes tools/gltf_auto_export/dynamic.py | 32 + tools/gltf_auto_export/helpers_export.py | 50 +- tools/gltf_auto_export/helpers_scenes.py | 15 +- tools/gltf_auto_export/preferences.py | 11 +- tools/gltf_auto_export/ui/main.py | 1 + 109 files changed, 5815 insertions(+), 2470 deletions(-) delete mode 100644 crates/bevy_gltf_blueprints/src/clone_entity.rs create mode 100644 crates/bevy_gltf_blueprints/src/copy_components.rs create mode 100644 crates/bevy_gltf_save_load/Cargo.toml create mode 100644 crates/bevy_gltf_save_load/LICENSE.md create mode 100644 crates/bevy_gltf_save_load/LICENSE_APACHE.md create mode 100644 crates/bevy_gltf_save_load/LICENSE_MIT.md create mode 100644 crates/bevy_gltf_save_load/README.md create mode 100644 crates/bevy_gltf_save_load/src/gltf_out_test.rs create mode 100644 crates/bevy_gltf_save_load/src/lib.rs create mode 100644 crates/bevy_gltf_save_load/src/loading.rs create mode 100644 crates/bevy_gltf_save_load/src/saveable.rs create mode 100644 crates/bevy_gltf_save_load/src/saving.rs delete mode 100644 examples/bevy_gltf_blueprints/basic/src/core/save_load/loading.rs delete mode 100644 examples/bevy_gltf_blueprints/basic/src/core/save_load/mod.rs delete mode 100644 examples/bevy_gltf_blueprints/basic/src/core/save_load/old.rs delete mode 100644 examples/bevy_gltf_blueprints/basic/src/core/save_load/saveable.rs delete mode 100644 examples/bevy_gltf_blueprints/basic/src/core/save_load/saving.rs delete mode 100644 examples/bevy_gltf_blueprints/multiple_levels/src/core/save_load/loading.rs delete mode 100644 examples/bevy_gltf_blueprints/multiple_levels/src/core/save_load/mod.rs delete mode 100644 examples/bevy_gltf_blueprints/multiple_levels/src/core/save_load/old.rs delete mode 100644 examples/bevy_gltf_blueprints/multiple_levels/src/core/save_load/saveable.rs delete mode 100644 examples/bevy_gltf_blueprints/multiple_levels/src/core/save_load/saving.rs delete mode 100644 examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles/src/core/save_load/loading.rs delete mode 100644 examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles/src/core/save_load/mod.rs delete mode 100644 examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles/src/core/save_load/old.rs delete mode 100644 examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles/src/core/save_load/saveable.rs delete mode 100644 examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles/src/core/save_load/saving.rs delete mode 100644 examples/bevy_gltf_blueprints/nested_blueprints/src/core/save_load/loading.rs delete mode 100644 examples/bevy_gltf_blueprints/nested_blueprints/src/core/save_load/mod.rs delete mode 100644 examples/bevy_gltf_blueprints/nested_blueprints/src/core/save_load/old.rs delete mode 100644 examples/bevy_gltf_blueprints/nested_blueprints/src/core/save_load/saveable.rs delete mode 100644 examples/bevy_gltf_blueprints/nested_blueprints/src/core/save_load/saving.rs create mode 100644 examples/bevy_gltf_save_load/basic/Cargo.toml create mode 100644 examples/bevy_gltf_save_load/basic/README.md create mode 100644 examples/bevy_gltf_save_load/basic/assets/assets_core.assets.ron create mode 100644 examples/bevy_gltf_save_load/basic/assets/assets_game.assets.ron create mode 100644 examples/bevy_gltf_save_load/basic/assets/basic.blend create mode 100644 examples/bevy_gltf_save_load/basic/assets/models/World.glb create mode 100644 examples/bevy_gltf_save_load/basic/assets/models/World_dynamic.glb create mode 100644 examples/bevy_gltf_save_load/basic/assets/models/library/Container.glb create mode 100644 examples/bevy_gltf_save_load/basic/assets/models/library/Ground.glb create mode 100644 examples/bevy_gltf_save_load/basic/assets/models/library/Health_Pickup.glb create mode 100644 examples/bevy_gltf_save_load/basic/assets/models/library/MagicTeapot.glb create mode 100644 examples/bevy_gltf_save_load/basic/assets/models/library/Pillar.glb create mode 100644 examples/bevy_gltf_save_load/basic/assets/models/library/Player.glb create mode 100644 examples/bevy_gltf_save_load/basic/assets/models/library/Sphero.glb create mode 100644 examples/bevy_gltf_save_load/basic/assets/models/library/Unused_in_level_test.glb create mode 100644 examples/bevy_gltf_save_load/basic/assets/scenes/save.scn.ron create mode 100644 examples/bevy_gltf_save_load/basic/src/assets/assets_core.rs create mode 100644 examples/bevy_gltf_save_load/basic/src/assets/assets_game.rs create mode 100644 examples/bevy_gltf_save_load/basic/src/assets/mod.rs create mode 100644 examples/bevy_gltf_save_load/basic/src/core/camera/camera_replace_proxies.rs create mode 100644 examples/bevy_gltf_save_load/basic/src/core/camera/camera_tracking.rs create mode 100644 examples/bevy_gltf_save_load/basic/src/core/camera/mod.rs create mode 100644 examples/bevy_gltf_save_load/basic/src/core/lighting/lighting_replace_proxies.rs create mode 100644 examples/bevy_gltf_save_load/basic/src/core/lighting/mod.rs create mode 100644 examples/bevy_gltf_save_load/basic/src/core/mod.rs create mode 100644 examples/bevy_gltf_save_load/basic/src/core/physics/controls.rs create mode 100644 examples/bevy_gltf_save_load/basic/src/core/physics/mod.rs create mode 100644 examples/bevy_gltf_save_load/basic/src/core/physics/physics_replace_proxies.rs create mode 100644 examples/bevy_gltf_save_load/basic/src/core/physics/utils.rs create mode 100644 examples/bevy_gltf_save_load/basic/src/core/physics/utils_old.rs create mode 100644 examples/bevy_gltf_save_load/basic/src/core/relationships/mod.rs create mode 100644 examples/bevy_gltf_save_load/basic/src/core/relationships/relationships_insert_dependant_components.rs create mode 100644 examples/bevy_gltf_save_load/basic/src/game/in_game.rs create mode 100644 examples/bevy_gltf_save_load/basic/src/game/in_game_loading.rs create mode 100644 examples/bevy_gltf_save_load/basic/src/game/in_game_saving.rs create mode 100644 examples/bevy_gltf_save_load/basic/src/game/in_main_menu.rs create mode 100644 examples/bevy_gltf_save_load/basic/src/game/mod.rs create mode 100644 examples/bevy_gltf_save_load/basic/src/game/picking.rs create mode 100644 examples/bevy_gltf_save_load/basic/src/main.rs create mode 100644 examples/bevy_gltf_save_load/basic/src/state.rs create mode 100644 examples/bevy_gltf_save_load/basic/src/test_components.rs create mode 100644 tools/gltf_auto_export/dynamic.py diff --git a/Cargo.lock b/Cargo.lock index 966d15a5..e2827be3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -779,10 +779,20 @@ dependencies = [ [[package]] name = "bevy_gltf_blueprints" -version = "0.5.1" +version = "0.6.0" dependencies = [ "bevy", - "bevy_gltf_components 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bevy_gltf_components 0.2.0", +] + +[[package]] +name = "bevy_gltf_blueprints" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2123036f5738c3bc75607b04799c105dff8a5626c454369bcd1401b4ec0184" +dependencies = [ + "bevy", + "bevy_gltf_components 0.2.0", ] [[package]] @@ -792,7 +802,7 @@ dependencies = [ "bevy", "bevy_asset_loader", "bevy_editor_pls", - "bevy_gltf_blueprints", + "bevy_gltf_blueprints 0.6.0", "bevy_rapier3d", "rand", ] @@ -804,7 +814,7 @@ dependencies = [ "bevy", "bevy_asset_loader", "bevy_editor_pls", - "bevy_gltf_blueprints", + "bevy_gltf_blueprints 0.6.0", "bevy_rapier3d", "rand", ] @@ -816,7 +826,7 @@ dependencies = [ "bevy", "bevy_asset_loader", "bevy_editor_pls", - "bevy_gltf_blueprints", + "bevy_gltf_blueprints 0.6.0", "bevy_rapier3d", "rand", ] @@ -828,7 +838,7 @@ dependencies = [ "bevy", "bevy_asset_loader", "bevy_editor_pls", - "bevy_gltf_blueprints", + "bevy_gltf_blueprints 0.6.0", "bevy_rapier3d", "rand", ] @@ -840,7 +850,7 @@ dependencies = [ "bevy", "bevy_asset_loader", "bevy_editor_pls", - "bevy_gltf_blueprints", + "bevy_gltf_blueprints 0.6.0", "bevy_xpbd_3d", "rand", ] @@ -852,7 +862,7 @@ dependencies = [ "bevy", "bevy_asset_loader", "bevy_editor_pls", - "bevy_gltf_blueprints", + "bevy_gltf_blueprints 0.6.0", "bevy_rapier3d", "rand", ] @@ -864,7 +874,7 @@ dependencies = [ "bevy", "bevy_asset_loader", "bevy_editor_pls", - "bevy_gltf_blueprints", + "bevy_gltf_blueprints 0.6.0", "bevy_rapier3d", "rand", ] @@ -876,7 +886,7 @@ dependencies = [ "bevy", "bevy_asset_loader", "bevy_editor_pls", - "bevy_gltf_blueprints", + "bevy_gltf_blueprints 0.6.0", "bevy_rapier3d", "rand", ] @@ -888,7 +898,7 @@ dependencies = [ "bevy", "bevy_asset_loader", "bevy_editor_pls", - "bevy_gltf_blueprints", + "bevy_gltf_blueprints 0.6.0", "bevy_rapier3d", "rand", ] @@ -896,6 +906,8 @@ dependencies = [ [[package]] name = "bevy_gltf_components" version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd745a2988c631286404e12d184d4a30a634fbbba1deceaaa1ca7fcbc607cc7a" dependencies = [ "bevy", "ron", @@ -904,9 +916,7 @@ dependencies = [ [[package]] name = "bevy_gltf_components" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd745a2988c631286404e12d184d4a30a634fbbba1deceaaa1ca7fcbc607cc7a" +version = "0.2.1" dependencies = [ "bevy", "ron", @@ -919,7 +929,7 @@ version = "0.3.0" dependencies = [ "bevy", "bevy_editor_pls", - "bevy_gltf_components 0.2.0", + "bevy_gltf_components 0.2.1", "bevy_rapier3d", ] @@ -929,10 +939,33 @@ version = "0.3.0" dependencies = [ "bevy", "bevy_editor_pls", - "bevy_gltf_components 0.2.0", + "bevy_gltf_components 0.2.1", "bevy_rapier3d", ] +[[package]] +name = "bevy_gltf_save_load" +version = "0.1.0" +dependencies = [ + "bevy", + "bevy_gltf_blueprints 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bevy_gltf_save_load_basic_example" +version = "0.3.0" +dependencies = [ + "bevy", + "bevy_asset_loader", + "bevy_editor_pls", + "bevy_gltf_blueprints 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bevy_gltf_save_load", + "bevy_rapier3d", + "rand", + "serde", + "serde_json", +] + [[package]] name = "bevy_hierarchy" version = "0.12.1" diff --git a/Cargo.toml b/Cargo.toml index b029158b..77216a37 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ members = [ "crates/bevy_gltf_components", "crates/bevy_gltf_blueprints", + "crates/bevy_gltf_save_load", "examples/bevy_gltf_components/basic/", "examples/bevy_gltf_components/basic_wasm/", "examples/bevy_gltf_blueprints/basic/", @@ -12,7 +13,9 @@ members = [ "examples/bevy_gltf_blueprints/animation/", "examples/bevy_gltf_blueprints/multiple_levels/", "examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles", - "examples/bevy_gltf_blueprints/materials/" + "examples/bevy_gltf_blueprints/materials/", + "examples/bevy_gltf_save_load/basic/", + ] resolver = "2" diff --git a/README.md b/README.md index d317efd4..ed274698 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,10 @@ There is a [video tutorial/explanation](https://youtu.be/CgyNtwgYwdM) for this o The examples for the crate are [here](./examples/bevy_gltf_blueprints/) > Note: this is the recomended crate to use and uses ```bevy_gltf_components``` under the hood +- [bevy_gltf_save_load](./crates/bevy_gltf_save_load/) This crate adds the ability to save & load your game state in a relatively simple way, by leveraging the blueprint functionality of +bevy_gltf_blueprints to only save a minimal subset of dynamic data, seperating dynamic & static parts of levels etc. +The examples for the crate are [here](./examples/bevy_gltf_save_load/) +> Note: this uses ```bevy_gltf_blueprints``` under the hood ## Tools diff --git a/crates/bevy_gltf_blueprints/Cargo.toml b/crates/bevy_gltf_blueprints/Cargo.toml index 23478bcf..59387c9b 100644 --- a/crates/bevy_gltf_blueprints/Cargo.toml +++ b/crates/bevy_gltf_blueprints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bevy_gltf_blueprints" -version = "0.5.1" +version = "0.6.0" authors = ["Mark 'kaosat-dev' Moissette"] description = "Adds the ability to define Blueprints/Prefabs for [Bevy](https://bevyengine.org/) inside gltf files and spawn them in Bevy." homepage = "https://github.com/kaosat-dev/Blender_bevy_components_workflow" diff --git a/crates/bevy_gltf_blueprints/README.md b/crates/bevy_gltf_blueprints/README.md index fe8c36d0..48ac9c1e 100644 --- a/crates/bevy_gltf_blueprints/README.md +++ b/crates/bevy_gltf_blueprints/README.md @@ -26,7 +26,7 @@ Here's a minimal usage example: # Cargo.toml [dependencies] bevy="0.12" -bevy_gltf_blueprints = { version = "0.5"} +bevy_gltf_blueprints = { version = "0.6"} ``` @@ -64,7 +64,7 @@ fn spawn_blueprint( Add the following to your `[dependencies]` section in `Cargo.toml`: ```toml -bevy_gltf_blueprints = "0.5" +bevy_gltf_blueprints = "0.6" ``` Or use `cargo add`: @@ -97,8 +97,7 @@ use bevy_gltf_blueprints::*; fn main() { App::new() - .add_plugins(DefaultPlugins) - .add_plugin( + .add_plugins(( BlueprintsPlugin{ library_folder: "advanced/models/library".into() // replace this with your blueprints library path , relative to the assets folder, format: GltfFormat::GLB,// optional, use either format: GltfFormat::GLB, or format: GltfFormat::GLTF, or ..Default::default() if you want to keep the default .glb extension, this sets what extensions/ gltf files will be looked for by the library @@ -107,7 +106,7 @@ fn main() { material_library_folder: "materials".into() //defaults to "materials" the folder to look for for the material files ..Default::default() } - ) + )) .run(); } @@ -120,7 +119,8 @@ You can spawn entities from blueprints like this: commands.spawn(( BlueprintName("Health_Pickup".to_string()), // mandatory !! SpawnHere, // mandatory !! - TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), // VERY important !! + + TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), // optional // any other component you want to insert )) @@ -168,11 +168,51 @@ commands.spawn(( There is also a bundle for convenience , which just has * a ```BlueprintName``` component * a ```SpawnHere``` component - * a ```TransformBundle``` sub-bundle (so we know where to spawn) [```BluePrintBundle```](./src/lib.rs#22) +## Additional information + +- When a blueprint is spawned, all its children entities (and nested children etc) also have an ```InBlueprint``` component that gets insert +- In cases where that is undesirable, you can add a ```NoInBlueprint``` component on the entity you spawn the blueprint with, and the components above will not be add +- if you want to overwrite the **path** where this crate looks for blueprints (gltf files) , you can add a ```Library``` component , and that will be used instead of the default path +ie : + +```rust no_run +commands + .spawn(( + Name::from("test"), + BluePrintBundle { + blueprint: BlueprintName("TestBlueprint".to_string()), + ..Default::default() + }, + Library("models".into()) // now the path to the blueprint above will be /assets/models/TestBlueprint.glb + )) +``` +- this crate also provides a special optional ```GameWorldTag``` component: this is useful when you want to keep all your spawned entities inside a root entity + +You can use it in your queries to add your entities as children of this "world" +This way all your levels, your dynamic entities etc, are kept seperated from UI nodes & other entities that are not relevant to the game world + +> Note: you should only have a SINGLE entity tagged with that component ! + +```rust no_run + commands.spawn(( + SceneBundle { + scene: models + .get(game_assets.world.id()) + .expect("main level should have been loaded") + .scenes[0] + .clone(), + ..default() + }, + bevy::prelude::Name::from("world"), + GameWorldTag, // here it is + )); +``` + + ## SystemSet the ordering of systems is very important ! @@ -239,9 +279,9 @@ pub fn animation_change_on_proximity_foxes( } ``` -see https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/animation for how to set it up correctly +see https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/animation for how to set it up correctly -particularly from https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/animation/game/in_game.rs#86 +particularly from https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/animation/game/in_game.rs#86 onward @@ -263,27 +303,27 @@ material_library_folder: "materials".into() //defaults to "materials" the folder ```bevy_gltf_blueprints``` currently does NOT take care of loading those at runtime -see https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/materials for how to set it up correctly +see https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/materials for how to set it up correctly Generating optimised blueprints and material libraries can be automated using the latests version of the [Blender plugin](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/tools/gltf_auto_export) ## Examples -https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/basic +https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/basic -https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/basic_xpbd_physics +https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/basic_xpbd_physics -https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/basic_scene_components +https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/basic_scene_components -https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/animation +https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/animation -https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/multiple_levels +https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/multiple_levels -https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/materials +https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/materials -https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/nested_blueprints +https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/nested_blueprints -https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/multiple_levels_multiple_blendfiles +https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles ## Compatible Bevy versions @@ -293,7 +333,7 @@ The main branch is compatible with the latest Bevy release, while the branch `be Compatibility of `bevy_gltf_blueprints` versions: | `bevy_gltf_blueprints` | `bevy` | | :-- | :-- | -| `0.3 - 0.5` | `0.12` | +| `0.3 - 0.6` | `0.12` | | `0.1 - 0.2` | `0.11` | | branch `main` | `0.12` | | branch `bevy_main` | `main` | diff --git a/crates/bevy_gltf_blueprints/src/aabb.rs b/crates/bevy_gltf_blueprints/src/aabb.rs index 68cca60d..ab3a21f4 100644 --- a/crates/bevy_gltf_blueprints/src/aabb.rs +++ b/crates/bevy_gltf_blueprints/src/aabb.rs @@ -1,13 +1,10 @@ use bevy::{math::Vec3A, prelude::*, render::primitives::Aabb}; -use crate::{BluePrintsConfig, BlueprintName, SpawnedRoot}; +use crate::{BluePrintsConfig, Spawned}; /// helper system that computes the compound aabbs of the scenes/blueprints pub fn compute_scene_aabbs( - root_entities: Query< - (Entity, &Name, &Children, &BlueprintName), - (With, Without), - >, + root_entities: Query<(Entity, &Name), (With, Without)>, children: Query<&Children>, existing_aabbs: Query<&Aabb>, @@ -15,10 +12,8 @@ pub fn compute_scene_aabbs( mut commands: Commands, ) { // compute compound aabb - for root_entity in root_entities.iter() { - let name = &root_entity.3 .0; - - let root_entity = root_entity.2.first().unwrap(); + for (root_entity, name) in root_entities.iter() { + // info!("generating aabb for {:?}", name); // only recompute aabb if it has not already been done before if blueprints_config.aabb_cache.contains_key(&name.to_string()) { @@ -26,10 +21,10 @@ pub fn compute_scene_aabbs( .aabb_cache .get(&name.to_string()) .expect("we should have the aabb available"); - commands.entity(*root_entity).insert(*aabb); + commands.entity(root_entity).insert(*aabb); } else { - let aabb = compute_descendant_aabb(*root_entity, &children, &existing_aabbs); - commands.entity(*root_entity).insert(aabb); + let aabb = compute_descendant_aabb(root_entity, &children, &existing_aabbs); + commands.entity(root_entity).insert(aabb); blueprints_config.aabb_cache.insert(name.to_string(), aabb); } } diff --git a/crates/bevy_gltf_blueprints/src/clone_entity.rs b/crates/bevy_gltf_blueprints/src/clone_entity.rs deleted file mode 100644 index 07e94d80..00000000 --- a/crates/bevy_gltf_blueprints/src/clone_entity.rs +++ /dev/null @@ -1,70 +0,0 @@ -use bevy::ecs::system::Command; -use bevy::prelude::*; - -// modified version from https://github.com/bevyengine/bevy/issues/1515, -// more specifically https://gist.github.com/nwtnni/85d6b87ae75337a522166c500c9a8418 -// to work with Bevy 0.11 -pub struct CloneEntity { - pub source: Entity, - pub destination: Entity, -} - -impl CloneEntity { - // Copy all components from an entity to another. - // Using an entity with no components as the destination creates a copy of the source entity. - // Panics if: - // - the components are not registered in the type registry, - // - the world does not have a type registry - // - the source or destination entity do not exist - fn clone_entity(self, world: &mut World) { - let components = { - let registry = world - .get_resource::() - .expect("the world should have a type registry") - .read(); - - world - .get_entity(self.source) - .expect("source entity should exist") - .archetype() - .components() - .map(|component_id| { - let component_info = world - .components() - .get_info(component_id) - .expect("component info should be available"); - - let type_id = component_info.type_id().unwrap(); - let type_id = registry.get(type_id).expect( - format!( - "cannot clone entity: component: {:?} is not registered", - component_info.name() - ) - .as_str(), - ); - return type_id.data::().unwrap().clone(); - }) - .collect::>() - }; - - for component in components { - let source = component - .reflect(world.get_entity(self.source).unwrap()) - .unwrap() - .clone_value(); - - let mut destination = world - .get_entity_mut(self.destination) - .expect("destination entity should exist"); - - component.apply_or_insert(&mut destination, &*source); - } - } -} - -// This allows the command to be used in systems -impl Command for CloneEntity { - fn apply(self, world: &mut World) { - self.clone_entity(world) - } -} diff --git a/crates/bevy_gltf_blueprints/src/copy_components.rs b/crates/bevy_gltf_blueprints/src/copy_components.rs new file mode 100644 index 00000000..bb241701 --- /dev/null +++ b/crates/bevy_gltf_blueprints/src/copy_components.rs @@ -0,0 +1,109 @@ +use bevy::ecs::system::Command; +use bevy::prelude::*; +use std::any::TypeId; + +// originally based https://github.com/bevyengine/bevy/issues/1515, +// more specifically https://gist.github.com/nwtnni/85d6b87ae75337a522166c500c9a8418 +// to work with Bevy 0.11 +// to copy components between entities but NOT overwriting any existing components +// plus some bells & whistles +pub struct CopyComponents { + pub source: Entity, + pub destination: Entity, + pub exclude: Vec, + pub stringent: bool, +} + +impl CopyComponents { + // Copy all components from an entity to another. + // Using an entity with no components as the destination creates a copy of the source entity. + // Panics if: + // - the components are not registered in the type registry, + // - the world does not have a type registry + // - the source or destination entity do not exist + fn transfer_components(self, world: &mut World) { + let components = { + let registry = world + .get_resource::() + .expect("the world should have a type registry") + .read(); + + world + .get_entity(self.source) + .expect("source entity should exist") + .archetype() + .components() + .filter_map(|component_id| { + let component_info = world + .components() + .get_info(component_id) + .expect("component info should be available"); + + let type_id = component_info.type_id().unwrap(); + if self.exclude.contains(&type_id) { + debug!("excluding component: {:?}", component_info.name()); + return None; + } else { + debug!( + "cloning: component: {:?} {:?}", + component_info.name(), + type_id + ); + + if let Some(type_registration) = registry.get(type_id) { + return Some(type_registration); + } else { + if self.stringent { + return Some( + registry.get(type_id).expect( + format!( + "cannot clone entity: component: {:?} is not registered", + component_info.name() + ) + .as_str(), + ), + ); + } else { + warn!( + "cannot clone component: component: {:?} is not registered", + component_info.name() + ); + return None; + } + } + } + }) + .map(|type_id| { + return ( + type_id.data::().unwrap().clone(), + type_id.type_info().type_id().clone(), // we need the original type_id down the line + ); + }) + .collect::>() + }; + + for (component, type_id) in components { + let source = component + .reflect(world.get_entity(self.source).unwrap()) + .unwrap() + .clone_value(); + + let mut destination = world + .get_entity_mut(self.destination) + .expect("destination entity should exist"); + + // println!("contains typeid {:?} {}", type_id, destination.contains_type_id(type_id)); + // we only want to copy components that are NOT already in the destination (ie no overwriting existing components) + if !destination.contains_type_id(type_id) { + component.insert(&mut destination, &*source); + } + } + } +} + +// This allows the command to be used in systems +impl Command for CopyComponents { + fn apply(self, world: &mut World) { + self.transfer_components(world) + } +} diff --git a/crates/bevy_gltf_blueprints/src/lib.rs b/crates/bevy_gltf_blueprints/src/lib.rs index 70d9419b..e335437f 100644 --- a/crates/bevy_gltf_blueprints/src/lib.rs +++ b/crates/bevy_gltf_blueprints/src/lib.rs @@ -13,8 +13,8 @@ pub use aabb::*; pub mod materials; pub use materials::*; -pub mod clone_entity; -pub use clone_entity::*; +pub mod copy_components; +pub use copy_components::*; use core::fmt; use std::path::PathBuf; @@ -33,14 +33,12 @@ pub enum GltfBlueprintsSet { pub struct BluePrintBundle { pub blueprint: BlueprintName, pub spawn_here: SpawnHere, - pub transform: TransformBundle, } impl Default for BluePrintBundle { fn default() -> Self { BluePrintBundle { blueprint: BlueprintName("default".into()), spawn_here: SpawnHere, - transform: TransformBundle::default(), } } } @@ -140,6 +138,7 @@ impl Plugin for BlueprintsPlugin { spawn_from_blueprints, compute_scene_aabbs.run_if(aabbs_enabled), apply_deferred.run_if(aabbs_enabled), + apply_deferred, materials_inject.run_if(materials_library_enabled), ) .chain() @@ -147,12 +146,7 @@ impl Plugin for BlueprintsPlugin { ) .add_systems( Update, - ( - update_spawned_root_first_child, - apply_deferred, - cleanup_scene_instances, - apply_deferred, - ) + (spawned_blueprint_post_process, apply_deferred) .chain() .in_set(GltfBlueprintsSet::AfterSpawn), ); diff --git a/crates/bevy_gltf_blueprints/src/spawn_from_blueprints.rs b/crates/bevy_gltf_blueprints/src/spawn_from_blueprints.rs index 0baaaa03..57ca64b5 100644 --- a/crates/bevy_gltf_blueprints/src/spawn_from_blueprints.rs +++ b/crates/bevy_gltf_blueprints/src/spawn_from_blueprints.rs @@ -1,4 +1,4 @@ -use std::path::Path; +use std::path::{Path, PathBuf}; use bevy::{gltf::Gltf, prelude::*}; @@ -19,44 +19,87 @@ pub struct BlueprintName(pub String); pub struct SpawnHere; #[derive(Component)] -/// FlagComponent for spawned entity +/// FlagComponent for dynamically spawned scenes pub struct Spawned; -#[derive(Component)] -/// helper component, just to transfer some data -pub(crate) struct Original(pub Entity); +#[derive(Component, Reflect, Default, Debug)] +#[reflect(Component)] +/// flag component marking any spwaned child of blueprints ..unless the original entity was marked with the 'NoInBlueprint' marker component +pub struct InBlueprint; + +#[derive(Component, Reflect, Default, Debug)] +#[reflect(Component)] +/// flag component preventing any spwaned child of blueprints to be marked with the InBlueprint component +pub struct NoInBlueprint; + +#[derive(Component, Reflect, Default, Debug)] +#[reflect(Component)] +// this allows overriding the default library path for a given entity/blueprint +pub struct Library(pub PathBuf); + +#[derive(Component, Reflect, Default, Debug)] +#[reflect(Component)] +/// flag component to force adding newly spawned entity as child of game world +pub struct AddToGameWorld; #[derive(Component)] -/// FlagComponent for dynamically spawned scenes -pub struct SpawnedRoot; +/// helper component, just to transfer child data +pub(crate) struct OriginalChildren(pub Vec); /// main spawning functions, /// * also takes into account the already exisiting "override" components, ie "override components" > components from blueprint pub(crate) fn spawn_from_blueprints( spawn_placeholders: Query< - (Entity, &Name, &BlueprintName, &Transform, Option<&Parent>), ( - Added, - Added, - Without, - Without, + Entity, + &BlueprintName, + Option<&Transform>, + Option<&Parent>, + Option<&Library>, + Option<&AddToGameWorld>, + Option<&Name>, ), + (Added, Added, Without), >, mut commands: Commands, - mut game_world: Query<(Entity, &Children), With>, + mut game_world: Query>, assets_gltf: Res>, asset_server: Res, blueprints_config: Res, + + children: Query<&Children>, ) { - for (entity, name, blupeprint_name, transform, original_parent) in spawn_placeholders.iter() { - debug!("need to spawn {:?}, id: {:?}", blupeprint_name.0, entity); + for ( + entity, + blupeprint_name, + transform, + original_parent, + library_override, + add_to_world, + name, + ) in spawn_placeholders.iter() + { + debug!( + "need to spawn {:?} for entity {:?}, id: {:?}, parent:{:?}", + blupeprint_name.0, name, entity, original_parent + ); + + let mut original_children: Vec = vec![]; + if let Ok(c) = children.get(entity) { + for child in c.iter() { + original_children.push(*child); + } + } let what = &blupeprint_name.0; let model_file_name = format!("{}.{}", &what, &blueprints_config.format); - let model_path = - Path::new(&blueprints_config.library_folder).join(Path::new(model_file_name.as_str())); + + // library path is either defined at the plugin level or overriden by optional Library components + let library_path = + library_override.map_or_else(|| &blueprints_config.library_folder, |l| &l.0); + let model_path = Path::new(&library_path).join(Path::new(model_file_name.as_str())); debug!("attempting to spawn {:?}", model_path); let model_handle: Handle = asset_server.load(model_path); @@ -73,32 +116,30 @@ pub(crate) fn spawn_from_blueprints( .expect("there should be at least one named scene in the gltf file to spawn"); let scene = &gltf.named_scenes[main_scene_name]; - let child_scene = commands - .spawn(( - SceneBundle { - scene: scene.clone(), - transform: transform.clone(), - ..Default::default() - }, - name.clone(), - // Parent(world) // FIXME/ would be good if this worked directly - SpawnedRoot, - BlueprintName(blupeprint_name.0.clone()), - Original(entity), - Animations { - named_animations: gltf.named_animations.clone(), - }, - )) - .id(); - - let world = game_world.single_mut(); - let mut parent = world.1[0]; // FIXME: dangerous hack because our gltf data have a single child like this, but might not always be the case - - // ideally, insert the newly created entity as a child of the original parent, if any, the world otherwise - if let Some(original_parent) = original_parent { - parent = original_parent.get(); + // transforms are optional, but still deal with them correctly + let mut transforms: Transform = Transform::default(); + if transform.is_some() { + transforms = transform.unwrap().clone(); } - commands.entity(parent).add_child(child_scene); + commands.entity(entity).insert(( + SceneBundle { + scene: scene.clone(), + transform: transforms, + ..Default::default() + }, + Animations { + named_animations: gltf.named_animations.clone(), + }, + Spawned, + OriginalChildren(original_children), + )); + + if add_to_world.is_some() { + let world = game_world + .get_single_mut() + .expect("there should be a game world present"); + commands.entity(world).add_child(entity); + } } } diff --git a/crates/bevy_gltf_blueprints/src/spawn_post_process.rs b/crates/bevy_gltf_blueprints/src/spawn_post_process.rs index a2aa6ea8..2c3f3fcb 100644 --- a/crates/bevy_gltf_blueprints/src/spawn_post_process.rs +++ b/crates/bevy_gltf_blueprints/src/spawn_post_process.rs @@ -1,128 +1,94 @@ +use std::any::TypeId; + use bevy::prelude::*; +use bevy::scene::SceneInstance; +// use bevy::utils::hashbrown::HashSet; use super::{AnimationPlayerLink, Animations}; -use super::{CloneEntity, SpawnHere}; -use super::{Original, SpawnedRoot}; - -#[derive(Component)] -/// FlagComponent for dynamically spawned scenes -pub(crate) struct SpawnedRootProcessed; - -/// this system updates the first (and normally only) child of a scene flaged SpawnedRoot -/// - adds a name based on parent component (spawned scene) which is named on the scene name/prefab to be instanciated -// FIXME: updating hierarchy does not work in all cases ! this is sadly dependant on the structure of the exported blend data -// - blender root-> object with properties => WORKS -// - scene instance -> does not work -// it might be due to how we add components to the PARENT item in gltf to components -pub(crate) fn update_spawned_root_first_child( - // +use super::{SpawnHere, Spawned}; +use crate::{CopyComponents, InBlueprint, NoInBlueprint, OriginalChildren}; + +/// this system is in charge of doing any necessary post processing after a blueprint scene has been spawned +/// - it removes one level of useless nesting +/// - it copies the blueprint's root components to the entity it was spawned on (original entity) +/// - it copies the children of the blueprint scene into the original entity +/// - it add AnimationLink components so that animations can be controlled from the original entity +/// - it cleans up/ removes a few , by then uneeded components +pub(crate) fn spawned_blueprint_post_process( unprocessed_entities: Query< - (Entity, &Children, &Name, &Parent, &Original), - (With, Without), + ( + Entity, + &Children, + &OriginalChildren, + &Animations, + Option<&NoInBlueprint>, + Option<&Name>, + ), + (With, With, With), >, - mut commands: Commands, - - animations: Query<&Animations>, added_animation_players: Query<(Entity, &Parent), Added>, -) { - /* - currently we have - - scene instance - - root node ? - - the actual stuff - we want to remove the root node - so we wend up with - - scene instance - - the actual stuff - - so - - get root node - - add its children to the scene instance - - remove root node - - Another issue is, the scene instance become empty if we have a pickabke as the "actual stuff", so that would lead to a lot of - empty scenes if we spawn pickables - - perhaps another system that cleans up empty scene instances ? + all_children: Query<&Children>, - FIME: this is all highly dependent on the hierachy ;.. - */ + mut commands: Commands, +) { + for (original, children, original_children, animations, no_inblueprint, name) in + unprocessed_entities.iter() + { + debug!("post processing blueprint for entity {:?}", name); - for (scene_instance, children, name, parent, original) in unprocessed_entities.iter() { - // if children.len() == 0 { warn!("timing issue ! no children found, please restart your bevy app (bug being investigated)"); - // println!("children of scene {:?}", children); continue; } // the root node is the first & normally only child inside a scene, it is the one that has all relevant components - let root_entity = children.first().unwrap(); //FIXME: and what about childless ones ?? => should not be possible normally - // let root_entity_data = all_children.get(*root_entity).unwrap(); - - // fixme : randomization should be controlled via parameters, perhaps even the seed could be specified ? - // use this https://rust-random.github.io/book/guide-seeding.html#a-simple-number, blenders seeds are also uInts - // also this is not something we want every time, this should be a settable parameter when requesting a spawn - - // add missing name of entity, based on the wrapper's name - let name = name.clone(); - - // this is our new actual entity - commands.entity(*root_entity).insert(( - bevy::prelude::Name::from(name.clone()), - // ItemType {name}, - )); - - // flag the spawned_root as being processed - commands.entity(scene_instance).insert(SpawnedRootProcessed); - - // parent is either the world or an entity with a marker (BlueprintName) - commands.entity(parent.get()).add_child(*root_entity); - - let matching_animations = animations.get(scene_instance); + let mut root_entity = Entity::PLACEHOLDER; //FIXME: and what about childless ones ?? => should not be possible normally + // let diff = HashSet::from_iter(original_children.0).difference(HashSet::from_iter(children)); + // we find the first child that was not in the entity before (aka added during the scene spawning) + for c in children.iter() { + if !original_children.0.contains(c) { + root_entity = *c; + break; + } + } - if let Ok(animations) = matching_animations { - if animations.named_animations.keys().len() > 0 { - for (added, parent) in added_animation_players.iter() { - if parent.get() == *root_entity { - // FIXME: stopgap solution: since we cannot use an AnimationPlayer at the root entity level - // and we cannot update animation clips so that the EntityPaths point to one level deeper, - // BUT we still want to have some marker/control at the root entity level, we add this - commands - .entity(*root_entity) - .insert(AnimationPlayerLink(added)); - commands.entity(*root_entity).insert(Animations { - named_animations: animations.named_animations.clone(), - }); - } - } + // we flag all children of the blueprint instance with 'InBlueprint' + // can be usefull to filter out anything that came from blueprints vs normal children + if no_inblueprint.is_none() { + for child in all_children.iter_descendants(root_entity) { + commands.entity(child).insert(InBlueprint); } } - commands.add(CloneEntity { - source: original.0, - destination: *root_entity, + // copy components into from blueprint instance's root_entity to original entity + commands.add(CopyComponents { + source: root_entity, + destination: original, + exclude: vec![TypeId::of::(), TypeId::of::()], + stringent: false, }); - // remove the original entity, now that we have cloned it into the spawned scenes first child - commands.entity(original.0).despawn_recursive(); - commands.entity(*root_entity).remove::(); - } -} + // we move all of children of the blueprint instance one level to the original entity + if let Ok(root_entity_children) = all_children.get(root_entity) { + for child in root_entity_children.iter() { + // info!("copying child {:?} upward from {:?} to {:?}", names.get(*child), root_entity, original); + commands.entity(original).add_child(*child); + } + } -/// cleans up dynamically spawned scenes so that they get despawned if they have no more children -pub(crate) fn cleanup_scene_instances( - scene_instances: Query<(Entity, &Children), With>, - without_children: Query, Without)>, // if there are not children left, bevy removes Children ? - mut commands: Commands, -) { - for (entity, children) in scene_instances.iter() { - if children.len() == 0 { - // it seems this does not happen ? - debug!("cleaning up emptied spawned scene instance"); - commands.entity(entity).despawn_recursive(); + if animations.named_animations.keys().len() > 0 { + for (added, parent) in added_animation_players.iter() { + if parent.get() == root_entity { + // FIXME: stopgap solution: since we cannot use an AnimationPlayer at the root entity level + // and we cannot update animation clips so that the EntityPaths point to one level deeper, + // BUT we still want to have some marker/control at the root entity level, we add this + commands.entity(original).insert(AnimationPlayerLink(added)); + } + } } - } - for entity in without_children.iter() { - debug!("cleaning up emptied spawned scene instance"); - commands.entity(entity).despawn_recursive(); + + commands.entity(original).remove::(); + commands.entity(original).remove::(); + commands.entity(original).remove::>(); + commands.entity(root_entity).despawn_recursive(); } } diff --git a/crates/bevy_gltf_components/Cargo.toml b/crates/bevy_gltf_components/Cargo.toml index d7ba1224..c0b08fb3 100644 --- a/crates/bevy_gltf_components/Cargo.toml +++ b/crates/bevy_gltf_components/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bevy_gltf_components" -version = "0.2.0" +version = "0.2.1" authors = ["Mark 'kaosat-dev' Moissette"] description = "Allows you to define [Bevy](https://bevyengine.org/) components direclty inside gltf files and instanciate the components on the Bevy side." homepage = "https://github.com/kaosat-dev/Blender_bevy_components_workflow" diff --git a/crates/bevy_gltf_components/src/gltf_to_components.rs b/crates/bevy_gltf_components/src/gltf_to_components.rs index 82c774eb..cf319f6d 100644 --- a/crates/bevy_gltf_components/src/gltf_to_components.rs +++ b/crates/bevy_gltf_components/src/gltf_to_components.rs @@ -10,7 +10,7 @@ use bevy::reflect::{Reflect, TypeInfo, TypeRegistry}; use bevy::scene::Scene; use bevy::utils::HashMap; use bevy::{ - log::{debug, info, warn}, + log::{debug, warn}, prelude::{Assets, Name, Parent, ResMut}, }; @@ -252,5 +252,5 @@ pub fn gltf_extras_to_components( } } } - info!("done injecting components from gltf_extras /n"); + debug!("done injecting components from gltf_extras"); } diff --git a/crates/bevy_gltf_save_load/Cargo.toml b/crates/bevy_gltf_save_load/Cargo.toml new file mode 100644 index 00000000..c41ab0e3 --- /dev/null +++ b/crates/bevy_gltf_save_load/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "bevy_gltf_save_load" +version = "0.1.0" +authors = ["Mark 'kaosat-dev' Moissette"] +description = "Save & load your bevy games" +homepage = "https://github.com/kaosat-dev/Blender_bevy_components_workflow" +repository = "https://github.com/kaosat-dev/Blender_bevy_components_workflow" +keywords = ["gamedev", "bevy", "save", "load", "serialize"] +categories = ["game-development"] +edition = "2021" +license = "MIT OR Apache-2.0" + +[dev-dependencies] +bevy = { version = "0.12", default-features = false, features = ["bevy_asset", "bevy_scene", "bevy_gltf"] } + +[dependencies] +bevy = { version = "0.12", default-features = false, features = ["bevy_asset", "bevy_scene", "bevy_gltf"] } +bevy_gltf_blueprints = "0.6" diff --git a/crates/bevy_gltf_save_load/LICENSE.md b/crates/bevy_gltf_save_load/LICENSE.md new file mode 100644 index 00000000..ad21aacd --- /dev/null +++ b/crates/bevy_gltf_save_load/LICENSE.md @@ -0,0 +1,4 @@ +This crate is available under either: + +* The [MIT License](./LICENSE_MIT) +* The [Apache License, Version 2.0](./LICENSE_APACHE) \ No newline at end of file diff --git a/crates/bevy_gltf_save_load/LICENSE_APACHE.md b/crates/bevy_gltf_save_load/LICENSE_APACHE.md new file mode 100644 index 00000000..f7489773 --- /dev/null +++ b/crates/bevy_gltf_save_load/LICENSE_APACHE.md @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [2023] [Mark "kaosat-dev" Moissette] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/crates/bevy_gltf_save_load/LICENSE_MIT.md b/crates/bevy_gltf_save_load/LICENSE_MIT.md new file mode 100644 index 00000000..f8b90945 --- /dev/null +++ b/crates/bevy_gltf_save_load/LICENSE_MIT.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Mark "kaosat-dev" Moissette + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/crates/bevy_gltf_save_load/README.md b/crates/bevy_gltf_save_load/README.md new file mode 100644 index 00000000..e68daac8 --- /dev/null +++ b/crates/bevy_gltf_save_load/README.md @@ -0,0 +1,312 @@ +[![Crates.io](https://img.shields.io/crates/v/bevy_gltf_save_load)](https://crates.io/crates/bevy_gltf_save_load) +[![Docs](https://img.shields.io/docsrs/bevy_gltf_save_load)](https://docs.rs/bevy_gltf_save_load/latest/bevy_gltf_save_load/) +[![License](https://img.shields.io/crates/l/bevy_gltf_save_load)](https://github.com/kaosat-dev/Blender_bevy_components_workflow/blob/main/crates/bevy_gltf_save_load/License.md) +[![Bevy tracking](https://img.shields.io/badge/Bevy%20tracking-released%20version-lightblue)](https://github.com/bevyengine/bevy/blob/main/docs/plugins_guidelines.md#main-branch-tracking) + +# bevy_gltf_save_load + +Built upon [bevy_gltf_blueprints](https://crates.io/crates/bevy_gltf_blueprints) this crate adds the ability to easilly **save** and **load** your game worlds for [Bevy](https://bevyengine.org/) . + +* leverages blueprints & seperation between + * **dynamic** entities : entities that can change during the lifetime of your app/game + * **static** entities : entities that do NOT change (typically, a part of your levels/ environements) +* and allows allow for : + * a simple save/load workflow thanks to the above + * ability to specify **which entities** to save or to exclude + * ability to specify **which components** to save or to exclude + * ability to specify **which resources** to save or to exclude + * small(er) save files (only a portion of the entities is saved) + +Particularly useful when using [Blender](https://www.blender.org/) as an editor for the [Bevy](https://bevyengine.org/) game engine, combined with the [Blender plugin](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/tools/gltf_auto_export) that does a lot of the work for you (including spliting generating seperate gltf files for your static vs dynamic assets) + + +A bit of heads up: + +* very opinionated ! +* still in the early stages & not 100% feature complete +* fun fact: as the static level structure is stored seperatly, you can change your level layout & **still** reload an existing save file + + +## Usage + +Here's a minimal usage example: + +```toml +# Cargo.toml +[dependencies] +bevy="0.12" +bevy_gltf_save_load = "0.1" +bevy_gltf_blueprints = "0.6" // also needed +``` + +```rust no_run +use bevy::prelude::*; +use bevy_gltf_save_load::*; + +fn main() { + App::new() + .add_plugins(( + DefaultPlugins, + SaveLoadPlugin::default() + )) + .run(); +} + + + +// add a system to trigger saving +pub fn request_save( + mut save_requests: EventWriter, + keycode: Res>, +) +{ + if keycode.just_pressed(KeyCode::S) { + save_requests.send(SaveRequest { + path: "save.scn.ron".into(), + }) + } +} + +// add a system to trigger loading +pub fn request_load( + mut load_requests: EventWriter, + keycode: Res>, +) +{ + if keycode.just_pressed(KeyCode::L) { + save_requests.send(LoadRequest { + path: "save.scn.ron".into(), + }) + } +} + +// setting up your world +// on initial setup, the static entities & the dynamic entities are kept seperate for clarity & loaded as blueprints from 2 seperate files +pub fn setup_game( + mut commands: Commands, + mut next_game_state: ResMut>, +) { + info!("setting up game world"); + // here we actually spawn our game world/level + let world_root = commands + .spawn(( + Name::from("world"), + GameWorldTag, + InAppRunning, + TransformBundle::default(), + InheritedVisibility::default(), + )) + .id(); + + // and we fill it with static entities + let static_data = commands + .spawn(( + Name::from("static"), + BluePrintBundle { + blueprint: BlueprintName("World".to_string()), + ..Default::default() + }, + StaticEntitiesRoot, + Library("models".into()) + )) + .id(); + + // and we fill it with dynamic entities + let dynamic_data = commands + .spawn(( + Name::from("dynamic"), + BluePrintBundle { + blueprint: BlueprintName("World_dynamic".to_string()), + ..Default::default() + }, + DynamicEntitiesRoot, + NoInBlueprint, + Library("models".into()) + )) + .id(); + commands.entity(world_root).add_child(static_data); + commands.entity(world_root).add_child(dynamic_data); + + next_game_state.set(GameState::InGame) +} + + +``` + +take a look at the [example]('https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_save_load/basic/src/game/mod.rs) for more clarity + + +## Installation + +Add the following to your `[dependencies]` section in `Cargo.toml`: + +```toml +bevy_gltf_save_load = "0.1" +bevy_gltf_blueprints = "0.6" // also needed, as bevy_gltf_save_load does not re-export it at this time + +``` + +Or use `cargo add`: + +```toml +cargo add bevy_gltf_save_load +``` + +## Setup + +```rust no_run +use bevy::prelude::*; +use bevy_gltf_save_load::*; + +fn main() { + App::new() + .add_plugins(( + DefaultPlugins + SaveLoadPlugin::default() + )) + .run(); +} + +``` + +you likely need to configure your settings (otherwise, not much will be saved) + +```rust no_run +use bevy::prelude::*; +use bevy_gltf_save_load::*; + +fn main() { + App::new() + .add_plugins(( + DefaultPlugins, + SaveLoadPlugin { + save_path: "scenes".into(), // where do we save files to (under assets for now) defaults to "scenes" + component_filter: SceneFilter::Allowlist(HashSet::from([ // this is using Bevy's build in SceneFilter, you can compose what components you want to allow/deny + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + // and any other commponent you want to include/exclude + ])), + resource_filter: SceneFilter::deny_all(), // same logic as above, but for resources : also be careful & remember to register your resources ! + ..Default::default() + }, + // you need to configure the blueprints plugin as well (might be pre_configured in the future, but for now you need to do it manually) + BlueprintsPlugin { + library_folder: "models/library".into(), + format: GltfFormat::GLB, + aabbs: true, + ..Default::default() + }, + )) + .run(); +} + +``` +### How to make sure your entites will be saved + +- only entites that have a **Dynamic** component will be saved ! (the component is provided as part of the crate) +- you can either add that component at runtime or have it baked-in in the Blueprint + +### Component Filter: + +- by default only the following components are going to be saved + - **Parent** + - **Children** + - **BlueprintName** : part of bevy_gltf_blueprints, used under the hood + - **SpawnHere** :part of bevy_gltf_blueprints, used under the hood + - **Dynamic** : included in this crate, allows you to tag components as dynamic aka saveable ! Use this to make sure your entities are saved ! + +- you **CANNOT** remove these as they are part of the boilerplate +- you **CAN** add however many other components you want, allow them all etc as you see fit +- you can find more information about the SceneFilter object [here](https://bevyengine.org/news/bevy-0-11/#scene-filtering) and [here](https://docs.rs/bevy/latest/bevy/scene/enum.SceneFilter.html) + + +## Events + + +- to trigger **saving** use the ```SaveRequest``` event +```rust no_run +// add a system to trigger saving +pub fn request_save( + mut save_requests: EventWriter, + keycode: Res>, +) +{ + if keycode.just_pressed(KeyCode::S) { + save_requests.send(SaveRequest { + path: "save.scn.ron".into(), + }) + } +} + +``` + + +- to trigger **loading** use the ```LoadRequest``` event + +```rust no_run +// add a system to trigger saving +pub fn request_load( + mut load_requests: EventWriter, + keycode: Res>, +) +{ + if keycode.just_pressed(KeyCode::L) { + save_requests.send(LoadRequest { + path: "save.scn.ron".into(), + }) + } +} +``` + +- you also notified when saving / loading is done + - ```SavingFinished``` for saving + - ```LoadingFinished``` for loading + +> Note: I **highly** recomend you change states when you start/finish saving & loading, otherwise things **will** get unpredictable +Please see [the example]('https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_save_load/basic/src/game/mod.rs#77') for this. + +## Additional notes + +- the name + path of the **static** level blueprint/gltf file will be saved as part of the save file, and reused to dynamically +load the correct static assets, which is necessary when you have multiple levels, and thus all required information to reload a save is contained within the save + +## SystemSet + +For convenience ```bevy_gltf_save_load``` provides two **SystemSets** + - [```LoadingSet```](./src/lib.rs#19) + - [```SavingSet```](./src/lib.rs#24) + + +## Examples + +Highly advised to get a better understanding of how things work ! +To get started I recomend looking at + +- [world setup]('https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_save_load/basic/src/game/in_game.rs#13') +- [various events & co]('https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_save_load/basic/src/game/mod.rs#77') + + +All examples are here: + +- https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_save_load/basic + + +## Compatible Bevy versions + +The main branch is compatible with the latest Bevy release, while the branch `bevy_main` tries to track the `main` branch of Bevy (PRs updating the tracked commit are welcome). + +Compatibility of `bevy_gltf_save_load` versions: +| `bevy_gltf_save_load` | `bevy` | +| :-- | :-- | +| `0.1 ` | `0.12` | +| branch `main` | `0.12` | +| branch `bevy_main` | `main` | + + +## License + +This crate, all its code, contents & assets is Dual-licensed under either of + +- Apache License, Version 2.0, ([LICENSE-APACHE](./LICENSE_APACHE.md) or https://www.apache.org/licenses/LICENSE-2.0) +- MIT license ([LICENSE-MIT](./LICENSE_MIT.md) or https://opensource.org/licenses/MIT) \ No newline at end of file diff --git a/crates/bevy_gltf_save_load/src/gltf_out_test.rs b/crates/bevy_gltf_save_load/src/gltf_out_test.rs new file mode 100644 index 00000000..e47b1faa --- /dev/null +++ b/crates/bevy_gltf_save_load/src/gltf_out_test.rs @@ -0,0 +1,216 @@ +use gltf_json as json; +use json::camera::Type; +use json::validation::{Checked, Validate}; +use serde_json::value::{to_raw_value, RawValue}; +use serde::Serialize; +use bevy::reflect::TypeRegistryArc; + + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +enum Output { + /// Output standard glTF. + Standard, + + /// Output binary glTF. + Binary, +} +#[derive(Serialize)] +struct MyExtraData { + a: u32, + b: u32, + BlueprintName: String, + SpawnHere: String, +} + +/* +pub fn serialize_gltf_inner(serialize: S) -> Result +where + S: Serialize, +{ + let pretty_config = ron::ser::PrettyConfig::default() + .indentor(" ".to_string()) + .new_line("\n".to_string()); + ron::ser::to_string_pretty(&serialize, pretty_config) +}*/ + +pub fn serialize_gltf(scene:&DynamicScene, registry: &TypeRegistryArc) { + +} + +pub fn save_game( + world: &mut World, +) { + + let mut save_path:String = "".into(); + let mut events = world + .resource_mut::>(); + for event in events.get_reader().read(&events) { + info!("SAVE EVENT !! {:?}", event); + save_path = event.path.clone(); + } + info!("SAVING TO {}", save_path); + events.clear(); + + let saveable_entities: Vec = world + .query_filtered::>() + .iter(world) + .collect(); + + debug!("saveable entities {}", saveable_entities.len()); + + let components = HashSet::from([ + TypeId::of::(), + TypeId::of::(), + TypeId::of::() , + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + + + + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + + TypeId::of::(), + + + + ]); + + let filter = SceneFilter::Allowlist(components); + + + let mut scene_builder = DynamicSceneBuilder::from_world(world).with_filter(filter); + + let dyn_scene = scene_builder + + + /* .allow::() + .allow::() + .allow::()*/ + + /* .deny::() + .deny::() + .deny::() + .deny::() + .deny::() + .deny::() + .deny::() + .deny::() + .deny::() + // camera stuff + .deny::() + .deny::() + .deny::() + .deny::() + .deny::() + .deny::() + //.deny::() + */ + .extract_entities(saveable_entities.into_iter()) + .build(); + + let serialized_scene = dyn_scene + .serialize_ron(world.resource::()) + .unwrap(); + + let mut root = gltf_json::Root::default(); + + // unfortunatly, not available yet + /*let node = root.push(json::Node { + //mesh: Some(mesh), + ..Default::default() + }); + + root.push(json::Scene { + extensions: Default::default(), + extras: Default::default(), + name: None, + nodes: vec![node], + });*/ + + + + + + let camera = json::camera::Perspective{ + aspect_ratio: Some(0.5), + yfov: 32.0, + zfar: Some(30.), + znear: 0.0, + extensions: None, + extras: None + }; + /*let camera = json::Camera{ + name:Some("Camera".into()), + orthographic: None, + perspective:None, + extensions: None, + extras: None, + type_: Checked, + };*/ + let gna = to_raw_value(&MyExtraData { a: 1, b: 2, BlueprintName: "Foo".into(), SpawnHere:"".into() }).unwrap() ; + let node = json::Node { + camera: None,//Some(camera), + children: None, + extensions: None, + extras: Some(gna), + matrix: None, + mesh:None, + name: Some("yeah".into()), + rotation: None, + scale: None, + translation: Some([0.5, 10.0 ,-100.]), + skin: None, + weights: None + // mesh: Some(json::Index::new(0)), + //..Default::default() + }; + + let root = json::Root { + accessors: vec![], //[positions, colors], + buffers: vec![], + buffer_views: vec![], + meshes: vec![], + nodes: vec![node], + scenes: vec![json::Scene { + extensions: Default::default(), + extras: Default::default(), + name: Some("Foo".to_string()), + nodes: vec![json::Index::new(0)], + }], + ..Default::default() + }; + + + + + let gltf_save_name = "test.gltf"; + let writer = fs::File::create(format!("assets/scenes/{gltf_save_name}") ).expect("I/O error"); + json::serialize::to_writer_pretty(writer, &root).expect("Serialization error"); + + // let bin = to_padded_byte_vector(triangle_vertices); + // let mut writer = fs::File::create("triangle/buffer0.bin").expect("I/O error"); + // writer.write_all(&bin).expect("I/O error"); + + + #[cfg(not(target_arch = "wasm32"))] + IoTaskPool::get() + .spawn(async move { + // Write the scene RON data to file + File::create(format!("assets/scenes/{save_path}")) + .and_then(|mut file| file.write(serialized_scene.as_bytes())) + .expect("Error while writing scene to file"); + + + + }) + .detach(); +} \ No newline at end of file diff --git a/crates/bevy_gltf_save_load/src/lib.rs b/crates/bevy_gltf_save_load/src/lib.rs new file mode 100644 index 00000000..00c45b9f --- /dev/null +++ b/crates/bevy_gltf_save_load/src/lib.rs @@ -0,0 +1,108 @@ +pub mod saveable; +use std::path::PathBuf; + +pub use saveable::*; + +pub mod saving; +pub use saving::*; + +pub mod loading; +pub use loading::*; + +use bevy::core_pipeline::core_3d::{Camera3dDepthTextureUsage, ScreenSpaceTransmissionQuality}; +use bevy::prelude::*; +use bevy::prelude::{App, IntoSystemConfigs, Plugin}; +use bevy_gltf_blueprints::GltfBlueprintsSet; + +#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)] +pub enum SavingSet { + Save, +} + +#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)] +pub enum LoadingSet { + Load, +} + +// Plugin configuration + +#[derive(Clone, Resource)] +pub struct SaveLoadConfig { + pub(crate) save_path: PathBuf, + pub(crate) component_filter: SceneFilter, + pub(crate) resource_filter: SceneFilter, +} + +// define the plugin + +pub struct SaveLoadPlugin { + pub component_filter: SceneFilter, + pub resource_filter: SceneFilter, + pub save_path: PathBuf, +} + +impl Default for SaveLoadPlugin { + fn default() -> Self { + Self { + component_filter: SceneFilter::default(), + resource_filter: SceneFilter::default(), + save_path: PathBuf::from("scenes"), + } + } +} + +#[derive(Component, Reflect, Debug, Default)] +#[reflect(Component)] +pub struct StaticEntitiesRoot; + +#[derive(Component, Reflect, Debug, Default)] +#[reflect(Component)] +pub struct DynamicEntitiesRoot; + +impl Plugin for SaveLoadPlugin { + fn build(&self, app: &mut App) { + app.register_type::() + .register_type::() + // TODO: remove these in bevy 0.13, as these are now registered by default + .register_type::() + .register_type::() + .register_type::() + .add_event::() + .add_event::() + .add_event::() + .add_event::() + .insert_resource(SaveLoadConfig { + save_path: self.save_path.clone(), + + component_filter: self.component_filter.clone(), + resource_filter: self.resource_filter.clone(), + }) + .configure_sets( + Update, + (LoadingSet::Load).chain().before(GltfBlueprintsSet::Spawn), //.before(GltfComponentsSet::Injection) + ) + .add_systems( + PreUpdate, + (prepare_save_game, apply_deferred, save_game, cleanup_save) + .chain() + .run_if(should_save), + ) + .add_systems(Update, mark_load_requested) + .add_systems( + Update, + (unload_world, apply_deferred, load_game) + .chain() + .run_if(resource_exists::()) + .run_if(not(resource_exists::())) + .in_set(LoadingSet::Load), + ) + .add_systems( + Update, + (load_static, apply_deferred, cleanup_loaded_scene) + .chain() + .run_if(resource_exists::()) + // .run_if(in_state(AppState::LoadingGame)) + .in_set(LoadingSet::Load), + ); + } +} diff --git a/crates/bevy_gltf_save_load/src/loading.rs b/crates/bevy_gltf_save_load/src/loading.rs new file mode 100644 index 00000000..9342ecf2 --- /dev/null +++ b/crates/bevy_gltf_save_load/src/loading.rs @@ -0,0 +1,156 @@ +use bevy::{prelude::*, scene::SceneInstance}; +use bevy_gltf_blueprints::{BluePrintBundle, BlueprintName, GameWorldTag, Library}; +use std::path::Path; + +use crate::{DynamicEntitiesRoot, SaveLoadConfig, StaticEntitiesRoot, StaticEntitiesStorage}; + +#[derive(Event)] +pub struct LoadRequest { + pub path: String, +} + +#[derive(Event)] +pub struct LoadingFinished; + +#[derive(Resource, Default)] +pub struct LoadRequested { + pub path: String, +} + +#[derive(Resource, Default)] +pub(crate) struct LoadFirstStageDone; + +#[derive(Component, Reflect, Debug, Default)] +#[reflect(Component)] +pub(crate) struct CleanupScene; + +/// helper system that "converts" loadRequest events to LoadRequested resources +pub(crate) fn mark_load_requested( + mut load_requests: EventReader, + mut commands: Commands, +) { + let mut save_path: String = "".into(); + for load_request in load_requests.read() { + if load_request.path != "" { + save_path = load_request.path.clone(); + } + } + if save_path != "" { + commands.insert_resource(LoadRequested { path: save_path }); + } +} + +// TODO: replace with generic despawner ? +pub(crate) fn unload_world(mut commands: Commands, gameworlds: Query>) { + for e in gameworlds.iter() { + info!("--loading: despawn old world/level"); + commands.entity(e).despawn_recursive(); + } +} + +pub(crate) fn load_game( + mut commands: Commands, + asset_server: Res, + load_request: Res, + save_load_config: Res, +) { + info!("--loading: load dynamic data"); + let save_path = load_request.path.clone(); + let save_path = Path::new(&save_load_config.save_path).join(Path::new(save_path.as_str())); + + info!("LOADING FROM {:?}", save_path); + + let world_root = commands + .spawn(( + bevy::prelude::Name::from("world"), + GameWorldTag, + TransformBundle::default(), + InheritedVisibility::default(), + )) + .id(); + + // and we fill it with dynamic data + // let input = std::fs::read(&path)?; + let dynamic_data = commands + .spawn(( + DynamicSceneBundle { + scene: asset_server.load(save_path), + ..default() + }, + bevy::prelude::Name::from("dynamic"), + DynamicEntitiesRoot, + )) + .id(); + + // commands.entity(world_root).add_child(static_data); + commands.entity(world_root).add_child(dynamic_data); + + commands.insert_resource(LoadFirstStageDone); + + info!("--loading: loaded dynamic data"); +} + +pub(crate) fn load_static( + dynamic_worlds: Query>, + world_root: Query>, + mut commands: Commands, + mut loading_finished: EventWriter, + + static_entities: Option>, +) { + if let Some(info) = static_entities { + info!("--loading static data {:?}", info.name); + let static_data = commands + .spawn(( + Name::from("static"), + BluePrintBundle { + blueprint: BlueprintName(info.name.clone()), + ..Default::default() + }, + StaticEntitiesRoot, + )) + .id(); + + if info.library_path != "" { + commands + .entity(static_data) + .insert(Library(info.library_path.clone().into())); + } + + let world_root = world_root.get_single().unwrap(); + commands.entity(world_root).add_child(static_data); + + info!("--loading: loaded static data"); + for entity in dynamic_worlds.iter() { + commands.entity(entity).insert( + CleanupScene, // we mark this scene as needing a cleanup + ); + } + + loading_finished.send(LoadingFinished); + } +} + +pub(crate) fn cleanup_loaded_scene( + loaded_scenes: Query< + Entity, + ( + Added, + With, + With, + ), + >, + mut commands: Commands, +) { + for loaded_scene in loaded_scenes.iter() { + info!("REMOVING DynamicScene"); + commands + .entity(loaded_scene) + .remove::>() + .remove::() + .remove::(); + + commands.remove_resource::(); + commands.remove_resource::(); + } +} diff --git a/crates/bevy_gltf_save_load/src/saveable.rs b/crates/bevy_gltf_save_load/src/saveable.rs new file mode 100644 index 00000000..b2a40321 --- /dev/null +++ b/crates/bevy_gltf_save_load/src/saveable.rs @@ -0,0 +1,6 @@ +use bevy::prelude::*; + +#[derive(Component, Reflect, Debug, Default)] +#[reflect(Component)] +/// component used to mark any entity as Dynamic: aka add this to make sure your entity is going to be saved +pub struct Dynamic(pub bool); diff --git a/crates/bevy_gltf_save_load/src/saving.rs b/crates/bevy_gltf_save_load/src/saving.rs new file mode 100644 index 00000000..51aeb0c1 --- /dev/null +++ b/crates/bevy_gltf_save_load/src/saving.rs @@ -0,0 +1,192 @@ +use bevy::prelude::*; +use bevy::tasks::IoTaskPool; +use bevy_gltf_blueprints::{BlueprintName, InBlueprint, Library, SpawnHere}; + +use std::fs::File; +use std::io::Write; +use std::path::Path; + +use crate::{Dynamic, DynamicEntitiesRoot, SaveLoadConfig, StaticEntitiesRoot}; + +#[derive(Event, Debug)] +pub struct SaveRequest { + pub path: String, +} + +#[derive(Event)] +pub struct SavingFinished; + +pub fn should_save(save_requests: EventReader) -> bool { + return save_requests.len() > 0; +} + +#[derive(Resource, Clone, Debug, Default, Reflect)] +#[reflect(Resource)] +pub struct StaticEntitiesStorage { + pub name: String, + pub library_path: String, +} + +#[derive(Component, Reflect, Debug, Default)] +#[reflect(Component)] +/// marker component for entities that do not have parents, or whose parents should be ignored when serializing +pub(crate) struct RootEntity; + +#[derive(Component, Debug)] +/// internal helper component to store parents before resetting them +pub(crate) struct OriginalParent(pub(crate) Entity); + +// any child of dynamic/ saveable entities that is not saveable itself should be removed from the list of children +pub(crate) fn prepare_save_game( + saveables: Query, With)>, + root_entities: Query, Without)>>, // With + dynamic_entities: Query<(Entity, &Parent, Option<&Children>), With>, + static_entities: Query<(Entity, &BlueprintName, Option<&Library>), With>, + + mut commands: Commands, +) { + for entity in saveables.iter() { + commands.entity(entity).insert(SpawnHere); + } + + for (entity, parent, children) in dynamic_entities.iter() { + let parent = parent.get(); + if root_entities.contains(parent) { + commands.entity(entity).insert(RootEntity); + } + + if let Some(children) = children { + for sub_child in children.iter() { + if !dynamic_entities.contains(*sub_child) { + commands.entity(*sub_child).insert(OriginalParent(entity)); + commands.entity(entity).remove_children(&[*sub_child]); + } + } + } + } + for (_, blueprint_name, library) in static_entities.iter() { + let library_path: String = library + .map_or_else(|| "", |l| &l.0.to_str().unwrap()) + .into(); + commands.insert_resource(StaticEntitiesStorage { + name: blueprint_name.0.clone(), + library_path, + }); + } +} +pub(crate) fn save_game(world: &mut World) { + info!("saving"); + + let mut save_path: String = "".into(); + let mut events = world.resource_mut::>(); + + for event in events.get_reader().read(&events) { + info!("SAVE EVENT !! {:?}", event); + save_path = event.path.clone(); + } + events.clear(); + + let saveable_entities: Vec = world + .query_filtered::, Without, Without)>() + .iter(world) + .collect(); + + let saveable_root_entities: Vec = world + .query_filtered::, Without, With)>() + .iter(world) + .collect(); + + info!("saveable entities {}", saveable_entities.len()); + info!("saveable root entities {}", saveable_root_entities.len()); + + let save_load_config = world + .get_resource::() + .expect("SaveLoadConfig should exist at this stage"); + + // we hardcode some of the always allowed types + let filter = save_load_config + .component_filter + .clone() + .allow::() + .allow::() + .allow::() + .allow::() + .allow::(); + + // for root entities, it is the same EXCEPT we make sure parents are not included + let filter_root = filter.clone().deny::(); + + let filter_resources = save_load_config + .resource_filter + .clone() + .allow::(); + + // for default stuff + let scene_builder = DynamicSceneBuilder::from_world(world) + .with_filter(filter.clone()) + .with_resource_filter(filter_resources.clone()); + + let mut dyn_scene = scene_builder + .extract_resources() + .extract_entities(saveable_entities.clone().into_iter()) + .remove_empty_entities() + .build(); + + // for root entities + let scene_builder_root = DynamicSceneBuilder::from_world(world) + .with_filter(filter_root.clone()) + .with_resource_filter(filter_resources.clone()); + + let mut dyn_scene_root = scene_builder_root + .extract_resources() + .extract_entities( + saveable_root_entities.clone().into_iter(), // .chain(static_world_markers.into_iter()), + ) + .remove_empty_entities() + .build(); + + dyn_scene.entities.append(&mut dyn_scene_root.entities); + // dyn_scene.resources.append(&mut dyn_scene_root.resources); + + let serialized_scene = dyn_scene + .serialize_ron(world.resource::()) + .unwrap(); + + let save_path = Path::new("assets") + .join(&save_load_config.save_path) + .join(Path::new(save_path.as_str())); // Path::new(&save_load_config.save_path).join(Path::new(save_path.as_str())); + info!("saving game to {:?}", save_path); + + // world.send_event(SavingFinished); + + #[cfg(not(target_arch = "wasm32"))] + IoTaskPool::get() + .spawn(async move { + // Write the scene RON data to file + File::create(save_path) + .and_then(|mut file| file.write(serialized_scene.as_bytes())) + .expect("Error while writing save to file"); + }) + .detach(); +} + +pub(crate) fn cleanup_save( + needs_parent_reset: Query<(Entity, &OriginalParent)>, + mut saving_finished: EventWriter, + mut commands: Commands, +) { + for (entity, original_parent) in needs_parent_reset.iter() { + commands.entity(original_parent.0).add_child(entity); + } + commands.remove_resource::(); + saving_finished.send(SavingFinished); +} +/* +pub(crate) fn cleanup_save(mut world: &mut World) { + + let mut query = world.query::<(Entity, &OriginalParent)>(); + for (mut entity, original_parent) in query.iter_mut(&mut world) { + let e = world.entity_mut(original_parent.0); + // .add_child(entity); + } +}*/ diff --git a/examples/bevy_gltf_blueprints/animation/src/game/in_game.rs b/examples/bevy_gltf_blueprints/animation/src/game/in_game.rs index 19c3f169..f29efcf8 100644 --- a/examples/bevy_gltf_blueprints/animation/src/game/in_game.rs +++ b/examples/bevy_gltf_blueprints/animation/src/game/in_game.rs @@ -72,13 +72,12 @@ pub fn spawn_test( .spawn(( BluePrintBundle { blueprint: BlueprintName("Fox".to_string()), - transform: TransformBundle::from_transform(Transform::from_xyz(x, 0.0, y)), ..Default::default() }, bevy::prelude::Name::from(format!("Spawned{}", name_index)), // BlueprintName("Health_Pickup".to_string()), // SpawnHere, - // TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), + TransformBundle::from_transform(Transform::from_xyz(x, 0.0, y)), Velocity { linvel: Vec3::new(vel_x, vel_y, vel_z), angvel: Vec3::new(0.0, 0.0, 0.0), diff --git a/examples/bevy_gltf_blueprints/basic/src/core/mod.rs b/examples/bevy_gltf_blueprints/basic/src/core/mod.rs index f9145633..9c38fa77 100644 --- a/examples/bevy_gltf_blueprints/basic/src/core/mod.rs +++ b/examples/bevy_gltf_blueprints/basic/src/core/mod.rs @@ -10,9 +10,6 @@ pub use relationships::*; pub mod physics; pub use physics::*; -// pub mod save_load; -// pub use save_load::*; - use bevy::prelude::*; use bevy_gltf_blueprints::*; @@ -23,7 +20,6 @@ impl Plugin for CorePlugin { LightingPlugin, CameraPlugin, PhysicsPlugin, - // SaveLoadPlugin, BlueprintsPlugin { library_folder: "models/library".into(), format: GltfFormat::GLB, diff --git a/examples/bevy_gltf_blueprints/basic/src/core/save_load/loading.rs b/examples/bevy_gltf_blueprints/basic/src/core/save_load/loading.rs deleted file mode 100644 index 73ef523d..00000000 --- a/examples/bevy_gltf_blueprints/basic/src/core/save_load/loading.rs +++ /dev/null @@ -1,218 +0,0 @@ -use bevy::prelude::*; -use bevy_gltf_blueprints::{clone_entity::CloneEntity, GameWorldTag, SpawnHere}; - -use crate::{ - assets::GameAssets, - state::{AppState, GameState, InAppRunning}, -}; - -use super::Saveable; - -const SCENE_FILE_PATH: &str = "scenes/save.scn.ron"; - -#[derive(Component, Debug)] -pub struct TempLoadedSceneMarker; - -#[derive(Component, Debug)] -pub struct SaveablesToRemove(Vec<(Entity, Name)>); - -#[derive(Component, Event)] -pub struct LoadRequest { - pub path: String, -} - -pub fn should_load(save_requested_events: EventReader) -> bool { - return save_requested_events.len() > 0; -} - -pub fn load_prepare( - mut next_app_state: ResMut>, - mut next_game_state: ResMut>, -) { - next_app_state.set(AppState::LoadingGame); - next_game_state.set(GameState::None); - info!("--loading: prepare") -} - -/// unload the level recursively -pub fn _unload_world_old(world: &mut World) { - let entities: Vec = world - // .query_filtered::, With)>>() - .query_filtered::>() // our level/world contains this component - .iter(world) - .collect(); - for entity in entities { - // Check the entity again in case it was despawned recursively - if world.get_entity(entity).is_some() { - world.entity_mut(entity).despawn_recursive(); - } - } -} - -pub fn unload_world(mut commands: Commands, gameworlds: Query>) { - for e in gameworlds.iter() { - info!("--loading: despawn old world/level"); - commands.entity(e).despawn_recursive(); - } -} - -// almost identical to setup_game, !!?? -pub fn load_world( - mut commands: Commands, - game_assets: Res, - // scenes: ResMut, -) { - info!("--loading: loading world/level"); - - commands.spawn(( - SceneBundle { - scene: game_assets.world.clone(), - ..default() - }, - bevy::prelude::Name::from("world"), - GameWorldTag, - InAppRunning, - )); -} - -pub fn load_saved_scene(mut commands: Commands, asset_server: Res) { - commands.spawn(( - DynamicSceneBundle { - // Scenes are loaded just like any other asset. - scene: asset_server.load(SCENE_FILE_PATH), - ..default() - }, - TempLoadedSceneMarker, - )); - // commands.entity(world).add_child(child_scene); - info!("--loading: loaded saved scene"); -} - -pub fn process_loaded_scene( - loaded_scene: Query<(Entity, &Children), With>, - named_entities: Query<(Entity, &Name, &Parent)>, // FIXME: very inneficient - mut commands: Commands, - - mut game_world: Query<(Entity, &Children), With>, - saveables: Query<(Entity, &Name), With>, - asset_server: Res, -) { - for (loaded_scene, children) in loaded_scene.iter() { - info!("--loading: post processing loaded scene"); - - let mut entities_to_load: Vec<(Entity, Name)> = vec![]; - - for loaded_entity in children.iter() { - if let Ok((source, name, _)) = named_entities.get(*loaded_entity) { - entities_to_load.push((source, name.clone())); - - let mut found = false; - for (e, n, p) in named_entities.iter() { - // if we have an entity with the same name as in same file, overwrite - if e != source && name.as_str() == n.as_str() { - // println!("found entity with same name {} {} {:?} {:?}", name, n, source, e); - // source is entity within the newly loaded scene (source), e is within the existing world (destination) - info!("copying data from {:?} to {:?}", source, e); - commands.add(CloneEntity { - source: source, - destination: e, - }); - // FIXME: issue with hierarchy & parenting, would be nice to be able to filter out components from CloneEntity - commands.entity(p.get()).add_child(e); - commands.entity(source).despawn_recursive(); - found = true; - break; - } - } - // entity not found in the list of existing entities (ie entities that came as part of the level) - // so we spawn a new one - if !found { - info!("generating new entity"); - let world = game_world.single_mut(); - let world = world.1[0]; - - let new_entity = commands - .spawn((bevy::prelude::Name::from(name.clone()), SpawnHere)) - .id(); - - commands.add(CloneEntity { - source: source, - destination: new_entity, - }); - - commands.entity(world).add_child(new_entity); - info!("copying data from {:?} to {:?}", source, new_entity); - } - } - } - commands.spawn(SaveablesToRemove(entities_to_load.clone())); - - // if an entity is present in the world but NOT in the saved entities , it should be removed from the world - // ideally this should be run between spawning of the world/level AND spawn_placeholders - - // remove the dynamic scene - info!("--loading: DESPAWNING LOADED SCENE"); - commands.entity(loaded_scene).despawn_recursive(); - - asset_server.mark_unused_assets(); - asset_server.free_unused_assets(); - } - //for saveable in saveables.iter(){ - // println!("SAVEABLE BEFORE {:?}", saveable) - //} -} - -pub fn final_cleanup( - saveables_to_remove: Query<(Entity, &SaveablesToRemove)>, - mut commands: Commands, - saveables: Query<(Entity, &Name), With>, - mut next_app_state: ResMut>, - mut next_game_state: ResMut>, -) { - if let Ok((e, entities_to_load)) = saveables_to_remove.get_single() { - info!("saveables to remove {:?}", entities_to_load); - for (e, n) in saveables.iter() { - let mut found = false; - println!("SAVEABLE {}", n); - - //let entities_to_load = saveables_to_remove.single(); - for (en, na) in entities_to_load.0.iter() { - found = na.as_str() == n.as_str(); - if found { - break; - } - } - if !found { - println!("REMOVING THIS ONE {}", n); - commands.entity(e).despawn_recursive(); - } - } - // if there is a saveable that is NOT in the list of entities to load, despawn it - - // despawn list - commands.entity(e).despawn_recursive(); - - info!("--loading: done, move to InGame state"); - // next_app_state.set(AppState::AppRunning); - next_game_state.set(GameState::InGame); - } -} - -fn process_loaded_scene_load_alt( - entities: Query<(Entity, &Children), With>, - named_entities: Query<(Entity, &Name, &Parent)>, // FIXME: very inneficient - mut commands: Commands, -) { - for (entity, children) in entities.iter() { - let mut entities_to_load: Vec<(Entity, Name)> = vec![]; - for saved_source in children.iter() { - if let Ok((source, name, _)) = named_entities.get(*saved_source) { - println!("AAAAAAA {}", name); - entities_to_load.push((source, name.clone())); - } - } - println!("entities to load {:?}", entities_to_load); - - commands.entity(entity).despawn_recursive(); - } -} diff --git a/examples/bevy_gltf_blueprints/basic/src/core/save_load/mod.rs b/examples/bevy_gltf_blueprints/basic/src/core/save_load/mod.rs deleted file mode 100644 index 3d0e91ed..00000000 --- a/examples/bevy_gltf_blueprints/basic/src/core/save_load/mod.rs +++ /dev/null @@ -1,70 +0,0 @@ -pub mod saveable; -use bevy::asset::free_unused_assets_system; -use bevy_gltf_components::GltfComponentsSet; -pub use saveable::*; - -pub mod saving; -pub use saving::*; - -pub mod loading; -pub use loading::*; - -use bevy::prelude::*; -use bevy::prelude::{App, IntoSystemConfigs, Plugin}; -use bevy::utils::Uuid; - -use bevy_gltf_blueprints::GltfBlueprintsSet; - -#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)] -pub enum LoadingSet { - Load, - PostLoad, -} - -pub struct SaveLoadPlugin; -impl Plugin for SaveLoadPlugin { - fn build(&self, app: &mut App) { - app - .register_type::() - .register_type::() - .add_event::() - .add_event::() - - .configure_sets( - Update, - (LoadingSet::Load, LoadingSet::PostLoad) - .chain() - .before(GltfBlueprintsSet::Spawn) - .before(GltfComponentsSet::Injection) - ) - - .add_systems(PreUpdate, save_game.run_if(should_save)) - - .add_systems(Update, - ( - load_prepare, - unload_world, - load_world, - load_saved_scene, - // process_loaded_scene - ) - .chain() - .run_if(should_load) // .run_if(in_state(AppState::AppRunning)) - .in_set(LoadingSet::Load) - ) - .add_systems(Update, - ( - process_loaded_scene, - apply_deferred, - final_cleanup, - apply_deferred, - free_unused_assets_system - ) - .chain() - .in_set(LoadingSet::PostLoad) - ) - - // .add_systems(Update, bla) - ; - } -} diff --git a/examples/bevy_gltf_blueprints/basic/src/core/save_load/old.rs b/examples/bevy_gltf_blueprints/basic/src/core/save_load/old.rs deleted file mode 100644 index 7d8a3899..00000000 --- a/examples/bevy_gltf_blueprints/basic/src/core/save_load/old.rs +++ /dev/null @@ -1,137 +0,0 @@ -const NEW_SCENE_FILE_PATH:&str="save.scn.ron"; - - - - -use bevy::ecs::component::Components; -use bevy::ecs::entity::EntityMap; -use serde::{Deserialize, Serialize}; - - -use std::io::Read; -use bevy::scene::serde::SceneDeserializer; -use ron::Deserializer; -use serde::de::DeserializeSeed; - - - - -#[derive(Debug, Deserialize)] -struct Components2; - -#[derive(Debug, Deserialize)] -struct Fake { - resources: HashMap, - entities: HashMap -} - -fn ron_test(){ - let full_path = "/home/ckaos/projects/grappling-boom-bot/assets/save.ron"; - match File::open(full_path) { - Ok(mut file) => { - let mut serialized_scene = Vec::new(); - if let Err(why) = file.read_to_end(&mut serialized_scene) { - error!("file read failed: {why:?}"); - } - match Deserializer::from_bytes(&serialized_scene) { - Ok(mut deserializer) => { - // deserializer. - let bla:Fake = ron::from_str("( - resources: {}, - entities: {} - )").unwrap(); - info!("testing {:?}", bla); - info!("YOYO DONE YO !") - } - Err(why) => { - error!("deserializer creation failed: {why:?}"); - } - } - } - Err(why) => { - error!("load failed: {why:?}"); - } - } -} - -fn inject_component_data(world: &mut World, scene: DynamicScene){ - let mut entity_map = EntityMap::default(); - if let Err(why) = scene.write_to_world(world, &mut entity_map) { - panic!("world write failed: {why:?}"); - } - println!("entity map {:?}", entity_map); - // TODO: EntityMap doesn't implement `iter()` - for old_entity in entity_map.keys() { - let entity = entity_map.get(old_entity).unwrap(); - info!("entity update required: {old_entity:?} -> {entity:?}"); - let e_mut = world - .entity_mut(entity); - } - - info!("done loading scene"); -} - -fn post_load(world: &mut World){ - let full_path = "/home/ckaos/projects/grappling-boom-bot/assets/save.ron"; - match File::open(full_path) { - Ok(mut file) => { - let mut serialized_scene = Vec::new(); - if let Err(why) = file.read_to_end(&mut serialized_scene) { - error!("file read failed: {why:?}"); - } - match Deserializer::from_bytes(&serialized_scene) { - Ok(mut deserializer) => { - let result = SceneDeserializer { - type_registry: &world.resource::().read(), - } - .deserialize(&mut deserializer); - info!("deserialize done"); - match result { - Ok(scene) => { - info!("scene loaded"); - // scene.write_to_world(world, entity_map) - // println!("{:?}", scene.entities); - inject_component_data(world, scene); - /*for dyn_ent in scene.entities.iter(){ - // let mut query = scene.world.query::<(Entity, &Name, &GltfExtras, &Parent)>(); - }*/ - } - Err(why) => { - error!("deserialization failed: {why:?}"); - } - } - } - Err(why) => { - error!("deserializer creation failed: {why:?}"); - } - } - } - Err(why) => { - error!("load failed: {why:?}"); - } - } - -} - - - -#[derive(Component, Reflect, Debug, Default )] -#[reflect(Component)] -pub struct Hackish; - - - -/// unload saveables -fn unload_saveables(world: &mut World) { - let entities: Vec = world - .query_filtered::>()// our level/world contains this component - .iter(world) - .collect(); - for entity in entities { - // Check the entity again in case it was despawned recursively - if world.get_entity(entity).is_some() { - info!("despawning"); - world.entity_mut(entity).despawn_recursive(); - } - } -} \ No newline at end of file diff --git a/examples/bevy_gltf_blueprints/basic/src/core/save_load/saveable.rs b/examples/bevy_gltf_blueprints/basic/src/core/save_load/saveable.rs deleted file mode 100644 index 67a4c657..00000000 --- a/examples/bevy_gltf_blueprints/basic/src/core/save_load/saveable.rs +++ /dev/null @@ -1,14 +0,0 @@ -use bevy::prelude::*; -use bevy::utils::Uuid; - -#[derive(Component, Reflect, Debug)] -#[reflect(Component)] -pub struct Saveable { - id: Uuid, -} - -impl Default for Saveable { - fn default() -> Self { - Saveable { id: Uuid::new_v4() } - } -} diff --git a/examples/bevy_gltf_blueprints/basic/src/core/save_load/saving.rs b/examples/bevy_gltf_blueprints/basic/src/core/save_load/saving.rs deleted file mode 100644 index 46d0b1af..00000000 --- a/examples/bevy_gltf_blueprints/basic/src/core/save_load/saving.rs +++ /dev/null @@ -1,87 +0,0 @@ -use bevy::pbr::{Clusters, VisiblePointLights}; -use bevy::render::camera::CameraRenderGraph; -use bevy::render::view::VisibleEntities; -use bevy::tasks::IoTaskPool; -use bevy::{gltf::GltfExtras, prelude::*}; -use bevy_rapier3d::prelude::RigidBody; -use std::fs::File; -use std::io::Write; - -use crate::core::physics::Collider; -use crate::game::{Pickable, Player}; - -use super::Saveable; - -const NEW_SCENE_FILE_PATH: &str = "save.scn.ron"; - -#[derive(Component, Event)] -pub struct SaveRequest { - pub path: String, -} - -pub fn should_save( - // keycode: Res>, - save_requested_events: EventReader, -) -> bool { - return save_requested_events.len() > 0; - - // return keycode.just_pressed(KeyCode::S) -} - -pub fn save_game( - world: &mut World, - // save_requested_events: EventReader, -) { - info!("saving"); - // world. - /*for bli in save_requested_events.iter(){ - println!("SAAAAVE TO THISSSSS {:?}", bli.path) - }*/ - - let saveable_entities: Vec = world - .query_filtered::>() - .iter(world) - .collect(); - - /*let static_entities: Vec = world - .query_filtered::>() - .iter(world) - .collect();*/ - println!("saveable entities {}", saveable_entities.len()); - - let mut scene_builder = DynamicSceneBuilder::from_world(world); - scene_builder - .deny::() - .deny::() - .deny::() - .deny::() - .deny::() - .deny::() - .deny::() - .deny::() - .deny::() - // camera stuff - .deny::() - .deny::() - .deny::() - .deny::() - .deny::() - .deny::() - //.deny::() - .extract_entities(saveable_entities.into_iter()); - - let dyn_scene = scene_builder.build(); - let serialized_scene = dyn_scene - .serialize_ron(world.resource::()) - .unwrap(); - - #[cfg(not(target_arch = "wasm32"))] - IoTaskPool::get() - .spawn(async move { - // Write the scene RON data to file - File::create(format!("assets/scenes/{NEW_SCENE_FILE_PATH}")) - .and_then(|mut file| file.write(serialized_scene.as_bytes())) - .expect("Error while writing scene to file"); - }) - .detach(); -} diff --git a/examples/bevy_gltf_blueprints/basic/src/game/in_game.rs b/examples/bevy_gltf_blueprints/basic/src/game/in_game.rs index 86fa8de0..2a077f7d 100644 --- a/examples/bevy_gltf_blueprints/basic/src/game/in_game.rs +++ b/examples/bevy_gltf_blueprints/basic/src/game/in_game.rs @@ -71,13 +71,12 @@ pub fn spawn_test( .spawn(( BluePrintBundle { blueprint: BlueprintName("Health_Pickup".to_string()), - transform: TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), ..Default::default() }, bevy::prelude::Name::from(format!("test{}", name_index)), // BlueprintName("Health_Pickup".to_string()), // SpawnHere, - // TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), + TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), Velocity { linvel: Vec3::new(vel_x, vel_y, vel_z), angvel: Vec3::new(0.0, 0.0, 0.0), @@ -88,7 +87,7 @@ pub fn spawn_test( } } -pub fn spawn_test_failing( +pub fn spawn_test_unregisted_components( keycode: Res>, mut commands: Commands, @@ -115,13 +114,12 @@ pub fn spawn_test_failing( .spawn(( BluePrintBundle { blueprint: BlueprintName("Health_Pickup".to_string()), - transform: TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), ..Default::default() }, bevy::prelude::Name::from(format!("test{}", name_index)), // BlueprintName("Health_Pickup".to_string()), // SpawnHere, - // TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), + TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), Velocity { linvel: Vec3::new(vel_x, vel_y, vel_z), angvel: Vec3::new(0.0, 0.0, 0.0), diff --git a/examples/bevy_gltf_blueprints/basic/src/game/mod.rs b/examples/bevy_gltf_blueprints/basic/src/game/mod.rs index 67716de8..00689c22 100644 --- a/examples/bevy_gltf_blueprints/basic/src/game/mod.rs +++ b/examples/bevy_gltf_blueprints/basic/src/game/mod.rs @@ -104,7 +104,7 @@ impl Plugin for GamePlugin { player_move_demo, //.run_if(in_state(AppState::Running)), // test_collision_events spawn_test, - spawn_test_failing, + spawn_test_unregisted_components, ) .run_if(in_state(GameState::InGame)), ) diff --git a/examples/bevy_gltf_blueprints/basic_scene_components/src/game/in_game.rs b/examples/bevy_gltf_blueprints/basic_scene_components/src/game/in_game.rs index f98f8c33..e8090faa 100644 --- a/examples/bevy_gltf_blueprints/basic_scene_components/src/game/in_game.rs +++ b/examples/bevy_gltf_blueprints/basic_scene_components/src/game/in_game.rs @@ -63,13 +63,12 @@ pub fn spawn_test( .spawn(( BluePrintBundle { blueprint: BlueprintName("Health_Pickup".to_string()), - transform: TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), ..Default::default() }, bevy::prelude::Name::from(format!("test{}", name_index)), // BlueprintName("Health_Pickup".to_string()), // SpawnHere, - // TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), + TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), Velocity { linvel: Vec3::new(vel_x, vel_y, vel_z), angvel: Vec3::new(0.0, 0.0, 0.0), diff --git a/examples/bevy_gltf_blueprints/basic_wasm/src/assets/assets_game.rs b/examples/bevy_gltf_blueprints/basic_wasm/src/assets/assets_game.rs index c3f93d7a..066f7db9 100644 --- a/examples/bevy_gltf_blueprints/basic_wasm/src/assets/assets_game.rs +++ b/examples/bevy_gltf_blueprints/basic_wasm/src/assets/assets_game.rs @@ -8,20 +8,22 @@ pub struct GameAssets { #[asset(key = "world")] pub world: Handle, - - - - #[asset(paths("models/library/Container.glb", "models/library/Health_Pickup.glb", "models/library/MagicTeapot.glb", - "models/library/Pillar.glb", - "models/library/Player.glb", - "models/library/Unused_in_level_test.glb"), collection(typed))] + #[asset( + paths( + "models/library/Container.glb", + "models/library/Health_Pickup.glb", + "models/library/MagicTeapot.glb", + "models/library/Pillar.glb", + "models/library/Player.glb", + "models/library/Unused_in_level_test.glb" + ), + collection(typed) + )] pub models: Vec>, - - /* #[asset(key = "models", collection(typed, mapped))] pub models: HashMap>, - + #[asset(key = "models", collection(typed))] pub models: Vec>,*/ } diff --git a/examples/bevy_gltf_blueprints/basic_wasm/src/game/in_game.rs b/examples/bevy_gltf_blueprints/basic_wasm/src/game/in_game.rs index 86fa8de0..f1861ce7 100644 --- a/examples/bevy_gltf_blueprints/basic_wasm/src/game/in_game.rs +++ b/examples/bevy_gltf_blueprints/basic_wasm/src/game/in_game.rs @@ -71,13 +71,12 @@ pub fn spawn_test( .spawn(( BluePrintBundle { blueprint: BlueprintName("Health_Pickup".to_string()), - transform: TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), ..Default::default() }, bevy::prelude::Name::from(format!("test{}", name_index)), // BlueprintName("Health_Pickup".to_string()), // SpawnHere, - // TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), + TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), Velocity { linvel: Vec3::new(vel_x, vel_y, vel_z), angvel: Vec3::new(0.0, 0.0, 0.0), @@ -88,7 +87,7 @@ pub fn spawn_test( } } -pub fn spawn_test_failing( +pub fn spawn_test_unregisted_components( keycode: Res>, mut commands: Commands, @@ -115,13 +114,10 @@ pub fn spawn_test_failing( .spawn(( BluePrintBundle { blueprint: BlueprintName("Health_Pickup".to_string()), - transform: TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), ..Default::default() }, bevy::prelude::Name::from(format!("test{}", name_index)), - // BlueprintName("Health_Pickup".to_string()), - // SpawnHere, - // TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), + TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), Velocity { linvel: Vec3::new(vel_x, vel_y, vel_z), angvel: Vec3::new(0.0, 0.0, 0.0), diff --git a/examples/bevy_gltf_blueprints/basic_wasm/src/game/mod.rs b/examples/bevy_gltf_blueprints/basic_wasm/src/game/mod.rs index 67716de8..00689c22 100644 --- a/examples/bevy_gltf_blueprints/basic_wasm/src/game/mod.rs +++ b/examples/bevy_gltf_blueprints/basic_wasm/src/game/mod.rs @@ -104,7 +104,7 @@ impl Plugin for GamePlugin { player_move_demo, //.run_if(in_state(AppState::Running)), // test_collision_events spawn_test, - spawn_test_failing, + spawn_test_unregisted_components, ) .run_if(in_state(GameState::InGame)), ) diff --git a/examples/bevy_gltf_blueprints/basic_xpbd_physics/src/game/in_game.rs b/examples/bevy_gltf_blueprints/basic_xpbd_physics/src/game/in_game.rs index f87bc127..352197ba 100644 --- a/examples/bevy_gltf_blueprints/basic_xpbd_physics/src/game/in_game.rs +++ b/examples/bevy_gltf_blueprints/basic_xpbd_physics/src/game/in_game.rs @@ -69,13 +69,12 @@ pub fn spawn_test( .spawn(( BluePrintBundle { blueprint: BlueprintName("Health_Pickup".to_string()), - transform: TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), ..Default::default() }, bevy::prelude::Name::from(format!("test{}", name_index)), // BlueprintName("Health_Pickup".to_string()), // SpawnHere, - // TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), + TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), LinearVelocity(Vec3::new(vel_x, vel_y, vel_z)), AngularVelocity::ZERO, )) diff --git a/examples/bevy_gltf_blueprints/materials/src/game/in_game.rs b/examples/bevy_gltf_blueprints/materials/src/game/in_game.rs index 276e9485..579f1938 100644 --- a/examples/bevy_gltf_blueprints/materials/src/game/in_game.rs +++ b/examples/bevy_gltf_blueprints/materials/src/game/in_game.rs @@ -72,13 +72,12 @@ pub fn spawn_test( .spawn(( BluePrintBundle { blueprint: BlueprintName("Watermelon2".to_string()), - transform: TransformBundle::from_transform(Transform::from_xyz(x, 3.0, y)), ..Default::default() }, bevy::prelude::Name::from(format!("Watermelon{}", name_index)), // BlueprintName("Health_Pickup".to_string()), // SpawnHere, - // TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), + TransformBundle::from_transform(Transform::from_xyz(x, 3.0, y)), Velocity { linvel: Vec3::new(vel_x, vel_y, vel_z), angvel: Vec3::new(0.0, 0.0, 0.0), diff --git a/examples/bevy_gltf_blueprints/multiple_levels/src/core/mod.rs b/examples/bevy_gltf_blueprints/multiple_levels/src/core/mod.rs index e2771da1..3271ad45 100644 --- a/examples/bevy_gltf_blueprints/multiple_levels/src/core/mod.rs +++ b/examples/bevy_gltf_blueprints/multiple_levels/src/core/mod.rs @@ -10,9 +10,6 @@ pub use relationships::*; pub mod physics; pub use physics::*; -// pub mod save_load; -// pub use save_load::*; - use bevy::prelude::*; use bevy_gltf_blueprints::*; @@ -23,7 +20,6 @@ impl Plugin for CorePlugin { LightingPlugin, CameraPlugin, PhysicsPlugin, - // SaveLoadPlugin, BlueprintsPlugin { library_folder: "models/library".into(), ..Default::default() diff --git a/examples/bevy_gltf_blueprints/multiple_levels/src/core/save_load/loading.rs b/examples/bevy_gltf_blueprints/multiple_levels/src/core/save_load/loading.rs deleted file mode 100644 index 73ef523d..00000000 --- a/examples/bevy_gltf_blueprints/multiple_levels/src/core/save_load/loading.rs +++ /dev/null @@ -1,218 +0,0 @@ -use bevy::prelude::*; -use bevy_gltf_blueprints::{clone_entity::CloneEntity, GameWorldTag, SpawnHere}; - -use crate::{ - assets::GameAssets, - state::{AppState, GameState, InAppRunning}, -}; - -use super::Saveable; - -const SCENE_FILE_PATH: &str = "scenes/save.scn.ron"; - -#[derive(Component, Debug)] -pub struct TempLoadedSceneMarker; - -#[derive(Component, Debug)] -pub struct SaveablesToRemove(Vec<(Entity, Name)>); - -#[derive(Component, Event)] -pub struct LoadRequest { - pub path: String, -} - -pub fn should_load(save_requested_events: EventReader) -> bool { - return save_requested_events.len() > 0; -} - -pub fn load_prepare( - mut next_app_state: ResMut>, - mut next_game_state: ResMut>, -) { - next_app_state.set(AppState::LoadingGame); - next_game_state.set(GameState::None); - info!("--loading: prepare") -} - -/// unload the level recursively -pub fn _unload_world_old(world: &mut World) { - let entities: Vec = world - // .query_filtered::, With)>>() - .query_filtered::>() // our level/world contains this component - .iter(world) - .collect(); - for entity in entities { - // Check the entity again in case it was despawned recursively - if world.get_entity(entity).is_some() { - world.entity_mut(entity).despawn_recursive(); - } - } -} - -pub fn unload_world(mut commands: Commands, gameworlds: Query>) { - for e in gameworlds.iter() { - info!("--loading: despawn old world/level"); - commands.entity(e).despawn_recursive(); - } -} - -// almost identical to setup_game, !!?? -pub fn load_world( - mut commands: Commands, - game_assets: Res, - // scenes: ResMut, -) { - info!("--loading: loading world/level"); - - commands.spawn(( - SceneBundle { - scene: game_assets.world.clone(), - ..default() - }, - bevy::prelude::Name::from("world"), - GameWorldTag, - InAppRunning, - )); -} - -pub fn load_saved_scene(mut commands: Commands, asset_server: Res) { - commands.spawn(( - DynamicSceneBundle { - // Scenes are loaded just like any other asset. - scene: asset_server.load(SCENE_FILE_PATH), - ..default() - }, - TempLoadedSceneMarker, - )); - // commands.entity(world).add_child(child_scene); - info!("--loading: loaded saved scene"); -} - -pub fn process_loaded_scene( - loaded_scene: Query<(Entity, &Children), With>, - named_entities: Query<(Entity, &Name, &Parent)>, // FIXME: very inneficient - mut commands: Commands, - - mut game_world: Query<(Entity, &Children), With>, - saveables: Query<(Entity, &Name), With>, - asset_server: Res, -) { - for (loaded_scene, children) in loaded_scene.iter() { - info!("--loading: post processing loaded scene"); - - let mut entities_to_load: Vec<(Entity, Name)> = vec![]; - - for loaded_entity in children.iter() { - if let Ok((source, name, _)) = named_entities.get(*loaded_entity) { - entities_to_load.push((source, name.clone())); - - let mut found = false; - for (e, n, p) in named_entities.iter() { - // if we have an entity with the same name as in same file, overwrite - if e != source && name.as_str() == n.as_str() { - // println!("found entity with same name {} {} {:?} {:?}", name, n, source, e); - // source is entity within the newly loaded scene (source), e is within the existing world (destination) - info!("copying data from {:?} to {:?}", source, e); - commands.add(CloneEntity { - source: source, - destination: e, - }); - // FIXME: issue with hierarchy & parenting, would be nice to be able to filter out components from CloneEntity - commands.entity(p.get()).add_child(e); - commands.entity(source).despawn_recursive(); - found = true; - break; - } - } - // entity not found in the list of existing entities (ie entities that came as part of the level) - // so we spawn a new one - if !found { - info!("generating new entity"); - let world = game_world.single_mut(); - let world = world.1[0]; - - let new_entity = commands - .spawn((bevy::prelude::Name::from(name.clone()), SpawnHere)) - .id(); - - commands.add(CloneEntity { - source: source, - destination: new_entity, - }); - - commands.entity(world).add_child(new_entity); - info!("copying data from {:?} to {:?}", source, new_entity); - } - } - } - commands.spawn(SaveablesToRemove(entities_to_load.clone())); - - // if an entity is present in the world but NOT in the saved entities , it should be removed from the world - // ideally this should be run between spawning of the world/level AND spawn_placeholders - - // remove the dynamic scene - info!("--loading: DESPAWNING LOADED SCENE"); - commands.entity(loaded_scene).despawn_recursive(); - - asset_server.mark_unused_assets(); - asset_server.free_unused_assets(); - } - //for saveable in saveables.iter(){ - // println!("SAVEABLE BEFORE {:?}", saveable) - //} -} - -pub fn final_cleanup( - saveables_to_remove: Query<(Entity, &SaveablesToRemove)>, - mut commands: Commands, - saveables: Query<(Entity, &Name), With>, - mut next_app_state: ResMut>, - mut next_game_state: ResMut>, -) { - if let Ok((e, entities_to_load)) = saveables_to_remove.get_single() { - info!("saveables to remove {:?}", entities_to_load); - for (e, n) in saveables.iter() { - let mut found = false; - println!("SAVEABLE {}", n); - - //let entities_to_load = saveables_to_remove.single(); - for (en, na) in entities_to_load.0.iter() { - found = na.as_str() == n.as_str(); - if found { - break; - } - } - if !found { - println!("REMOVING THIS ONE {}", n); - commands.entity(e).despawn_recursive(); - } - } - // if there is a saveable that is NOT in the list of entities to load, despawn it - - // despawn list - commands.entity(e).despawn_recursive(); - - info!("--loading: done, move to InGame state"); - // next_app_state.set(AppState::AppRunning); - next_game_state.set(GameState::InGame); - } -} - -fn process_loaded_scene_load_alt( - entities: Query<(Entity, &Children), With>, - named_entities: Query<(Entity, &Name, &Parent)>, // FIXME: very inneficient - mut commands: Commands, -) { - for (entity, children) in entities.iter() { - let mut entities_to_load: Vec<(Entity, Name)> = vec![]; - for saved_source in children.iter() { - if let Ok((source, name, _)) = named_entities.get(*saved_source) { - println!("AAAAAAA {}", name); - entities_to_load.push((source, name.clone())); - } - } - println!("entities to load {:?}", entities_to_load); - - commands.entity(entity).despawn_recursive(); - } -} diff --git a/examples/bevy_gltf_blueprints/multiple_levels/src/core/save_load/mod.rs b/examples/bevy_gltf_blueprints/multiple_levels/src/core/save_load/mod.rs deleted file mode 100644 index 3d0e91ed..00000000 --- a/examples/bevy_gltf_blueprints/multiple_levels/src/core/save_load/mod.rs +++ /dev/null @@ -1,70 +0,0 @@ -pub mod saveable; -use bevy::asset::free_unused_assets_system; -use bevy_gltf_components::GltfComponentsSet; -pub use saveable::*; - -pub mod saving; -pub use saving::*; - -pub mod loading; -pub use loading::*; - -use bevy::prelude::*; -use bevy::prelude::{App, IntoSystemConfigs, Plugin}; -use bevy::utils::Uuid; - -use bevy_gltf_blueprints::GltfBlueprintsSet; - -#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)] -pub enum LoadingSet { - Load, - PostLoad, -} - -pub struct SaveLoadPlugin; -impl Plugin for SaveLoadPlugin { - fn build(&self, app: &mut App) { - app - .register_type::() - .register_type::() - .add_event::() - .add_event::() - - .configure_sets( - Update, - (LoadingSet::Load, LoadingSet::PostLoad) - .chain() - .before(GltfBlueprintsSet::Spawn) - .before(GltfComponentsSet::Injection) - ) - - .add_systems(PreUpdate, save_game.run_if(should_save)) - - .add_systems(Update, - ( - load_prepare, - unload_world, - load_world, - load_saved_scene, - // process_loaded_scene - ) - .chain() - .run_if(should_load) // .run_if(in_state(AppState::AppRunning)) - .in_set(LoadingSet::Load) - ) - .add_systems(Update, - ( - process_loaded_scene, - apply_deferred, - final_cleanup, - apply_deferred, - free_unused_assets_system - ) - .chain() - .in_set(LoadingSet::PostLoad) - ) - - // .add_systems(Update, bla) - ; - } -} diff --git a/examples/bevy_gltf_blueprints/multiple_levels/src/core/save_load/old.rs b/examples/bevy_gltf_blueprints/multiple_levels/src/core/save_load/old.rs deleted file mode 100644 index 7d8a3899..00000000 --- a/examples/bevy_gltf_blueprints/multiple_levels/src/core/save_load/old.rs +++ /dev/null @@ -1,137 +0,0 @@ -const NEW_SCENE_FILE_PATH:&str="save.scn.ron"; - - - - -use bevy::ecs::component::Components; -use bevy::ecs::entity::EntityMap; -use serde::{Deserialize, Serialize}; - - -use std::io::Read; -use bevy::scene::serde::SceneDeserializer; -use ron::Deserializer; -use serde::de::DeserializeSeed; - - - - -#[derive(Debug, Deserialize)] -struct Components2; - -#[derive(Debug, Deserialize)] -struct Fake { - resources: HashMap, - entities: HashMap -} - -fn ron_test(){ - let full_path = "/home/ckaos/projects/grappling-boom-bot/assets/save.ron"; - match File::open(full_path) { - Ok(mut file) => { - let mut serialized_scene = Vec::new(); - if let Err(why) = file.read_to_end(&mut serialized_scene) { - error!("file read failed: {why:?}"); - } - match Deserializer::from_bytes(&serialized_scene) { - Ok(mut deserializer) => { - // deserializer. - let bla:Fake = ron::from_str("( - resources: {}, - entities: {} - )").unwrap(); - info!("testing {:?}", bla); - info!("YOYO DONE YO !") - } - Err(why) => { - error!("deserializer creation failed: {why:?}"); - } - } - } - Err(why) => { - error!("load failed: {why:?}"); - } - } -} - -fn inject_component_data(world: &mut World, scene: DynamicScene){ - let mut entity_map = EntityMap::default(); - if let Err(why) = scene.write_to_world(world, &mut entity_map) { - panic!("world write failed: {why:?}"); - } - println!("entity map {:?}", entity_map); - // TODO: EntityMap doesn't implement `iter()` - for old_entity in entity_map.keys() { - let entity = entity_map.get(old_entity).unwrap(); - info!("entity update required: {old_entity:?} -> {entity:?}"); - let e_mut = world - .entity_mut(entity); - } - - info!("done loading scene"); -} - -fn post_load(world: &mut World){ - let full_path = "/home/ckaos/projects/grappling-boom-bot/assets/save.ron"; - match File::open(full_path) { - Ok(mut file) => { - let mut serialized_scene = Vec::new(); - if let Err(why) = file.read_to_end(&mut serialized_scene) { - error!("file read failed: {why:?}"); - } - match Deserializer::from_bytes(&serialized_scene) { - Ok(mut deserializer) => { - let result = SceneDeserializer { - type_registry: &world.resource::().read(), - } - .deserialize(&mut deserializer); - info!("deserialize done"); - match result { - Ok(scene) => { - info!("scene loaded"); - // scene.write_to_world(world, entity_map) - // println!("{:?}", scene.entities); - inject_component_data(world, scene); - /*for dyn_ent in scene.entities.iter(){ - // let mut query = scene.world.query::<(Entity, &Name, &GltfExtras, &Parent)>(); - }*/ - } - Err(why) => { - error!("deserialization failed: {why:?}"); - } - } - } - Err(why) => { - error!("deserializer creation failed: {why:?}"); - } - } - } - Err(why) => { - error!("load failed: {why:?}"); - } - } - -} - - - -#[derive(Component, Reflect, Debug, Default )] -#[reflect(Component)] -pub struct Hackish; - - - -/// unload saveables -fn unload_saveables(world: &mut World) { - let entities: Vec = world - .query_filtered::>()// our level/world contains this component - .iter(world) - .collect(); - for entity in entities { - // Check the entity again in case it was despawned recursively - if world.get_entity(entity).is_some() { - info!("despawning"); - world.entity_mut(entity).despawn_recursive(); - } - } -} \ No newline at end of file diff --git a/examples/bevy_gltf_blueprints/multiple_levels/src/core/save_load/saveable.rs b/examples/bevy_gltf_blueprints/multiple_levels/src/core/save_load/saveable.rs deleted file mode 100644 index 67a4c657..00000000 --- a/examples/bevy_gltf_blueprints/multiple_levels/src/core/save_load/saveable.rs +++ /dev/null @@ -1,14 +0,0 @@ -use bevy::prelude::*; -use bevy::utils::Uuid; - -#[derive(Component, Reflect, Debug)] -#[reflect(Component)] -pub struct Saveable { - id: Uuid, -} - -impl Default for Saveable { - fn default() -> Self { - Saveable { id: Uuid::new_v4() } - } -} diff --git a/examples/bevy_gltf_blueprints/multiple_levels/src/core/save_load/saving.rs b/examples/bevy_gltf_blueprints/multiple_levels/src/core/save_load/saving.rs deleted file mode 100644 index 46d0b1af..00000000 --- a/examples/bevy_gltf_blueprints/multiple_levels/src/core/save_load/saving.rs +++ /dev/null @@ -1,87 +0,0 @@ -use bevy::pbr::{Clusters, VisiblePointLights}; -use bevy::render::camera::CameraRenderGraph; -use bevy::render::view::VisibleEntities; -use bevy::tasks::IoTaskPool; -use bevy::{gltf::GltfExtras, prelude::*}; -use bevy_rapier3d::prelude::RigidBody; -use std::fs::File; -use std::io::Write; - -use crate::core::physics::Collider; -use crate::game::{Pickable, Player}; - -use super::Saveable; - -const NEW_SCENE_FILE_PATH: &str = "save.scn.ron"; - -#[derive(Component, Event)] -pub struct SaveRequest { - pub path: String, -} - -pub fn should_save( - // keycode: Res>, - save_requested_events: EventReader, -) -> bool { - return save_requested_events.len() > 0; - - // return keycode.just_pressed(KeyCode::S) -} - -pub fn save_game( - world: &mut World, - // save_requested_events: EventReader, -) { - info!("saving"); - // world. - /*for bli in save_requested_events.iter(){ - println!("SAAAAVE TO THISSSSS {:?}", bli.path) - }*/ - - let saveable_entities: Vec = world - .query_filtered::>() - .iter(world) - .collect(); - - /*let static_entities: Vec = world - .query_filtered::>() - .iter(world) - .collect();*/ - println!("saveable entities {}", saveable_entities.len()); - - let mut scene_builder = DynamicSceneBuilder::from_world(world); - scene_builder - .deny::() - .deny::() - .deny::() - .deny::() - .deny::() - .deny::() - .deny::() - .deny::() - .deny::() - // camera stuff - .deny::() - .deny::() - .deny::() - .deny::() - .deny::() - .deny::() - //.deny::() - .extract_entities(saveable_entities.into_iter()); - - let dyn_scene = scene_builder.build(); - let serialized_scene = dyn_scene - .serialize_ron(world.resource::()) - .unwrap(); - - #[cfg(not(target_arch = "wasm32"))] - IoTaskPool::get() - .spawn(async move { - // Write the scene RON data to file - File::create(format!("assets/scenes/{NEW_SCENE_FILE_PATH}")) - .and_then(|mut file| file.write(serialized_scene.as_bytes())) - .expect("Error while writing scene to file"); - }) - .detach(); -} diff --git a/examples/bevy_gltf_blueprints/multiple_levels/src/game/in_game.rs b/examples/bevy_gltf_blueprints/multiple_levels/src/game/in_game.rs index add9561c..4047b83e 100644 --- a/examples/bevy_gltf_blueprints/multiple_levels/src/game/in_game.rs +++ b/examples/bevy_gltf_blueprints/multiple_levels/src/game/in_game.rs @@ -67,13 +67,12 @@ pub fn spawn_test( .spawn(( BluePrintBundle { blueprint: BlueprintName("Health_Pickup".to_string()), - transform: TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), ..Default::default() }, bevy::prelude::Name::from(format!("test{}", name_index)), // BlueprintName("Health_Pickup".to_string()), // SpawnHere, - // TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), + TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), Velocity { linvel: Vec3::new(vel_x, vel_y, vel_z), angvel: Vec3::new(0.0, 0.0, 0.0), diff --git a/examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles/src/core/mod.rs b/examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles/src/core/mod.rs index c253af1c..610f2054 100644 --- a/examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles/src/core/mod.rs +++ b/examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles/src/core/mod.rs @@ -10,9 +10,6 @@ pub use relationships::*; pub mod physics; pub use physics::*; -// pub mod save_load; -// pub use save_load::*; - use bevy::prelude::*; use bevy_gltf_blueprints::*; @@ -23,7 +20,6 @@ impl Plugin for CorePlugin { LightingPlugin, CameraPlugin, PhysicsPlugin, - // SaveLoadPlugin, BlueprintsPlugin { library_folder: "models/library".into(), material_library: true, diff --git a/examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles/src/core/save_load/loading.rs b/examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles/src/core/save_load/loading.rs deleted file mode 100644 index 73ef523d..00000000 --- a/examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles/src/core/save_load/loading.rs +++ /dev/null @@ -1,218 +0,0 @@ -use bevy::prelude::*; -use bevy_gltf_blueprints::{clone_entity::CloneEntity, GameWorldTag, SpawnHere}; - -use crate::{ - assets::GameAssets, - state::{AppState, GameState, InAppRunning}, -}; - -use super::Saveable; - -const SCENE_FILE_PATH: &str = "scenes/save.scn.ron"; - -#[derive(Component, Debug)] -pub struct TempLoadedSceneMarker; - -#[derive(Component, Debug)] -pub struct SaveablesToRemove(Vec<(Entity, Name)>); - -#[derive(Component, Event)] -pub struct LoadRequest { - pub path: String, -} - -pub fn should_load(save_requested_events: EventReader) -> bool { - return save_requested_events.len() > 0; -} - -pub fn load_prepare( - mut next_app_state: ResMut>, - mut next_game_state: ResMut>, -) { - next_app_state.set(AppState::LoadingGame); - next_game_state.set(GameState::None); - info!("--loading: prepare") -} - -/// unload the level recursively -pub fn _unload_world_old(world: &mut World) { - let entities: Vec = world - // .query_filtered::, With)>>() - .query_filtered::>() // our level/world contains this component - .iter(world) - .collect(); - for entity in entities { - // Check the entity again in case it was despawned recursively - if world.get_entity(entity).is_some() { - world.entity_mut(entity).despawn_recursive(); - } - } -} - -pub fn unload_world(mut commands: Commands, gameworlds: Query>) { - for e in gameworlds.iter() { - info!("--loading: despawn old world/level"); - commands.entity(e).despawn_recursive(); - } -} - -// almost identical to setup_game, !!?? -pub fn load_world( - mut commands: Commands, - game_assets: Res, - // scenes: ResMut, -) { - info!("--loading: loading world/level"); - - commands.spawn(( - SceneBundle { - scene: game_assets.world.clone(), - ..default() - }, - bevy::prelude::Name::from("world"), - GameWorldTag, - InAppRunning, - )); -} - -pub fn load_saved_scene(mut commands: Commands, asset_server: Res) { - commands.spawn(( - DynamicSceneBundle { - // Scenes are loaded just like any other asset. - scene: asset_server.load(SCENE_FILE_PATH), - ..default() - }, - TempLoadedSceneMarker, - )); - // commands.entity(world).add_child(child_scene); - info!("--loading: loaded saved scene"); -} - -pub fn process_loaded_scene( - loaded_scene: Query<(Entity, &Children), With>, - named_entities: Query<(Entity, &Name, &Parent)>, // FIXME: very inneficient - mut commands: Commands, - - mut game_world: Query<(Entity, &Children), With>, - saveables: Query<(Entity, &Name), With>, - asset_server: Res, -) { - for (loaded_scene, children) in loaded_scene.iter() { - info!("--loading: post processing loaded scene"); - - let mut entities_to_load: Vec<(Entity, Name)> = vec![]; - - for loaded_entity in children.iter() { - if let Ok((source, name, _)) = named_entities.get(*loaded_entity) { - entities_to_load.push((source, name.clone())); - - let mut found = false; - for (e, n, p) in named_entities.iter() { - // if we have an entity with the same name as in same file, overwrite - if e != source && name.as_str() == n.as_str() { - // println!("found entity with same name {} {} {:?} {:?}", name, n, source, e); - // source is entity within the newly loaded scene (source), e is within the existing world (destination) - info!("copying data from {:?} to {:?}", source, e); - commands.add(CloneEntity { - source: source, - destination: e, - }); - // FIXME: issue with hierarchy & parenting, would be nice to be able to filter out components from CloneEntity - commands.entity(p.get()).add_child(e); - commands.entity(source).despawn_recursive(); - found = true; - break; - } - } - // entity not found in the list of existing entities (ie entities that came as part of the level) - // so we spawn a new one - if !found { - info!("generating new entity"); - let world = game_world.single_mut(); - let world = world.1[0]; - - let new_entity = commands - .spawn((bevy::prelude::Name::from(name.clone()), SpawnHere)) - .id(); - - commands.add(CloneEntity { - source: source, - destination: new_entity, - }); - - commands.entity(world).add_child(new_entity); - info!("copying data from {:?} to {:?}", source, new_entity); - } - } - } - commands.spawn(SaveablesToRemove(entities_to_load.clone())); - - // if an entity is present in the world but NOT in the saved entities , it should be removed from the world - // ideally this should be run between spawning of the world/level AND spawn_placeholders - - // remove the dynamic scene - info!("--loading: DESPAWNING LOADED SCENE"); - commands.entity(loaded_scene).despawn_recursive(); - - asset_server.mark_unused_assets(); - asset_server.free_unused_assets(); - } - //for saveable in saveables.iter(){ - // println!("SAVEABLE BEFORE {:?}", saveable) - //} -} - -pub fn final_cleanup( - saveables_to_remove: Query<(Entity, &SaveablesToRemove)>, - mut commands: Commands, - saveables: Query<(Entity, &Name), With>, - mut next_app_state: ResMut>, - mut next_game_state: ResMut>, -) { - if let Ok((e, entities_to_load)) = saveables_to_remove.get_single() { - info!("saveables to remove {:?}", entities_to_load); - for (e, n) in saveables.iter() { - let mut found = false; - println!("SAVEABLE {}", n); - - //let entities_to_load = saveables_to_remove.single(); - for (en, na) in entities_to_load.0.iter() { - found = na.as_str() == n.as_str(); - if found { - break; - } - } - if !found { - println!("REMOVING THIS ONE {}", n); - commands.entity(e).despawn_recursive(); - } - } - // if there is a saveable that is NOT in the list of entities to load, despawn it - - // despawn list - commands.entity(e).despawn_recursive(); - - info!("--loading: done, move to InGame state"); - // next_app_state.set(AppState::AppRunning); - next_game_state.set(GameState::InGame); - } -} - -fn process_loaded_scene_load_alt( - entities: Query<(Entity, &Children), With>, - named_entities: Query<(Entity, &Name, &Parent)>, // FIXME: very inneficient - mut commands: Commands, -) { - for (entity, children) in entities.iter() { - let mut entities_to_load: Vec<(Entity, Name)> = vec![]; - for saved_source in children.iter() { - if let Ok((source, name, _)) = named_entities.get(*saved_source) { - println!("AAAAAAA {}", name); - entities_to_load.push((source, name.clone())); - } - } - println!("entities to load {:?}", entities_to_load); - - commands.entity(entity).despawn_recursive(); - } -} diff --git a/examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles/src/core/save_load/mod.rs b/examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles/src/core/save_load/mod.rs deleted file mode 100644 index 3d0e91ed..00000000 --- a/examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles/src/core/save_load/mod.rs +++ /dev/null @@ -1,70 +0,0 @@ -pub mod saveable; -use bevy::asset::free_unused_assets_system; -use bevy_gltf_components::GltfComponentsSet; -pub use saveable::*; - -pub mod saving; -pub use saving::*; - -pub mod loading; -pub use loading::*; - -use bevy::prelude::*; -use bevy::prelude::{App, IntoSystemConfigs, Plugin}; -use bevy::utils::Uuid; - -use bevy_gltf_blueprints::GltfBlueprintsSet; - -#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)] -pub enum LoadingSet { - Load, - PostLoad, -} - -pub struct SaveLoadPlugin; -impl Plugin for SaveLoadPlugin { - fn build(&self, app: &mut App) { - app - .register_type::() - .register_type::() - .add_event::() - .add_event::() - - .configure_sets( - Update, - (LoadingSet::Load, LoadingSet::PostLoad) - .chain() - .before(GltfBlueprintsSet::Spawn) - .before(GltfComponentsSet::Injection) - ) - - .add_systems(PreUpdate, save_game.run_if(should_save)) - - .add_systems(Update, - ( - load_prepare, - unload_world, - load_world, - load_saved_scene, - // process_loaded_scene - ) - .chain() - .run_if(should_load) // .run_if(in_state(AppState::AppRunning)) - .in_set(LoadingSet::Load) - ) - .add_systems(Update, - ( - process_loaded_scene, - apply_deferred, - final_cleanup, - apply_deferred, - free_unused_assets_system - ) - .chain() - .in_set(LoadingSet::PostLoad) - ) - - // .add_systems(Update, bla) - ; - } -} diff --git a/examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles/src/core/save_load/old.rs b/examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles/src/core/save_load/old.rs deleted file mode 100644 index 7d8a3899..00000000 --- a/examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles/src/core/save_load/old.rs +++ /dev/null @@ -1,137 +0,0 @@ -const NEW_SCENE_FILE_PATH:&str="save.scn.ron"; - - - - -use bevy::ecs::component::Components; -use bevy::ecs::entity::EntityMap; -use serde::{Deserialize, Serialize}; - - -use std::io::Read; -use bevy::scene::serde::SceneDeserializer; -use ron::Deserializer; -use serde::de::DeserializeSeed; - - - - -#[derive(Debug, Deserialize)] -struct Components2; - -#[derive(Debug, Deserialize)] -struct Fake { - resources: HashMap, - entities: HashMap -} - -fn ron_test(){ - let full_path = "/home/ckaos/projects/grappling-boom-bot/assets/save.ron"; - match File::open(full_path) { - Ok(mut file) => { - let mut serialized_scene = Vec::new(); - if let Err(why) = file.read_to_end(&mut serialized_scene) { - error!("file read failed: {why:?}"); - } - match Deserializer::from_bytes(&serialized_scene) { - Ok(mut deserializer) => { - // deserializer. - let bla:Fake = ron::from_str("( - resources: {}, - entities: {} - )").unwrap(); - info!("testing {:?}", bla); - info!("YOYO DONE YO !") - } - Err(why) => { - error!("deserializer creation failed: {why:?}"); - } - } - } - Err(why) => { - error!("load failed: {why:?}"); - } - } -} - -fn inject_component_data(world: &mut World, scene: DynamicScene){ - let mut entity_map = EntityMap::default(); - if let Err(why) = scene.write_to_world(world, &mut entity_map) { - panic!("world write failed: {why:?}"); - } - println!("entity map {:?}", entity_map); - // TODO: EntityMap doesn't implement `iter()` - for old_entity in entity_map.keys() { - let entity = entity_map.get(old_entity).unwrap(); - info!("entity update required: {old_entity:?} -> {entity:?}"); - let e_mut = world - .entity_mut(entity); - } - - info!("done loading scene"); -} - -fn post_load(world: &mut World){ - let full_path = "/home/ckaos/projects/grappling-boom-bot/assets/save.ron"; - match File::open(full_path) { - Ok(mut file) => { - let mut serialized_scene = Vec::new(); - if let Err(why) = file.read_to_end(&mut serialized_scene) { - error!("file read failed: {why:?}"); - } - match Deserializer::from_bytes(&serialized_scene) { - Ok(mut deserializer) => { - let result = SceneDeserializer { - type_registry: &world.resource::().read(), - } - .deserialize(&mut deserializer); - info!("deserialize done"); - match result { - Ok(scene) => { - info!("scene loaded"); - // scene.write_to_world(world, entity_map) - // println!("{:?}", scene.entities); - inject_component_data(world, scene); - /*for dyn_ent in scene.entities.iter(){ - // let mut query = scene.world.query::<(Entity, &Name, &GltfExtras, &Parent)>(); - }*/ - } - Err(why) => { - error!("deserialization failed: {why:?}"); - } - } - } - Err(why) => { - error!("deserializer creation failed: {why:?}"); - } - } - } - Err(why) => { - error!("load failed: {why:?}"); - } - } - -} - - - -#[derive(Component, Reflect, Debug, Default )] -#[reflect(Component)] -pub struct Hackish; - - - -/// unload saveables -fn unload_saveables(world: &mut World) { - let entities: Vec = world - .query_filtered::>()// our level/world contains this component - .iter(world) - .collect(); - for entity in entities { - // Check the entity again in case it was despawned recursively - if world.get_entity(entity).is_some() { - info!("despawning"); - world.entity_mut(entity).despawn_recursive(); - } - } -} \ No newline at end of file diff --git a/examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles/src/core/save_load/saveable.rs b/examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles/src/core/save_load/saveable.rs deleted file mode 100644 index 67a4c657..00000000 --- a/examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles/src/core/save_load/saveable.rs +++ /dev/null @@ -1,14 +0,0 @@ -use bevy::prelude::*; -use bevy::utils::Uuid; - -#[derive(Component, Reflect, Debug)] -#[reflect(Component)] -pub struct Saveable { - id: Uuid, -} - -impl Default for Saveable { - fn default() -> Self { - Saveable { id: Uuid::new_v4() } - } -} diff --git a/examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles/src/core/save_load/saving.rs b/examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles/src/core/save_load/saving.rs deleted file mode 100644 index 46d0b1af..00000000 --- a/examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles/src/core/save_load/saving.rs +++ /dev/null @@ -1,87 +0,0 @@ -use bevy::pbr::{Clusters, VisiblePointLights}; -use bevy::render::camera::CameraRenderGraph; -use bevy::render::view::VisibleEntities; -use bevy::tasks::IoTaskPool; -use bevy::{gltf::GltfExtras, prelude::*}; -use bevy_rapier3d::prelude::RigidBody; -use std::fs::File; -use std::io::Write; - -use crate::core::physics::Collider; -use crate::game::{Pickable, Player}; - -use super::Saveable; - -const NEW_SCENE_FILE_PATH: &str = "save.scn.ron"; - -#[derive(Component, Event)] -pub struct SaveRequest { - pub path: String, -} - -pub fn should_save( - // keycode: Res>, - save_requested_events: EventReader, -) -> bool { - return save_requested_events.len() > 0; - - // return keycode.just_pressed(KeyCode::S) -} - -pub fn save_game( - world: &mut World, - // save_requested_events: EventReader, -) { - info!("saving"); - // world. - /*for bli in save_requested_events.iter(){ - println!("SAAAAVE TO THISSSSS {:?}", bli.path) - }*/ - - let saveable_entities: Vec = world - .query_filtered::>() - .iter(world) - .collect(); - - /*let static_entities: Vec = world - .query_filtered::>() - .iter(world) - .collect();*/ - println!("saveable entities {}", saveable_entities.len()); - - let mut scene_builder = DynamicSceneBuilder::from_world(world); - scene_builder - .deny::() - .deny::() - .deny::() - .deny::() - .deny::() - .deny::() - .deny::() - .deny::() - .deny::() - // camera stuff - .deny::() - .deny::() - .deny::() - .deny::() - .deny::() - .deny::() - //.deny::() - .extract_entities(saveable_entities.into_iter()); - - let dyn_scene = scene_builder.build(); - let serialized_scene = dyn_scene - .serialize_ron(world.resource::()) - .unwrap(); - - #[cfg(not(target_arch = "wasm32"))] - IoTaskPool::get() - .spawn(async move { - // Write the scene RON data to file - File::create(format!("assets/scenes/{NEW_SCENE_FILE_PATH}")) - .and_then(|mut file| file.write(serialized_scene.as_bytes())) - .expect("Error while writing scene to file"); - }) - .detach(); -} diff --git a/examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles/src/game/in_game.rs b/examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles/src/game/in_game.rs index add9561c..4047b83e 100644 --- a/examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles/src/game/in_game.rs +++ b/examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles/src/game/in_game.rs @@ -67,13 +67,12 @@ pub fn spawn_test( .spawn(( BluePrintBundle { blueprint: BlueprintName("Health_Pickup".to_string()), - transform: TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), ..Default::default() }, bevy::prelude::Name::from(format!("test{}", name_index)), // BlueprintName("Health_Pickup".to_string()), // SpawnHere, - // TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), + TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), Velocity { linvel: Vec3::new(vel_x, vel_y, vel_z), angvel: Vec3::new(0.0, 0.0, 0.0), diff --git a/examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles/src/game/level_transitions.rs b/examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles/src/game/level_transitions.rs index 2cfc8e0f..b59494aa 100644 --- a/examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles/src/game/level_transitions.rs +++ b/examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles/src/game/level_transitions.rs @@ -30,7 +30,6 @@ pub fn trigger_level_transition( // we need to accomodate for the fact that the collider may be a child of the level transition (FIXME: is this a missunderstanding on my part about rapier child colliders ?) let entity1_parent = parents.get(*entity1).unwrap(); let entity2_parent = parents.get(*entity2).unwrap(); - if level_transition_triggers.get(*entity1).is_ok() || level_transition_triggers.get(*entity2).is_ok() @@ -51,10 +50,13 @@ pub fn trigger_level_transition( level_transition_triggers.get(entity2_parent.get()).unwrap(); } - if players.get(*entity1).is_ok() || players.get(entity1_parent.get()).is_ok() || players.get(*entity2).is_ok() || players.get(entity2_parent.get()).is_ok() { + if players.get(*entity1).is_ok() + || players.get(entity1_parent.get()).is_ok() + || players.get(*entity2).is_ok() + || players.get(entity2_parent.get()).is_ok() + { println!("one entity is the player, we can enter") - } - else { + } else { // if none of our entities is a player, bail out, as only entities with player components should trigger a transition return; } diff --git a/examples/bevy_gltf_blueprints/nested_blueprints/src/core/mod.rs b/examples/bevy_gltf_blueprints/nested_blueprints/src/core/mod.rs index f9145633..9c38fa77 100644 --- a/examples/bevy_gltf_blueprints/nested_blueprints/src/core/mod.rs +++ b/examples/bevy_gltf_blueprints/nested_blueprints/src/core/mod.rs @@ -10,9 +10,6 @@ pub use relationships::*; pub mod physics; pub use physics::*; -// pub mod save_load; -// pub use save_load::*; - use bevy::prelude::*; use bevy_gltf_blueprints::*; @@ -23,7 +20,6 @@ impl Plugin for CorePlugin { LightingPlugin, CameraPlugin, PhysicsPlugin, - // SaveLoadPlugin, BlueprintsPlugin { library_folder: "models/library".into(), format: GltfFormat::GLB, diff --git a/examples/bevy_gltf_blueprints/nested_blueprints/src/core/save_load/loading.rs b/examples/bevy_gltf_blueprints/nested_blueprints/src/core/save_load/loading.rs deleted file mode 100644 index 73ef523d..00000000 --- a/examples/bevy_gltf_blueprints/nested_blueprints/src/core/save_load/loading.rs +++ /dev/null @@ -1,218 +0,0 @@ -use bevy::prelude::*; -use bevy_gltf_blueprints::{clone_entity::CloneEntity, GameWorldTag, SpawnHere}; - -use crate::{ - assets::GameAssets, - state::{AppState, GameState, InAppRunning}, -}; - -use super::Saveable; - -const SCENE_FILE_PATH: &str = "scenes/save.scn.ron"; - -#[derive(Component, Debug)] -pub struct TempLoadedSceneMarker; - -#[derive(Component, Debug)] -pub struct SaveablesToRemove(Vec<(Entity, Name)>); - -#[derive(Component, Event)] -pub struct LoadRequest { - pub path: String, -} - -pub fn should_load(save_requested_events: EventReader) -> bool { - return save_requested_events.len() > 0; -} - -pub fn load_prepare( - mut next_app_state: ResMut>, - mut next_game_state: ResMut>, -) { - next_app_state.set(AppState::LoadingGame); - next_game_state.set(GameState::None); - info!("--loading: prepare") -} - -/// unload the level recursively -pub fn _unload_world_old(world: &mut World) { - let entities: Vec = world - // .query_filtered::, With)>>() - .query_filtered::>() // our level/world contains this component - .iter(world) - .collect(); - for entity in entities { - // Check the entity again in case it was despawned recursively - if world.get_entity(entity).is_some() { - world.entity_mut(entity).despawn_recursive(); - } - } -} - -pub fn unload_world(mut commands: Commands, gameworlds: Query>) { - for e in gameworlds.iter() { - info!("--loading: despawn old world/level"); - commands.entity(e).despawn_recursive(); - } -} - -// almost identical to setup_game, !!?? -pub fn load_world( - mut commands: Commands, - game_assets: Res, - // scenes: ResMut, -) { - info!("--loading: loading world/level"); - - commands.spawn(( - SceneBundle { - scene: game_assets.world.clone(), - ..default() - }, - bevy::prelude::Name::from("world"), - GameWorldTag, - InAppRunning, - )); -} - -pub fn load_saved_scene(mut commands: Commands, asset_server: Res) { - commands.spawn(( - DynamicSceneBundle { - // Scenes are loaded just like any other asset. - scene: asset_server.load(SCENE_FILE_PATH), - ..default() - }, - TempLoadedSceneMarker, - )); - // commands.entity(world).add_child(child_scene); - info!("--loading: loaded saved scene"); -} - -pub fn process_loaded_scene( - loaded_scene: Query<(Entity, &Children), With>, - named_entities: Query<(Entity, &Name, &Parent)>, // FIXME: very inneficient - mut commands: Commands, - - mut game_world: Query<(Entity, &Children), With>, - saveables: Query<(Entity, &Name), With>, - asset_server: Res, -) { - for (loaded_scene, children) in loaded_scene.iter() { - info!("--loading: post processing loaded scene"); - - let mut entities_to_load: Vec<(Entity, Name)> = vec![]; - - for loaded_entity in children.iter() { - if let Ok((source, name, _)) = named_entities.get(*loaded_entity) { - entities_to_load.push((source, name.clone())); - - let mut found = false; - for (e, n, p) in named_entities.iter() { - // if we have an entity with the same name as in same file, overwrite - if e != source && name.as_str() == n.as_str() { - // println!("found entity with same name {} {} {:?} {:?}", name, n, source, e); - // source is entity within the newly loaded scene (source), e is within the existing world (destination) - info!("copying data from {:?} to {:?}", source, e); - commands.add(CloneEntity { - source: source, - destination: e, - }); - // FIXME: issue with hierarchy & parenting, would be nice to be able to filter out components from CloneEntity - commands.entity(p.get()).add_child(e); - commands.entity(source).despawn_recursive(); - found = true; - break; - } - } - // entity not found in the list of existing entities (ie entities that came as part of the level) - // so we spawn a new one - if !found { - info!("generating new entity"); - let world = game_world.single_mut(); - let world = world.1[0]; - - let new_entity = commands - .spawn((bevy::prelude::Name::from(name.clone()), SpawnHere)) - .id(); - - commands.add(CloneEntity { - source: source, - destination: new_entity, - }); - - commands.entity(world).add_child(new_entity); - info!("copying data from {:?} to {:?}", source, new_entity); - } - } - } - commands.spawn(SaveablesToRemove(entities_to_load.clone())); - - // if an entity is present in the world but NOT in the saved entities , it should be removed from the world - // ideally this should be run between spawning of the world/level AND spawn_placeholders - - // remove the dynamic scene - info!("--loading: DESPAWNING LOADED SCENE"); - commands.entity(loaded_scene).despawn_recursive(); - - asset_server.mark_unused_assets(); - asset_server.free_unused_assets(); - } - //for saveable in saveables.iter(){ - // println!("SAVEABLE BEFORE {:?}", saveable) - //} -} - -pub fn final_cleanup( - saveables_to_remove: Query<(Entity, &SaveablesToRemove)>, - mut commands: Commands, - saveables: Query<(Entity, &Name), With>, - mut next_app_state: ResMut>, - mut next_game_state: ResMut>, -) { - if let Ok((e, entities_to_load)) = saveables_to_remove.get_single() { - info!("saveables to remove {:?}", entities_to_load); - for (e, n) in saveables.iter() { - let mut found = false; - println!("SAVEABLE {}", n); - - //let entities_to_load = saveables_to_remove.single(); - for (en, na) in entities_to_load.0.iter() { - found = na.as_str() == n.as_str(); - if found { - break; - } - } - if !found { - println!("REMOVING THIS ONE {}", n); - commands.entity(e).despawn_recursive(); - } - } - // if there is a saveable that is NOT in the list of entities to load, despawn it - - // despawn list - commands.entity(e).despawn_recursive(); - - info!("--loading: done, move to InGame state"); - // next_app_state.set(AppState::AppRunning); - next_game_state.set(GameState::InGame); - } -} - -fn process_loaded_scene_load_alt( - entities: Query<(Entity, &Children), With>, - named_entities: Query<(Entity, &Name, &Parent)>, // FIXME: very inneficient - mut commands: Commands, -) { - for (entity, children) in entities.iter() { - let mut entities_to_load: Vec<(Entity, Name)> = vec![]; - for saved_source in children.iter() { - if let Ok((source, name, _)) = named_entities.get(*saved_source) { - println!("AAAAAAA {}", name); - entities_to_load.push((source, name.clone())); - } - } - println!("entities to load {:?}", entities_to_load); - - commands.entity(entity).despawn_recursive(); - } -} diff --git a/examples/bevy_gltf_blueprints/nested_blueprints/src/core/save_load/mod.rs b/examples/bevy_gltf_blueprints/nested_blueprints/src/core/save_load/mod.rs deleted file mode 100644 index 3d0e91ed..00000000 --- a/examples/bevy_gltf_blueprints/nested_blueprints/src/core/save_load/mod.rs +++ /dev/null @@ -1,70 +0,0 @@ -pub mod saveable; -use bevy::asset::free_unused_assets_system; -use bevy_gltf_components::GltfComponentsSet; -pub use saveable::*; - -pub mod saving; -pub use saving::*; - -pub mod loading; -pub use loading::*; - -use bevy::prelude::*; -use bevy::prelude::{App, IntoSystemConfigs, Plugin}; -use bevy::utils::Uuid; - -use bevy_gltf_blueprints::GltfBlueprintsSet; - -#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)] -pub enum LoadingSet { - Load, - PostLoad, -} - -pub struct SaveLoadPlugin; -impl Plugin for SaveLoadPlugin { - fn build(&self, app: &mut App) { - app - .register_type::() - .register_type::() - .add_event::() - .add_event::() - - .configure_sets( - Update, - (LoadingSet::Load, LoadingSet::PostLoad) - .chain() - .before(GltfBlueprintsSet::Spawn) - .before(GltfComponentsSet::Injection) - ) - - .add_systems(PreUpdate, save_game.run_if(should_save)) - - .add_systems(Update, - ( - load_prepare, - unload_world, - load_world, - load_saved_scene, - // process_loaded_scene - ) - .chain() - .run_if(should_load) // .run_if(in_state(AppState::AppRunning)) - .in_set(LoadingSet::Load) - ) - .add_systems(Update, - ( - process_loaded_scene, - apply_deferred, - final_cleanup, - apply_deferred, - free_unused_assets_system - ) - .chain() - .in_set(LoadingSet::PostLoad) - ) - - // .add_systems(Update, bla) - ; - } -} diff --git a/examples/bevy_gltf_blueprints/nested_blueprints/src/core/save_load/old.rs b/examples/bevy_gltf_blueprints/nested_blueprints/src/core/save_load/old.rs deleted file mode 100644 index 7d8a3899..00000000 --- a/examples/bevy_gltf_blueprints/nested_blueprints/src/core/save_load/old.rs +++ /dev/null @@ -1,137 +0,0 @@ -const NEW_SCENE_FILE_PATH:&str="save.scn.ron"; - - - - -use bevy::ecs::component::Components; -use bevy::ecs::entity::EntityMap; -use serde::{Deserialize, Serialize}; - - -use std::io::Read; -use bevy::scene::serde::SceneDeserializer; -use ron::Deserializer; -use serde::de::DeserializeSeed; - - - - -#[derive(Debug, Deserialize)] -struct Components2; - -#[derive(Debug, Deserialize)] -struct Fake { - resources: HashMap, - entities: HashMap -} - -fn ron_test(){ - let full_path = "/home/ckaos/projects/grappling-boom-bot/assets/save.ron"; - match File::open(full_path) { - Ok(mut file) => { - let mut serialized_scene = Vec::new(); - if let Err(why) = file.read_to_end(&mut serialized_scene) { - error!("file read failed: {why:?}"); - } - match Deserializer::from_bytes(&serialized_scene) { - Ok(mut deserializer) => { - // deserializer. - let bla:Fake = ron::from_str("( - resources: {}, - entities: {} - )").unwrap(); - info!("testing {:?}", bla); - info!("YOYO DONE YO !") - } - Err(why) => { - error!("deserializer creation failed: {why:?}"); - } - } - } - Err(why) => { - error!("load failed: {why:?}"); - } - } -} - -fn inject_component_data(world: &mut World, scene: DynamicScene){ - let mut entity_map = EntityMap::default(); - if let Err(why) = scene.write_to_world(world, &mut entity_map) { - panic!("world write failed: {why:?}"); - } - println!("entity map {:?}", entity_map); - // TODO: EntityMap doesn't implement `iter()` - for old_entity in entity_map.keys() { - let entity = entity_map.get(old_entity).unwrap(); - info!("entity update required: {old_entity:?} -> {entity:?}"); - let e_mut = world - .entity_mut(entity); - } - - info!("done loading scene"); -} - -fn post_load(world: &mut World){ - let full_path = "/home/ckaos/projects/grappling-boom-bot/assets/save.ron"; - match File::open(full_path) { - Ok(mut file) => { - let mut serialized_scene = Vec::new(); - if let Err(why) = file.read_to_end(&mut serialized_scene) { - error!("file read failed: {why:?}"); - } - match Deserializer::from_bytes(&serialized_scene) { - Ok(mut deserializer) => { - let result = SceneDeserializer { - type_registry: &world.resource::().read(), - } - .deserialize(&mut deserializer); - info!("deserialize done"); - match result { - Ok(scene) => { - info!("scene loaded"); - // scene.write_to_world(world, entity_map) - // println!("{:?}", scene.entities); - inject_component_data(world, scene); - /*for dyn_ent in scene.entities.iter(){ - // let mut query = scene.world.query::<(Entity, &Name, &GltfExtras, &Parent)>(); - }*/ - } - Err(why) => { - error!("deserialization failed: {why:?}"); - } - } - } - Err(why) => { - error!("deserializer creation failed: {why:?}"); - } - } - } - Err(why) => { - error!("load failed: {why:?}"); - } - } - -} - - - -#[derive(Component, Reflect, Debug, Default )] -#[reflect(Component)] -pub struct Hackish; - - - -/// unload saveables -fn unload_saveables(world: &mut World) { - let entities: Vec = world - .query_filtered::>()// our level/world contains this component - .iter(world) - .collect(); - for entity in entities { - // Check the entity again in case it was despawned recursively - if world.get_entity(entity).is_some() { - info!("despawning"); - world.entity_mut(entity).despawn_recursive(); - } - } -} \ No newline at end of file diff --git a/examples/bevy_gltf_blueprints/nested_blueprints/src/core/save_load/saveable.rs b/examples/bevy_gltf_blueprints/nested_blueprints/src/core/save_load/saveable.rs deleted file mode 100644 index 67a4c657..00000000 --- a/examples/bevy_gltf_blueprints/nested_blueprints/src/core/save_load/saveable.rs +++ /dev/null @@ -1,14 +0,0 @@ -use bevy::prelude::*; -use bevy::utils::Uuid; - -#[derive(Component, Reflect, Debug)] -#[reflect(Component)] -pub struct Saveable { - id: Uuid, -} - -impl Default for Saveable { - fn default() -> Self { - Saveable { id: Uuid::new_v4() } - } -} diff --git a/examples/bevy_gltf_blueprints/nested_blueprints/src/core/save_load/saving.rs b/examples/bevy_gltf_blueprints/nested_blueprints/src/core/save_load/saving.rs deleted file mode 100644 index 46d0b1af..00000000 --- a/examples/bevy_gltf_blueprints/nested_blueprints/src/core/save_load/saving.rs +++ /dev/null @@ -1,87 +0,0 @@ -use bevy::pbr::{Clusters, VisiblePointLights}; -use bevy::render::camera::CameraRenderGraph; -use bevy::render::view::VisibleEntities; -use bevy::tasks::IoTaskPool; -use bevy::{gltf::GltfExtras, prelude::*}; -use bevy_rapier3d::prelude::RigidBody; -use std::fs::File; -use std::io::Write; - -use crate::core::physics::Collider; -use crate::game::{Pickable, Player}; - -use super::Saveable; - -const NEW_SCENE_FILE_PATH: &str = "save.scn.ron"; - -#[derive(Component, Event)] -pub struct SaveRequest { - pub path: String, -} - -pub fn should_save( - // keycode: Res>, - save_requested_events: EventReader, -) -> bool { - return save_requested_events.len() > 0; - - // return keycode.just_pressed(KeyCode::S) -} - -pub fn save_game( - world: &mut World, - // save_requested_events: EventReader, -) { - info!("saving"); - // world. - /*for bli in save_requested_events.iter(){ - println!("SAAAAVE TO THISSSSS {:?}", bli.path) - }*/ - - let saveable_entities: Vec = world - .query_filtered::>() - .iter(world) - .collect(); - - /*let static_entities: Vec = world - .query_filtered::>() - .iter(world) - .collect();*/ - println!("saveable entities {}", saveable_entities.len()); - - let mut scene_builder = DynamicSceneBuilder::from_world(world); - scene_builder - .deny::() - .deny::() - .deny::() - .deny::() - .deny::() - .deny::() - .deny::() - .deny::() - .deny::() - // camera stuff - .deny::() - .deny::() - .deny::() - .deny::() - .deny::() - .deny::() - //.deny::() - .extract_entities(saveable_entities.into_iter()); - - let dyn_scene = scene_builder.build(); - let serialized_scene = dyn_scene - .serialize_ron(world.resource::()) - .unwrap(); - - #[cfg(not(target_arch = "wasm32"))] - IoTaskPool::get() - .spawn(async move { - // Write the scene RON data to file - File::create(format!("assets/scenes/{NEW_SCENE_FILE_PATH}")) - .and_then(|mut file| file.write(serialized_scene.as_bytes())) - .expect("Error while writing scene to file"); - }) - .detach(); -} diff --git a/examples/bevy_gltf_blueprints/nested_blueprints/src/game/in_game.rs b/examples/bevy_gltf_blueprints/nested_blueprints/src/game/in_game.rs index 2500bbb3..2047798b 100644 --- a/examples/bevy_gltf_blueprints/nested_blueprints/src/game/in_game.rs +++ b/examples/bevy_gltf_blueprints/nested_blueprints/src/game/in_game.rs @@ -67,13 +67,12 @@ pub fn spawn_test( .spawn(( BluePrintBundle { blueprint: BlueprintName("Health_Pickup".to_string()), - transform: TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), ..Default::default() }, bevy::prelude::Name::from(format!("test{}", name_index)), // BlueprintName("Health_Pickup".to_string()), // SpawnHere, - // TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), + TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), Velocity { linvel: Vec3::new(vel_x, vel_y, vel_z), angvel: Vec3::new(0.0, 0.0, 0.0), diff --git a/examples/bevy_gltf_save_load/basic/Cargo.toml b/examples/bevy_gltf_save_load/basic/Cargo.toml new file mode 100644 index 00000000..71138dfa --- /dev/null +++ b/examples/bevy_gltf_save_load/basic/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "bevy_gltf_save_load_basic_example" +version = "0.3.0" +edition = "2021" +license = "MIT OR Apache-2.0" + +[dependencies] +bevy="0.12" +bevy_gltf_blueprints = "0.6" +bevy_gltf_save_load = { path = "../../../crates/bevy_gltf_save_load" } + +bevy_rapier3d = { version = "0.23.0", features = [ "serde-serialize", "debug-render-3d", "enhanced-determinism"] } +bevy_asset_loader = { version = "0.18", features = ["standard_dynamic_assets" ]} +bevy_editor_pls = { version = "0.6" } +rand = "0.8.5" +serde_json="1.0.108" +serde="1.0.193" \ No newline at end of file diff --git a/examples/bevy_gltf_save_load/basic/README.md b/examples/bevy_gltf_save_load/basic/README.md new file mode 100644 index 00000000..d680ade1 --- /dev/null +++ b/examples/bevy_gltf_save_load/basic/README.md @@ -0,0 +1,16 @@ +# Basic save_load example/demo + +This example showcases how to use ```bevy_save_load``` crate to save & load your bevy game + +## Notes Workflow with blender / demo information + +- the gltf files for this demo where generated using the **Export dynamic and static objects seperatly** feature of the auto_export addon: +so the static & dynamic level files where generated automatically, based on the entities that have a **Dynamic** component/custom property + + +## Running this example + +``` +cargo run --features bevy/dynamic_linking +``` + diff --git a/examples/bevy_gltf_save_load/basic/assets/assets_core.assets.ron b/examples/bevy_gltf_save_load/basic/assets/assets_core.assets.ron new file mode 100644 index 00000000..8d0a0994 --- /dev/null +++ b/examples/bevy_gltf_save_load/basic/assets/assets_core.assets.ron @@ -0,0 +1 @@ +({}) \ No newline at end of file diff --git a/examples/bevy_gltf_save_load/basic/assets/assets_game.assets.ron b/examples/bevy_gltf_save_load/basic/assets/assets_game.assets.ron new file mode 100644 index 00000000..aa737c48 --- /dev/null +++ b/examples/bevy_gltf_save_load/basic/assets/assets_game.assets.ron @@ -0,0 +1,8 @@ +({ + "world":File (path: "models/World.glb"), + "world_dynamic":File (path: "models/World_dynamic.glb"), + + "models": Folder ( + path: "models/library", + ), +}) \ No newline at end of file diff --git a/examples/bevy_gltf_save_load/basic/assets/basic.blend b/examples/bevy_gltf_save_load/basic/assets/basic.blend new file mode 100644 index 0000000000000000000000000000000000000000..3538a2a9c7c17ff7832b4baae1f50f8be8d7bc2c GIT binary patch literal 1597452 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

OcCAbVf)Tl;U@d-~A+`zbtrc>n#epC3=H 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 diff --git a/tools/gltf_auto_export/dynamic.py b/tools/gltf_auto_export/dynamic.py new file mode 100644 index 00000000..62addc16 --- /dev/null +++ b/tools/gltf_auto_export/dynamic.py @@ -0,0 +1,32 @@ +import bpy + + +# checks if an object is dynamic +# TODO: for efficiency, it might make sense to write this flag semi automatically at the root level of the object so we can skip the inner loop +# TODO: we need to recompute these on blueprint changes too +# even better, keep a list of dynamic objects per scene , updated only when needed ? +def is_object_dynamic(object): + is_dynamic = object['Dynamic'] if 'Dynamic' in object else False + # only look for data in the original collection if it is not alread marked as dynamic at instance level + if not is_dynamic and object.type == 'EMPTY' and hasattr(object, 'instance_collection'): + # get the name of the collection this is an instance of + collection_name = object.instance_collection.name + original_collection = bpy.data.collections[collection_name] + + #print("inspecting insides", original_collection) + + # scan original collection, look for a 'Dynamic' flag + for object in original_collection.objects: + #print(" inner", object) + if object.type == 'EMPTY' and object.name.endswith("components"): + for component_name in object.keys(): + #print(" compo", component_name) + if component_name == 'Dynamic': + is_dynamic = True + break + print("is object dynamic", is_dynamic, object) + + return is_dynamic + +def is_object_static(object): + return not is_object_dynamic(object) \ No newline at end of file diff --git a/tools/gltf_auto_export/helpers_export.py b/tools/gltf_auto_export/helpers_export.py index 346adb94..9acfb7d2 100644 --- a/tools/gltf_auto_export/helpers_export.py +++ b/tools/gltf_auto_export/helpers_export.py @@ -1,11 +1,13 @@ import os import bpy + from .preferences import (AutoExportGltfPreferenceNames) from .helpers_scenes import (generate_hollow_scene, clear_hollow_scene) from .helpers_collections import (recurLayerCollection) from .blueprints import clear_blueprint_hollow_scene, generate_blueprint_hollow_scene from .helpers import (traverse_tree) +from .dynamic import (is_object_dynamic, is_object_static) ###################################################### #### Export logic ##### @@ -138,19 +140,9 @@ def export_main_scene(scene, folder_path, addon_prefs, library_collections): gltf_export_preferences = generate_gltf_export_preferences(addon_prefs) export_output_folder = getattr(addon_prefs,"export_output_folder") export_blueprints = getattr(addon_prefs,"export_blueprints") - - if export_blueprints : - (hollow_scene, temporary_collections, root_objects, special_properties) = generate_hollow_scene(scene, library_collections, addon_prefs) - #except Exception: - # print("failed to create hollow scene") - - # set active scene to be the given scene - bpy.context.window.scene = hollow_scene - + export_separate_dynamic_and_static_objects = getattr(addon_prefs, "export_separate_dynamic_and_static_objects") + gltf_output_path = os.path.join(folder_path, export_output_folder, scene.name) - print(" exporting gltf to", gltf_output_path, ".gltf/glb") - - export_settings = { **gltf_export_preferences, 'use_active_scene': True, 'use_active_collection':True, @@ -159,10 +151,40 @@ def export_main_scene(scene, folder_path, addon_prefs, library_collections): 'use_renderable': False, 'export_apply':True } - export_gltf(gltf_output_path, export_settings) if export_blueprints : - clear_hollow_scene(hollow_scene, scene, temporary_collections, root_objects, special_properties) + if export_separate_dynamic_and_static_objects: + # first export all dynamic objects + (hollow_scene, temporary_collections, root_objects, special_properties) = generate_hollow_scene(scene, library_collections, addon_prefs, is_object_dynamic) + gltf_output_path = os.path.join(folder_path, export_output_folder, scene.name+ "_dynamic") + # set active scene to be the given scene + bpy.context.window.scene = hollow_scene + print(" exporting gltf to", gltf_output_path, ".gltf/glb") + export_gltf(gltf_output_path, export_settings) + clear_hollow_scene(hollow_scene, scene, temporary_collections, root_objects, special_properties) + + # now export static objects + (hollow_scene, temporary_collections, root_objects, special_properties) = generate_hollow_scene(scene, library_collections, addon_prefs, is_object_static) + gltf_output_path = os.path.join(folder_path, export_output_folder, scene.name) + # set active scene to be the given scene + bpy.context.window.scene = hollow_scene + print(" exporting gltf to", gltf_output_path, ".gltf/glb") + export_gltf(gltf_output_path, export_settings) + clear_hollow_scene(hollow_scene, scene, temporary_collections, root_objects, special_properties) + + else: + print("NO SPLIT") + # todo: add exception handling + (hollow_scene, temporary_collections, root_objects, special_properties) = generate_hollow_scene(scene, library_collections, addon_prefs) + # set active scene to be the given scene + bpy.context.window.scene = hollow_scene + print(" exporting gltf to", gltf_output_path, ".gltf/glb") + export_gltf(gltf_output_path, export_settings) + + clear_hollow_scene(hollow_scene, scene, temporary_collections, root_objects, special_properties) + else: + print(" exporting gltf to", gltf_output_path, ".gltf/glb") + export_gltf(gltf_output_path, export_settings) #https://docs.blender.org/api/current/bpy.ops.export_scene.html#bpy.ops.export_scene.gltf diff --git a/tools/gltf_auto_export/helpers_scenes.py b/tools/gltf_auto_export/helpers_scenes.py index 98a6b0af..d0a8bbdd 100644 --- a/tools/gltf_auto_export/helpers_scenes.py +++ b/tools/gltf_auto_export/helpers_scenes.py @@ -4,7 +4,7 @@ # generate a copy of a scene that replaces collection instances with empties # copy original names before creating a new scene, & reset them -def generate_hollow_scene(scene, library_collections, addon_prefs): +def generate_hollow_scene(scene, library_collections, addon_prefs, filter=None): collection_instances_combine_mode = getattr(addon_prefs, "collection_instances_combine_mode") root_collection = scene.collection @@ -29,6 +29,8 @@ def generate_hollow_scene(scene, library_collections, addon_prefs): # copies the contents of a collection into another one while replacing library instances with empties def copy_hollowed_collection_into(source_collection, destination_collection, parent_empty=None): for object in source_collection.objects: + if filter is not None and filter(object) is False: + continue #check if a specific collection instance does not have an ovveride for combine_mode combine_mode = object['_combine'] if '_combine' in object else collection_instances_combine_mode @@ -75,17 +77,6 @@ def copy_hollowed_collection_into(source_collection, destination_collection, par copy_hollowed_collection_into(collection, destination_collection, collection_placeholder) - - """ - copy_collection = bpy.data.collections.new(collection.name + "____collection_export") - # save the newly created collection for later reuse - temporary_collections.append(copy_collection) - - # copy & link objects - copy_hollowed_collection_into(collection, copy_collection) - destination_collection.children.link(copy_collection) - """ - copy_hollowed_collection_into(root_collection, copy_root_collection) return (temp_scene, temporary_collections, root_objects, special_properties) diff --git a/tools/gltf_auto_export/preferences.py b/tools/gltf_auto_export/preferences.py index 2c11c469..b86e5d45 100644 --- a/tools/gltf_auto_export/preferences.py +++ b/tools/gltf_auto_export/preferences.py @@ -21,6 +21,7 @@ 'export_marked_assets', 'collection_instances_combine_mode', + 'export_separate_dynamic_and_static_objects', 'export_materials_library', 'export_materials_path', @@ -89,7 +90,6 @@ class AutoExportGltfAddonPreferences(AddonPreferences): default='materials' ) - """ combine mode can be - 'Split' (default): replace with an empty, creating links to sub blueprints - 'Embed' : treat it as an embeded object and do not replace it with an empty @@ -115,6 +115,15 @@ class AutoExportGltfAddonPreferences(AddonPreferences): default=True ) + export_separate_dynamic_and_static_objects: BoolProperty( + name='Export dynamic and static objects seperatly', + description="""For MAIN scenes only (aka levels), toggle this to generate 2 files per level: + - one with all dynamic data: collection or instances marked as dynamic/ saveable + - one with all static data: anything else that is NOT marked as dynamic""", + default=True + ) + + main_scenes: CollectionProperty(name="main scenes", type=CUSTOM_PG_sceneName) main_scenes_index: IntProperty(name = "Index for main scenes list", default = 0) diff --git a/tools/gltf_auto_export/ui/main.py b/tools/gltf_auto_export/ui/main.py index a4f2368b..78be74c4 100644 --- a/tools/gltf_auto_export/ui/main.py +++ b/tools/gltf_auto_export/ui/main.py @@ -302,6 +302,7 @@ def draw(self, context): layout.prop(operator, "export_blueprints_path") layout.prop(operator, "collection_instances_combine_mode") layout.prop(operator, "export_marked_assets") + layout.prop(operator, "export_separate_dynamic_and_static_objects") layout.separator() # materials layout.prop(operator, "export_materials_library")

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 literal 0 HcmV?d00001 diff --git a/examples/bevy_gltf_save_load/basic/assets/models/World.glb b/examples/bevy_gltf_save_load/basic/assets/models/World.glb new file mode 100644 index 0000000000000000000000000000000000000000..6366b824a85ae29a56f61b9f7dd4d49d99258925 GIT binary patch literal 1536 zcmb_cO>fgc6pWDIcSL*Mntl8BGshmFP@t$Sr#7mrWR*s8?O;2FD#}0RM=+bDQUfUE z&>SNBdHmkY>^#q^%XhB^_`Mkne!U(HK3<%CS&6diQZGg;F-vu7%f4yFNSxlbP2F_E z8D1G4SE-(+cKG4#+3?;JsxU01xKC|2Z)$*bp~PB>^s`TOE_L6!G!>&Oae8uoQ_W|$ zeRs21){}l&R$}}Vc%Hs5=k0%jGsk~*M0q;|BUfV5R9V3lNmT^a!3XbxF-AFJluU5e zXdAR8ii&~*pLx9@-am*DdF8DX{lg+b*c99*Jrser6m@x*i=VO}u^z7>xtqW(kxhW(&Z(%{1kwN|Mx)rIA*;@fyP)pRAtv_ncBp%+vP>NzpcanaN_z5ri70 zNPEjlMIRj*h<7nWr;KLfqBU%@ACUo41Xr9kF{8BCF$BB=9I@J1)VF2bRgdkF0S`2K z`=~WLmJ|vXoXg#`#X#re5fw^VfTA&4p_vckwcN;TOL(yo$JH_|TDbpZ>+H4oI)bu9%~C-hgT}IRm8Oj3!T! zgTY@G9i6j2;8v?>h;l6UHEV{YR!7Us0cw=Z3SkhMVx%?RXmm%797tHFg4N2ZmsPEy zdY>2R0uHl^F?wr*%||GS3U;KKg5nU3GW?R(=k5Q41hR!qizaAz=VH`bBPg)(IEjd- z@Jka`(mE_zUP2U~PsIDSS=Q4-iP$0#k;BpYT+Zf`%Tz9!{!q6kuwBUZm2!d8C8Q-- zA4!pQl8fNA@&=ow7+E*+$QO)YV2`jOjGBMe{)@$JYMVpMwury2$5AVGk-aD0<`UsC$F*W0$P>aJMG zk>Y5TtDD>wpWmMrYcgQSDXrGI?Ur>V&enk1Y1Pf-0yTlhS*IqKTUC|!VpDy-Z>|px zT2=K;9(F;aw%Sd1(`VaO&E!N|?(fMz`nD9$t@^MxUbN-xZdol(=kq=Dr40r&CHakq zsoe8%BN3M~HR;=jEKrZ}59futt^2ayV_%NJxa7c*x6uK{l$_IIBo`4wu$C<)pg7np zI%E&lvxO8QTMZ^A?+8NzAWj^tL>JtuTRjDE(L_!}A^0H%k@nGeh~yn(A|JtN;dT@l zfE_a-L749JRy{xcX!Verc3Jf&hg4pvvsJmt?Uj<}oHyn7>NvMUj%E3m*B=hZ{!2bE z!a>Oe2noG&2{oF)DJExQq9DwJg_x1SB3ce0j{oF4&gH7Vy*^vc?jD-gn9o&t;b;Kx z72+6;b1-a58HkCBXc`G^sNgXEe16Xh#Hl8i-<^o$V^d6von2>~T0t$xl+X=wy$%%4>r=3r#CACgoMv7?ou^qXdo(eIIW(_K*% FzX16jGQ0o) literal 0 HcmV?d00001 diff --git a/examples/bevy_gltf_save_load/basic/assets/models/library/Container.glb b/examples/bevy_gltf_save_load/basic/assets/models/library/Container.glb new file mode 100644 index 0000000000000000000000000000000000000000..3fbdb91c617a29d472a97688c5cad523d57a8490 GIT binary patch literal 6060 zcmeHKTXWk)6c(l2pS%q#E5Xd7y4XIxQq|CJBX^PDYXKwIfg@k0gg=GM(`c z;V1AP(x1k6R=c)r*|`jbCwAi9vuDrw&SlT;D#PU9$sI*e{`yK$p59ZGANM;u7iv@# ztW?)8)FDe*9+g?HuB$&C=UJK+YeT54Jx*BKXZhN*?>cLzc1v&Bma3_zEHC0Lg|OMu z)t8!D^Z=l)>w+rO^%oZ^J1g@D41^KeecbTl;@ zjgs^5Br6Bky(QZW5i(bDpv&DUZWl#5o?- zMYp42+&f^=C@bsipVM){`eB?_{)Z(iunq^KahZj~q#SH=Pxo0_B0X@Dc8YutvvD~Z zm*F5wu;uU>31Ufscu(MG$oDv;a40bKmn={EDsYo(Hy79CCLg{;{zMWa4-7yPjIJ4) zsac2QpyXYs(;5(Z(^R!|-QmH`YHKeZ#{I`x|9m&k&QM9}llYAFAta5Wj;cI|z3gO^ zrM#GEA7&V|XK@wtHkBWgD;H1+!i74D5T;^}5^U}Zqh%PrYulb-TaIJ9zOUVgg`jan z(jtkdUU)hl=q*Dx(H!44d@t~Us_BRG{_eB1;Aa(kv(k5SiSI?F_@8=?AaSr*9!IDp zIuZSB+)da%5{~m%=3_Q>bTd5-yUCa#tWh`LW+je!+}q2>!{h3VU#Q)vUc7^4*grstcEg|Kh}XycZ!{z;5HAH^}h zfoAvdxXW6)LGRlrk5A$|zfjGL4$%iYfYvx8@y9sd68boRC%ZSg1LVDs0F zPG|3jP*>N@mzo-<{kTUe*6aX4=OsVO@?zsQb>rqXZB3On@sQPRx+`rO%WU2vPH$$@ zpSKyTv}wX7(j4`W0^C*A$?uK_1C~FJ*&jT4)BE_~e1sF_7^aK2H5;e+CP5wJ+Bl=j z6%PbIr!n1-4-8G$9?-nxO6v2?4U0bsb#UC<*MVQ9j;r(L>Sn__HoU3MnLIQLQ3~t( z8;`ag?KJ=pdlTSw3fRu_}LEryPRC2qw(Q2bh{?AiY6T3O);0z zq$~N$X!t>m`*RiDuF0&T35RS+oMklWO8zn$YrIXwKyyJ8r=GV(zFa51%!!oYbMR&3 zJ}lUxHL1sAm{igY-G`MQd{xL0J&had+y@$|O?Y2n3z|6GhXonQ=i?HbSKkXeG~!2d zYHN@s`!rHJqPbXa6OZIDl041V@T-qUBqP?PUVS8anlFxoC-v$h$g9`joO6w%WH2zhKp0a(IzxbZh!-fMO$27U`w1S516vi T8eb>IL~>_5sk2w34bhtKf2WDVQvoKD+*Ie)c#V1cPBreDcV2!4;kl-Ihd|AHF8O-rY?PF&Fxi$h8yBhQftqS24dNhr&y!_T z2nU!AFWyVm*zLi2;C{j|je;~veX{0AtFupxqQob(uEj3fs*+4XpL7W_s<+g%8f5@$ z+B_5Nks(Wn`qoOJ*j!Njf_7ieJpUbxZLtqNs;!kateh$#fI=R-u_uyl5t1yp5L82} zBQjOeP!8Qs!G)l^Pm#Bb3bV-;sI#q4O=q;?#fs5dnN#=|**2`qxa|06hBep+LE0LX zRl=;V9MdH;2J(QU?Ud!7sn*o3$<|7s<=_l~m}pS%7CbZ8%qg!?? zWY*jDx$9idn88f);!d^IrW?pT$!Wt-hth9o;SLwrMNtqX<0J`&<8gW(CS!1U85TmR zZJ*{45N84+bQzD>AbGN?TDhFa$C1NyNe0dd^r|)Aa>E~c3u=t!d5dYW1F@JduUFUe zTX)88=XbM<8+R;LSHDf?^Sd9ifJ`EEgv*i_)+M$>(rE{uCnqK?u+=EtiJ7i|4~80SacT}@** z{-^PAfWHv`zr3UW;T?UJ8%lhh+kvh&RN3cVPA_gQ?%dJ*m;0$!PN#fl*bS4F!KFAn z_lfSP+L7*S zKPfmI+EMxw|F>wJ+S)nW&h5DV%6kpu IwfD;V2Lcl`k^lez literal 0 HcmV?d00001 diff --git a/examples/bevy_gltf_save_load/basic/assets/models/library/Health_Pickup.glb b/examples/bevy_gltf_save_load/basic/assets/models/library/Health_Pickup.glb new file mode 100644 index 0000000000000000000000000000000000000000..01e90810c67ef28b472f09b7c9b8252bfae46f51 GIT binary patch literal 5572 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 NIJ%lfpwns${XeUspLqZP literal 0 HcmV?d00001 diff --git a/examples/bevy_gltf_save_load/basic/assets/models/library/Pillar.glb b/examples/bevy_gltf_save_load/basic/assets/models/library/Pillar.glb new file mode 100644 index 0000000000000000000000000000000000000000..6ce36be5b5e601f607a3c6020048ed6589514b2b GIT binary patch literal 4104 zcmb_fZFAd15I#x^1?(BMnh{U-*`Ra;;l@4s59Ol`_V`Yjuu{gH|QmPat@&MO%MgcbLA69k?2crBGR!! z>TAQ)Pedev19Z((czzfvgB%C_H<3tbyWVc7%QQPyqhoapJ`9jH@s^w{$Kd{S8lHQT zc)&MIgD(*@CX1Y;`p#6Y#vIFVwo z6^9|LXb)}7c;rt2$q#}s^b_=|ux!PV^aJ$0Y&;14^JU9R;fHeU9iYx^T6W#@M?wEs z_|sUH&A&xiDh6H-7IFr*%dc!!xk%KcD zB*GN+PQcSsXY5fqR2cs~PQn2PZn!wj>VAI6Cl~0SZbEK@0nh}~FigWbA$qxG#)}D9 zYMnO{@kB_B4fXJ9wy591g6|%Uj=>LuQ83ty2N)~{4g9M34HW}PIVAxy-*w>F=j|6nJ8knqcbl~Xx>3(+DrC?#IOea!#Ep_ z7n3^UJwFv79w#sSzIy9k7t`A^9m{cA&6d%qTTR-_n(8-_3ZO*dU?4YUWD81w5_(&Xqrv4?Ya)?a$T%k z)9qNU*=brfOs3OuTDEO>8ZN4C+3!=O@=eETpsrr8tS9P?Mu!SWf=M8QGwt9@nFPHI zF9yy2LHB6qc&EFsJ}3L#!@Xy_>I-nZ{l`|fd-&XI@D1z2;6XG9`V`W!_I#i0c~5d5wT3;vXkGQ8z1*gHF<*GE#6DT@P`lcP^m6|c2l%OL zu0^#L;!yD+&nkzq&p$uw7?QYYO;-&#{IqF zdw}Ny_7VGmeZlVGd5__bpQRp^`&HG8S-mXHZAAy1!Yg92U)9U=xmHK53I}q2gI@4~ z&vm-Oxh&zV(F+baj`k_+SgUKU1+BGC7UG!KSheT-YKCva!Twqt9jAJs^C~~B=Q;9T z-N*r4)iu|Gu(eJW!d;6~;ZXLWxm9bDxYZm3tA<=;CrQ^St@Zv>vO#B%^qxG`<51W$=UH||9 literal 0 HcmV?d00001 diff --git a/examples/bevy_gltf_save_load/basic/assets/models/library/Player.glb b/examples/bevy_gltf_save_load/basic/assets/models/library/Player.glb new file mode 100644 index 0000000000000000000000000000000000000000..b9f31498fbd9eb177120c625af82a558618bd0e2 GIT binary patch literal 28896 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 B(ggqj literal 0 HcmV?d00001 diff --git a/examples/bevy_gltf_save_load/basic/assets/models/library/Sphero.glb b/examples/bevy_gltf_save_load/basic/assets/models/library/Sphero.glb new file mode 100644 index 0000000000000000000000000000000000000000..9477a17b9f9564c409ffb3dbddb09fe1bffca7a3 GIT binary patch literal 9668 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 W^pJi-2e1dUM, + + #[asset(key = "world_dynamic")] + pub world_dynamic: Handle, + + #[asset(key = "models", collection(typed, mapped))] + pub models: HashMap>, +} diff --git a/examples/bevy_gltf_save_load/basic/src/assets/mod.rs b/examples/bevy_gltf_save_load/basic/src/assets/mod.rs new file mode 100644 index 00000000..a2c8b22a --- /dev/null +++ b/examples/bevy_gltf_save_load/basic/src/assets/mod.rs @@ -0,0 +1,35 @@ +pub mod assets_core; +pub use assets_core::*; + +pub mod assets_game; +pub use assets_game::*; + +use bevy::prelude::*; +use bevy_asset_loader::prelude::*; + +use crate::state::AppState; + +pub struct AssetsPlugin; +impl Plugin for AssetsPlugin { + fn build(&self, app: &mut App) { + app + // load core assets (ie assets needed in the main menu, and everywhere else before loading more assets in game) + .add_loading_state( + LoadingState::new(AppState::CoreLoading).continue_to_state(AppState::MenuRunning), + ) + .add_dynamic_collection_to_loading_state::<_, StandardDynamicAssetCollection>( + AppState::CoreLoading, + "assets_core.assets.ron", + ) + .add_collection_to_loading_state::<_, CoreAssets>(AppState::CoreLoading) + // load game assets + .add_loading_state( + LoadingState::new(AppState::AppLoading).continue_to_state(AppState::AppRunning), + ) + .add_dynamic_collection_to_loading_state::<_, StandardDynamicAssetCollection>( + AppState::AppLoading, + "assets_game.assets.ron", + ) + .add_collection_to_loading_state::<_, GameAssets>(AppState::AppLoading); + } +} diff --git a/examples/bevy_gltf_save_load/basic/src/core/camera/camera_replace_proxies.rs b/examples/bevy_gltf_save_load/basic/src/core/camera/camera_replace_proxies.rs new file mode 100644 index 00000000..9055c95b --- /dev/null +++ b/examples/bevy_gltf_save_load/basic/src/core/camera/camera_replace_proxies.rs @@ -0,0 +1,24 @@ +use bevy::core_pipeline::bloom::{BloomCompositeMode, BloomSettings}; +use bevy::core_pipeline::tonemapping::{DebandDither, Tonemapping}; +use bevy::prelude::*; + +use super::CameraTrackingOffset; + +pub fn camera_replace_proxies( + mut commands: Commands, + mut added_cameras: Query<(Entity, &mut Camera), (Added, With)>, +) { + for (entity, mut camera) in added_cameras.iter_mut() { + info!("detected added camera, updating proxy"); + camera.hdr = true; + commands + .entity(entity) + .insert(DebandDither::Enabled) + .insert(Tonemapping::BlenderFilmic) + .insert(BloomSettings { + intensity: 0.01, + composite_mode: BloomCompositeMode::Additive, + ..default() + }); + } +} diff --git a/examples/bevy_gltf_save_load/basic/src/core/camera/camera_tracking.rs b/examples/bevy_gltf_save_load/basic/src/core/camera/camera_tracking.rs new file mode 100644 index 00000000..62da84d7 --- /dev/null +++ b/examples/bevy_gltf_save_load/basic/src/core/camera/camera_tracking.rs @@ -0,0 +1,58 @@ +use bevy::prelude::*; + +#[derive(Component, Reflect, Debug)] +#[reflect(Component)] +/// Component for cameras, with an offset from the Trackable target +/// +pub struct CameraTracking { + pub offset: Vec3, +} +impl Default for CameraTracking { + fn default() -> Self { + CameraTracking { + offset: Vec3::new(0.0, 6.0, 8.0), + } + } +} + +#[derive(Component, Reflect, Debug, Deref, DerefMut)] +#[reflect(Component)] +/// Component for cameras, with an offset from the Trackable target +pub struct CameraTrackingOffset(Vec3); +impl Default for CameraTrackingOffset { + fn default() -> Self { + CameraTrackingOffset(Vec3::new(0.0, 6.0, 8.0)) + } +} + +impl CameraTrackingOffset { + fn new(input: Vec3) -> Self { + CameraTrackingOffset(input) + } +} + +#[derive(Component, Reflect, Default, Debug)] +#[reflect(Component)] +/// Add this component to an entity if you want it to be tracked by a Camera +pub struct CameraTrackable; + +pub fn camera_track( + mut tracking_cameras: Query< + (&mut Transform, &CameraTrackingOffset), + ( + With, + With, + Without, + ), + >, + camera_tracked: Query<&Transform, With>, +) { + for (mut camera_transform, tracking_offset) in tracking_cameras.iter_mut() { + for tracked_transform in camera_tracked.iter() { + let target_position = tracked_transform.translation + tracking_offset.0; + let eased_position = camera_transform.translation.lerp(target_position, 0.1); + camera_transform.translation = eased_position; // + tracking.offset;// tracked_transform.translation + tracking.offset; + *camera_transform = camera_transform.looking_at(tracked_transform.translation, Vec3::Y); + } + } +} diff --git a/examples/bevy_gltf_save_load/basic/src/core/camera/mod.rs b/examples/bevy_gltf_save_load/basic/src/core/camera/mod.rs new file mode 100644 index 00000000..a6bbb65d --- /dev/null +++ b/examples/bevy_gltf_save_load/basic/src/core/camera/mod.rs @@ -0,0 +1,24 @@ +pub mod camera_tracking; +pub use camera_tracking::*; + +pub mod camera_replace_proxies; +pub use camera_replace_proxies::*; + +use bevy::prelude::*; +use bevy_gltf_blueprints::GltfBlueprintsSet; + +pub struct CameraPlugin; +impl Plugin for CameraPlugin { + fn build(&self, app: &mut App) { + app.register_type::() + .register_type::() + .register_type::() + .add_systems( + Update, + ( + camera_replace_proxies.after(GltfBlueprintsSet::AfterSpawn), + camera_track, + ), + ); + } +} diff --git a/examples/bevy_gltf_save_load/basic/src/core/lighting/lighting_replace_proxies.rs b/examples/bevy_gltf_save_load/basic/src/core/lighting/lighting_replace_proxies.rs new file mode 100644 index 00000000..48c09087 --- /dev/null +++ b/examples/bevy_gltf_save_load/basic/src/core/lighting/lighting_replace_proxies.rs @@ -0,0 +1,25 @@ +use bevy::prelude::*; + +use bevy::pbr::{CascadeShadowConfig, CascadeShadowConfigBuilder}; + +// fixme might be too specific to might needs, should it be moved out ? also these are all for lights, not models +pub fn lighting_replace_proxies( + mut added_dirights: Query<(Entity, &mut DirectionalLight), Added>, + mut added_spotlights: Query<&mut SpotLight, Added>, + mut commands: Commands, +) { + for (entity, mut light) in added_dirights.iter_mut() { + light.illuminance *= 5.0; + light.shadows_enabled = true; + let shadow_config: CascadeShadowConfig = CascadeShadowConfigBuilder { + first_cascade_far_bound: 15.0, + maximum_distance: 135.0, + ..default() + } + .into(); + commands.entity(entity).insert(shadow_config); + } + for mut light in added_spotlights.iter_mut() { + light.shadows_enabled = true; + } +} diff --git a/examples/bevy_gltf_save_load/basic/src/core/lighting/mod.rs b/examples/bevy_gltf_save_load/basic/src/core/lighting/mod.rs new file mode 100644 index 00000000..c9688cda --- /dev/null +++ b/examples/bevy_gltf_save_load/basic/src/core/lighting/mod.rs @@ -0,0 +1,18 @@ +mod lighting_replace_proxies; +use lighting_replace_proxies::*; + +use bevy::pbr::{DirectionalLightShadowMap, NotShadowCaster}; +use bevy::prelude::*; + +pub struct LightingPlugin; +impl Plugin for LightingPlugin { + fn build(&self, app: &mut App) { + app + .insert_resource(DirectionalLightShadowMap { size: 4096 }) + // FIXME: adding these since they are missing + .register_type::() + + .add_systems(PreUpdate, lighting_replace_proxies) // FIXME: you should actually run this in a specific state most likely + ; + } +} diff --git a/examples/bevy_gltf_save_load/basic/src/core/mod.rs b/examples/bevy_gltf_save_load/basic/src/core/mod.rs new file mode 100644 index 00000000..2a42178e --- /dev/null +++ b/examples/bevy_gltf_save_load/basic/src/core/mod.rs @@ -0,0 +1,62 @@ +pub mod camera; +pub use camera::*; + +pub mod lighting; +pub use lighting::*; + +pub mod relationships; +pub use relationships::*; + +pub mod physics; +pub use physics::*; + +use bevy::{ + core_pipeline::tonemapping::Tonemapping, + prelude::*, + render::{camera::CameraRenderGraph, primitives::Frustum, view::VisibleEntities}, + utils::HashSet, +}; +use bevy_rapier3d::dynamics::Velocity; +use std::any::TypeId; + +use bevy_gltf_blueprints::*; +use bevy_gltf_save_load::*; + +use crate::game::Pickable; + +pub struct CorePlugin; +impl Plugin for CorePlugin { + fn build(&self, app: &mut App) { + app.add_plugins(( + LightingPlugin, + CameraPlugin, + PhysicsPlugin, + SaveLoadPlugin { + save_path: "scenes".into(), + component_filter: SceneFilter::Allowlist(HashSet::from([ + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + ])), + ..Default::default() + }, + BlueprintsPlugin { + library_folder: "models/library".into(), + format: GltfFormat::GLB, + aabbs: true, + ..Default::default() + }, + )); + } +} diff --git a/examples/bevy_gltf_save_load/basic/src/core/physics/controls.rs b/examples/bevy_gltf_save_load/basic/src/core/physics/controls.rs new file mode 100644 index 00000000..25625485 --- /dev/null +++ b/examples/bevy_gltf_save_load/basic/src/core/physics/controls.rs @@ -0,0 +1,26 @@ +use bevy::{ + ecs::system::Res, + input::{keyboard::KeyCode, Input}, + log::info, + prelude::ResMut, +}; +use bevy_rapier3d::{prelude::RapierConfiguration, render::DebugRenderContext}; + +pub fn pause_physics(mut physics_config: ResMut) { + info!("pausing physics"); + physics_config.physics_pipeline_active = false; +} + +pub fn resume_physics(mut physics_config: ResMut) { + info!("unpausing physics"); + physics_config.physics_pipeline_active = true; +} + +pub fn toggle_physics_debug( + mut debug_config: ResMut, + keycode: Res>, +) { + if keycode.just_pressed(KeyCode::D) { + debug_config.enabled = !debug_config.enabled; + } +} diff --git a/examples/bevy_gltf_save_load/basic/src/core/physics/mod.rs b/examples/bevy_gltf_save_load/basic/src/core/physics/mod.rs new file mode 100644 index 00000000..ac1b0019 --- /dev/null +++ b/examples/bevy_gltf_save_load/basic/src/core/physics/mod.rs @@ -0,0 +1,38 @@ +pub mod physics_replace_proxies; +use bevy_rapier3d::{ + prelude::{NoUserData, RapierPhysicsPlugin}, + render::RapierDebugRenderPlugin, +}; +pub use physics_replace_proxies::*; + +pub mod utils; + +pub mod controls; +pub use controls::*; + +use crate::state::GameState; +use bevy::prelude::*; +// use super::blueprints::GltfBlueprintsSet; +use bevy_gltf_blueprints::GltfBlueprintsSet; +// use crate::Collider; +pub struct PhysicsPlugin; +impl Plugin for PhysicsPlugin { + fn build(&self, app: &mut App) { + app.add_plugins(( + RapierPhysicsPlugin::::default(), + RapierDebugRenderPlugin::default(), + )) + .register_type::() + .register_type::() + // find a way to make serde's stuff serializable + // .register_type::() + //bevy_rapier3d::dynamics::CoefficientCombineRule + .add_systems( + Update, + physics_replace_proxies.after(GltfBlueprintsSet::AfterSpawn), + ) + .add_systems(OnEnter(GameState::InGame), resume_physics) + .add_systems(OnExit(GameState::InGame), pause_physics) + .add_systems(Update, toggle_physics_debug); + } +} diff --git a/examples/bevy_gltf_save_load/basic/src/core/physics/physics_replace_proxies.rs b/examples/bevy_gltf_save_load/basic/src/core/physics/physics_replace_proxies.rs new file mode 100644 index 00000000..b91462c7 --- /dev/null +++ b/examples/bevy_gltf_save_load/basic/src/core/physics/physics_replace_proxies.rs @@ -0,0 +1,101 @@ +use bevy::prelude::*; +// use bevy::render::primitives::Aabb; +use bevy_rapier3d::geometry::Collider as RapierCollider; +use bevy_rapier3d::prelude::{ActiveCollisionTypes, ActiveEvents, ComputedColliderShape}; + +use super::utils::*; + +#[derive(Component, Reflect, Default, Debug)] +#[reflect(Component)] +pub enum Collider { + Ball(f32), + Cuboid(Vec3), + Capsule(Vec3, Vec3, f32), + #[default] + Mesh, +} + +#[derive(Component, Reflect, Default, Debug)] +#[reflect(Component)] +pub enum AutoAABBCollider { + #[default] + Cuboid, + Ball, + Capsule, +} + +// replaces all physics stand-ins with the actual rapier types +pub fn physics_replace_proxies( + meshes: Res>, + mesh_handles: Query<&Handle>, + mut proxy_colliders: Query< + (Entity, &Collider, &Name, &mut Visibility), + (Without, Added), + >, + // needed for tri meshes + children: Query<&Children>, + + mut commands: Commands, +) { + for proxy_colider in proxy_colliders.iter_mut() { + let (entity, collider_proxy, name, mut visibility) = proxy_colider; + // we hide the collider meshes: perhaps they should be removed altogether once processed ? + if name.ends_with("_collider") || name.ends_with("_sensor") { + *visibility = Visibility::Hidden; + } + + let mut rapier_collider: RapierCollider; + match collider_proxy { + Collider::Ball(radius) => { + info!("generating collider from proxy: ball"); + rapier_collider = RapierCollider::ball(*radius); + commands.entity(entity) + .insert(rapier_collider) + .insert(ActiveEvents::COLLISION_EVENTS) // FIXME: this is just for demo purposes !!! + ; + } + Collider::Cuboid(size) => { + info!("generating collider from proxy: cuboid"); + rapier_collider = RapierCollider::cuboid(size.x, size.y, size.z); + commands.entity(entity) + .insert(rapier_collider) + .insert(ActiveEvents::COLLISION_EVENTS) // FIXME: this is just for demo purposes !!! + ; + } + Collider::Capsule(a, b, radius) => { + info!("generating collider from proxy: capsule"); + rapier_collider = RapierCollider::capsule(*a, *b, *radius); + commands.entity(entity) + .insert(rapier_collider) + .insert(ActiveEvents::COLLISION_EVENTS) // FIXME: this is just for demo purposes !!! + ; + } + Collider::Mesh => { + info!("generating collider from proxy: mesh"); + for (_, collider_mesh) in + Mesh::search_in_children(entity, &children, &meshes, &mesh_handles) + { + rapier_collider = RapierCollider::from_bevy_mesh( + collider_mesh, + &ComputedColliderShape::TriMesh, + ) + .unwrap(); + commands + .entity(entity) + .insert(rapier_collider) + // FIXME: this is just for demo purposes !!! + .insert( + ActiveCollisionTypes::default() + | ActiveCollisionTypes::KINEMATIC_STATIC + | ActiveCollisionTypes::STATIC_STATIC + | ActiveCollisionTypes::DYNAMIC_STATIC, + ) + .insert(ActiveEvents::COLLISION_EVENTS); + // .insert(ActiveEvents::COLLISION_EVENTS) + // break; + // RapierCollider::convex_hull(points) + } + } + } + } +} diff --git a/examples/bevy_gltf_save_load/basic/src/core/physics/utils.rs b/examples/bevy_gltf_save_load/basic/src/core/physics/utils.rs new file mode 100644 index 00000000..7886710f --- /dev/null +++ b/examples/bevy_gltf_save_load/basic/src/core/physics/utils.rs @@ -0,0 +1,175 @@ +use bevy::prelude::*; +use bevy::render::mesh::{MeshVertexAttributeId, PrimitiveTopology, VertexAttributeValues}; +// TAKEN VERBATIB FROM https://github.com/janhohenheim/foxtrot/blob/src/util/trait_extension.rs + +pub(crate) trait Vec3Ext: Copy { + fn is_approx_zero(self) -> bool; + fn split(self, up: Vec3) -> SplitVec3; +} +impl Vec3Ext for Vec3 { + #[inline] + fn is_approx_zero(self) -> bool { + self.length_squared() < 1e-5 + } + + #[inline] + fn split(self, up: Vec3) -> SplitVec3 { + let vertical = up * self.dot(up); + let horizontal = self - vertical; + SplitVec3 { + vertical, + horizontal, + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub(crate) struct SplitVec3 { + pub(crate) vertical: Vec3, + pub(crate) horizontal: Vec3, +} + +pub(crate) trait Vec2Ext: Copy { + fn is_approx_zero(self) -> bool; + fn x0y(self) -> Vec3; +} +impl Vec2Ext for Vec2 { + #[inline] + fn is_approx_zero(self) -> bool { + self.length_squared() < 1e-5 + } + + #[inline] + fn x0y(self) -> Vec3 { + Vec3::new(self.x, 0., self.y) + } +} + +pub(crate) trait MeshExt { + fn transform(&mut self, transform: Transform); + fn transformed(&self, transform: Transform) -> Mesh; + fn read_coords_mut(&mut self, id: impl Into) -> &mut Vec<[f32; 3]>; + fn search_in_children<'a>( + parent: Entity, + children: &'a Query<&Children>, + meshes: &'a Assets, + mesh_handles: &'a Query<&Handle>, + ) -> Vec<(Entity, &'a Mesh)>; +} + +impl MeshExt for Mesh { + fn transform(&mut self, transform: Transform) { + for coords in self.read_coords_mut(Mesh::ATTRIBUTE_POSITION.clone()) { + let vec3 = (*coords).into(); + let transformed = transform.transform_point(vec3); + *coords = transformed.into(); + } + for normal in self.read_coords_mut(Mesh::ATTRIBUTE_NORMAL.clone()) { + let vec3 = (*normal).into(); + let transformed = transform.rotation.mul_vec3(vec3); + *normal = transformed.into(); + } + } + + fn transformed(&self, transform: Transform) -> Mesh { + let mut mesh = self.clone(); + mesh.transform(transform); + mesh + } + + fn read_coords_mut(&mut self, id: impl Into) -> &mut Vec<[f32; 3]> { + // Guaranteed by Bevy for the current usage + match self + .attribute_mut(id) + .expect("Failed to read unknown mesh attribute") + { + VertexAttributeValues::Float32x3(values) => values, + // Guaranteed by Bevy for the current usage + _ => unreachable!(), + } + } + + fn search_in_children<'a>( + parent: Entity, + children_query: &'a Query<&Children>, + meshes: &'a Assets, + mesh_handles: &'a Query<&Handle>, + ) -> Vec<(Entity, &'a Mesh)> { + if let Ok(children) = children_query.get(parent) { + let mut result: Vec<_> = children + .iter() + .filter_map(|entity| mesh_handles.get(*entity).ok().map(|mesh| (*entity, mesh))) + .map(|(entity, mesh_handle)| { + ( + entity, + meshes + .get(mesh_handle) + .expect("Failed to get mesh from handle"), + ) + }) + .map(|(entity, mesh)| { + assert_eq!(mesh.primitive_topology(), PrimitiveTopology::TriangleList); + (entity, mesh) + }) + .collect(); + let mut inner_result = children + .iter() + .flat_map(|entity| { + Self::search_in_children(*entity, children_query, meshes, mesh_handles) + }) + .collect(); + result.append(&mut inner_result); + result + } else { + Vec::new() + } + } +} + +pub(crate) trait F32Ext: Copy { + fn is_approx_zero(self) -> bool; + fn squared(self) -> f32; + fn lerp(self, other: f32, ratio: f32) -> f32; +} + +impl F32Ext for f32 { + #[inline] + fn is_approx_zero(self) -> bool { + self.abs() < 1e-5 + } + + #[inline] + fn squared(self) -> f32 { + self * self + } + + #[inline] + fn lerp(self, other: f32, ratio: f32) -> f32 { + self.mul_add(1. - ratio, other * ratio) + } +} + +pub(crate) trait TransformExt: Copy { + fn horizontally_looking_at(self, target: Vec3, up: Vec3) -> Transform; + fn lerp(self, other: Transform, ratio: f32) -> Transform; +} + +impl TransformExt for Transform { + fn horizontally_looking_at(self, target: Vec3, up: Vec3) -> Transform { + let direction = target - self.translation; + let horizontal_direction = direction - up * direction.dot(up); + let look_target = self.translation + horizontal_direction; + self.looking_at(look_target, up) + } + + fn lerp(self, other: Transform, ratio: f32) -> Transform { + let translation = self.translation.lerp(other.translation, ratio); + let rotation = self.rotation.slerp(other.rotation, ratio); + let scale = self.scale.lerp(other.scale, ratio); + Transform { + translation, + rotation, + scale, + } + } +} diff --git a/examples/bevy_gltf_save_load/basic/src/core/physics/utils_old.rs b/examples/bevy_gltf_save_load/basic/src/core/physics/utils_old.rs new file mode 100644 index 00000000..c210dd3a --- /dev/null +++ b/examples/bevy_gltf_save_load/basic/src/core/physics/utils_old.rs @@ -0,0 +1,75 @@ +use bevy::prelude::*; +use bevy::render::mesh::{MeshVertexAttributeId, PrimitiveTopology, VertexAttributeValues}; +// TAKEN VERBATIB FROM https://github.com/janhohenheim/foxtrot/blob/6e31fc02652fc9d085a4adde0a73ab007dbbb0dc/src/util/trait_extension.rs + +pub trait Vec3Ext { + #[allow(clippy::wrong_self_convention)] // Because [`Vec3`] is [`Copy`] + fn is_approx_zero(self) -> bool; + fn x0z(self) -> Vec3; +} +impl Vec3Ext for Vec3 { + fn is_approx_zero(self) -> bool { + [self.x, self.y, self.z].iter().all(|&x| x.abs() < 1e-5) + } + fn x0z(self) -> Vec3 { + Vec3::new(self.x, 0., self.z) + } +} + +pub trait MeshExt { + fn transform(&mut self, transform: Transform); + fn transformed(&self, transform: Transform) -> Mesh; + fn read_coords_mut(&mut self, id: impl Into) -> &mut Vec<[f32; 3]>; + fn search_in_children<'a>( + children: &'a Children, + meshes: &'a Assets, + mesh_handles: &'a Query<&Handle>, + ) -> (Entity, &'a Mesh); +} + +impl MeshExt for Mesh { + fn transform(&mut self, transform: Transform) { + for attribute in [Mesh::ATTRIBUTE_POSITION, Mesh::ATTRIBUTE_NORMAL] { + for coords in self.read_coords_mut(attribute.clone()) { + let vec3 = (*coords).into(); + let transformed = transform.transform_point(vec3); + *coords = transformed.into(); + } + } + } + + fn transformed(&self, transform: Transform) -> Mesh { + let mut mesh = self.clone(); + mesh.transform(transform); + mesh + } + + fn read_coords_mut(&mut self, id: impl Into) -> &mut Vec<[f32; 3]> { + match self.attribute_mut(id).unwrap() { + VertexAttributeValues::Float32x3(values) => values, + // Guaranteed by Bevy + _ => unreachable!(), + } + } + + fn search_in_children<'a>( + children: &'a Children, + meshes: &'a Assets, + mesh_handles: &'a Query<&Handle>, + ) -> (Entity, &'a Mesh) { + let entity_handles: Vec<_> = children + .iter() + .filter_map(|entity| mesh_handles.get(*entity).ok().map(|mesh| (*entity, mesh))) + .collect(); + assert_eq!( + entity_handles.len(), + 1, + "Collider must contain exactly one mesh, but found {}", + entity_handles.len() + ); + let (entity, mesh_handle) = entity_handles.first().unwrap(); + let mesh = meshes.get(mesh_handle).unwrap(); + assert_eq!(mesh.primitive_topology(), PrimitiveTopology::TriangleList); + (*entity, mesh) + } +} diff --git a/examples/bevy_gltf_save_load/basic/src/core/relationships/mod.rs b/examples/bevy_gltf_save_load/basic/src/core/relationships/mod.rs new file mode 100644 index 00000000..41284536 --- /dev/null +++ b/examples/bevy_gltf_save_load/basic/src/core/relationships/mod.rs @@ -0,0 +1,11 @@ +pub mod relationships_insert_dependant_components; +pub use relationships_insert_dependant_components::*; + +use bevy::prelude::*; + +pub struct EcsRelationshipsPlugin; +impl Plugin for EcsRelationshipsPlugin { + fn build(&self, app: &mut App) { + app; + } +} diff --git a/examples/bevy_gltf_save_load/basic/src/core/relationships/relationships_insert_dependant_components.rs b/examples/bevy_gltf_save_load/basic/src/core/relationships/relationships_insert_dependant_components.rs new file mode 100644 index 00000000..4e9ad172 --- /dev/null +++ b/examples/bevy_gltf_save_load/basic/src/core/relationships/relationships_insert_dependant_components.rs @@ -0,0 +1,15 @@ +use bevy::prelude::*; + +pub fn insert_dependant_component< + Dependant: Component, + Dependency: Component + std::default::Default, +>( + mut commands: Commands, + entities_without_depency: Query<(Entity, &Name), (With, Without)>, +) { + for (entity, name) in entities_without_depency.iter() { + let name = name.clone().to_string(); + commands.entity(entity).insert(Dependency::default()); + warn!("found an entity called {} with a {} component but without an {}, please check your assets", name.clone(), std::any::type_name::(), std::any::type_name::()); + } +} diff --git a/examples/bevy_gltf_save_load/basic/src/game/in_game.rs b/examples/bevy_gltf_save_load/basic/src/game/in_game.rs new file mode 100644 index 00000000..d31b1a87 --- /dev/null +++ b/examples/bevy_gltf_save_load/basic/src/game/in_game.rs @@ -0,0 +1,203 @@ +use super::Player; +use crate::state::{GameState, InAppRunning}; +use bevy::prelude::*; +use bevy_gltf_blueprints::{BluePrintBundle, BlueprintName, GameWorldTag, Library, NoInBlueprint}; +use bevy_gltf_save_load::{Dynamic, DynamicEntitiesRoot, StaticEntitiesRoot}; +use bevy_rapier3d::prelude::Velocity; +use rand::Rng; + +pub fn setup_game(mut commands: Commands, mut next_game_state: ResMut>) { + info!("setting up game world"); + // here we actually spawn our game world/level + let world_root = commands + .spawn(( + Name::from("world"), + GameWorldTag, + InAppRunning, + TransformBundle::default(), + InheritedVisibility::default(), + //StaticEntities("World"), + //DynamicEntities("World_dynamic") + )) + .id(); + + // and we fill it with static entities + let static_data = commands + .spawn(( + Name::from("static"), + BluePrintBundle { + blueprint: BlueprintName("World".to_string()), + ..Default::default() + }, + StaticEntitiesRoot, + Library("models".into()), + )) + .id(); + + // and we fill it with dynamic entities + let dynamic_data = commands + .spawn(( + Name::from("dynamic"), + BluePrintBundle { + blueprint: BlueprintName("World_dynamic".to_string()), + ..Default::default() + }, + DynamicEntitiesRoot, + NoInBlueprint, // we do not want the descendants of this entity to be filtered out when saving + Library("models".into()), + )) + .id(); + commands.entity(world_root).add_child(static_data); + commands.entity(world_root).add_child(dynamic_data); + + next_game_state.set(GameState::InGame) +} + +// TODO: Same as in load, reuse +pub fn unload_world(mut commands: Commands, gameworlds: Query>) { + for e in gameworlds.iter() { + info!("--loading: despawn old world/level"); + commands.entity(e).despawn_recursive(); + } +} + +pub fn should_reset(keycode: Res>) -> bool { + return keycode.just_pressed(KeyCode::N); +} + +pub fn spawn_test( + keycode: Res>, + mut dynamic_entities_world: Query>, + mut commands: Commands, +) { + if keycode.just_pressed(KeyCode::T) { + let world = dynamic_entities_world.single_mut(); + + let mut rng = rand::thread_rng(); + let range = 5.5; + let x: f32 = rng.gen_range(-range..range); + let y: f32 = rng.gen_range(-range..range); + + let mut rng = rand::thread_rng(); + let range = 0.8; + let vel_x: f32 = rng.gen_range(-range..range); + let vel_y: f32 = rng.gen_range(2.0..2.5); + let vel_z: f32 = rng.gen_range(-range..range); + + let name_index: u64 = rng.gen(); + + let new_entity = commands + .spawn(( + BluePrintBundle { + blueprint: BlueprintName("Health_Pickup".to_string()), + ..Default::default() + }, + // AddToGameWorld, // automatically added to entity (should be only one) that has the GameWorldTag + bevy::prelude::Name::from(format!("test{}", name_index)), + TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), + Velocity { + linvel: Vec3::new(vel_x, vel_y, vel_z), + angvel: Vec3::new(0.0, 0.0, 0.0), + }, + )) + .id(); + commands.entity(world).add_child(new_entity); + } +} + +#[derive(Component, Reflect, Default, Debug)] +#[reflect(Component)] +struct UnregisteredComponent; + +pub fn spawn_test_unregisted_components( + keycode: Res>, + mut commands: Commands, + + mut dynamic_entities_world: Query>, +) { + if keycode.just_pressed(KeyCode::U) { + let world = dynamic_entities_world.single_mut(); + + let mut rng = rand::thread_rng(); + let range = 5.5; + let x: f32 = rng.gen_range(-range..range); + let y: f32 = rng.gen_range(-range..range); + + let mut rng = rand::thread_rng(); + let range = 0.8; + let vel_x: f32 = rng.gen_range(-range..range); + let vel_y: f32 = rng.gen_range(2.0..2.5); + let vel_z: f32 = rng.gen_range(-range..range); + + let name_index: u64 = rng.gen(); + + let new_entity = commands + .spawn(( + BluePrintBundle { + blueprint: BlueprintName("Health_Pickup".to_string()), + ..Default::default() + }, + bevy::prelude::Name::from(format!("test{}", name_index)), + // BlueprintName("Health_Pickup".to_string()), + // SpawnHere, + TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), + Velocity { + linvel: Vec3::new(vel_x, vel_y, vel_z), + angvel: Vec3::new(0.0, 0.0, 0.0), + }, + UnregisteredComponent, + )) + .id(); + commands.entity(world).add_child(new_entity); + } +} + +pub fn spawn_test_parenting( + keycode: Res>, + players: Query>, + mut commands: Commands, + + names: Query<(Entity, &Name)>, +) { + if keycode.just_pressed(KeyCode::P) { + let mut rng = rand::thread_rng(); + let range = 5.5; + let x: f32 = rng.gen_range(-range..range); + let y: f32 = rng.gen_range(-range..range); + + let child_test = commands + .spawn(( + BluePrintBundle { + blueprint: BlueprintName("Sphero".to_string()), + ..Default::default() + }, + bevy::prelude::Name::from(format!("SubParentingTest")), + TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), + Dynamic(true), + )) + .id(); + + let parenting_test_entity = commands + .spawn(( + BluePrintBundle { + blueprint: BlueprintName("Container".into()), + ..Default::default() + }, + bevy::prelude::Name::from(format!("ParentingTest")), + Dynamic(true), + TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), + )) + .id(); + + commands.entity(parenting_test_entity).add_child(child_test); + + for player in players.iter() { + commands.entity(player).add_child(parenting_test_entity); + } + for (e, name) in names.iter() { + if name.to_string() == "Player" { + commands.entity(e).add_child(parenting_test_entity); + } + } + } +} diff --git a/examples/bevy_gltf_save_load/basic/src/game/in_game_loading.rs b/examples/bevy_gltf_save_load/basic/src/game/in_game_loading.rs new file mode 100644 index 00000000..e0d3770d --- /dev/null +++ b/examples/bevy_gltf_save_load/basic/src/game/in_game_loading.rs @@ -0,0 +1,47 @@ +use bevy::{core_pipeline::clear_color::ClearColorConfig, prelude::*}; + +use crate::state::InGameLoading; + +pub fn setup_loading_screen(mut commands: Commands) { + commands.spawn(( + Camera2dBundle { + camera_2d: Camera2d { + clear_color: ClearColorConfig::Custom(Color::BLACK), + }, + camera: Camera { + // renders after / on top of the main camera + order: 2, + ..default() + }, + ..Default::default() + }, + InGameLoading, + )); + + commands.spawn(( + TextBundle::from_section( + "Loading...", + TextStyle { + font_size: 28.0, + color: Color::WHITE, + ..Default::default() + }, + ) + .with_style(Style { + position_type: PositionType::Relative, + top: Val::Percent(45.0), + left: Val::Percent(45.0), + ..default() + }), + InGameLoading, + )); +} + +pub fn teardown_loading_screen( + in_main_menu: Query>, + mut commands: Commands, +) { + for entity in in_main_menu.iter() { + commands.entity(entity).despawn_recursive(); + } +} diff --git a/examples/bevy_gltf_save_load/basic/src/game/in_game_saving.rs b/examples/bevy_gltf_save_load/basic/src/game/in_game_saving.rs new file mode 100644 index 00000000..1605ad06 --- /dev/null +++ b/examples/bevy_gltf_save_load/basic/src/game/in_game_saving.rs @@ -0,0 +1,32 @@ +use bevy::prelude::*; + +use crate::state::InGameSaving; + +pub fn setup_saving_screen(mut commands: Commands) { + commands.spawn(( + TextBundle::from_section( + "Saving...", + TextStyle { + font_size: 28.0, + color: Color::WHITE, + ..Default::default() + }, + ) + .with_style(Style { + position_type: PositionType::Relative, + top: Val::Percent(90.0), + left: Val::Percent(80.0), + ..default() + }), + InGameSaving, + )); +} + +pub fn teardown_saving_screen( + in_main_menu: Query>, + mut commands: Commands, +) { + for entity in in_main_menu.iter() { + commands.entity(entity).despawn_recursive(); + } +} diff --git a/examples/bevy_gltf_save_load/basic/src/game/in_main_menu.rs b/examples/bevy_gltf_save_load/basic/src/game/in_main_menu.rs new file mode 100644 index 00000000..c8fc24a3 --- /dev/null +++ b/examples/bevy_gltf_save_load/basic/src/game/in_main_menu.rs @@ -0,0 +1,116 @@ +use bevy::prelude::*; + +use crate::state::{AppState, InMainMenu}; + +pub fn setup_main_menu(mut commands: Commands) { + commands.spawn((Camera2dBundle::default(), InMainMenu)); + + commands.spawn(( + TextBundle::from_section( + "SOME GAME TITLE !!", + TextStyle { + font_size: 18.0, + color: Color::WHITE, + ..Default::default() + }, + ) + .with_style(Style { + position_type: PositionType::Absolute, + top: Val::Px(100.0), + left: Val::Px(200.0), + ..default() + }), + InMainMenu, + )); + + commands.spawn(( + TextBundle::from_section( + "New Game (press Enter to start) + - press N to restart (once the game is started) + - press S to save (once the game is started) + - press L to load (once the game is started) + - press T for demo spawning (once the game is started) + - press U to spawn entities with unregistered components (once the game is started) + - press P to spawn entities attached to the player (once the game is started) + ", + TextStyle { + font_size: 18.0, + color: Color::WHITE, + ..Default::default() + }, + ) + .with_style(Style { + position_type: PositionType::Absolute, + top: Val::Px(200.0), + left: Val::Px(200.0), + ..default() + }), + InMainMenu, + )); + + /* + commands.spawn(( + TextBundle::from_section( + "Load Game", + TextStyle { + font_size: 18.0, + color: Color::WHITE, + ..Default::default() + }, + ) + .with_style(Style { + position_type: PositionType::Absolute, + top: Val::Px(250.0), + left: Val::Px(200.0), + ..default() + }), + InMainMenu + )); + + commands.spawn(( + TextBundle::from_section( + "Exit Game", + TextStyle { + font_size: 18.0, + color: Color::WHITE, + ..Default::default() + }, + ) + .with_style(Style { + position_type: PositionType::Absolute, + top: Val::Px(300.0), + left: Val::Px(200.0), + ..default() + }), + InMainMenu + ));*/ +} + +pub fn teardown_main_menu(in_main_menu: Query>, mut commands: Commands) { + for entity in in_main_menu.iter() { + commands.entity(entity).despawn_recursive(); + } +} + +pub fn main_menu( + keycode: Res>, + + mut next_app_state: ResMut>, + // mut next_game_state: ResMut>, + // mut save_requested_events: EventWriter, + // mut load_requested_events: EventWriter, +) { + if keycode.just_pressed(KeyCode::Return) { + next_app_state.set(AppState::AppLoading); + // next_game_state.set(GameState::None); + } + + if keycode.just_pressed(KeyCode::L) { + next_app_state.set(AppState::AppLoading); + // load_requested_events.send(LoadRequest { path: "toto".into() }) + } + + if keycode.just_pressed(KeyCode::S) { + // save_requested_events.send(SaveRequest { path: "toto".into() }) + } +} diff --git a/examples/bevy_gltf_save_load/basic/src/game/mod.rs b/examples/bevy_gltf_save_load/basic/src/game/mod.rs new file mode 100644 index 00000000..8e44a20a --- /dev/null +++ b/examples/bevy_gltf_save_load/basic/src/game/mod.rs @@ -0,0 +1,171 @@ +pub mod in_game; +pub use in_game::*; + +pub mod in_main_menu; +pub use in_main_menu::*; + +pub mod in_game_loading; +pub use in_game_loading::*; + +pub mod in_game_saving; +pub use in_game_saving::*; + +pub mod picking; +pub use picking::*; + +use crate::state::{AppState, GameState}; +use bevy::prelude::*; +use bevy_gltf_save_load::{LoadRequest, LoadingFinished, SaveRequest, SavingFinished}; + +// this file is just for demo purposes, contains various types of components, systems etc + +#[derive(Component, Reflect, Default, Debug)] +#[reflect(Component)] +pub enum SoundMaterial { + Metal, + Wood, + Rock, + Cloth, + Squishy, + #[default] + None, +} + +#[derive(Component, Reflect, Default, Debug)] +#[reflect(Component)] +/// Demo marker component +pub struct Player; + +#[derive(Component, Reflect, Default, Debug)] +#[reflect(Component)] +/// Demo component showing auto injection of components +pub struct ShouldBeWithPlayer; + +#[derive(Component, Reflect, Default, Debug)] +#[reflect(Component)] +/// Demo marker component +pub struct Interactible; + +fn player_move_demo( + keycode: Res>, + mut players: Query<&mut Transform, With>, +) { + let speed = 0.2; + if let Ok(mut player) = players.get_single_mut() { + if keycode.pressed(KeyCode::Left) { + player.translation.x += speed; + } + if keycode.pressed(KeyCode::Right) { + player.translation.x -= speed; + } + + if keycode.pressed(KeyCode::Up) { + player.translation.z += speed; + } + if keycode.pressed(KeyCode::Down) { + player.translation.z -= speed; + } + } +} + +pub fn request_save( + mut save_requests: EventWriter, + keycode: Res>, + + current_state: Res>, + mut next_game_state: ResMut>, +) { + if keycode.just_pressed(KeyCode::S) + && (current_state.get() != &GameState::InLoading) + && (current_state.get() != &GameState::InSaving) + { + next_game_state.set(GameState::InSaving); + save_requests.send(SaveRequest { + path: "save.scn.ron".into(), + }) + } +} + +pub fn on_saving_finished( + mut saving_finished: EventReader, + mut next_game_state: ResMut>, +) { + for _ in saving_finished.read() { + next_game_state.set(GameState::InGame); + } +} + +pub fn request_load( + mut load_requests: EventWriter, + keycode: Res>, + current_state: Res>, + mut next_game_state: ResMut>, +) { + if keycode.just_pressed(KeyCode::L) + && (current_state.get() != &GameState::InLoading) + && (current_state.get() != &GameState::InSaving) + { + next_game_state.set(GameState::InLoading); + load_requests.send(LoadRequest { + path: "save.scn.ron".into(), + }) + } +} + +pub fn on_loading_finished( + mut loading_finished: EventReader, + mut next_game_state: ResMut>, +) { + for _ in loading_finished.read() { + next_game_state.set(GameState::InGame); + } +} + +pub struct GamePlugin; +impl Plugin for GamePlugin { + fn build(&self, app: &mut App) { + app.add_plugins(PickingPlugin) + .register_type::() + .register_type::() + .register_type::() + .add_systems( + Update, + ( + // little helper utility, to automatically inject components that are dependant on an other component + // ie, here an Entity with a Player component should also always have a ShouldBeWithPlayer component + // you get a warning if you use this, as I consider this to be stop-gap solution (usually you should have either a bundle, or directly define all needed components) + + // insert_dependant_component::, + player_move_demo, //.run_if(in_state(AppState::Running)), + spawn_test, + spawn_test_unregisted_components, + spawn_test_parenting, + ) + .run_if(in_state(GameState::InGame)), + ) + .add_systems( + Update, + (unload_world, apply_deferred, setup_game) + .chain() + .run_if(should_reset) + .run_if(in_state(AppState::AppRunning)), + ) + .add_systems( + Update, + ( + request_save, + request_load, + on_saving_finished, + on_loading_finished, + ), + ) + .add_systems(OnEnter(AppState::MenuRunning), setup_main_menu) + .add_systems(OnExit(AppState::MenuRunning), teardown_main_menu) + .add_systems(Update, main_menu.run_if(in_state(AppState::MenuRunning))) + .add_systems(OnEnter(GameState::InLoading), setup_loading_screen) + .add_systems(OnExit(GameState::InLoading), teardown_loading_screen) + .add_systems(OnEnter(GameState::InSaving), setup_saving_screen) + .add_systems(OnExit(GameState::InSaving), teardown_saving_screen) + .add_systems(OnEnter(AppState::AppRunning), setup_game); + } +} diff --git a/examples/bevy_gltf_save_load/basic/src/game/picking.rs b/examples/bevy_gltf_save_load/basic/src/game/picking.rs new file mode 100644 index 00000000..6731bbb5 --- /dev/null +++ b/examples/bevy_gltf_save_load/basic/src/game/picking.rs @@ -0,0 +1,34 @@ +use super::Player; +use bevy::prelude::*; +use bevy_gltf_blueprints::GltfBlueprintsSet; + +#[derive(Component, Reflect, Default, Debug)] +#[reflect(Component)] +pub struct Pickable; + +// very simple, crude picking (as in picking up objects) implementation + +pub fn picking( + players: Query<&GlobalTransform, With>, + pickables: Query<(Entity, &GlobalTransform), With>, + mut commands: Commands, +) { + for player_transforms in players.iter() { + for (pickable, pickable_transforms) in pickables.iter() { + let distance = player_transforms + .translation() + .distance(pickable_transforms.translation()); + if distance < 2.5 { + commands.entity(pickable).despawn_recursive(); + } + } + } +} + +pub struct PickingPlugin; +impl Plugin for PickingPlugin { + fn build(&self, app: &mut App) { + app.register_type::() + .add_systems(Update, (picking.after(GltfBlueprintsSet::AfterSpawn),)); + } +} diff --git a/examples/bevy_gltf_save_load/basic/src/main.rs b/examples/bevy_gltf_save_load/basic/src/main.rs new file mode 100644 index 00000000..6edba337 --- /dev/null +++ b/examples/bevy_gltf_save_load/basic/src/main.rs @@ -0,0 +1,33 @@ +use bevy::prelude::*; +use bevy_editor_pls::prelude::*; + +mod core; +use crate::core::*; + +pub mod assets; +use assets::*; + +pub mod state; +use state::*; + +mod game; +use game::*; + +mod test_components; +use test_components::*; + +fn main() { + App::new() + .add_plugins(( + DefaultPlugins.set(AssetPlugin::default()), + // editor + EditorPlugin::default(), + // our custom plugins + StatePlugin, + AssetsPlugin, + CorePlugin, // reusable plugins + GamePlugin, // specific to our game + ComponentsTestPlugin, // Showcases different type of components /structs + )) + .run(); +} diff --git a/examples/bevy_gltf_save_load/basic/src/state.rs b/examples/bevy_gltf_save_load/basic/src/state.rs new file mode 100644 index 00000000..725ac7f2 --- /dev/null +++ b/examples/bevy_gltf_save_load/basic/src/state.rs @@ -0,0 +1,57 @@ +use bevy::prelude::*; + +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Default, States)] +pub enum AppState { + #[default] + CoreLoading, + MenuRunning, + AppLoading, + AppRunning, + AppEnding, +} + +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Default, States)] +pub enum GameState { + #[default] + None, + + InMenu, + InGame, + + InGameOver, + + InSaving, + InLoading, +} + +// tag components for all entities within a certain state (for despawning them if needed) , FIXME: seems kinda hack-ish +#[derive(Component)] +pub struct InCoreLoading; +#[derive(Component, Default)] +pub struct InMenuRunning; +#[derive(Component)] + +pub struct InAppRunning; + +// components for tagging in game vs in game menu stuff +#[derive(Component, Default)] +pub struct InMainMenu; + +#[derive(Component, Default)] +pub struct InMenu; + +#[derive(Component, Default)] +pub struct InGame; + +#[derive(Component, Default)] +pub struct InGameSaving; + +#[derive(Component, Default)] +pub struct InGameLoading; + +pub struct StatePlugin; +impl Plugin for StatePlugin { + fn build(&self, app: &mut App) { + app.add_state::().add_state::(); + } +} diff --git a/examples/bevy_gltf_save_load/basic/src/test_components.rs b/examples/bevy_gltf_save_load/basic/src/test_components.rs new file mode 100644 index 00000000..d0e6fbd7 --- /dev/null +++ b/examples/bevy_gltf_save_load/basic/src/test_components.rs @@ -0,0 +1,80 @@ +use bevy::prelude::*; + +#[derive(Component, Reflect, Default, Debug)] +#[reflect(Component)] +struct UnitTest; + +#[derive(Component, Reflect, Default, Debug, Deref, DerefMut)] +#[reflect(Component)] +struct TuppleTestF32(f32); + +#[derive(Component, Reflect, Default, Debug, Deref, DerefMut)] +#[reflect(Component)] +struct TuppleTestU64(u64); + +#[derive(Component, Reflect, Default, Debug, Deref, DerefMut)] +#[reflect(Component)] +pub struct TuppleTestStr(String); + +#[derive(Component, Reflect, Default, Debug)] +#[reflect(Component)] +struct TuppleTest2(f32, u64, String); + +#[derive(Component, Reflect, Default, Debug)] +#[reflect(Component)] +struct TuppleTestBool(bool); + +#[derive(Component, Reflect, Default, Debug)] +#[reflect(Component)] +struct TuppleVec2(Vec2); + +#[derive(Component, Reflect, Default, Debug)] +#[reflect(Component)] +struct TuppleVec3(Vec3); + +#[derive(Component, Reflect, Default, Debug)] +#[reflect(Component)] +struct TuppleVec(Vec); + +#[derive(Component, Reflect, Default, Debug)] +#[reflect(Component)] +struct TuppleTestColor(Color); + +#[derive(Component, Reflect, Default, Debug)] +#[reflect(Component)] +struct BasicTest { + a: f32, + b: u64, + c: String, +} + +#[derive(Component, Reflect, Default, Debug)] +#[reflect(Component)] +pub enum EnumTest { + Metal, + Wood, + Rock, + Cloth, + Squishy, + #[default] + None, +} + +pub struct ComponentsTestPlugin; +impl Plugin for ComponentsTestPlugin { + fn build(&self, app: &mut App) { + app.register_type::() + .register_type::() + .register_type::() + .register_type::() + .register_type::() + .register_type::() + .register_type::() + .register_type::() + .register_type::() + .register_type::() + .register_type::() + .register_type::() + .register_type::>(); + } +} diff --git a/tools/gltf_auto_export/README.md b/tools/gltf_auto_export/README.md index 7a53fd9c..fb577bd7 100644 --- a/tools/gltf_auto_export/README.md +++ b/tools/gltf_auto_export/README.md @@ -102,6 +102,17 @@ This issue has been resolved in v0.9. please read the dedicated [section](#collection-instances--nested-blueprints) below for more information + + - Export dynamic and static objects seperatly : For MAIN scenes only (aka levels), toggle this to generate 2 files per level: + + - one with all dynamic data: collection or instances marked as dynamic (aka saveable) + - one with all static data: anything else that is NOT marked as dynamic, the file name will have the suffix **_dynamic** + + Ie if you add a "Dynamic" custom property/ component to either your collection instances or your blueprint, you get a clean seperation between + + - your static level data (anything that will never change during the lifetime of your Bevy app) + - your dynamic objects (anything that will change during the lifetime of your Bevy app, that can be saved & reloaded in save files for example) + - export materials library: check this if you want to automatically export material libraries (default: False) please read the dedicated [section](#materials) below for more information @@ -233,7 +244,7 @@ There are only a few things to keep in mind Take a look at the [relevant](../../examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles/) example for more [details](../../examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles/art/) -### Process +### Internal Process overview This is the internal logic of the export process with blueprints (simplified) diff --git a/tools/gltf_auto_export/__init__.py b/tools/gltf_auto_export/__init__.py index 716e1848..a9eceebb 100644 --- a/tools/gltf_auto_export/__init__.py +++ b/tools/gltf_auto_export/__init__.py @@ -1,7 +1,7 @@ bl_info = { "name": "gltf_auto_export", "author": "kaosigh", - "version": (0, 9, 0), + "version": (0, 10, 0), "blender": (3, 4, 0), "location": "File > Import-Export", "description": "glTF/glb auto-export", diff --git a/tools/gltf_auto_export/docs/blender_addon_use3.png b/tools/gltf_auto_export/docs/blender_addon_use3.png index f16899bf8c452853d0cf0680a4c600732dd1001e..322cd69f5e7c64c15daa9f2c13354cb4e7371137 100644 GIT binary patch literal 17186 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