Compare commits

..

1 Commits

Author SHA1 Message Date
Mark Moissette
b0b241df21
Merge f27c8a078c into 9b50d77790 2024-08-12 12:35:42 +00:00
79 changed files with 1717 additions and 618 deletions

View File

@ -13,15 +13,6 @@ inside Blender. Aka "Blender as editor for Bevy"
It also allows you to setup 'blueprints' in Blender by using collections (the recomended way to go most of the time), or directly on single use objects .
> [!CAUTION]
> Blenvy is currently in **Alpha 1** state so there are still quite a few bugs, missing functionality, missing docs, broken examples etc
> Please make sure you back up your Blender files before using it !
> [!CAUTION]
> Please make sure to use matching versions numbers for the Blender add-on & the rust crate !
> This is the only way to make sure everything works as intended
## Quickstart
Want to jump right in? See the [quickstart guide](./docs/quickstart/readme.md) for how to setup a basic project as fast as possible.

36
TODO.md
View File

@ -360,38 +360,4 @@ Bevy Side:
- [ ] add information & screenshots about adding assets to the Blender add-on docs
- [x] finally deal cleanly with gltf export failures & make sure to always reset the state of the blend file
clear && pytest -svv --blender-template ../../testing/bevy_example/art/testing_library.blend --blender-executable /home/ckaos/tools/blender/blender-4.1.0-linux-x64/blender tests/test_bevy_integration_prepare.py && pytest -svv --blender-executable /home/ckaos/tools/blender/blender-4.1.0-linux-x64/blender tests/test_bevy_integration.py
-----------
PRE ALPHA1 RELEASE:
- [x] compress images in blender docs
- [x] add a small note about using the same version of the Blender add-on & the Bevy crate
- [x] do a quick check for the basics
- [x] blender add-on
- [x] barebones bevy test
- [x] hot reload => some of it works, the rest not anymore ? (see bug https://github.com/bevyengine/bevy/issues/14698)
- [ ] merge blenvy branch into main
- [ ] push to crates.io
- [ ] tag
------------
POST ALPHA1 RELEASE:
BEVY:
- [ ] split up "spawn from blueprint"
- [ ] cleanup very verbose messages
- [ ] fix & cleanup trigger_instance_animation_markers_events
- [ ] fix & cleanup save & load
- [ ] experiment with Bevy side for splitting out animations
- [ ] test hot reload a bit more , improve missing parts (see above)
- [ ] make a fleshed out demo
BLENDER:
- [ ] add "right click to edit blueprint"
- [ ] overall cleanup
- [ ] review wonky logic for cross file components injections
- [ ] update & fix tests
------------
BEFORE FINAL RELASE:
- [ ] cleanup & regenerate all examples assets
- [ ] cleanup & improve all docs
clear && pytest -svv --blender-template ../../testing/bevy_example/art/testing_library.blend --blender-executable /home/ckaos/tools/blender/blender-4.1.0-linux-x64/blender tests/test_bevy_integration_prepare.py && pytest -svv --blender-executable /home/ckaos/tools/blender/blender-4.1.0-linux-x64/blender tests/test_bevy_integration.py

18
crates/blenvy/OLD.md Normal file
View File

@ -0,0 +1,18 @@
## SystemSet
the ordering of systems is very important !
For example to replace your proxy components (stand-in components when you cannot/ do not want to use real components in the gltf file) with actual ones, which should happen **AFTER** the Blueprint based spawning,
so ```blenvy``` provides a **SystemSet** for that purpose: ```GltfBlueprintsSet```
Typically , the order of systems should be
***blenvy (GltfComponentsSet::Injection)*** => ***blenvy (GltfBlueprintsSet::Spawn, GltfBlueprintsSet::AfterSpawn)*** => ***replace_proxies***
see https://github.com/kaosat-dev/Blenvy/tree/main/examples/blenvy/basic for how to set it up correctly

View File

@ -219,7 +219,7 @@ This way all your levels, your dynamic entities etc, are kept seperated from UI
## Registry
Blenvy automatically exports a Json file containing of all your registered components/ types, in order to create UIs that allows you to add & edit your components directly in Blender in the [Blenvy](https://github.com/kaosat-dev/Blenvy/tree/main/tools/blenvy) Blender add-on
Blenvy automatically exports a Json file containing of all your registered components/ types, in order to be able to create UIs that allows you to add & edit your components directly in Blender in the [Blenvy](https://github.com/kaosat-dev/Blenvy/tree/main/tools/blenvy) Blender add-on
- The output file will be generated in the ```Startup``` schedule whenever you run your app.
- Every time you compile & run your app, the output json file will be updated.
@ -314,7 +314,7 @@ The main branch is compatible with the latest Bevy release, while the branch `be
Compatibility of `blenvy` versions:
| `blenvy` | `bevy` |
| :-- | :-- |
| `0.1.0-alpha.1` | `0.14` |
| `0.1` | `0.14` |
| branch `main` | `0.14` |
| branch `bevy_main` | `main` |

View File

@ -0,0 +1,362 @@
[![Crates.io](https://img.shields.io/crates/v/bevy_gltf_blueprints)](https://crates.io/crates/bevy_gltf_blueprints)
[![Docs](https://img.shields.io/docsrs/bevy_gltf_blueprints)](https://docs.rs/bevy_gltf_blueprints/latest/bevy_gltf_blueprints/)
[![License](https://img.shields.io/crates/l/bevy_gltf_blueprints)](https://github.com/kaosat-dev/Blenvy/blob/main/crates/bevy_gltf_blueprints/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_blueprints (deprecated in favor of Blenvy)
> bevy_gltf_blueprints has been deprecated in favor of its successor [Blenvy](https://crates.io/crates/blenvy), part of the [Blenvy project](https://github.com/kaosat-dev/Blenvy). No further development or maintenance will be done for Bevy bevy_gltf_blueprints. See [#194](https://github.com/kaosat-dev/Blenvy/issues/194) for background.
Built on [bevy_gltf_components](https://crates.io/crates/bevy_gltf_components) this crate adds the ability to define Blueprints/Prefabs for [Bevy](https://bevyengine.org/) inside gltf files and spawn them in Bevy.
* Allows you to create lightweight levels, where all assets are different gltf files and loaded after the main level is loaded
* Allows you to spawn different entities from gtlf files at runtime in a clean manner, including simplified animation support !
A blueprint is a set of **overrideable** components + a hierarchy: ie
* just a Gltf file with Gltf_extras specifying components
* a component called BlueprintName
Particularly useful when using [Blender](https://www.blender.org/) as an editor for the [Bevy](https://bevyengine.org/) game engine, combined with the Blender add-ons that do a lot of the work for you
- [gltf_auto_export](https://github.com/kaosat-dev/Blenvy/tree/main/tools/gltf_auto_export)
- [bevy_components](https://github.com/kaosat-dev/Blenvy/tree/main/tools/bevy_components)
## Usage
Here's a minimal usage example:
```toml
# Cargo.toml
[dependencies]
bevy="0.14"
bevy_gltf_blueprints = { version = "0.11.0"}
```
```rust no_run
use bevy::prelude::*;
use bevy_gltf_blueprints::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugins(BlueprintsPlugin)
.run();
}
// not shown here: any other setup that is not specific to blueprints
fn spawn_blueprint(
mut commands: Commands,
keycode: Res<Input<KeyCode>>,
){
if keycode.just_pressed(KeyCode::S) {
let new_entity = commands.spawn((
BlueprintName("Health_Pickup".to_string()), // mandatory !!
SpawnHere, // mandatory !!
TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), // VERY important !!
// any other component you want to insert
));
}
}
```
## Installation
Add the following to your `[dependencies]` section in `Cargo.toml`:
```toml
bevy_gltf_blueprints = "0.11.0"
```
Or use `cargo add`:
```toml
cargo add bevy_gltf_blueprints
```
## Setup
```rust no_run
use bevy::prelude::*;
use bevy_gltf_blueprints::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugin(BlueprintsPlugin)
.run();
}
```
you may want to configure your "library"/"blueprints" settings:
```rust no_run
use bevy::prelude::*;
use bevy_gltf_blueprints::*;
fn main() {
App::new()
.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
aabbs: true, // defaults to false, enable this to automatically calculate aabb for the scene/blueprint
material_library: true, // defaults to false, enable this to enable automatic injection of materials from material library files
material_library_folder: "materials".into() //defaults to "materials" the folder to look for for the material files
..Default::default()
}
))
.run();
}
```
## Spawning entities from blueprints
You can spawn entities from blueprints like this:
```rust no_run
commands.spawn((
BlueprintName("Health_Pickup".to_string()), // mandatory !!
SpawnHere, // mandatory !!
TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)), // optional
// any other component you want to insert
))
```
Once spawning of the actual entity is done, the spawned Blueprint will be *gone/merged* with the contents of Blueprint !
> Important :
you can **add** or **override** components present inside your Blueprint when spawning the BluePrint itself: ie
### Adding components not specified inside the blueprint
you can just add any additional components you need when spawning :
```rust no_run
commands.spawn((
BlueprintName("Health_Pickup".to_string()),
SpawnHere,
TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)),
// from Rapier/bevy_xpbd: this means the entity will also have a velocity component when inserted into the world
Velocity {
linvel: Vec3::new(vel_x, vel_y, vel_z),
angvel: Vec3::new(0.0, 0.0, 0.0),
},
))
```
### Overriding components specified inside the blueprint
any component you specify when spawning the Blueprint that is also specified **within** the Blueprint will **override** that component in the final spawned entity
for example
```rust no_run
commands.spawn((
BlueprintName("Health_Pickup".to_string()),
SpawnHere,
TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)),
HealthPowerUp(20)// if this is component is also present inside the "Health_Pickup" blueprint, that one will be replaced with this component during spawning
))
```
### BluePrintBundle
There is also a ```BluePrintBundle``` for convenience , which just has
* a ```BlueprintName``` component
* a ```SpawnHere``` component
## 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 !
For example to replace your proxy components (stand-in components when you cannot/ do not want to use real components in the gltf file) with actual ones, which should happen **AFTER** the Blueprint based spawning,
so ```bevy_gltf_blueprints``` provides a **SystemSet** for that purpose: ```GltfBlueprintsSet```
Typically , the order of systems should be
***bevy_gltf_components (GltfComponentsSet::Injection)*** => ***bevy_gltf_blueprints (GltfBlueprintsSet::Spawn, GltfBlueprintsSet::AfterSpawn)*** => ***replace_proxies***
see an example [here](https://github.com/kaosat-dev/Blenvy/tree/main/examples/bevy_gltf_blueprints/basic) for how to set it up correctly
## Animation
```bevy_gltf_blueprints``` provides some lightweight helpers to deal with animations stored in gltf files
* an ```Animations``` component that gets inserted into spawned (root) entities that contains a hashmap of all animations contained inside that entity/gltf file .
(this is a copy of the ```named_animations``` inside Bevy's gltf structures )
* an ```AnimationPlayerLink``` component that gets inserted into spawned (root) entities, to make it easier to trigger/ control animations than it usually is inside Bevy + Gltf files
The workflow for animations is as follows:
* create a gltf file with animations (using Blender & co) as you would normally do
* inside Bevy, use the ```bevy_gltf_blueprints``` boilerplate (see sections above), no specific setup beyond that is required
* to control the animation of an entity, you need to query for entities that have both ```AnimationPlayerLink``` and ```Animations``` components (added by ```bevy_gltf_blueprints```) AND entities with the ```AnimationPlayer``` component
For example:
```rust no_run
// example of changing animation of entities based on proximity to the player, for "fox" entities (Tag component)
pub fn animation_change_on_proximity_foxes(
players: Query<&GlobalTransform, With<Player>>,
animated_foxes: Query<(&GlobalTransform, &AnimationPlayerLink, &Animations ), With<Fox>>,
mut animation_players: Query<&mut AnimationPlayer>,
){
for player_transforms in players.iter() {
for (fox_tranforms, link, animations) in animated_foxes.iter() {
let distance = player_transforms
.translation()
.distance(fox_tranforms.translation());
let mut anim_name = "Walk";
if distance < 8.5 {
anim_name = "Run";
}
else if distance >= 8.5 && distance < 10.0{
anim_name = "Walk";
}
else if distance >= 10.0 && distance < 15.0{
anim_name = "Survey";
}
// now play the animation based on the chosen animation name
let mut animation_player = animation_players.get_mut(link.0).unwrap();
animation_player.play_with_transition(
animations.named_animations.get(anim_name).expect("animation name should be in the list").clone(),
Duration::from_secs(3)
).repeat();
}
}
}
```
see [here](https://github.com/kaosat-dev/Blenvy/tree/main/examples/bevy_gltf_blueprints/animation) for how to set it up correctly
particularly from [here](https://github.com/kaosat-dev/Blenvy/tree/main/examples/bevy_gltf_blueprints/animation/src/game/in_game.rs)
## Materials
You have the option of using "material libraries" to share common textures/materials between blueprints, in order to avoid asset & memory bloat:
Ie for example without this option, 56 different blueprints using the same material with a large texture would lead to the material/texture being embeded
56 times !!
you can configure this with the settings:
```rust
material_library: true // defaults to false, enable this to enable automatic injection of materials from material library files
material_library_folder: "materials".into() //defaults to "materials" the folder to look for for the material files
```
> Important! you must take care of preloading your material librairy gltf files in advance, using for example ```bevy_asset_loader```since
```bevy_gltf_blueprints``` currently does NOT take care of loading those at runtime
see an example [here](https://github.com/kaosat-dev/Blenvy/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/Blenvy/tree/main/tools/gltf_auto_export)
## Legacy mode
Starting in version 0.7 there is a new parameter ```legacy_mode``` for backwards compatibility
To disable the legacy mode: (enabled by default)
```rust no_run
BlueprintsPlugin{legacy_mode: false}
```
You **need** to disable legacy mode if you want to use the [```bevy_components```](https://github.com/kaosat-dev/Blenvy/tree/tools_bevy_blueprints/tools/bevy_components) Blender addon + the [```bevy_registry_export crate```](https://crates.io/crates/bevy_registry_export) !
As it create custom properties that are writen in real **ron** file format instead of a simplified version (the one in the legacy mode)
> Note: the legacy mode support will be dropped in future versions, and the default behaviour will be NO legacy mode
## Examples
* [basic](https://github.com/kaosat-dev/Blenvy/tree/main/examples/bevy_gltf_blueprints/basic)
* [xbpd](https://github.com/kaosat-dev/Blenvy/tree/main/examples/bevy_gltf_blueprints/basic_xpbd_physics)
* [animation](https://github.com/kaosat-dev/Blenvy/tree/main/examples/bevy_gltf_blueprints/animation)
* [materials](https://github.com/kaosat-dev/Blenvy/tree/main/examples/bevy_gltf_blueprints/materials)
* [multiple_levels_multiple_blendfiles](https://github.com/kaosat-dev/Blenvy/tree/main/examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles)
## 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_blueprints` versions:
| `bevy_gltf_blueprints` | `bevy` |
| :-- | :-- |
| `0.11` | `0.14` |
| `0.9 - 0.10` | `0.13` |
| `0.3 - 0.8` | `0.12` |
| `0.1 - 0.2` | `0.11` |
| branch `main` | `0.13` |
| 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)

View File

@ -0,0 +1,145 @@
[![Crates.io](https://img.shields.io/crates/v/bevy_gltf_components)](https://crates.io/crates/bevy_gltf_components)
[![Docs](https://img.shields.io/docsrs/bevy_gltf_components)](https://docs.rs/bevy_gltf_components/latest/bevy_gltf_components/)
[![License](https://img.shields.io/crates/l/bevy_gltf_components)](https://github.com/kaosat-dev/Blenvy/blob/main/crates/bevy_gltf_components/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_components (deprecated in favor of Blenvy)
> bevy_gltf_components has been deprecated in favor of its successor [Blenvy](https://crates.io/crates/blenvy), part of the [Blenvy project](https://github.com/kaosat-dev/Blenvy). No further development or maintenance will be done for Bevy bevy_gltf_components. See [#194](https://github.com/kaosat-dev/Blenvy/issues/194) for background.
This crate allows you to define [Bevy](https://bevyengine.org/) components direclty inside gltf files and instanciate the components on the Bevy side.
## Usage
***important*** : the plugin for processing gltf files runs in ***update*** , so you cannot use the components directly if you spawn your scene from gltf in ***setup*** (the additional components will not show up)
Please see the
* [example](https://github.com/kaosat-dev/Blenvy/tree/main/examples/bevy_gltf_components/basic)
* or use [```bevy_asset_loader```](https://github.com/NiklasEi/bevy_asset_loader) for reliable preloading of files, as this crate does not deal with loading your assets.
* alternatively, use the [```bevy_gltf_blueprints```](https://crates.io/crates/bevy_gltf_blueprints) crate, built on this crate's features,
that allows you to directly spawn entities from gltf based blueprints.
Here's a minimal usage example:
```toml
# Cargo.toml
[dependencies]
bevy="0.14"
bevy_gltf_components = { version = "0.6"}
```
```rust no_run
//too barebones of an example to be meaningfull, please see https://github.com/kaosat-dev/Blenvy/bevy_gltf_components/examples/basic for a real example
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugin(ComponentsFromGltfPlugin::default())
.add_system(spawn_level)
.run();
}
fn spawn_level(
asset_server: Res<AssetServer>,
mut commands: bevy::prelude::Commands,
keycode: Res<Input<KeyCode>>,
){
if keycode.just_pressed(KeyCode::Return) {
commands.spawn(SceneBundle {
scene: asset_server.load("basic/models/level1.glb#Scene0"),
transform: Transform::from_xyz(2.0, 0.0, -5.0),
..Default::default()
});
}
}
```
## Installation
Add the following to your `[dependencies]` section in `Cargo.toml`:
```toml
bevy_gltf_components = "0.6"
```
Or use `cargo add`:
```toml
cargo add bevy_gltf_components
```
## Configuration
starting with version 0.3, this plugin is configurable
Use the default configuration:
```rust no_run
ComponentsFromGltfPlugin::default()
```
Or disable the legacy mode: (enabled by default)
```rust no_run
ComponentsFromGltfPlugin{legacy_mode: false}
```
You **need** to disable legacy mode if you want to use the [```bevy_components```](https://github.com/kaosat-dev/Blenvy/tree/main/tools/bevy_components) Blender addon + the [```bevy_registry_export crate```](https://crates.io/crates/bevy_registry_export) !
As it create custom properties that are writen in real **ron** file format
instead of a simplified version (the one in the legacy mode)
> Note: the legacy mode support will be dropped in future versions, and the default behaviour will be NO legacy mode
## SystemSet
the ordering of systems is very important !
For example to replace your proxy components (stand-in components when you cannot/ do not want to use real components in the gltf file) with actual ones,
which should happen **AFTER** the components from the gltf files have been injected,
so ```bevy_gltf_components``` provides a **SystemSet** for that purpose:```GltfComponentsSet```
Typically , the order of systems should be
***bevy_gltf_components (GltfComponentsSet::Injection)*** => ***replace_proxies***
## Additional features
- as of version 0.5 , this crate also includes automatic handling of lights in gltf files, to attempt to match Blender's eevee rendering as close as possible:
* **BlenderLightShadows** (automatically generated by the gltf_auto_export Blender add-on) allows you to toggle light's shadows on/off in Blender and have matching
behaviour in Bevy
* **BlenderBackgroundShader** aka background color is also automatically set on the Bevy side
* **BlenderShadowSettings** sets the cascade_size on the bevy side to match the one configured in Blender
If these components are present in your gltf file, they will be handled automatically by this crate, will be ignored otherwise.
## Examples
https://github.com/kaosat-dev/Blenvy/tree/main/examples/bevy_gltf_components/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_components` versions:
| `bevy_gltf_components` | `bevy` |
| :-- | :-- |
| `0.6` | `0.14` |
| `0.5` | `0.13` |
| `0.2 - 0.4` | `0.12` |
| `0.1` | `0.11` |
| branch `main` | `0.13` |
| 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)

View File

@ -0,0 +1,136 @@
[![Crates.io](https://img.shields.io/crates/v/bevy_registry_export)](https://crates.io/crates/bevy_registry_export)
[![Docs](https://img.shields.io/docsrs/bevy_registry_export)](https://docs.rs/bevy_registry_export/latest/bevy_registry_export/)
[![License](https://img.shields.io/crates/l/bevy_registry_export)](https://github.com/kaosat-dev/Blenvy/blob/main/crates/bevy_registry_export/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_registry_export (deprecated in favor of Blenvy)
> bevy_registry_export has been deprecated in favor of its successor [Blenvy](https://crates.io/crates/blenvy), part of the [Blenvy project](https://github.com/kaosat-dev/Blenvy). No further development or maintenance will be done for Bevy bevy_registry_export. See [#194](https://github.com/kaosat-dev/Blenvy/issues/194) for background.
This plugin allows you to create a Json export of all your components/ registered types.
Its main use case is as a backbone for the [```bevy_components``` Blender add-on](https://github.com/kaosat-dev/Blenvy/tree/main/tools/bevy_components), that allows you to add & edit components directly in Blender, using the actual type definitions from Bevy
(and any of your custom types & components that you register in Bevy).
## Usage
Here's a minimal usage example:
```toml
# Cargo.toml
[dependencies]
bevy="0.14"
bevy_registry_export = "0.4"
```
```rust no_run
use bevy::prelude::*;
use bevy_registry_export::*;
fn main() {
App::new()
.add_plugins((
DefaultPlugins,
ExportRegistryPlugin::default() // will save your registry schema json file to assets/registry.json
))
.run();
}
```
take a look at the [example](https://github.com/kaosat-dev/Blenvy/tree/main/examples/bevy_registry_export/basic/src/core/mod.rs) for more clarity
## Installation
Add the following to your `[dependencies]` section in `Cargo.toml`:
```toml
bevy_registry_export = "0.4"
```
Or use `cargo add`:
```toml
cargo add bevy_registry_export
```
## Setup
```rust no_run
use bevy::prelude::*;
use bevy_registry_export::*;
fn main() {
App::new()
.add_plugins((
DefaultPlugins
ExportRegistryPlugin::default()
))
.run();
}
```
you can also configure the output path
```rust no_run
use bevy::prelude::*;
use bevy_registry_export::*;
fn main() {
App::new()
.add_plugins((
DefaultPlugins
ExportRegistryPlugin {
save_path: "assets/registry.json".into(),
..Default::default()
},
))
.run();
}
```
## Usage
- The output file will be generated in the ```Startup``` schedule whenever you run your app.
- Every time you compile & run your app, the output json file will be updated.
## Examples
All examples are here:
> the examples use ```bevy_gltf_blueprints``` with the **legacy_mode** set to **FALSE** as the new custom properties generated by the Blender add-on require newer/ non legacy logic.
- https://github.com/kaosat-dev/Blenvy/tree/main/examples/bevy_registry_export/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_registry_export` versions:
| `bevy_registry_export` | `bevy` | `bevy_components (Blender add-on)` |
| :-- | :-- |:-- |
| `0.4 ` | `0.14` | `0.3` |
| `0.3 ` | `0.13` | `0.3` |
| `0.2 ` | `0.12` | `0.3` |
| `0.1 ` | `0.12` | `0.1 -0.2` |
| branch `main` | `0.12` | `0.1` |
| branch `bevy_main` | `main` | `n/a` |
## Contributors
Thanks to all the contributors helping out with this project ! Big kudos to you, contributions are always appreciated ! :)
A big shout out to [killercup](https://github.com/killercup), that did the bulk of the Bevy side code !
## 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)

View File

@ -0,0 +1,315 @@
[![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/Blenvy/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 (deprecated in favor of Blenvy)
> bevy_gltf_save_load has been deprecated in favor of its successor [Blenvy](https://crates.io/crates/blenvy), part of the [Blenvy project](https://github.com/kaosat-dev/Blenvy). No further development or maintenance will be done for Bevy bevy_gltf_save_load. See [#194](https://github.com/kaosat-dev/Blenvy/issues/194) for background.
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/Blenvy/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.14"
bevy_gltf_save_load = "0.5"
bevy_gltf_blueprints = "0.11" // 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<SavingRequest>,
keycode: Res<Input<KeyCode>>,
)
{
if keycode.just_pressed(KeyCode::S) {
save_requests.send(SavingRequest {
path: "save.scn.ron".into(),
})
}
}
// add a system to trigger loading
pub fn request_load(
mut load_requests: EventWriter<LoadRequest>,
keycode: Res<Input<KeyCode>>,
)
{
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<NextState<GameState>>,
) {
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/Blenvy/blob/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.3"
bevy_gltf_blueprints = "0.10" // 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::<Name>(),
TypeId::of::<Transform>(),
TypeId::of::<Velocity>(),
// 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 ```SavingRequest``` event
```rust no_run
// add a system to trigger saving
pub fn request_save(
mut save_requests: EventWriter<SavingRequest>,
keycode: Res<Input<KeyCode>>,
)
{
if keycode.just_pressed(KeyCode::S) {
save_requests.send(SavingRequest {
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<LoadRequest>,
keycode: Res<Input<KeyCode>>,
)
{
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/Blenvy/tree/main/examples/bevy_gltf_save_load/basic/src/game/mod.rs) 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/Blenvy/tree/main/examples/bevy_gltf_save_load/basic/src/game/in_game.rs)
- [various events & co](https://github.com/kaosat-dev/Blenvy/tree/main/examples/bevy_gltf_save_load/basic/src/game/mod.rs)
All examples are here:
- https://github.com/kaosat-dev/Blenvy/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.5 ` | `0.14` |
| `0.4 ` | `0.13` |
| `0.1 -0.3` | `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)

View File

@ -164,7 +164,6 @@ pub fn trigger_blueprint_animation_markers_events(
}
/// triggers events when a given animation marker is reached for INSTANCE animations
// TODO: implement this correctly
pub fn trigger_instance_animation_markers_events(
animation_infos: Query<(
Entity,
@ -175,23 +174,22 @@ pub fn trigger_instance_animation_markers_events(
)>,
animation_players: Query<&AnimationPlayer>,
animation_clips: Res<Assets<AnimationClip>>,
__animation_graphs: Res<Assets<AnimationGraph>>,
mut _animation_marker_events: EventWriter<AnimationMarkerReached>,
animation_graphs: Res<Assets<AnimationGraph>>,
mut animation_marker_events: EventWriter<AnimationMarkerReached>,
) {
for (__entity, __markers, player_link, animations, __animation_infos) in animation_infos.iter()
{
for (entity, markers, player_link, animations, animation_infos) in animation_infos.iter() {
//let (animation_player, animation_transitions) = animation_players.get(player_link.0).unwrap();
//let foo = animation_transitions.get_main_animation().unwrap();
for (animation_name, node_index) in animations.named_indices.iter() {
let animation_player = animation_players.get(player_link.0).unwrap();
if animation_player.animation_is_playing(*node_index) {
if let Some(__animation) = animation_player.animation(*node_index) {
if let Some(animation) = animation_player.animation(*node_index) {
if let Some(animation_clip_handle) =
animations.named_animations.get(animation_name)
{
if let Some(__animation_clip) = animation_clips.get(animation_clip_handle) {
println!("found the animation clip");
if let Some(animation_clip) = animation_clips.get(animation_clip_handle) {
println!("helooo")
}
}
}

View File

@ -59,30 +59,41 @@ pub struct AssetLoadTracker {
}
/// helper component, for tracking loaded assets
#[derive(Component, Default, Debug)]
#[derive(Component, Debug)]
pub(crate) struct BlueprintAssetsLoadState {
pub all_loaded: bool,
pub asset_infos: Vec<AssetLoadTracker>,
pub progress: f32,
}
impl Default for BlueprintAssetsLoadState {
fn default() -> Self {
Self {
all_loaded: Default::default(),
asset_infos: Default::default(),
progress: Default::default(),
}
}
}
// for preloading asset files
#[derive(serde::Deserialize, bevy::asset::Asset, bevy::reflect::TypePath, Debug)]
pub(crate) struct File {
pub(crate) struct File{
pub(crate) path: String,
}
#[derive(serde::Deserialize, bevy::asset::Asset, bevy::reflect::TypePath, Debug)]
pub(crate) struct BlueprintPreloadAssets {
pub(crate) assets: Vec<(String, File)>,
pub(crate) struct BlueprintPreloadAssets{
pub(crate) assets: Vec<(String, File)>
}
#[derive(Component)]
pub(crate) struct BlueprintMetaHandle(pub Handle<BlueprintPreloadAssets>);
/// flag component, usually added when a blueprint meta file is loaded
/// flag component, usually added when a blueprint is loaded
#[derive(Component)]
pub(crate) struct BlueprintMetaLoaded;
#[derive(Component)]
pub(crate) struct BlueprintMetaLoading;
pub(crate) struct BlueprintMetaLoading;

View File

@ -14,12 +14,11 @@ pub(crate) struct AssetToBlueprintInstancesMapper {
pub(crate) untyped_id_to_blueprint_entity_ids: HashMap<String, Vec<Entity>>,
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn react_to_asset_changes(
mut gltf_events: EventReader<AssetEvent<Gltf>>, // FIXME: Problem: we need to react to any asset change, not just gltf files !
// mut untyped_events: EventReader<AssetEvent<LoadedUntypedAsset>>,
blueprint_assets: Query<(Entity, Option<&Name>, &BlueprintInfo, Option<&Children>)>,
_blueprint_children_entities: Query<&FromBlueprint>, //=> can only be used if the entites are tagged
blueprint_children_entities: Query<&FromBlueprint>, //=> can only be used if the entites are tagged
assets_to_blueprint_instances: Res<AssetToBlueprintInstancesMapper>,
all_parents: Query<&Parent>,
spawning_blueprints: Query<&BlueprintSpawning>,
@ -31,32 +30,34 @@ pub(crate) fn react_to_asset_changes(
for event in gltf_events.read() {
// LoadedUntypedAsset
if let AssetEvent::Modified { id } = event {
// React to the gltf file being modified
// println!("Modified gltf {:?}", asset_server.get_path(*id));
if let Some(asset_path) = asset_server.get_path(*id) {
// let untyped = asset_server.get_handle_untyped(asset_path.clone());
// println!("matching untyped handle {:?}", untyped);
// let bla = untyped.unwrap().id();
// asset_server.get
// in order to avoid respawn both a parent & a child , which would crash Bevy, we do things in two steps
if let Some(entities) = assets_to_blueprint_instances
.untyped_id_to_blueprint_entity_ids
.get(&asset_path.to_string())
{
for entity in entities.iter() {
// println!("matching blueprint instance {}", entity);
// disregard entities that are already (re) spawning
if !respawn_candidates.contains(&entity)
&& blueprint_assets.get(*entity).is_ok()
&& spawning_blueprints.get(*entity).is_err()
{
respawn_candidates.push(entity);
match event {
AssetEvent::Modified { id } => {
// React to the gltf file being modified
// println!("Modified gltf {:?}", asset_server.get_path(*id));
if let Some(asset_path) = asset_server.get_path(*id) {
// let untyped = asset_server.get_handle_untyped(asset_path.clone());
// println!("matching untyped handle {:?}", untyped);
// let bla = untyped.unwrap().id();
// asset_server.get
// in order to avoid respawn both a parent & a child , which would crash Bevy, we do things in two steps
if let Some(entities) = assets_to_blueprint_instances
.untyped_id_to_blueprint_entity_ids
.get(&asset_path.to_string())
{
for entity in entities.iter() {
// println!("matching blueprint instance {}", entity);
// disregard entities that are already (re) spawning
if !respawn_candidates.contains(&entity)
&& blueprint_assets.get(*entity).is_ok()
&& spawning_blueprints.get(*entity).is_err()
{
respawn_candidates.push(entity);
}
}
}
}
}
_ => {}
}
}
// we process all candidates here to deal with the case where multiple assets have changed in a single frame, which could cause respawn chaos

View File

@ -11,13 +11,13 @@ pub struct MaterialInfo {
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
/// component containing the full list of `MaterialInfos` for a given entity/object
/// component containing the full list of MaterialInfos for a given entity/object
pub struct MaterialInfos(Vec<MaterialInfo>);
#[derive(Component, Default, Debug)]
pub struct MaterialProcessed;
/// system that injects / replaces materials from materials library
/// system that injects / replaces materials from material library
pub(crate) fn inject_materials(
mut blenvy_config: ResMut<BlenvyConfig>,
material_infos_query: Query<
@ -58,11 +58,9 @@ pub(crate) fn inject_materials(
} else {
let model_handle: Handle<Gltf> = asset_server.load(material_info.path.clone()); // FIXME: kinda weird now
let Some(mat_gltf) = assets_gltf.get(model_handle.id()) else {
warn!(
"materials file {} should have been preloaded skipping",
material_info.path
);
warn!("materials file {} should have been preloaded skipping",material_info.path);
continue;
};
/*let mat_gltf = assets_gltf.get(model_handle.id()).unwrap_or_else(|| {
panic!(
@ -88,19 +86,25 @@ pub(crate) fn inject_materials(
if let Some(material) = material_found {
info!("Step 6: injecting/replacing materials");
for (child_index, child) in children.iter().enumerate() {
if child_index == material_index && with_materials_and_meshes.contains(*child) {
info!(
"injecting material {}, path: {:?}",
material_info.name,
material_info.path.clone()
);
commands.entity(*child).insert(material.clone());
if child_index == material_index {
if with_materials_and_meshes.contains(*child) {
info!(
"injecting material {}, path: {:?}",
material_info.name,
material_info.path.clone()
);
commands.entity(*child).insert(material.clone());
}
}
}
}
}
commands.entity(entity).insert(MaterialProcessed);
}
}

View File

@ -22,7 +22,7 @@ pub(crate) use hot_reload::*;
use bevy::{prelude::*, utils::hashbrown::HashMap};
use crate::GltfComponentsSet;
use crate::{BlenvyConfig, GltfComponentsSet};
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
/// set for the two stages of blueprint based spawning :
@ -48,10 +48,16 @@ impl Default for BluePrintBundle {
}
}
#[derive(Debug, Default, Clone)]
#[derive(Debug, Clone)]
/// Plugin for gltf blueprints
pub struct BlueprintsPlugin {}
impl Default for BlueprintsPlugin {
fn default() -> Self {
Self {}
}
}
fn hot_reload(watching_for_changes: Res<WatchingForChanges>) -> bool {
// println!("hot reload ? {}", watching_for_changes.0);
watching_for_changes.0
@ -87,6 +93,7 @@ impl Plugin for BlueprintsPlugin {
.register_type::<BlueprintInfo>()
.register_type::<MaterialInfo>()
.register_type::<MaterialInfos>()
.register_type::<SpawnBlueprint>()
.register_type::<BlueprintInstanceDisabled>()
.register_type::<HideUntilReady>()
@ -104,7 +111,10 @@ impl Plugin for BlueprintsPlugin {
.register_type::<Vec<String>>()
.register_type::<BlueprintAssets>()
.register_type::<HashMap<String, Vec<String>>>()
.add_plugins(RonAssetPlugin::<BlueprintPreloadAssets>::new(&["meta.ron"]))
//.init_asset::<RawGltfAsset>()
//.init_asset_loader::<RawGltfAssetLoader>()
.add_plugins(RonAssetPlugin::<BlueprintPreloadAssets>::new(&["meta.ron"]),)
.configure_sets(
Update,
(GltfBlueprintsSet::Spawn, GltfBlueprintsSet::AfterSpawn)

View File

@ -1,15 +1,17 @@
use std::path::Path;
use bevy::{gltf::Gltf, prelude::*, scene::SceneInstance, utils::hashbrown::HashMap};
use bevy::{
gltf::Gltf,
prelude::*,
scene::SceneInstance,
utils::{hashbrown::HashMap, warn},
};
use crate::{
AnimationInfos, AssetLoadTracker, AssetToBlueprintInstancesMapper, BlueprintAnimationInfosLink,
BlueprintAnimationPlayerLink, BlueprintAnimations, BlueprintAssetsLoadState,
BlueprintAssetsLoaded, BlueprintAssetsNotLoaded, BlueprintMetaLoaded, BlueprintMetaLoading,
BlueprintPreloadAssets, InstanceAnimationInfosLink, InstanceAnimationPlayerLink,
InstanceAnimations, WatchingForChanges,
AnimationInfos, AssetLoadTracker, AssetToBlueprintInstancesMapper, BlueprintAnimationInfosLink, BlueprintAnimationPlayerLink, BlueprintAnimations, BlueprintAssets, BlueprintAssetsLoadState, BlueprintAssetsLoaded, BlueprintMetaLoading, BlueprintAssetsNotLoaded, BlueprintPreloadAssets, InstanceAnimationInfosLink, InstanceAnimationPlayerLink, InstanceAnimations, WatchingForChanges
};
/// this is a flag component for our levels/game world
#[derive(Component)]
pub struct GameWorldTag;
@ -44,6 +46,12 @@ pub struct SpawnBlueprint;
/// flag component marking any spawned child of blueprints
pub struct FromBlueprint;
// TODO: move to save_load
#[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 DynamicBlueprintInstance;
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
/// flag component to force adding newly spawned entity as child of game world
@ -65,6 +73,8 @@ pub struct HideUntilReady;
/// Companion to the `HideUntilReady` component: this stores the visibility of the entity before the blueprint was inserted into it
pub(crate) struct OriginalVisibility(Visibility);
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
/// marker component, gets added to all children of a currently spawning blueprint instance, can be usefull to avoid manipulating still in progress entities
@ -113,45 +123,25 @@ Overview of the Blueprint Spawning process
*/
pub(super) fn blueprints_prepare_metadata_file_for_spawn(
blueprint_instances_to_spawn: Query<
(
Entity,
&BlueprintInfo,
Option<&Name>,
Option<&Parent>,
Option<&HideUntilReady>,
Option<&Visibility>,
Option<&AddToGameWorld>,
),
(
Without<BlueprintMetaLoading>,
Without<BlueprintSpawning>,
Without<BlueprintInstanceReady>,
),
>,
blueprint_instances_to_spawn: Query<(
Entity,
&BlueprintInfo,
Option<&Name>,
Option<&Parent>,
Option<&HideUntilReady>,
Option<&Visibility>,
Option<&AddToGameWorld>,
), (Without<BlueprintMetaLoading>, Without<BlueprintSpawning>, Without<BlueprintInstanceReady>)>,
mut game_world: Query<Entity, With<GameWorldTag>>,
asset_server: Res<AssetServer>,
mut commands: Commands,
) {
for (
entity,
blueprint_info,
entity_name,
original_parent,
hide_until_ready,
original_visibility,
add_to_world,
) in blueprint_instances_to_spawn.iter()
{
for (entity, blueprint_info, entity_name, original_parent, hide_until_ready, original_visibility, add_to_world) in blueprint_instances_to_spawn.iter() {
// get path to assets / metadata file
info!(
"Step 1: spawn request detected: loading metadata file for {:?}",
blueprint_info
);
info!("Step 1: spawn request detected: loading metadata file for {:?}", blueprint_info);
let blueprint_path = blueprint_info.path.clone();
let metadata_path = blueprint_path
.replace(".glb", ".meta.ron")
.replace(".gltf", ".meta.ron"); // FIXME: horrible
let metadata_path = blueprint_path.replace(".glb", ".meta.ron").replace(".gltf", ".meta.ron"); // FIXME: horrible
let mut asset_infos: Vec<AssetLoadTracker> = vec![];
//let foo_handle:Handle<BlueprintPreloadAssets> = asset_server.load(metadata_path);
let untyped_handle = asset_server.load_untyped(metadata_path.clone());
@ -173,11 +163,11 @@ pub(super) fn blueprints_prepare_metadata_file_for_spawn(
..Default::default()
},
BlueprintMetaLoading,
BlueprintSpawning,
BlueprintSpawning
));
// if the entity has no name, add one based on the blueprint's
if entity_name.is_none() {
if entity_name.is_none(){
commands
.entity(entity)
.insert(bevy::prelude::Name::from(blueprint_info.name.clone()));
@ -186,14 +176,14 @@ pub(super) fn blueprints_prepare_metadata_file_for_spawn(
if original_parent.is_none() {
// only allow hiding until ready when the entity does not have a parent (?)
if hide_until_ready.is_some() {
// if there is already a set visibility, save it for later
if let Some(original_visibility) = original_visibility {
commands
.entity(entity)
.insert(OriginalVisibility(*original_visibility));
commands.entity(entity).insert(OriginalVisibility(*original_visibility));
}
// & now hide the instance until it is ready
commands.entity(entity).insert(Visibility::Hidden);
commands.entity(entity)
.insert(Visibility::Hidden);
}
// only allow automatically adding a newly spawned blueprint instance to the "world", if the entity does not have a parent
@ -216,7 +206,9 @@ pub(crate) fn blueprints_check_assets_metadata_files_loading(
asset_server: Res<AssetServer>,
mut commands: Commands,
) {
for (entity, _blueprint_info, mut assets_to_load) in blueprint_assets_to_load.iter_mut() {
for (entity, blueprint_info, mut assets_to_load) in blueprint_assets_to_load.iter_mut() {
let mut all_loaded = true;
let mut loaded_amount = 0;
let total = assets_to_load.asset_infos.len();
@ -226,7 +218,7 @@ pub(crate) fn blueprints_check_assets_metadata_files_loading(
let mut failed = false;
if let bevy::asset::LoadState::Failed(_) = asset_server.load_state(asset_id) {
failed = true;
failed = true
}
tracker.loaded = loaded || failed;
if loaded || failed {
@ -235,12 +227,10 @@ pub(crate) fn blueprints_check_assets_metadata_files_loading(
all_loaded = false;
}
if all_loaded {
commands
.entity(entity)
.insert(BlueprintMetaHandle(asset_server.load(tracker.path.clone())))
.remove::<BlueprintAssetsLoadState>();
commands.entity(entity).insert(BlueprintMetaHandle(asset_server.load(tracker.path.clone()))).remove::<BlueprintAssetsLoadState>();
break;
}
}
let progress: f32 = loaded_amount as f32 / total as f32;
assets_to_load.progress = progress;
@ -248,11 +238,9 @@ pub(crate) fn blueprints_check_assets_metadata_files_loading(
}
}
pub(super) fn blueprints_prepare_spawn(
blueprint_instances_to_spawn: Query<
(Entity, &BlueprintInfo, &BlueprintMetaHandle),
Added<BlueprintMetaHandle>,
>,
blueprint_instances_to_spawn: Query<(Entity, &BlueprintInfo, &BlueprintMetaHandle, Option<&Name>), Added<BlueprintMetaHandle>>,
mut commands: Commands,
asset_server: Res<AssetServer>,
// for hot reload
@ -261,11 +249,12 @@ pub(super) fn blueprints_prepare_spawn(
// for debug
// all_names: Query<&Name>
blueprint_metas: Res<Assets<BlueprintPreloadAssets>>,
) {
for (entity, blueprint_info, blueprint_meta_handle) in blueprint_instances_to_spawn.iter() {
for (entity, blueprint_info, blueprint_meta_handle, entity_name) in blueprint_instances_to_spawn.iter() {
info!(
"Step 2: metadata loaded: loading assets for {:?}",
blueprint_info,
blueprint_info,
);
// we add the asset of the blueprint itself
// TODO: add detection of already loaded data
@ -287,55 +276,56 @@ pub(super) fn blueprints_prepare_spawn(
// and we also add all its assets
/* prefetch attempt */
if let Some(blenvy_metadata) = blueprint_metas.get(&blueprint_meta_handle.0) {
for asset in blenvy_metadata.assets.iter() {
let asset_path = asset.1.path.clone();
let asset_name = asset.0.clone();
for asset in blenvy_metadata.assets.iter() {
let asset_path = asset.1.path.clone();
let asset_name = asset.0.clone();
let untyped_handle = asset_server.load_untyped(&asset_path);
let asset_id = untyped_handle.id();
let loaded = asset_server.is_loaded_with_dependencies(asset_id);
if !loaded {
asset_infos.push(AssetLoadTracker {
name: asset_name.clone(),
path: asset_path.clone(),
id: asset_id,
loaded: false,
handle: untyped_handle.clone(),
});
let untyped_handle = asset_server.load_untyped(&asset_path);
let asset_id = untyped_handle.id();
let loaded = asset_server.is_loaded_with_dependencies(asset_id);
if !loaded {
asset_infos.push(AssetLoadTracker {
name: asset_name.clone(),
path: asset_path.clone(),
id: asset_id,
loaded: false,
handle: untyped_handle.clone(),
});
}
// FIXME: dang, too early, asset server has not yet started loading yet
// let path_id = asset_server.get_path_id(&asset.path).expect("we should have alread checked for this asset");
let path_id = asset_path.clone();
// Only do this if hot reload is enabled
if watching_for_changes.0 {
if !assets_to_blueprint_instances
.untyped_id_to_blueprint_entity_ids
.contains_key(&path_id)
{
assets_to_blueprint_instances
.untyped_id_to_blueprint_entity_ids
.insert(path_id.clone(), vec![]);
}
// FIXME: dang, too early, asset server has not yet started loading yet
// let path_id = asset_server.get_path_id(&asset.path).expect("we should have alread checked for this asset");
let path_id = asset_path.clone();
// Only do this if hot reload is enabled
if watching_for_changes.0 {
if !assets_to_blueprint_instances
// only insert if not already present in mapping
if !assets_to_blueprint_instances.untyped_id_to_blueprint_entity_ids
[&path_id]
.contains(&entity)
{
// println!("adding mapping between {} and entity {:?}", path_id, all_names.get(entity));
assets_to_blueprint_instances
.untyped_id_to_blueprint_entity_ids
.contains_key(&path_id)
{
assets_to_blueprint_instances
.untyped_id_to_blueprint_entity_ids
.insert(path_id.clone(), vec![]);
}
// only insert if not already present in mapping
if !assets_to_blueprint_instances.untyped_id_to_blueprint_entity_ids[&path_id]
.contains(&entity)
{
// println!("adding mapping between {} and entity {:?}", path_id, all_names.get(entity));
assets_to_blueprint_instances
.untyped_id_to_blueprint_entity_ids
.get_mut(&path_id)
.unwrap()
.push(entity);
}
.get_mut(&path_id)
.unwrap()
.push(entity);
}
}
} else {
warn!("no asset metadata found for {}, please make sure to generate them using the Blender add-on, or preload your assets manually", blueprint_info.path);
}
}else {
warn!("no asset metadata found for {}, please make sure to generate them using the Blender add-on, or preload your assets manually", blueprint_info.path);
}
// Only do this if hot reload is enabled
// TODO: should this be added to the list of "all assets" on the blender side instead
if watching_for_changes.0 {
@ -377,9 +367,7 @@ pub(super) fn blueprints_prepare_spawn(
commands.entity(entity).insert(BlueprintAssetsLoaded);
}
commands
.entity(entity)
.insert(BlueprintMetaLoaded)
commands.entity(entity)
.remove::<BlueprintMetaLoading>()
.remove::<BlueprintMetaHandle>();
}
@ -388,14 +376,14 @@ pub(super) fn blueprints_prepare_spawn(
/// This system tracks & updates the loading state of all blueprints assets
pub(crate) fn blueprints_check_assets_loading(
mut blueprint_assets_to_load: Query<
(Entity, &BlueprintInfo, &mut BlueprintAssetsLoadState),
(Entity, &BlueprintInfo, &mut BlueprintAssetsLoadState, Option<&Name>),
With<BlueprintAssetsNotLoaded>,
>,
asset_server: Res<AssetServer>,
mut commands: Commands,
mut blueprint_events: EventWriter<BlueprintEvent>,
) {
for (entity, blueprint_info, mut assets_to_load) in blueprint_assets_to_load.iter_mut() {
for (entity, blueprint_info, mut assets_to_load, entity_name) in blueprint_assets_to_load.iter_mut() {
let mut all_loaded = true;
let mut loaded_amount = 0;
let total = assets_to_load.asset_infos.len();
@ -408,7 +396,7 @@ pub(crate) fn blueprints_check_assets_loading(
let mut failed = false;
if let bevy::asset::LoadState::Failed(_) = asset_server.load_state(asset_id) {
warn!("FAILED TO LOAD {}", tracker.path.clone());
failed = true;
failed = true
}
tracker.loaded = loaded || failed;
if loaded || failed {
@ -440,7 +428,13 @@ pub(crate) fn blueprints_check_assets_loading(
pub(crate) fn blueprints_assets_loaded(
spawn_placeholders: Query<
(Entity, &BlueprintInfo, Option<&Transform>, Option<&Name>),
(
Entity,
&BlueprintInfo,
Option<&Transform>,
Option<&Name>,
Option<&AnimationInfos>,
),
(
Added<BlueprintAssetsLoaded>,
Without<BlueprintAssetsNotLoaded>,
@ -454,7 +448,14 @@ pub(crate) fn blueprints_assets_loaded(
mut commands: Commands,
) {
for (entity, blueprint_info, transform, name) in spawn_placeholders.iter() {
for (
entity,
blueprint_info,
transform,
name,
animation_infos,
) in spawn_placeholders.iter()
{
/*info!(
"BLUEPRINT: all assets loaded, attempting to spawn blueprint SCENE {:?} for entity {:?}, id: {:}, parent:{:?}",
blueprint_info.name, name, entity, original_parent
@ -528,6 +529,8 @@ pub(crate) fn blueprints_assets_loaded(
graph,
},
));
}
}
@ -649,7 +652,7 @@ pub(crate) fn blueprints_scenes_spawned(
use crate::CopyComponents;
use std::any::TypeId;
use super::BlueprintMetaHandle;
use super::{BlueprintMetaHandle, BlueprintMetaLoaded};
#[derive(Component, Reflect, Debug)]
#[reflect(Component)]
@ -660,7 +663,6 @@ pub struct BlueprintReadyForPostProcess;
/// - 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 adds an `AnimationLink` component containing the entity that has the `AnimationPlayer` so that animations can be controlled from the original entity
#[allow(clippy::too_many_arguments)]
pub(crate) fn blueprints_cleanup_spawned_scene(
blueprint_scenes: Query<
(
@ -838,7 +840,6 @@ pub(crate) fn blueprints_finalize_instances(
info!("Step 8: Finalizing blueprint instance {:?}", name);
commands
.entity(entity)
.remove::<BlueprintMetaLoaded>()
.remove::<BlueprintReadyForFinalizing>()
.remove::<BlueprintReadyForPostProcess>()
.remove::<BlueprintSpawning>()
@ -880,9 +881,8 @@ pub(crate) fn blueprints_finalize_instances(
}
}
commands
.entity(entity)
.remove::<BlueprintInstanceDisabled>();
commands.entity(entity).remove::<BlueprintInstanceDisabled>();
for child in all_children.iter_descendants(entity) {
commands.entity(child).remove::<BlueprintInstanceDisabled>();
}
@ -890,7 +890,7 @@ pub(crate) fn blueprints_finalize_instances(
if hide_until_ready.is_some() {
if let Some(original_visibility) = original_visibility {
commands.entity(entity).insert(original_visibility.0);
} else {
}else {
commands.entity(entity).insert(Visibility::Inherited);
}
}

View File

@ -173,6 +173,8 @@ fn process_colorgrading(
gamma: blender_colorgrading.gamma,
..Default::default()
},
..Default::default()
});
commands.entity(scene_id).remove::<ColorGrading>();
}

View File

@ -70,13 +70,13 @@ fn components_string_to_components(
.expect("deserialzer should have been generated from string");
let reflect_deserializer = ReflectDeserializer::new(type_registry);
/*let component = reflect_deserializer
.deserialize(&mut deserializer)
.unwrap_or_else(|_| {
panic!(
"failed to deserialize component {} with value: {:?}",
name, value
)
});*/
.deserialize(&mut deserializer)
.unwrap_or_else(|_| {
panic!(
"failed to deserialize component {} with value: {:?}",
name, value
)
});*/
let Ok(component) = reflect_deserializer.deserialize(&mut deserializer) else {
warn!(
"failed to deserialize component {} with value: {:?}",

View File

@ -1,4 +1,4 @@
use bevy::{render::primitives::Aabb, utils::HashMap};
use bevy::{prelude::*, render::primitives::Aabb, utils::HashMap};
use std::path::PathBuf;
pub mod components;
@ -29,8 +29,8 @@ pub struct BlenvyConfig {
// save & load
pub(crate) save_component_filter: SceneFilter,
pub(crate) save_resource_filter: SceneFilter,
#[allow(dead_code)]
pub(crate) save_path: PathBuf,
//pub(crate) save_path: PathBuf,
// save_path: PathBuf::from("saves"),
}
#[derive(Debug, Clone)]
@ -45,7 +45,6 @@ pub struct BlenvyPlugin {
// for save & load
pub save_component_filter: SceneFilter,
pub save_resource_filter: SceneFilter,
pub save_path: PathBuf,
}
impl Default for BlenvyPlugin {
@ -58,7 +57,6 @@ impl Default for BlenvyPlugin {
save_component_filter: SceneFilter::default(),
save_resource_filter: SceneFilter::default(),
save_path: PathBuf::from("blenvy_saves"), // TODO: use https://docs.rs/dirs/latest/dirs/ to default to the correct user directory
}
}
}
@ -71,7 +69,7 @@ impl Plugin for BlenvyPlugin {
ExportRegistryPlugin::default(),
BlueprintsPlugin::default(),
#[cfg(not(target_arch = "wasm32"))] // save & load is only for non wasm platforms
SaveLoadPlugin::default(),
SaveLoadPlugin::default()
))
.insert_resource(BlenvyConfig {
export_registry: self.export_registry,
@ -85,7 +83,6 @@ impl Plugin for BlenvyPlugin {
save_component_filter: self.save_component_filter.clone(),
save_resource_filter: self.save_resource_filter.clone(),
save_path: self.save_path.clone(),
});
}
}

View File

@ -229,7 +229,8 @@ fn add_min_max(
field_index: usize,
variant_index: Option<usize>,
) -> Value {
/*fn get_min_max(
#[cfg(feature = "support-inspector")]
fn get_min_max(
reg: &TypeRegistration,
field_index: usize,
variant_index: Option<usize>,
@ -251,8 +252,9 @@ fn add_min_max(
})
.and_then(|o| o.downcast_ref::<NumberOptions<f32>>())
.map(|num| (num.min, num.max))
}*/
}
#[cfg(not(feature = "support-inspector"))]
fn get_min_max(
_reg: &TypeRegistration,
_field_index: usize,

View File

@ -35,8 +35,7 @@ fn export_registry(blenvy_config: Res<BlenvyConfig>) -> bool {
impl Plugin for ExportRegistryPlugin {
fn build(&self, app: &mut App) {
app.register_asset_root()
.add_systems(Startup, export_types.run_if(export_registry));
app.register_asset_root().add_systems(Startup, export_types.run_if(export_registry));
}
}

View File

@ -2,47 +2,36 @@ pub use bevy::prelude::*;
use crate::{BlueprintInfo, GameWorldTag, HideUntilReady, SpawnBlueprint};
use super::BlueprintWorld;
use super::{BlueprintWorld, Dynamic};
pub(crate) fn spawn_from_blueprintworld(
added_blueprint_worlds: Query<(Entity, &BlueprintWorld), Added<BlueprintWorld>>,
added_blueprint_worlds: Query<(Entity, &BlueprintWorld), Added<BlueprintWorld> >,
mut commands: Commands,
) {
for (__entity, blueprint_world) in added_blueprint_worlds.iter() {
){
for (entity, blueprint_world) in added_blueprint_worlds.iter(){
println!("added blueprintWorld {:?}", blueprint_world);
// here we spawn the static part our game world/level, which is also a blueprint !
let __static_world = commands
.spawn((
BlueprintInfo::from_path(blueprint_world.path.as_str()), // all we need is a Blueprint info...
SpawnBlueprint, // and spawnblueprint to tell blenvy to spawn the blueprint now
HideUntilReady, // only reveal the level once it is ready
GameWorldTag,
))
.id();
let static_world = commands.spawn((
BlueprintInfo::from_path(blueprint_world.path.as_str()), // all we need is a Blueprint info...
SpawnBlueprint, // and spawnblueprint to tell blenvy to spawn the blueprint now
HideUntilReady, // only reveal the level once it is ready
GameWorldTag,
)).id();
// here we spawn the dynamic entities part of our game world/level, which is also a blueprint !
let __dynamic_world = commands
.spawn((
BlueprintInfo::from_path(
blueprint_world
.path
.replace(".glb", "_dynamic.glb")
.replace(".gltf", "_dynamic.gltf")
.as_str(),
), // all we need is a Blueprint info...
SpawnBlueprint, // and spawnblueprint to tell blenvy to spawn the blueprint now
HideUntilReady, // only reveal the level once it is ready
GameWorldTag,
))
.id();
let dynamic_world = commands.spawn((
BlueprintInfo::from_path(blueprint_world.path.replace(".glb", "_dynamic.glb").replace(".gltf", "_dynamic.gltf").as_str()), // all we need is a Blueprint info...
SpawnBlueprint, // and spawnblueprint to tell blenvy to spawn the blueprint now
HideUntilReady, // only reveal the level once it is ready
GameWorldTag
)).id();
// commands.entity(entity).add_child(static_world);
// commands.entity(entity).add_child(dynamic_world);
}
}
}
/*
pub(crate) fn inject_dynamic_into_children(
added_dynamic: Query<Entity, Added<Dynamic> >,
all_children: Query<&Children>,
@ -53,4 +42,4 @@ pub(crate) fn inject_dynamic_into_children(
commands.entity(child).insert(Dynamic);
}
}
}*/
}

View File

@ -1,6 +1,9 @@
use std::path::Path;
use bevy::prelude::*;
use crate::{BlueprintInfo, DynamicEntitiesRoot, GameWorldTag, HideUntilReady, SpawnBlueprint};
use crate::{BlenvyConfig, BlueprintInfo, DynamicEntitiesRoot, GameWorldTag, HideUntilReady, SpawnBlueprint};
#[derive(Event)]
pub struct LoadingRequest {
@ -16,22 +19,23 @@ pub struct LoadingRequested {
pub path: String,
}
/*
- Loading
- load request recieved
- pause things ?
- unload everything
- load static data using blueprintInfo
- load dynamic data from save file
- load dynamic data from save file
General:
General:
* wrap loading a bevy scene as a blueprint ?
* meh, has no assets & co, different logic ?
*/
pub fn process_load_requests(
mut load_requests: EventReader<LoadingRequest>,
mut commands: Commands,
mut commands: Commands
) {
let mut save_path: String = "".into();
for load_request in load_requests.read() {
@ -45,13 +49,14 @@ pub fn process_load_requests(
}
pub fn should_load(loading_requests: Option<Res<LoadingRequested>>) -> bool {
resource_exists::<LoadingRequested>(loading_requests)
return resource_exists::<LoadingRequested>(loading_requests)
}
// TODO: replace with generic despawner ?
pub(crate) fn prepare_loading(
mut commands: Commands,
mut commands: Commands,
gameworlds: Query<Entity, With<GameWorldTag>>,
) {
for e in gameworlds.iter() {
info!("--loading: despawn old world/level");
@ -59,29 +64,32 @@ pub(crate) fn prepare_loading(
}
}
pub(crate) fn load_game(
mut commands: Commands,
asset_server: Res<AssetServer>,
load_request: Res<LoadingRequested>,
) {
info!("--loading: load dynamic data");
//let save_path = Path::new(load_request.path.clone().as_str());
info!("LOADING FROM {:?}", load_request.path.clone());
/*let world_root = commands
.spawn((
bevy::prelude::Name::from("world"),
GameWorldTag,
TransformBundle::default(),
InheritedVisibility::default(),
))
.id();*/
.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
let dynamic_data = commands
.spawn((
DynamicSceneBundle {
scene: asset_server.load(load_request.path.clone()),
@ -89,18 +97,16 @@ pub(crate) fn load_game(
},
bevy::prelude::Name::from("World_dynamic"),
DynamicEntitiesRoot,
GameWorldTag,
GameWorldTag
))
.id();
let _static_data = commands
.spawn((
BlueprintInfo::from_path("levels/World.glb"), // all we need is a Blueprint info...
SpawnBlueprint,
HideUntilReady,
GameWorldTag,
))
.id();
let static_data = commands.spawn((
BlueprintInfo::from_path("levels/World.glb"), // all we need is a Blueprint info...
SpawnBlueprint,
HideUntilReady,
GameWorldTag,
)).id();
//commands.entity(world_root).add_child(static_data);
//commands.entity(world_root).add_child(dynamic_data);
@ -109,4 +115,4 @@ pub(crate) fn load_game(
info!("--loading: loaded dynamic data");
commands.remove_resource::<LoadingRequested>();
}
}

View File

@ -1,3 +1,6 @@
use std::path::Path;
use bevy::prelude::*;
pub mod common;
pub use common::*;
@ -21,6 +24,7 @@ pub(crate) struct RootEntity;
/// internal helper component to store parents before resetting them
pub(crate) struct OriginalParent(pub(crate) Entity);
/// Marker component to Flag the root entity of all static entities (immutables)
#[derive(Component, Reflect, Debug, Default)]
#[reflect(Component)]
@ -31,6 +35,7 @@ pub struct StaticEntitiesRoot;
#[reflect(Component)]
pub struct DynamicEntitiesRoot;
#[derive(Resource, Clone, Debug, Default, Reflect)]
#[reflect(Resource)]
pub struct StaticEntitiesBlueprintInfo {
@ -38,20 +43,24 @@ pub struct StaticEntitiesBlueprintInfo {
pub path: String,
}
#[derive(Component, Debug)]
pub struct BlueprintWorld {
pub struct BlueprintWorld{
pub path: String,
}
impl BlueprintWorld {
pub fn from_path(path: &str) -> BlueprintWorld {
// let p = Path::new(&path);
BlueprintWorld {
let p = Path::new(&path);
return BlueprintWorld {
// name: p.file_stem().unwrap().to_os_string().into_string().unwrap(), // seriously ? , also unwraps !!
path: path.into(),
}
};
}
}
#[derive(Debug, Clone, Default)]
/// Plugin for saving & loading
pub struct SaveLoadPlugin {}
@ -60,10 +69,13 @@ impl Plugin for SaveLoadPlugin {
fn build(&self, app: &mut App) {
app.register_type::<Dynamic>()
.register_type::<StaticEntitiesRoot>()
.add_event::<SavingRequest>()
.add_event::<SaveFinished>()
// common
.add_systems(Update, (spawn_from_blueprintworld,)) // inject_dynamic_into_children
.add_systems(Update, (spawn_from_blueprintworld, )) // inject_dynamic_into_children
// saving
.add_systems(Update, process_save_requests)
.add_systems(
@ -71,7 +83,9 @@ impl Plugin for SaveLoadPlugin {
(prepare_save_game, apply_deferred, save_game, cleanup_save)
.chain()
.run_if(should_save),
)
.add_event::<LoadingRequest>()
.add_event::<LoadingFinished>()
//loading
@ -81,8 +95,9 @@ impl Plugin for SaveLoadPlugin {
(prepare_loading, apply_deferred, load_game)
.chain()
.run_if(should_load),
//.run_if(not(resource_exists::<LoadFirstStageDone>))
// .in_set(LoadingSet::Load),
);
//.run_if(not(resource_exists::<LoadFirstStageDone>))
// .in_set(LoadingSet::Load),
)
;
}
}

View File

@ -2,8 +2,9 @@ use std::fs::File;
use std::io::Write;
use std::path::Path;
use bevy::prelude::World;
use bevy::render::camera::{CameraMainTextureUsages, CameraRenderGraph};
use bevy::{prelude::*, tasks::IoTaskPool};
use bevy::prelude::World;
use crate::{BlenvyConfig, BlueprintInfo, Dynamic, FromBlueprint, RootEntity, SpawnBlueprint};
@ -16,15 +17,17 @@ pub struct SavingRequest {
#[derive(Event)]
pub struct SaveFinished; // TODO: merge the the events above
/// resource that keeps track of the current save request
#[derive(Resource, Default)]
pub struct SavingRequested {
pub path: String,
}
pub fn process_save_requests(
mut saving_requests: EventReader<SavingRequest>,
mut commands: Commands,
mut commands: Commands
) {
let mut save_path: String = "".into();
for saving_request in saving_requests.read() {
@ -37,21 +40,22 @@ pub fn process_save_requests(
}
}
pub fn should_save(saving_requests: Option<Res<SavingRequested>>) -> bool {
resource_exists::<SavingRequested>(saving_requests)
return resource_exists::<SavingRequested>(saving_requests)
}
// 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<Entity, (With<Dynamic>, With<BlueprintInfo>)>,
root_entities: Query<Entity, Or<(With<DynamicEntitiesRoot>, Without<Parent>)>>, // With<DynamicEntitiesRoot>
dynamic_entities: Query<(Entity, &Parent, Option<&Children>), With<Dynamic>>,
_static_entities: Query<(Entity, &BlueprintInfo), With<StaticEntitiesRoot>>,
static_entities: Query<(Entity, &BlueprintInfo), With<StaticEntitiesRoot>>,
mut commands: Commands,
) {
for entity in saveables.iter() {
// FIXME : not sure about this one
for entity in saveables.iter() { // FIXME : not sure about this one
commands.entity(entity).insert(SpawnBlueprint);
}
@ -80,6 +84,8 @@ pub(crate) fn prepare_save_game(
}*/
}
pub(crate) fn save_game(world: &mut World) {
info!("saving");
@ -120,6 +126,7 @@ pub(crate) fn save_game(world: &mut World) {
.allow::<BlueprintInfo>()
.allow::<SpawnBlueprint>()
.allow::<Dynamic>()
/*.deny::<CameraRenderGraph>()
.deny::<CameraMainTextureUsages>()
.deny::<Handle<Mesh>>()
@ -129,19 +136,18 @@ pub(crate) fn save_game(world: &mut World) {
// for root entities, it is the same EXCEPT we make sure parents are not included
let filter_root = filter.clone().deny::<Parent>();
let filter_resources = config
.clone()
let filter_resources = config.clone()
.save_resource_filter
.deny::<Time<Real>>()
.clone();
//.allow::<StaticEntitiesStorage>();
//.allow::<StaticEntitiesStorage>();
// for default stuff
let scene_builder = DynamicSceneBuilder::from_world(world)
.with_filter(filter.clone())
.with_resource_filter(filter_resources.clone());
let dyn_scene = scene_builder
let mut dyn_scene = scene_builder
.extract_resources()
.extract_entities(saveable_entities.clone().into_iter())
.remove_empty_entities()
@ -152,7 +158,7 @@ pub(crate) fn save_game(world: &mut World) {
.with_filter(filter_root.clone())
.with_resource_filter(filter_resources.clone());
let mut __dyn_scene_root = scene_builder_root
let mut dyn_scene_root = scene_builder_root
.extract_resources()
.extract_entities(
saveable_root_entities.clone().into_iter(), // .chain(static_world_markers.into_iter()),
@ -185,6 +191,7 @@ pub(crate) fn save_game(world: &mut World) {
})
.detach();
let static_world_path = "levels/world.glb";
let fake_foo = format!("(dynamic: {bla}, static: {static_world_path})");
let real_save_path = format!("{bla}.save.ron");
@ -211,4 +218,5 @@ pub(crate) fn cleanup_save(
saving_finished.send(SaveFinished);
commands.remove_resource::<SavingRequested>();
}
}

View File

@ -203,19 +203,6 @@
"type": "array",
"typeInfo": "List"
},
"alloc::vec::Vec<blenvy::blueprints::materials::MaterialInfo>": {
"isComponent": false,
"isResource": false,
"items": {
"type": {
"$ref": "#/$defs/blenvy::blueprints::materials::MaterialInfo"
}
},
"long_name": "alloc::vec::Vec<blenvy::blueprints::materials::MaterialInfo>",
"short_name": "Vec<MaterialInfo>",
"type": "array",
"typeInfo": "List"
},
"alloc::vec::Vec<f32>": {
"isComponent": false,
"isResource": false,
@ -1423,44 +1410,6 @@
"type": "object",
"typeInfo": "Enum"
},
"bevy_asset::handle::Handle<blenvy::blueprints::assets::BlueprintPreloadAssets>": {
"isComponent": true,
"isResource": false,
"long_name": "bevy_asset::handle::Handle<blenvy::blueprints::assets::BlueprintPreloadAssets>",
"oneOf": [
{
"items": false,
"long_name": "Strong",
"prefixItems": [
{
"type": {
"$ref": "#/$defs/std::sync::Arc<bevy_asset::handle::StrongHandle>"
}
}
],
"short_name": "Strong",
"type": "array",
"typeInfo": "Tuple"
},
{
"items": false,
"long_name": "Weak",
"prefixItems": [
{
"type": {
"$ref": "#/$defs/bevy_asset::id::AssetId<blenvy::blueprints::assets::BlueprintPreloadAssets>"
}
}
],
"short_name": "Weak",
"type": "array",
"typeInfo": "Tuple"
}
],
"short_name": "Handle<BlueprintPreloadAssets>",
"type": "object",
"typeInfo": "Enum"
},
"bevy_asset::id::AssetId<()>": {
"isComponent": false,
"isResource": false,
@ -2473,52 +2422,6 @@
"type": "object",
"typeInfo": "Enum"
},
"bevy_asset::id::AssetId<blenvy::blueprints::assets::BlueprintPreloadAssets>": {
"isComponent": false,
"isResource": false,
"long_name": "bevy_asset::id::AssetId<blenvy::blueprints::assets::BlueprintPreloadAssets>",
"oneOf": [
{
"additionalProperties": false,
"long_name": "Index",
"properties": {
"index": {
"long_name": "index",
"type": {
"$ref": "#/$defs/bevy_asset::assets::AssetIndex"
}
}
},
"required": [
"index"
],
"short_name": "Index",
"type": "object",
"typeInfo": "Struct"
},
{
"additionalProperties": false,
"long_name": "Uuid",
"properties": {
"uuid": {
"long_name": "uuid",
"type": {
"$ref": "#/$defs/uuid::Uuid"
}
}
},
"required": [
"uuid"
],
"short_name": "Uuid",
"type": "object",
"typeInfo": "Struct"
}
],
"short_name": "AssetId<BlueprintPreloadAssets>",
"type": "object",
"typeInfo": "Enum"
},
"bevy_asset::path::AssetPath": {
"isComponent": false,
"isResource": false,
@ -12450,7 +12353,7 @@
},
"blenvy::blueprints::materials::MaterialInfo": {
"additionalProperties": false,
"isComponent": false,
"isComponent": true,
"isResource": false,
"long_name": "blenvy::blueprints::materials::MaterialInfo",
"properties": {
@ -12473,22 +12376,6 @@
"type": "object",
"typeInfo": "Struct"
},
"blenvy::blueprints::materials::MaterialInfos": {
"isComponent": true,
"isResource": false,
"items": false,
"long_name": "blenvy::blueprints::materials::MaterialInfos",
"prefixItems": [
{
"type": {
"$ref": "#/$defs/alloc::vec::Vec<blenvy::blueprints::materials::MaterialInfo>"
}
}
],
"short_name": "MaterialInfos",
"type": "array",
"typeInfo": "TupleStruct"
},
"blenvy::blueprints::spawn_from_blueprints::BlueprintInfo": {
"additionalProperties": false,
"isComponent": true,
@ -12514,17 +12401,6 @@
"type": "object",
"typeInfo": "Struct"
},
"blenvy::blueprints::spawn_from_blueprints::BlueprintInstanceDisabled": {
"additionalProperties": false,
"isComponent": true,
"isResource": false,
"long_name": "blenvy::blueprints::spawn_from_blueprints::BlueprintInstanceDisabled",
"properties": {},
"required": [],
"short_name": "BlueprintInstanceDisabled",
"type": "object",
"typeInfo": "Struct"
},
"blenvy::blueprints::spawn_from_blueprints::HideUntilReady": {
"additionalProperties": false,
"isComponent": true,
@ -12665,28 +12541,6 @@
"type": "string",
"typeInfo": "Enum"
},
"blenvy::save_load::Dynamic": {
"additionalProperties": false,
"isComponent": true,
"isResource": false,
"long_name": "blenvy::save_load::Dynamic",
"properties": {},
"required": [],
"short_name": "Dynamic",
"type": "object",
"typeInfo": "Struct"
},
"blenvy::save_load::StaticEntitiesRoot": {
"additionalProperties": false,
"isComponent": true,
"isResource": false,
"long_name": "blenvy::save_load::StaticEntitiesRoot",
"properties": {},
"required": [],
"short_name": "StaticEntitiesRoot",
"type": "object",
"typeInfo": "Struct"
},
"blenvy_animation_example::Fox": {
"additionalProperties": false,
"isComponent": true,

View File

@ -2,9 +2,11 @@ use std::time::Duration;
use bevy::prelude::*;
use blenvy::{
BlenvyPlugin, BlueprintAnimationPlayerLink, BlueprintAnimations, BlueprintInfo, GameWorldTag,
HideUntilReady, SpawnBlueprint,
AddToGameWorld, BlenvyPlugin, BluePrintBundle, BlueprintAnimationPlayerLink,
BlueprintAnimations, BlueprintInfo, DynamicBlueprintInstance, GameWorldTag, HideUntilReady,
SpawnBlueprint,
};
use rand::Rng;
mod component_examples;
use component_examples::*;
@ -73,10 +75,11 @@ pub fn animation_control(
animation_transitions
.play(
&mut animation_player,
*animations
animations
.named_indices
.get(anim_name)
.expect("animation name should be in the list"),
.expect("animation name should be in the list")
.clone(),
Duration::from_secs(5),
)
.repeat();
@ -93,10 +96,11 @@ pub fn animation_control(
animation_transitions
.play(
&mut animation_player,
*animations
animations
.named_indices
.get(anim_name)
.expect("animation name should be in the list"),
.expect("animation name should be in the list")
.clone(),
Duration::from_secs(5),
)
.repeat();
@ -112,10 +116,11 @@ pub fn animation_control(
animation_transitions
.play(
&mut animation_player,
*animations
animations
.named_indices
.get(anim_name)
.expect("animation name should be in the list"),
.expect("animation name should be in the list")
.clone(),
Duration::from_secs(5),
)
.repeat();
@ -131,10 +136,11 @@ pub fn animation_control(
animation_transitions
.play(
&mut animation_player,
*animations
animations
.named_indices
.get(anim_name)
.expect("animation name should be in the list"),
.expect("animation name should be in the list")
.clone(),
Duration::from_secs(5),
)
.repeat();

View File

@ -1,7 +1,7 @@
use bevy::prelude::*;
use blenvy::{
AddToGameWorld, BlenvyPlugin, BluePrintBundle, BlueprintInfo, Dynamic, GameWorldTag,
HideUntilReady, SpawnBlueprint,
AddToGameWorld, BlenvyPlugin, BluePrintBundle, BlueprintInfo, DynamicBlueprintInstance,
GameWorldTag, HideUntilReady, SpawnBlueprint,
};
use rand::Rng;
@ -33,8 +33,16 @@ fn setup_game(mut commands: Commands) {
}
// you can also spawn blueprint instances at runtime
pub fn spawn_blueprint_instance(keycode: Res<ButtonInput<KeyCode>>, mut commands: Commands) {
pub fn spawn_blueprint_instance(
keycode: Res<ButtonInput<KeyCode>>,
mut commands: Commands,
mut game_world: Query<(Entity, &Children), With<GameWorldTag>>,
) {
if keycode.just_pressed(KeyCode::KeyS) {
let world = game_world.single_mut();
let world = world.1[0];
let mut rng = rand::thread_rng();
let range = 5.5;
let x: f32 = rng.gen_range(-range..range);
@ -42,7 +50,7 @@ pub fn spawn_blueprint_instance(keycode: Res<ButtonInput<KeyCode>>, mut commands
let name_index: u64 = rng.gen();
let __new_entity = commands
let new_entity = commands
.spawn((
BluePrintBundle {
blueprint: BlueprintInfo {
@ -51,7 +59,7 @@ pub fn spawn_blueprint_instance(keycode: Res<ButtonInput<KeyCode>>, mut commands
}, // FIXME
..Default::default()
},
Dynamic,
DynamicBlueprintInstance,
bevy::prelude::Name::from(format!("test{}", name_index)),
HideUntilReady,
AddToGameWorld,

View File

@ -7,5 +7,5 @@ license = "MIT OR Apache-2.0"
[dependencies]
bevy = { version = "0.14", features = ["dynamic_linking"] }
blenvy = { path = "../../crates/blenvy" }
rand = "0.8.5"
avian3d = "0.1.2"
bevy_rapier3d = { version = "0.25.0", features = ["serde-serialize", "debug-render-3d", "enhanced-determinism"] }
rand = "0.8.5"

View File

@ -0,0 +1,83 @@
use bevy::prelude::*;
use bevy_gltf_worlflow_examples_common_rapier::{assets::GameAssets, GameState, InAppRunning};
use blenvy::{BluePrintBundle, BlueprintName, GameWorldTag};
use bevy_rapier3d::prelude::Velocity;
use rand::Rng;
pub fn setup_game(
mut commands: Commands,
game_assets: Res<GameAssets>,
models: Res<Assets<bevy::gltf::Gltf>>,
mut next_game_state: ResMut<NextState<GameState>>,
) {
commands.insert_resource(AmbientLight {
color: Color::WHITE,
brightness: 0.2,
});
// here we actually spawn our game world/level
commands.spawn((
SceneBundle {
// note: because of this issue https://github.com/bevyengine/bevy/issues/10436, "world" is now a gltf file instead of a scene
scene: models
.get(game_assets.world.clone().unwrap().id())
.expect("main level should have been loaded")
.scenes[0]
.clone(),
..default()
},
bevy::prelude::Name::from("world"),
GameWorldTag,
InAppRunning,
));
next_game_state.set(GameState::InGame)
}
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
struct UnregisteredComponent;
pub fn spawn_test(
keycode: Res<ButtonInput<KeyCode>>,
mut commands: Commands,
mut game_world: Query<(Entity, &Children), With<GameWorldTag>>,
) {
if keycode.just_pressed(KeyCode::KeyT) {
let world = game_world.single_mut();
let world = world.1[0];
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),
},
))
.id();
commands.entity(world).add_child(new_entity);
}
}

View File

@ -0,0 +1,107 @@
use bevy::prelude::*;
use bevy_gltf_worlflow_examples_common_rapier::{AppState, InMainMenu};
pub fn setup_main_menu(mut commands: Commands) {
commands.spawn((
Camera2dBundle {
camera: Camera {
order: 102, // needed because of this: https://github.com/jakobhellermann/bevy_editor_pls/blob/crates/bevy_editor_pls_default_windows/src/cameras/mod.rs#L213C9-L213C28
..default()
},
..Default::default()
},
InMainMenu,
));
commands.spawn((
TextBundle::from_section(
"SOME GAME TITLE !!",
TextStyle {
//font: asset_server.load("fonts/FiraMono-Medium.ttf"),
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 T once the game is started for demo spawning)",
TextStyle {
//font: asset_server.load("fonts/FiraMono-Medium.ttf"),
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: asset_server.load("fonts/FiraMono-Medium.ttf"),
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: asset_server.load("fonts/FiraMono-Medium.ttf"),
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(bla: Query<Entity, With<InMainMenu>>, mut commands: Commands) {
for bli in bla.iter() {
commands.entity(bli).despawn_recursive();
}
}
pub fn main_menu(
keycode: Res<ButtonInput<KeyCode>>,
mut next_app_state: ResMut<NextState<AppState>>,
) {
if keycode.just_pressed(KeyCode::Enter) {
next_app_state.set(AppState::AppLoading);
}
}

View File

@ -1,4 +1,8 @@
use bevy::{gltf::Gltf, prelude::*};
use bevy_gltf_worlflow_examples_common_rapier::{
assets::GameAssets, GameState, InAppRunning, Player,
};
use bevy_rapier3d::prelude::*;
use blenvy::GameWorldTag;
#[derive(Component, Reflect, Default, Debug)]

View File

@ -1,11 +1,23 @@
// pub mod level_transitions;
// pub use level_transitions::*;
pub mod in_game;
pub use in_game::*;
pub mod in_main_menu;
pub use in_main_menu::*;
pub mod level_transitions;
pub use level_transitions::*;
use bevy::prelude::*;
use bevy_gltf_worlflow_examples_common_rapier::{AppState, GameState};
pub struct GamePlugin;
impl Plugin for GamePlugin {
fn build(&self, __app: &mut App) {
//app.add_plugins(LevelsPlugin);
fn build(&self, app: &mut App) {
app.add_plugins(LevelsPlugin)
.add_systems(Update, (spawn_test).run_if(in_state(GameState::InGame)))
.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(AppState::AppRunning), setup_game);
}
}

View File

@ -1,15 +1,12 @@
use avian3d::prelude::*;
use bevy::prelude::*;
use blenvy::{
AddToGameWorld, BlenvyPlugin, BluePrintBundle, BlueprintInfo, Dynamic, GameWorldTag,
HideUntilReady, SpawnBlueprint,
};
mod core;
use crate::core::*;
mod game;
use game::*;
mod test_components;
use rand::Rng;
use test_components::*;
fn main() {
@ -17,56 +14,9 @@ fn main() {
.add_plugins((
DefaultPlugins.set(AssetPlugin::default()),
// our custom plugins
ComponentsExamplesPlugin, // Showcases different type of components /structs
BlenvyPlugin::default(),
GamePlugin,
CorePlugin, // reusable plugins
GamePlugin, // specific to our game
ComponentsTestPlugin, // Showcases different type of components /structs
))
.add_systems(Startup, setup_game)
.add_systems(Update, spawn_blueprint_instance)
.run();
}
// this is how you setup & spawn a level from a blueprint
fn setup_game(mut commands: Commands) {
// here we actually spawn our game world/level
commands.spawn((
BlueprintInfo::from_path("levels/World.glb"), // all we need is a Blueprint info...
SpawnBlueprint, // and spawnblueprint to tell blenvy to spawn the blueprint now
HideUntilReady, // only reveal the level once it is ready
GameWorldTag,
));
}
// you can also spawn blueprint instances at runtime
pub fn spawn_blueprint_instance(keycode: Res<ButtonInput<KeyCode>>, mut commands: Commands) {
if keycode.just_pressed(KeyCode::KeyS) {
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: BlueprintInfo::from_path("blueprints/Health_Pickup.glb"),
..Default::default()
},
Dynamic,
bevy::prelude::Name::from(format!("test{}", name_index)),
HideUntilReady,
AddToGameWorld,
TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)),
LinearVelocity(Vec3::new(vel_x, vel_y, vel_z)),
))
.id();
// commands.entity(world).add_child(new_entity);
}
}

View File

@ -60,8 +60,8 @@ pub enum EnumTest {
None,
}
pub struct ComponentsExamplesPlugin;
impl Plugin for ComponentsExamplesPlugin {
pub struct ComponentsTestPlugin;
impl Plugin for ComponentsTestPlugin {
fn build(&self, app: &mut App) {
app.register_type::<BasicTest>()
.register_type::<UnitTest>()

View File

@ -1,18 +1,15 @@
use std::any::TypeId;
use bevy::{prelude::*, utils::hashbrown::HashSet};
use blenvy::{
AddToGameWorld, BlenvyPlugin, BlueprintInfo, BlueprintWorld, Dynamic, HideUntilReady,
LoadingRequest, SavingRequest, SpawnBlueprint,
};
use blenvy::{AddToGameWorld, BlenvyPlugin, BlueprintInfo, BlueprintWorld, Dynamic, DynamicBlueprintInstance, GameWorldTag, HideUntilReady, LoadingRequest, SavingRequest, SpawnBlueprint};
use rand::Rng;
// mod game;
// use game::*;
mod component_examples;
use bevy_inspector_egui::quick::WorldInspectorPlugin;
use component_examples::*;
use bevy_inspector_egui::quick::WorldInspectorPlugin;
fn main() {
App::new()
@ -42,16 +39,19 @@ fn main() {
// GamePlugin, // specific to our game
ComponentsExamplesPlugin, // Showcases different type of components /structs
))
.add_systems(Startup, setup_game)
.add_systems(
Update,
(spawn_blueprint_instance, move_movers, save_game, load_game),
)
.add_systems(Update, (spawn_blueprint_instance, move_movers, save_game, load_game))
.run();
}
// this is how you setup & spawn a level from a blueprint
fn setup_game(mut commands: Commands) {
fn setup_game(
mut commands: Commands,
) {
// would be nice: contains both static & dynamic stuff
commands.spawn((
BlueprintWorld::from_path("levels/World.glb"), // will take care of loading both static & dynamic data
@ -59,7 +59,10 @@ fn setup_game(mut commands: Commands) {
}
// you can also spawn blueprint instances at runtime
fn spawn_blueprint_instance(keycode: Res<ButtonInput<KeyCode>>, mut commands: Commands) {
fn spawn_blueprint_instance(
keycode: Res<ButtonInput<KeyCode>>,
mut commands: Commands,
) {
if keycode.just_pressed(KeyCode::KeyT) {
// random position
let mut rng = rand::thread_rng();
@ -70,26 +73,32 @@ fn spawn_blueprint_instance(keycode: Res<ButtonInput<KeyCode>>, mut commands: Co
// random name
let name_index: u64 = rng.gen();
commands.spawn((
BlueprintInfo::from_path("blueprints/test.glb"),
SpawnBlueprint,
Dynamic,
bevy::prelude::Name::from(format!("test{}", name_index)),
HideUntilReady,
AddToGameWorld,
TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)),
));
commands
.spawn((
BlueprintInfo::from_path("blueprints/test.glb"),
SpawnBlueprint,
DynamicBlueprintInstance,
bevy::prelude::Name::from(format!("test{}", name_index)),
HideUntilReady,
AddToGameWorld,
TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)),
));
}
}
fn move_movers(mut movers: Query<&mut Transform, With<Dynamic>>) {
for mut transform in movers.iter_mut() {
fn move_movers(
mut movers: Query<(&mut Transform), With<Dynamic>>
) {
for mut transform in movers.iter_mut(){
// println!("moving dynamic entity");
transform.translation.x += 0.005;
}
}
fn save_game(keycode: Res<ButtonInput<KeyCode>>, mut save_requests: EventWriter<SavingRequest>) {
fn save_game(
keycode: Res<ButtonInput<KeyCode>>,
mut save_requests: EventWriter<SavingRequest>,
) {
if keycode.just_pressed(KeyCode::KeyS) {
save_requests.send(SavingRequest {
path: "scenes/save.scn.ron".into(),
@ -97,7 +106,11 @@ fn save_game(keycode: Res<ButtonInput<KeyCode>>, mut save_requests: EventWriter<
}
}
fn load_game(keycode: Res<ButtonInput<KeyCode>>, mut load_requests: EventWriter<LoadingRequest>) {
fn load_game(
keycode: Res<ButtonInput<KeyCode>>,
mut load_requests: EventWriter<LoadingRequest>,
) {
if keycode.just_pressed(KeyCode::KeyL) {
load_requests.send(LoadingRequest {
path: "scenes/save.scn.ron".into(),

View File

@ -5,11 +5,11 @@ use std::time::Duration;
InstanceAnimationPlayerLink, InstanceAnimations,
};*/
use bevy::prelude::*;
use bevy::{animation::RepeatAnimation, gltf::Gltf, prelude::*};
use blenvy::{
AnimationInfos, AnimationMarkerReached, BlueprintAnimationPlayerLink, BlueprintAnimations,
InstanceAnimationPlayerLink, InstanceAnimations,
BlueprintInstanceDisabled, InstanceAnimationPlayerLink, InstanceAnimations,
};
#[derive(Component, Reflect, Default, Debug)]
@ -79,6 +79,68 @@ pub fn animations(
}
}*/
pub fn check_animations(
// (&BlueprintAnimationPlayerLink, &BlueprintAnimations)
foxes: Query<
(
Entity,
Option<&BlueprintAnimationPlayerLink>,
Option<&InstanceAnimationPlayerLink>,
),
(With<MarkerAllFoxes>, Without<BlueprintInstanceDisabled>),
>,
foo: Query<
(
Entity,
Option<&BlueprintAnimationPlayerLink>,
Option<&InstanceAnimationPlayerLink>,
),
(With<Marker1>, Without<BlueprintInstanceDisabled>),
>,
bar: Query<
(
Entity,
Option<&BlueprintAnimationPlayerLink>,
Option<&InstanceAnimationPlayerLink>,
),
(With<Marker2>, Without<BlueprintInstanceDisabled>),
>,
baz: Query<
(
Entity,
Option<&BlueprintAnimationPlayerLink>,
Option<&InstanceAnimationPlayerLink>,
),
(With<Marker3>, Without<BlueprintInstanceDisabled>),
>,
bli: Query<(Entity, &AnimationInfos)>,
anim_players: Query<(Entity, &AnimationPlayer)>,
all_names: Query<&Name>,
) {
/*for bla in foxes.iter() {
println!("MarkerAllFoxes {:?} {:?} {:?}", all_names.get(bla.0), bla.1, bla.2)
}
for bla in foo.iter() {
println!("Marker1 {:?} {:?} {:?}", all_names.get(bla.0), bla.1, bla.2)
}
for bla in bar.iter() {
println!("Marker2 {:?} {:?} {:?}", all_names.get(bla.0), bla.1, bla.2)
}
for bla in baz.iter() {
println!("Marker3 {:?} {:?} {:?}", all_names.get(bla.0), bla.1, bla.2)
}
println!(""); */
/*for blo in bli.iter() {
println!("YOOOOO {:?}", all_names.get(blo.0))
}
for anim in anim_players.iter() {
println!("Players {:?}", all_names.get(anim.0))
}*/
}
#[allow(clippy::type_complexity)]
pub fn play_animations(
animated_foxes: Query<
@ -121,10 +183,11 @@ pub fn play_animations(
let (mut animation_player, mut animation_transitions) =
animation_players.get_mut(link.0).unwrap();
let anim_name = "Survey";
let animation_index = *animations
let animation_index = animations
.named_indices
.get(anim_name)
.expect("animation name should be in the list");
.expect("animation name should be in the list")
.clone();
animation_transitions
.play(
@ -141,19 +204,23 @@ pub fn play_animations(
println!("Playing animation {:?}", playing_animation);
playing_animation.set_repeat(RepeatAnimation::Forever);*/
}
println!("");
}
if keycode.just_pressed(KeyCode::KeyP) {
println!("playing fox blueprint animation requested");
for (link, animations) in animated_foxes.iter() {
println!("FOO");
// println!("animations {:?}", animations.named_animations);
let (mut animation_player, mut animation_transitions) =
animation_players.get_mut(link.0).unwrap();
let anim_name = "Run";
let animation_index = *animations
let animation_index = animations
.named_indices
.get(anim_name)
.expect("animation name should be in the list");
.expect("animation name should be in the list")
.clone();
animation_transitions
.play(
@ -170,7 +237,7 @@ pub fn play_animations(
println!("Playing animation {:?}", playing_animation);
playing_animation.set_repeat(RepeatAnimation::Forever);*/
}
println!(" ");
println!("");
}
if keycode.just_pressed(KeyCode::KeyO) {
@ -181,10 +248,11 @@ pub fn play_animations(
let (mut animation_player, mut animation_transitions) =
animation_players.get_mut(link.0).unwrap();
let anim_name = "Walk";
let animation_index = *animations
let animation_index = animations
.named_indices
.get(anim_name)
.expect("animation name should be in the list");
.expect("animation name should be in the list")
.clone();
animation_transitions
.play(
@ -203,10 +271,11 @@ pub fn play_animations(
let (mut animation_player, mut animation_transitions) =
animation_players.get_mut(link.0).unwrap();
let anim_name = "Blueprint8_move";
let animation_index = *animations
let animation_index = animations
.named_indices
.get(anim_name)
.expect("animation name should be in the list");
.expect("animation name should be in the list")
.clone();
animation_transitions
.play(
@ -225,10 +294,11 @@ pub fn play_animations(
animation_players.get_mut(link.0).unwrap();
let anim_name = "Blueprint1_move";
let animation_index = *animations
let animation_index = animations
.named_indices
.get(anim_name)
.expect("animation name should be in the list");
.expect("animation name should be in the list")
.clone();
animation_transitions
.play(
@ -246,10 +316,11 @@ pub fn play_animations(
animation_players.get_mut(link.0).unwrap();
let anim_name = "Blueprint1_jump";
let animation_index = *animations
let animation_index = animations
.named_indices
.get(anim_name)
.expect("animation name should be in the list");
.expect("animation name should be in the list")
.clone();
animation_transitions
.play(
@ -267,10 +338,11 @@ pub fn play_animations(
animation_players.get_mut(link.0).unwrap();
let anim_name = "Blueprint1_move";
let animation_index = *animations
let animation_index = animations
.named_indices
.get(anim_name)
.expect("animation name should be in the list");
.expect("animation name should be in the list")
.clone();
animation_transitions
.play(
@ -283,7 +355,7 @@ pub fn play_animations(
}
}
pub fn __react_to_animation_markers(
pub fn react_to_animation_markers(
mut animation_marker_events: EventReader<AnimationMarkerReached>,
) {
for event in animation_marker_events.read() {

View File

@ -1,14 +1,18 @@
use crate::{GameState, InAppRunning};
use bevy::prelude::*;
use blenvy::{
AddToGameWorld, BluePrintBundle, BlueprintInfo, Dynamic, GameWorldTag, HideUntilReady,
SpawnBlueprint,
AddToGameWorld, BluePrintBundle, BlueprintInfo, DynamicBlueprintInstance, GameWorldTag,
HideUntilReady, SpawnBlueprint,
};
//use bevy_rapier3d::prelude::Velocity;
use rand::Rng;
pub fn setup_game(mut commands: Commands, mut next_game_state: ResMut<NextState<GameState>>) {
pub fn setup_game(
mut commands: Commands,
asset_server: Res<AssetServer>,
mut next_game_state: ResMut<NextState<GameState>>,
) {
// here we actually spawn our game world/level
commands.spawn((
BlueprintInfo::from_path("levels/World.glb"),
@ -25,22 +29,30 @@ pub fn setup_game(mut commands: Commands, mut next_game_state: ResMut<NextState<
#[reflect(Component)]
struct UnregisteredComponent;
pub fn spawn_test(keycode: Res<ButtonInput<KeyCode>>, mut commands: Commands) {
pub fn spawn_test(
keycode: Res<ButtonInput<KeyCode>>,
mut commands: Commands,
mut game_world: Query<(Entity, &Children), With<GameWorldTag>>,
) {
if keycode.just_pressed(KeyCode::KeyS) {
let world = game_world.single_mut();
let world = world.1[0];
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 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 vel_z: f32 = rng.gen_range(-range..range);
let name_index: u64 = rng.gen();
let __new_entity = commands
let new_entity = commands
.spawn((
BluePrintBundle {
blueprint: BlueprintInfo {
@ -49,7 +61,7 @@ pub fn spawn_test(keycode: Res<ButtonInput<KeyCode>>, mut commands: Commands) {
}, // FIXME
..Default::default()
},
Dynamic,
DynamicBlueprintInstance,
bevy::prelude::Name::from(format!("test{}", name_index)),
HideUntilReady,
AddToGameWorld,

View File

@ -8,7 +8,7 @@ use std::{collections::HashMap, fs, time::Duration};
use blenvy::{
BlueprintAnimationPlayerLink, BlueprintAssets, BlueprintEvent, BlueprintInfo,
InstanceAnimations,
GltfBlueprintsSet, InstanceAnimations,
};
use crate::{AppState, GameState};
@ -136,8 +136,8 @@ fn check_for_gltf_events(
match event {
BlueprintEvent::InstanceReady {
entity,
blueprint_name: _,
blueprint_path: _,
blueprint_name,
blueprint_path,
} => {
info!(
"BLUEPRINT EVENT: {:?} for {:?}",
@ -147,8 +147,8 @@ fn check_for_gltf_events(
}
BlueprintEvent::AssetsLoaded {
entity,
blueprint_name: _,
blueprint_path: _,
blueprint_name,
blueprint_path,
} => {
info!(
"BLUEPRINT EVENT: {:?} for {:?}",
@ -156,6 +156,9 @@ fn check_for_gltf_events(
all_names.get(*entity)
);
}
_ => {
info!("BLUEPRINT EVENT: {:?}", event);
}
}
}
}
@ -180,14 +183,14 @@ impl Plugin for GamePlugin {
.run_if(in_state(AppState::AppRunning))
.after(GltfBlueprintsSet::AfterSpawn)
)*/
.add_systems(Update, play_animations) // check_animations
.add_systems(Update, (play_animations, check_animations))
//.add_systems(Update, react_to_animation_markers)
.add_systems(Update, generate_screenshot.run_if(on_timer(Duration::from_secs_f32(0.2)))) // TODO: run once
/*.add_systems(Update, generate_screenshot.run_if(on_timer(Duration::from_secs_f32(0.2)))) // TODO: run once
.add_systems(
Update,
exit_game.run_if(on_timer(Duration::from_secs_f32(0.5))),
) // shut down the app after this time
) // shut down the app after this time*/
;
}
}

View File

@ -2,13 +2,14 @@ use bevy::{
gltf::{GltfMaterialExtras, GltfMeshExtras, GltfSceneExtras},
prelude::*,
};
use blenvy::{BlueprintAssets, BlueprintInstanceReady};
use crate::{EnumTest, RedirectPropHitImpulse};
use crate::{BasicTest, EnumComplex, EnumTest, RedirectPropHitImpulse};
#[derive(Component)]
pub struct HiearchyDebugTag;
pub fn setup_hierarchy_debug(mut commands: Commands) {
pub fn setup_hierarchy_debug(mut commands: Commands, asset_server: Res<AssetServer>) {
// a place to display the extras on screen
commands.spawn((
TextBundle::from_section(
@ -39,25 +40,25 @@ pub fn get_descendants(
all_children: &Query<&Children>,
all_names: &Query<&Name>,
root: &Entity,
__all_transforms: &Query<&Transform>,
__all_global_transforms: &Query<&GlobalTransform>,
all_transforms: &Query<&Transform>,
all_global_transforms: &Query<&GlobalTransform>,
nesting: usize,
to_check: &Query<&EnumTest>, //&Query<(&BlueprintInstanceReady, &BlueprintAssets)>,
) -> String {
let mut hierarchy_display: Vec<String> = vec![];
let root_name = all_names.get(*root);
let name;
if let Ok(root_name) = root_name {
name = root_name.to_string();
if root_name.is_ok() {
name = root_name.unwrap().to_string();
} else {
name = "no_name".to_string();
name = "no_name".to_string()
}
let mut component_display: String = "".into();
let __components_to_check = to_check.get(*root);
let components_to_check = to_check.get(*root);
if let Ok(compo) = to_check.get(*root) {
component_display = format!("{:?}", compo);
component_display = format!("{:?}", compo).clone();
}
hierarchy_display.push(format!(
@ -71,22 +72,22 @@ pub fn get_descendants(
if let Ok(children) = all_children.get(*root) {
for child in children.iter() {
let child_descendants_display = get_descendants(
all_children,
all_names,
child,
__all_transforms,
__all_global_transforms,
&all_children,
&all_names,
&child,
&all_transforms,
&all_global_transforms,
nesting + 4,
to_check,
&to_check,
);
hierarchy_display.push(child_descendants_display);
}
}
hierarchy_display.join("\n")
return hierarchy_display.join("\n");
}
pub fn draw_hierarchy_debug(
root: Query<Entity, Without<Parent>>,
root: Query<(Entity, Option<&Name>, &Children), (Without<Parent>)>,
all_children: Query<&Children>,
all_names: Query<&Name>,
all_transforms: Query<&Transform>,
@ -97,7 +98,7 @@ pub fn draw_hierarchy_debug(
) {
let mut hierarchy_display: Vec<String> = vec![];
for root_entity in root.iter() {
for (root_entity, name, children) in root.iter() {
// hierarchy_display.push( format!("Hierarchy root{:?}", name) );
hierarchy_display.push(get_descendants(
@ -123,8 +124,7 @@ pub fn draw_hierarchy_debug(
}
////////:just some testing for gltf extras
#[allow(clippy::type_complexity)]
fn __check_for_gltf_extras(
fn check_for_gltf_extras(
gltf_extras_per_entity: Query<(
Entity,
Option<&Name>,
@ -137,7 +137,7 @@ fn __check_for_gltf_extras(
) {
let mut gltf_extra_infos_lines: Vec<String> = vec![];
for (id, name, scene_extras, __extras, mesh_extras, material_extras) in
for (id, name, scene_extras, extras, mesh_extras, material_extras) in
gltf_extras_per_entity.iter()
{
if scene_extras.is_some()
@ -165,12 +165,12 @@ fn __check_for_gltf_extras(
}
}
fn __check_for_component(
specific_components: Query<(Entity, Option<&Name>, &RedirectPropHitImpulse)>,
fn check_for_component(
foo: Query<(Entity, Option<&Name>, &RedirectPropHitImpulse)>,
mut display: Query<&mut Text, With<HiearchyDebugTag>>,
) {
let mut info_lines: Vec<String> = vec![];
for (__entiity, name, enum_complex) in specific_components.iter() {
for (entiity, name, enum_complex) in foo.iter() {
let data = format!(
" We have a matching component: {:?} (on {:?})",
enum_complex, name

View File

@ -1,6 +1,5 @@
use bevy::prelude::*;
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Default, States)]
pub enum AppState {
CoreLoading,
@ -11,7 +10,6 @@ pub enum AppState {
AppEnding,
}
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Default, States)]
pub enum GameState {
#[default]
@ -27,6 +25,8 @@ pub enum GameState {
}
// 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)]

View File

@ -78,7 +78,7 @@ class AutoExportSettings(PropertyGroup):
) # type: ignore
split_out_animations: BoolProperty(
name='Split out animations (not functional yet)',
name='Split out animations',
description='removes animations/armatures from blueprints and exports them separately ',
default=False,
update=save_settings

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 19 KiB