Compare commits
7 Commits
3380f4c71d
...
b756819088
Author | SHA1 | Date |
---|---|---|
kaosat.dev | b756819088 | |
kaosat.dev | 30b052d4d2 | |
kaosat.dev | 0938a1d10c | |
kaosat.dev | 7acb1f3a57 | |
kaosat.dev | b25fc56ea6 | |
kaosat.dev | c1e2bb6ecf | |
kaosat.dev | e394edade2 |
|
@ -1,7 +1,6 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"crates/*",
|
"crates/*",
|
||||||
"examples/common*",
|
|
||||||
"examples/blenvy/*",
|
"examples/blenvy/*",
|
||||||
"testing/bevy_example/",
|
"testing/bevy_example/",
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Blender add-ons
|
# Blender add-ons
|
||||||
|
|
||||||
- gltf_auto_export and bevy_components have been replaced with a single add-on for simplicity
|
- gltf_auto_export and bevy_components have been replaced with a single Blenvy add-on for simplicity
|
||||||
|
|
||||||
## Components:
|
## Components:
|
||||||
|
|
||||||
|
@ -111,7 +111,7 @@ Blenvy will take care of loading all needed blueprints & other assets for you
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## BlueprintDisabled
|
## BlueprintInstanceDisabled
|
||||||
|
|
||||||
you can now query for this component
|
you can now query for this component
|
||||||
|
|
||||||
|
|
13
TODO.md
13
TODO.md
|
@ -58,7 +58,7 @@ Components:
|
||||||
- [x] BLENVY_OT_component_rename_component
|
- [x] BLENVY_OT_component_rename_component
|
||||||
- [x] BLENVY_OT_component_fix
|
- [x] BLENVY_OT_component_fix
|
||||||
- [x] add handling for core::ops::Range<f32> & other ranges
|
- [x] add handling for core::ops::Range<f32> & other ranges
|
||||||
- [x] fix is_component_valid that is used in gltf_auto_export
|
- [x] fix is_component_valid that is used in blenvy
|
||||||
- [x] Hashmap Support
|
- [x] Hashmap Support
|
||||||
- [x] fix parsing of keys's type either on Bevy side (prefered) or on the Blender side
|
- [x] fix parsing of keys's type either on Bevy side (prefered) or on the Blender side
|
||||||
- [x] fix weird issue with missing "0" property when adding new entry in empty hashmap => happens only if the values for the "setter" have never been set
|
- [x] fix weird issue with missing "0" property when adding new entry in empty hashmap => happens only if the values for the "setter" have never been set
|
||||||
|
@ -286,7 +286,7 @@ Bevy Side:
|
||||||
- [ ] invalidate despawned entity & parent entities AABB
|
- [ ] invalidate despawned entity & parent entities AABB
|
||||||
- [ ] add unloading/cache removal of materials
|
- [ ] add unloading/cache removal of materials
|
||||||
|
|
||||||
|
- [ ] add back and upgrade save-load
|
||||||
|
|
||||||
- [x] review & change general component insertion & spawning ordering & logic
|
- [x] review & change general component insertion & spawning ordering & logic
|
||||||
- GltfComponentsSet::Injection => GltfBlueprintsSet::Spawn => GltfBlueprintsSet::AfterSpawn
|
- GltfComponentsSet::Injection => GltfBlueprintsSet::Spawn => GltfBlueprintsSet::AfterSpawn
|
||||||
|
@ -299,8 +299,13 @@ Bevy Side:
|
||||||
- [x] how to deal with animation graphs ?
|
- [x] how to deal with animation graphs ?
|
||||||
|
|
||||||
|
|
||||||
- [ ] remove "Library" component & co
|
- [x] remove "Library" component & co
|
||||||
- [ ] BlueprintDisabled => BlueprintInstanceDisabled
|
- [x] make "InBlueprint" non optional,
|
||||||
|
- [ ] and perhaps rename it to "FromBlueprint(BlueprintInfo)"
|
||||||
|
|
||||||
|
- [x] BlueprintInstanceDisabled => BlueprintInstanceDisabled
|
||||||
|
- [x] fix "remove component" operator from the rename/fix/update components panel
|
||||||
|
- [ ] replace string in BlueprintInfo path with PathBuf ?
|
||||||
|
|
||||||
- [ ] update main docs
|
- [ ] update main docs
|
||||||
- [ ] rename project to Blenvy
|
- [ ] rename project to Blenvy
|
||||||
|
|
|
@ -3,8 +3,8 @@ name = "blenvy"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Mark 'kaosat-dev' Moissette"]
|
authors = ["Mark 'kaosat-dev' Moissette"]
|
||||||
description = "Allows you to define Bevy components direclty inside gltf files and instanciate the components on the Bevy side."
|
description = "Allows you to define Bevy components direclty inside gltf files and instanciate the components on the Bevy side."
|
||||||
homepage = "https://github.com/kaosat-dev/Blender_bevy_components_workflow"
|
homepage = "https://github.com/kaosat-dev/Blenvy"
|
||||||
repository = "https://github.com/kaosat-dev/Blender_bevy_components_workflow"
|
repository = "https://github.com/kaosat-dev/Blenvy"
|
||||||
keywords = ["gamedev", "bevy", "assets", "gltf", "components"]
|
keywords = ["gamedev", "bevy", "assets", "gltf", "components"]
|
||||||
categories = ["game-development"]
|
categories = ["game-development"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
|
@ -1,23 +1,39 @@
|
||||||
[![Crates.io](https://img.shields.io/crates/v/blenvy)](https://crates.io/crates/blenvy)
|
[![Crates.io](https://img.shields.io/crates/v/blenvy)](https://crates.io/crates/blenvy)
|
||||||
[![Docs](https://img.shields.io/docsrs/blenvy)](https://docs.rs/blenvy/latest/blenvy/)
|
[![Docs](https://img.shields.io/docsrs/blenvy)](https://docs.rs/blenvy/latest/blenvy/)
|
||||||
[![License](https://img.shields.io/crates/l/blenvy)](https://github.com/kaosat-dev/Blender_bevy_components_workflow/blob/main/crates/blenvy/License.md)
|
[![License](https://img.shields.io/crates/l/blenvy)](https://github.com/kaosat-dev/Blenvy/blob/main/crates/blenvy/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 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)
|
||||||
|
|
||||||
# blenvy
|
# blenvy
|
||||||
|
|
||||||
this crate adds the ability to define Blueprints/Prefabs for [Bevy](https://bevyengine.org/) inside gltf files and spawn them in Bevy.
|
This crate allows you to
|
||||||
|
- define [Bevy](https://bevyengine.org/) components direclty inside gltf files and instanciate the components on the Bevy side.
|
||||||
|
- 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 !
|
||||||
|
|
||||||
* Allows you to create lightweight levels, where all assets are different gltf files and loaded after the main level is loaded
|
A blueprint is a set of **overrideable** components + a hierarchy: ie
|
||||||
* 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 BlueprintInfo
|
||||||
|
|
||||||
* just a Gltf file with Gltf_extras specifying components
|
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-on that do a lot of the work for you
|
||||||
* a component called BlueprintInfo
|
- [blenvy](https://github.com/kaosat-dev/Blenvy/tree/main/tools/blenvy)
|
||||||
|
- allows you to create a Json export of all your components/ registered types.
|
||||||
|
Its main use case is as a backbone for the [```blenvy``` Blender add-on](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/tools/blenvy), 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).
|
||||||
|
- adds the ability to easilly **save** and **load** your game worlds for [Bevy](https://bevyengine.org/) .
|
||||||
|
|
||||||
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-on that do a lot of the work for you
|
* leverages blueprints & seperation between
|
||||||
- [blenvy](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/tools/blenvy)
|
* **dynamic** entities : entities that can change during the lifetime of your app/game
|
||||||
|
* **static** entities : entities that do NOT change (typically, a part of your levels/ environements)
|
||||||
|
* and allows allow for :
|
||||||
|
* a simple save/load workflow thanks to the above
|
||||||
|
* ability to specify **which entities** to save or to exclude
|
||||||
|
* ability to specify **which components** to save or to exclude
|
||||||
|
* ability to specify **which resources** to save or to exclude
|
||||||
|
* small(er) save files (only a portion of the entities is saved)
|
||||||
|
|
||||||
|
Particularly useful when using [Blender](https://www.blender.org/) as an editor for the [Bevy](https://bevyengine.org/) game engine, combined with the [Blender plugin](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/tools/blenvy) that does a lot of the work for you (including spliting generating seperate gltf files for your static vs dynamic assets)
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
@ -40,12 +56,27 @@ fn main() {
|
||||||
.add_plugins(DefaultPlugins)
|
.add_plugins(DefaultPlugins)
|
||||||
.add_plugins(BlenvyPlugin)
|
.add_plugins(BlenvyPlugin)
|
||||||
|
|
||||||
|
.add_systems(Startup, setup_game)
|
||||||
|
.add_systems(Update, spawn_blueprint_instance)
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
// not shown here: any other setup that is not specific to blueprints
|
|
||||||
|
|
||||||
fn spawn_blueprint(
|
// this is how you setup & spawn a level from a blueprint
|
||||||
|
fn setup_game(
|
||||||
|
mut commands: Commands,
|
||||||
|
) {
|
||||||
|
|
||||||
|
// here we spawn our game world/level, which is also a blueprint !
|
||||||
|
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,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn spawn_blueprint_instance(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
keycode: Res<Input<KeyCode>>,
|
keycode: Res<Input<KeyCode>>,
|
||||||
){
|
){
|
||||||
|
@ -152,7 +183,7 @@ any component you specify when spawning the Blueprint that is also specified **w
|
||||||
for example
|
for example
|
||||||
```rust no_run
|
```rust no_run
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
BlueprintInfo("Health_Pickup".to_string()),
|
BlueprintInfo(path: "Health_Pickup.glb".into()),
|
||||||
SpawnBlueprint,
|
SpawnBlueprint,
|
||||||
TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)),
|
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
|
HealthPowerUp(20)// if this is component is also present inside the "Health_Pickup" blueprint, that one will be replaced with this component during spawning
|
||||||
|
@ -168,22 +199,7 @@ There is also a ```BluePrintBundle``` for convenience , which just has
|
||||||
|
|
||||||
## Additional information
|
## Additional information
|
||||||
|
|
||||||
- When a blueprint is spawned, all its children entities (and nested children etc) also have an ```InBlueprint``` component that gets insert
|
- When a blueprint is spawned, an ```InBlueprint``` component is inserted into all its children entities (and nested children etc)
|
||||||
- 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: BlueprintInfo("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
|
- 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"
|
You can use it in your queries to add your entities as children of this "world"
|
||||||
|
@ -192,16 +208,10 @@ This way all your levels, your dynamic entities etc, are kept seperated from UI
|
||||||
> Note: you should only have a SINGLE entity tagged with that component !
|
> Note: you should only have a SINGLE entity tagged with that component !
|
||||||
|
|
||||||
```rust no_run
|
```rust no_run
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
SceneBundle {
|
BlueprintInfo::from_path("levels/World.glb"),
|
||||||
scene: models
|
SpawnBlueprint,
|
||||||
.get(game_assets.world.id())
|
HideUntilReady,
|
||||||
.expect("main level should have been loaded")
|
|
||||||
.scenes[0]
|
|
||||||
.clone(),
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
bevy::prelude::Name::from("world"),
|
|
||||||
GameWorldTag, // here it is
|
GameWorldTag, // here it is
|
||||||
));
|
));
|
||||||
```
|
```
|
||||||
|
@ -219,7 +229,7 @@ Typically , the order of systems should be
|
||||||
|
|
||||||
***bevy_gltf_components (GltfComponentsSet::Injection)*** => ***blenvy (GltfBlueprintsSet::Spawn, GltfBlueprintsSet::AfterSpawn)*** => ***replace_proxies***
|
***bevy_gltf_components (GltfComponentsSet::Injection)*** => ***blenvy (GltfBlueprintsSet::Spawn, GltfBlueprintsSet::AfterSpawn)*** => ***replace_proxies***
|
||||||
|
|
||||||
see https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/blenvy/basic for how to set it up correctly
|
see https://github.com/kaosat-dev/Blenvy/tree/main/examples/blenvy/basic for how to set it up correctly
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -273,44 +283,32 @@ pub fn animation_change_on_proximity_foxes(
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
see https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/blenvy/animation for how to set it up correctly
|
see https://github.com/kaosat-dev/Blenvy/tree/main/examples/blenvy/animation for how to set it up correctly
|
||||||
|
|
||||||
particularly from https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/blenvy/animation/game/in_game.rs
|
particularly from https://github.com/kaosat-dev/Blenvy/tree/main/examples/blenvy/animation/game/in_game.rs
|
||||||
|
|
||||||
|
|
||||||
## Materials
|
## Materials
|
||||||
|
|
||||||
You have the option of using "material libraries" to share common textures/materials between blueprints, in order to avoid asset & memory bloat:
|
Ff you enable it on the blender side, Blenvy will be 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
|
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 !!
|
56 times !!
|
||||||
|
|
||||||
|
|
||||||
you can configure this with the settings:
|
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/blenvy)
|
||||||
```rust
|
|
||||||
material_library: true // defaults to false, enable this to enable automatic injection of materials from material library files
|
|
||||||
```
|
|
||||||
|
|
||||||
> Important! you must take care of preloading your material librairy gltf files in advance, using for example ```bevy_asset_loader```since
|
|
||||||
```blenvy``` currently does NOT take care of loading those at runtime
|
|
||||||
|
|
||||||
|
|
||||||
see https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/blenvy/materials for how to set it up correctly
|
|
||||||
|
|
||||||
Generating optimised blueprints and material libraries can be automated using the latests version of the [Blender plugin](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/tools/gltf_auto_export)
|
|
||||||
|
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/blenvy/basic
|
https://github.com/kaosat-dev/Blenvy/tree/main/examples/blenvy/components
|
||||||
|
|
||||||
https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/blenvy/basic_xpbd_physics
|
https://github.com/kaosat-dev/Blenvy/tree/main/examples/blenvy/blueprints
|
||||||
|
|
||||||
https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/blenvy/animation
|
https://github.com/kaosat-dev/Blenvy/tree/main/examples/blenvy/animation
|
||||||
|
|
||||||
https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/blenvy/materials
|
https://github.com/kaosat-dev/Blenvy/tree/main/examples/blenvy/save_load
|
||||||
|
|
||||||
https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/blenvy/multiple_levels_multiple_blendfiles
|
https://github.com/kaosat-dev/Blenvy/tree/main/examples/blenvy/demo (a full fledged demo)
|
||||||
|
|
||||||
|
|
||||||
## Compatible Bevy versions
|
## Compatible Bevy versions
|
||||||
|
|
|
@ -1,190 +0,0 @@
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use bevy::{
|
|
||||||
asset::{AssetServer, Assets, Handle},
|
|
||||||
ecs::{
|
|
||||||
component::Component,
|
|
||||||
entity::Entity,
|
|
||||||
query::{Added, With},
|
|
||||||
reflect::ReflectComponent,
|
|
||||||
system::{Commands, Query, Res, ResMut},
|
|
||||||
},
|
|
||||||
gltf::Gltf,
|
|
||||||
hierarchy::{Children, Parent},
|
|
||||||
log::debug,
|
|
||||||
pbr::StandardMaterial,
|
|
||||||
reflect::Reflect,
|
|
||||||
render::mesh::Mesh,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{AssetLoadTracker, BlueprintAssetsLoadState, BlenvyConfig, BlueprintInstanceReady};
|
|
||||||
|
|
||||||
#[derive(Component, Reflect, Default, Debug)]
|
|
||||||
#[reflect(Component)]
|
|
||||||
/// struct containing the name & path of the material to apply
|
|
||||||
pub struct MaterialInfo {
|
|
||||||
pub name: String,
|
|
||||||
pub path: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// flag component
|
|
||||||
#[derive(Component)]
|
|
||||||
pub(crate) struct BlueprintMaterialAssetsLoaded;
|
|
||||||
/// flag component
|
|
||||||
#[derive(Component)]
|
|
||||||
pub(crate) struct BlueprintMaterialAssetsNotLoaded;
|
|
||||||
|
|
||||||
/// system that injects / replaces materials from material library
|
|
||||||
pub(crate) fn materials_inject(
|
|
||||||
blenvy_config: ResMut<BlenvyConfig>,
|
|
||||||
material_infos: Query<(Entity, &MaterialInfo), Added<MaterialInfo>>,
|
|
||||||
asset_server: Res<AssetServer>,
|
|
||||||
mut commands: Commands,
|
|
||||||
) {
|
|
||||||
|
|
||||||
|
|
||||||
for (entity, material_info) in material_infos.iter() {
|
|
||||||
println!("Entity with material info {:?} {:?}", entity, material_info);
|
|
||||||
let material_full_path = format!("{}#{}", material_info.path, material_info.name);
|
|
||||||
if blenvy_config
|
|
||||||
.materials_cache
|
|
||||||
.contains_key(&material_full_path)
|
|
||||||
{
|
|
||||||
debug!("material is cached, retrieving");
|
|
||||||
blenvy_config
|
|
||||||
.materials_cache
|
|
||||||
.get(&material_full_path)
|
|
||||||
.expect("we should have the material available");
|
|
||||||
commands
|
|
||||||
.entity(entity)
|
|
||||||
.insert(BlueprintMaterialAssetsLoaded);
|
|
||||||
} else {
|
|
||||||
let material_file_handle = asset_server.load_untyped(&material_info.path.clone()); // : Handle<Gltf>
|
|
||||||
let material_file_id = material_file_handle.id();
|
|
||||||
|
|
||||||
let asset_infos: Vec<AssetLoadTracker> = vec![AssetLoadTracker {
|
|
||||||
name: material_info.name.clone(),
|
|
||||||
path: material_info.path.clone(),
|
|
||||||
id: material_file_id,
|
|
||||||
loaded: false,
|
|
||||||
handle: material_file_handle.clone(),
|
|
||||||
}];
|
|
||||||
|
|
||||||
commands
|
|
||||||
.entity(entity)
|
|
||||||
.insert(BlueprintAssetsLoadState {
|
|
||||||
all_loaded: false,
|
|
||||||
asset_infos,
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
.insert(BlueprintMaterialAssetsNotLoaded);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO, merge with blueprints_check_assets_loading, make generic ?
|
|
||||||
pub(crate) fn check_for_material_loaded(
|
|
||||||
mut blueprint_assets_to_load: Query<
|
|
||||||
(Entity, &mut BlueprintAssetsLoadState),
|
|
||||||
With<BlueprintMaterialAssetsNotLoaded>,
|
|
||||||
>,
|
|
||||||
asset_server: Res<AssetServer>,
|
|
||||||
mut commands: Commands,
|
|
||||||
) {
|
|
||||||
for (entity, 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();
|
|
||||||
for tracker in assets_to_load.asset_infos.iter_mut() {
|
|
||||||
let asset_id = tracker.id;
|
|
||||||
let loaded = asset_server.is_loaded_with_dependencies(asset_id);
|
|
||||||
tracker.loaded = loaded;
|
|
||||||
if loaded {
|
|
||||||
loaded_amount += 1;
|
|
||||||
} else {
|
|
||||||
all_loaded = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let progress: f32 = loaded_amount as f32 / total as f32;
|
|
||||||
assets_to_load.progress = progress;
|
|
||||||
|
|
||||||
if all_loaded {
|
|
||||||
assets_to_load.all_loaded = true;
|
|
||||||
commands
|
|
||||||
.entity(entity)
|
|
||||||
.insert(BlueprintMaterialAssetsLoaded)
|
|
||||||
.remove::<BlueprintMaterialAssetsNotLoaded>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// system that injects / replaces materials from material library
|
|
||||||
pub(crate) fn materials_inject2(
|
|
||||||
mut blenvy_config: ResMut<BlenvyConfig>,
|
|
||||||
material_infos: Query<
|
|
||||||
(&MaterialInfo, &Children),
|
|
||||||
(
|
|
||||||
Added<BlueprintMaterialAssetsLoaded>,
|
|
||||||
With<BlueprintMaterialAssetsLoaded>,
|
|
||||||
),
|
|
||||||
>,
|
|
||||||
with_materials_and_meshes: Query<
|
|
||||||
(),
|
|
||||||
(
|
|
||||||
With<Parent>,
|
|
||||||
With<Handle<StandardMaterial>>,
|
|
||||||
With<Handle<Mesh>>,
|
|
||||||
),
|
|
||||||
>,
|
|
||||||
assets_gltf: Res<Assets<Gltf>>,
|
|
||||||
asset_server: Res<AssetServer>,
|
|
||||||
|
|
||||||
mut commands: Commands,
|
|
||||||
) {
|
|
||||||
for (material_info, children) in material_infos.iter() {
|
|
||||||
let material_full_path = format!("{}#{}", material_info.path, material_info.name);
|
|
||||||
let mut material_found: Option<&Handle<StandardMaterial>> = None;
|
|
||||||
|
|
||||||
if blenvy_config
|
|
||||||
.materials_cache
|
|
||||||
.contains_key(&material_full_path)
|
|
||||||
{
|
|
||||||
debug!("material is cached, retrieving");
|
|
||||||
let material = blenvy_config
|
|
||||||
.materials_cache
|
|
||||||
.get(&material_full_path)
|
|
||||||
.expect("we should have the material available");
|
|
||||||
material_found = Some(material);
|
|
||||||
} else {
|
|
||||||
let model_handle: Handle<Gltf> = asset_server.load(material_info.path.clone()); // FIXME: kinda weird now
|
|
||||||
let mat_gltf = assets_gltf
|
|
||||||
.get(model_handle.id())
|
|
||||||
.expect("material should have been preloaded");
|
|
||||||
if mat_gltf.named_materials.contains_key(&material_info.name as &str) {
|
|
||||||
let material = mat_gltf
|
|
||||||
.named_materials
|
|
||||||
.get(&material_info.name as &str)
|
|
||||||
.expect("this material should have been loaded");
|
|
||||||
blenvy_config
|
|
||||||
.materials_cache
|
|
||||||
.insert(material_full_path, material.clone());
|
|
||||||
material_found = Some(material);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(material) = material_found {
|
|
||||||
for child in children.iter() {
|
|
||||||
if with_materials_and_meshes.contains(*child) {
|
|
||||||
debug!(
|
|
||||||
"injecting material {}, path: {:?}",
|
|
||||||
material_info.name,
|
|
||||||
material_info.path.clone()
|
|
||||||
);
|
|
||||||
|
|
||||||
commands.entity(*child).insert(material.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,248 +0,0 @@
|
||||||
|
|
||||||
|
|
||||||
/// helper component, for tracking loaded assets's loading state, id , handle etc
|
|
||||||
#[derive(Default, Debug)]
|
|
||||||
pub(crate) struct AssetLoadTracker<T: bevy::prelude::Asset> {
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub name: String,
|
|
||||||
pub id: AssetId<T>,
|
|
||||||
pub loaded: bool,
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub handle: Handle<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// helper component, for tracking loaded assets
|
|
||||||
#[derive(Component, Debug)]
|
|
||||||
pub(crate) struct AssetsToLoad<T: bevy::prelude::Asset> {
|
|
||||||
pub all_loaded: bool,
|
|
||||||
pub asset_infos: Vec<AssetLoadTracker<T>>,
|
|
||||||
pub progress: f32,
|
|
||||||
}
|
|
||||||
impl<T: bevy::prelude::Asset> Default for AssetsToLoad<T> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
all_loaded: Default::default(),
|
|
||||||
asset_infos: Default::default(),
|
|
||||||
progress: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// flag component, usually added when a blueprint is loaded
|
|
||||||
#[derive(Component)]
|
|
||||||
pub(crate) struct BlueprintAssetsLoaded;
|
|
||||||
/// flag component
|
|
||||||
#[derive(Component)]
|
|
||||||
pub(crate) struct BlueprintAssetsNotLoaded;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// spawning prepare function,
|
|
||||||
/// * also takes into account the already exisiting "override" components, ie "override components" > components from blueprint
|
|
||||||
pub(crate) fn prepare_blueprints(
|
|
||||||
spawn_placeholders: Query<
|
|
||||||
(
|
|
||||||
Entity,
|
|
||||||
&BlueprintName,
|
|
||||||
Option<&Parent>,
|
|
||||||
Option<&Library>,
|
|
||||||
Option<&Name>,
|
|
||||||
Option<&BlueprintsList>,
|
|
||||||
),
|
|
||||||
(Added<BlueprintName>, Added<SpawnBlueprint>, Without<Spawned>),
|
|
||||||
>,
|
|
||||||
|
|
||||||
mut commands: Commands,
|
|
||||||
asset_server: Res<AssetServer>,
|
|
||||||
blenvy_config: Res<BluePrintsConfig>,
|
|
||||||
) {
|
|
||||||
for (entity, blupeprint_name, original_parent, library_override, name, blueprints_list) in
|
|
||||||
spawn_placeholders.iter()
|
|
||||||
{
|
|
||||||
debug!(
|
|
||||||
"requesting to spawn {:?} for entity {:?}, id: {:?}, parent:{:?}",
|
|
||||||
blupeprint_name.0, name, entity, original_parent
|
|
||||||
);
|
|
||||||
|
|
||||||
// println!("main model path {:?}", model_path);
|
|
||||||
if blueprints_list.is_some() {
|
|
||||||
let blueprints_list = blueprints_list.unwrap();
|
|
||||||
// println!("blueprints list {:?}", blueprints_list.0.keys());
|
|
||||||
let mut asset_infos: Vec<AssetLoadTracker<Gltf>> = vec![];
|
|
||||||
let library_path =
|
|
||||||
library_override.map_or_else(|| &blenvy_config.library_folder, |l| &l.0);
|
|
||||||
for (blueprint_name, _) in blueprints_list.0.iter() {
|
|
||||||
let model_file_name = format!("{}.{}", &blueprint_name, &blenvy_config.format);
|
|
||||||
let model_path = Path::new(&library_path).join(Path::new(model_file_name.as_str()));
|
|
||||||
|
|
||||||
let model_handle: Handle<Gltf> = asset_server.load(model_path.clone());
|
|
||||||
let model_id = model_handle.id();
|
|
||||||
let loaded = asset_server.is_loaded_with_dependencies(model_id);
|
|
||||||
if !loaded {
|
|
||||||
asset_infos.push(AssetLoadTracker {
|
|
||||||
name: model_path.to_string_lossy().into(),
|
|
||||||
id: model_id,
|
|
||||||
loaded: false,
|
|
||||||
handle: model_handle.clone(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// if not all assets are already loaded, inject a component to signal that we need them to be loaded
|
|
||||||
if !asset_infos.is_empty() {
|
|
||||||
commands
|
|
||||||
.entity(entity)
|
|
||||||
.insert(AssetsToLoad {
|
|
||||||
all_loaded: false,
|
|
||||||
asset_infos,
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
.insert(BlueprintAssetsNotLoaded);
|
|
||||||
} else {
|
|
||||||
commands.entity(entity).insert(BlueprintAssetsLoaded);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// in case there are no blueprintsList, we revert back to the old behaviour
|
|
||||||
commands.entity(entity).insert(BlueprintAssetsLoaded);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn blueprints_check_assets_loading(
|
|
||||||
mut blueprint_assets_to_load: Query<
|
|
||||||
(Entity, &mut AssetsToLoad<Gltf>),
|
|
||||||
With<BlueprintAssetsNotLoaded>,
|
|
||||||
>,
|
|
||||||
asset_server: Res<AssetServer>,
|
|
||||||
mut commands: Commands,
|
|
||||||
) {
|
|
||||||
for (entity, 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();
|
|
||||||
for tracker in assets_to_load.asset_infos.iter_mut() {
|
|
||||||
let asset_id = tracker.id;
|
|
||||||
let loaded = asset_server.is_loaded_with_dependencies(asset_id);
|
|
||||||
tracker.loaded = loaded;
|
|
||||||
if loaded {
|
|
||||||
loaded_amount += 1;
|
|
||||||
} else {
|
|
||||||
all_loaded = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let progress: f32 = loaded_amount as f32 / total as f32;
|
|
||||||
// println!("progress: {}",progress);
|
|
||||||
assets_to_load.progress = progress;
|
|
||||||
|
|
||||||
if all_loaded {
|
|
||||||
assets_to_load.all_loaded = true;
|
|
||||||
commands
|
|
||||||
.entity(entity)
|
|
||||||
.insert(BlueprintAssetsLoaded)
|
|
||||||
.remove::<BlueprintAssetsNotLoaded>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn blueprints_spawn(
|
|
||||||
spawn_placeholders: Query<
|
|
||||||
(
|
|
||||||
Entity,
|
|
||||||
&BlueprintName,
|
|
||||||
Option<&Transform>,
|
|
||||||
Option<&Parent>,
|
|
||||||
Option<&Library>,
|
|
||||||
Option<&AddToGameWorld>,
|
|
||||||
Option<&Name>,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
With<BlueprintAssetsLoaded>,
|
|
||||||
Added<BlueprintAssetsLoaded>,
|
|
||||||
Without<BlueprintAssetsNotLoaded>,
|
|
||||||
),
|
|
||||||
>,
|
|
||||||
|
|
||||||
mut commands: Commands,
|
|
||||||
mut game_world: Query<Entity, With<GameWorldTag>>,
|
|
||||||
|
|
||||||
assets_gltf: Res<Assets<Gltf>>,
|
|
||||||
asset_server: Res<AssetServer>,
|
|
||||||
blenvy_config: Res<BluePrintsConfig>,
|
|
||||||
|
|
||||||
children: Query<&Children>,
|
|
||||||
) {
|
|
||||||
for (
|
|
||||||
entity,
|
|
||||||
blupeprint_name,
|
|
||||||
transform,
|
|
||||||
original_parent,
|
|
||||||
library_override,
|
|
||||||
add_to_world,
|
|
||||||
name,
|
|
||||||
) in spawn_placeholders.iter()
|
|
||||||
{
|
|
||||||
debug!(
|
|
||||||
"attempting to spawn {:?} for entity {:?}, id: {:?}, parent:{:?}",
|
|
||||||
blupeprint_name.0, name, entity, original_parent
|
|
||||||
);
|
|
||||||
|
|
||||||
let what = &blupeprint_name.0;
|
|
||||||
let model_file_name = format!("{}.{}", &what, &blenvy_config.format);
|
|
||||||
|
|
||||||
// library path is either defined at the plugin level or overriden by optional Library components
|
|
||||||
let library_path =
|
|
||||||
library_override.map_or_else(|| &blenvy_config.library_folder, |l| &l.0);
|
|
||||||
let model_path = Path::new(&library_path).join(Path::new(model_file_name.as_str()));
|
|
||||||
|
|
||||||
// info!("attempting to spawn {:?}", model_path);
|
|
||||||
let model_handle: Handle<Gltf> = asset_server.load(model_path.clone()); // FIXME: kinda weird now
|
|
||||||
|
|
||||||
let gltf = assets_gltf.get(&model_handle).unwrap_or_else(|| {
|
|
||||||
panic!(
|
|
||||||
"gltf file {:?} should have been loaded",
|
|
||||||
model_path.to_str()
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
// WARNING we work under the assumtion that there is ONLY ONE named scene, and that the first one is the right one
|
|
||||||
let main_scene_name = gltf
|
|
||||||
.named_scenes
|
|
||||||
.keys()
|
|
||||||
.next()
|
|
||||||
.expect("there should be at least one named scene in the gltf file to spawn");
|
|
||||||
|
|
||||||
let scene = &gltf.named_scenes[main_scene_name];
|
|
||||||
|
|
||||||
// transforms are optional, but still deal with them correctly
|
|
||||||
let mut transforms: Transform = Transform::default();
|
|
||||||
if transform.is_some() {
|
|
||||||
transforms = *transform.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut original_children: Vec<Entity> = vec![];
|
|
||||||
if let Ok(c) = children.get(entity) {
|
|
||||||
for child in c.iter() {
|
|
||||||
original_children.push(*child);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
commands.entity(entity).insert((
|
|
||||||
SceneBundle {
|
|
||||||
scene: scene.clone(),
|
|
||||||
transform: transforms,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
Spawned,
|
|
||||||
OriginalChildren(original_children),
|
|
||||||
BlueprintAnimations {
|
|
||||||
// these are animations specific to the inside of the blueprint
|
|
||||||
named_animations: gltf.named_animations.clone(),
|
|
||||||
},
|
|
||||||
));
|
|
||||||
|
|
||||||
if add_to_world.is_some() {
|
|
||||||
let world = game_world
|
|
||||||
.get_single_mut()
|
|
||||||
.expect("there should be a game world present");
|
|
||||||
commands.entity(world).add_child(entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,396 +0,0 @@
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
|
|
||||||
use bevy::{gltf::Gltf, prelude::*, utils::hashbrown::HashMap};
|
|
||||||
use serde_json::Value;
|
|
||||||
|
|
||||||
use crate::{BlueprintAssets, AssetsToLoad, AssetLoadTracker, BlenvyConfig, BlueprintAnimations, BlueprintAssetsLoaded, BlueprintAssetsNotLoaded};
|
|
||||||
|
|
||||||
/// this is a flag component for our levels/game world
|
|
||||||
#[derive(Component)]
|
|
||||||
pub struct GameWorldTag;
|
|
||||||
|
|
||||||
/// Main component for the blueprints
|
|
||||||
#[derive(Component, Reflect, Default, Debug)]
|
|
||||||
#[reflect(Component)]
|
|
||||||
pub struct BlueprintName(pub String);
|
|
||||||
|
|
||||||
/// path component for the blueprints
|
|
||||||
#[derive(Component, Reflect, Default, Debug)]
|
|
||||||
#[reflect(Component)]
|
|
||||||
pub struct BlueprintPath(pub String);
|
|
||||||
|
|
||||||
/// flag component needed to signify the intent to spawn a Blueprint
|
|
||||||
#[derive(Component, Reflect, Default, Debug)]
|
|
||||||
#[reflect(Component)]
|
|
||||||
pub struct SpawnBlueprint;
|
|
||||||
|
|
||||||
#[derive(Component)]
|
|
||||||
/// flag component for dynamically spawned scenes
|
|
||||||
pub struct Spawned;
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Component, Debug)]
|
|
||||||
/// flag component added when a Blueprint instance ist Ready : ie :
|
|
||||||
/// - its assets have loaded
|
|
||||||
/// - it has finished spawning
|
|
||||||
pub struct BlueprintInstanceReady;
|
|
||||||
|
|
||||||
#[derive(Component, Reflect, Default, Debug)]
|
|
||||||
#[reflect(Component)]
|
|
||||||
/// flag component marking any spwaned child of blueprints ..unless the original entity was marked with the `NoInBlueprint` marker component
|
|
||||||
pub struct InBlueprint;
|
|
||||||
|
|
||||||
#[derive(Component, Reflect, Default, Debug)]
|
|
||||||
#[reflect(Component)]
|
|
||||||
/// flag component preventing any spawned child of blueprints to be marked with the `InBlueprint` component
|
|
||||||
pub struct NoInBlueprint;
|
|
||||||
|
|
||||||
#[derive(Component, Reflect, Default, Debug)]
|
|
||||||
#[reflect(Component)]
|
|
||||||
// this allows overriding the default library path for a given entity/blueprint
|
|
||||||
pub struct Library(pub PathBuf);
|
|
||||||
|
|
||||||
#[derive(Component, Reflect, Default, Debug)]
|
|
||||||
#[reflect(Component)]
|
|
||||||
/// flag component to force adding newly spawned entity as child of game world
|
|
||||||
pub struct AddToGameWorld;
|
|
||||||
|
|
||||||
#[derive(Component)]
|
|
||||||
/// helper component, just to transfer child data
|
|
||||||
pub(crate) struct OriginalChildren(pub Vec<Entity>);
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Event, Debug)]
|
|
||||||
pub enum BlueprintEvent {
|
|
||||||
|
|
||||||
/// event fired when a blueprint has finished loading its assets & before it attempts spawning
|
|
||||||
AssetsLoaded {
|
|
||||||
blueprint_name: String,
|
|
||||||
blueprint_path: String,
|
|
||||||
// TODO: add assets list ?
|
|
||||||
},
|
|
||||||
/// event fired when a blueprint is COMPLETELY done spawning ie
|
|
||||||
/// - all its assets have been loaded
|
|
||||||
/// - the spawning attempt has been sucessfull
|
|
||||||
Spawned {
|
|
||||||
blueprint_name: String,
|
|
||||||
blueprint_path: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
///
|
|
||||||
Ready {
|
|
||||||
blueprint_path: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
use gltf::Gltf as RawGltf;
|
|
||||||
|
|
||||||
pub(crate) fn blueprints_prepare_spawn(
|
|
||||||
spawn_placeholders: Query<
|
|
||||||
(
|
|
||||||
Entity,
|
|
||||||
&BlueprintPath,
|
|
||||||
),
|
|
||||||
(Added<BlueprintPath>, Without<Spawned>, Without<SpawnBlueprint>)>,
|
|
||||||
|
|
||||||
// before 0.14 we have to use a seperate query, after migrating we can query at the root level
|
|
||||||
entities_with_assets: Query<
|
|
||||||
(
|
|
||||||
Entity,
|
|
||||||
/*&BlueprintName,
|
|
||||||
&BlueprintPath,
|
|
||||||
Option<&Parent>,*/
|
|
||||||
Option<&Name>,
|
|
||||||
Option<&BlueprintAssets>,
|
|
||||||
),
|
|
||||||
(Added<BlueprintAssets>), // Added<BlueprintAssets>
|
|
||||||
>,
|
|
||||||
|
|
||||||
|
|
||||||
bla_bla : Query<
|
|
||||||
(
|
|
||||||
Entity,
|
|
||||||
&BlueprintName,
|
|
||||||
&BlueprintPath,
|
|
||||||
Option<&Parent>,
|
|
||||||
Option<&BlueprintAssets>,
|
|
||||||
),(Added<BlueprintPath>)
|
|
||||||
>,
|
|
||||||
mut commands: Commands,
|
|
||||||
asset_server: Res<AssetServer>,
|
|
||||||
|
|
||||||
|
|
||||||
) {
|
|
||||||
for (entity, blueprint_path) in spawn_placeholders.iter() {
|
|
||||||
println!("added blueprint_path {:?}", blueprint_path);
|
|
||||||
/*commands.entity(entity).insert(
|
|
||||||
SceneBundle {
|
|
||||||
scene: asset_server.load(format!("{}#Scene0", &blueprint_path.0)), // "levels/World.glb#Scene0"),
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
);*/
|
|
||||||
// let model_handle: Handle<Gltf> = asset_server.load(model_path.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
for (entity, blueprint_name, blueprint_path, parent, all_assets) in bla_bla.iter() {
|
|
||||||
println!("added blueprint to spawn {:?} {:?}", blueprint_name, blueprint_path);
|
|
||||||
println!("all assets {:?}", all_assets);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* prefetch attempt */
|
|
||||||
let gltf = RawGltf::open(format!("assets/{}", blueprint_path.0)).unwrap();// RawGltf::open("examples/Box.gltf")?;
|
|
||||||
for scene in gltf.scenes() {
|
|
||||||
let foo_extras = scene.extras().clone().unwrap();
|
|
||||||
|
|
||||||
let lookup: HashMap<String, Value> = serde_json::from_str(&foo_extras.get()).unwrap();
|
|
||||||
for (key, value) in lookup.clone().into_iter() {
|
|
||||||
println!("{} / {}", key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if lookup.contains_key("BlueprintAssets"){
|
|
||||||
let assets_raw = &lookup["BlueprintAssets"];
|
|
||||||
println!("ASSETS RAW {}", assets_raw);
|
|
||||||
let x: BlueprintAssets = ron::from_str(&assets_raw.as_str().unwrap()).unwrap();
|
|
||||||
println!("YAHA {:?}", x);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//println!("SCENE EXTRAS {:?}", foo_extras);
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////
|
|
||||||
|
|
||||||
|
|
||||||
let untyped_handle = asset_server.load_untyped(&blueprint_path.0);
|
|
||||||
let asset_id = untyped_handle.id();
|
|
||||||
let loaded = asset_server.is_loaded_with_dependencies(asset_id);
|
|
||||||
|
|
||||||
let mut asset_infos: Vec<AssetLoadTracker> = vec![];
|
|
||||||
if !loaded {
|
|
||||||
asset_infos.push(AssetLoadTracker {
|
|
||||||
name: blueprint_name.0.clone(),
|
|
||||||
id: asset_id,
|
|
||||||
loaded: false,
|
|
||||||
handle: untyped_handle.clone(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// now insert load tracker
|
|
||||||
if !asset_infos.is_empty() {
|
|
||||||
commands
|
|
||||||
.entity(entity)
|
|
||||||
.insert(AssetsToLoad {
|
|
||||||
all_loaded: false,
|
|
||||||
asset_infos,
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
.insert(BlueprintAssetsNotLoaded);
|
|
||||||
} else {
|
|
||||||
commands.entity(entity).insert(BlueprintAssetsLoaded);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (child_entity, child_entity_name, all_assets) in entities_with_assets.iter(){
|
|
||||||
println!("added assets {:?} to {:?}", all_assets, child_entity_name);
|
|
||||||
if all_assets.is_some() {
|
|
||||||
let mut asset_infos: Vec<AssetLoadTracker> = vec![];
|
|
||||||
|
|
||||||
for asset in all_assets.unwrap().0.iter() {
|
|
||||||
let untyped_handle = asset_server.load_untyped(&asset.path);
|
|
||||||
//println!("untyped handle {:?}", untyped_handle);
|
|
||||||
//asset_server.load(asset.path);
|
|
||||||
|
|
||||||
let asset_id = untyped_handle.id();
|
|
||||||
//println!("ID {:?}", asset_id);
|
|
||||||
let loaded = asset_server.is_loaded_with_dependencies(asset_id);
|
|
||||||
//println!("Loaded ? {:?}", loaded);
|
|
||||||
if !loaded {
|
|
||||||
asset_infos.push(AssetLoadTracker {
|
|
||||||
name: asset.name.clone(),
|
|
||||||
id: asset_id,
|
|
||||||
loaded: false,
|
|
||||||
handle: untyped_handle.clone(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// now insert load tracker
|
|
||||||
if !asset_infos.is_empty() {
|
|
||||||
commands
|
|
||||||
.entity(child_entity)
|
|
||||||
.insert(AssetsToLoad {
|
|
||||||
all_loaded: false,
|
|
||||||
asset_infos,
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
.insert(BlueprintAssetsNotLoaded);
|
|
||||||
} else {
|
|
||||||
commands.entity(child_entity).insert(BlueprintAssetsLoaded);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn blueprints_check_assets_loading(
|
|
||||||
mut blueprint_assets_to_load: Query<
|
|
||||||
(Entity, Option<&Name>, &BlueprintPath, &mut AssetsToLoad),
|
|
||||||
With<BlueprintAssetsNotLoaded>,
|
|
||||||
>,
|
|
||||||
asset_server: Res<AssetServer>,
|
|
||||||
mut commands: Commands,
|
|
||||||
mut blueprint_events: EventWriter<BlueprintEvent>,
|
|
||||||
|
|
||||||
) {
|
|
||||||
for (entity, entity_name, blueprint_path, 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();
|
|
||||||
for tracker in assets_to_load.asset_infos.iter_mut() {
|
|
||||||
let asset_id = tracker.id;
|
|
||||||
let loaded = asset_server.is_loaded_with_dependencies(asset_id);
|
|
||||||
println!("loading {}: // load state: {:?}", tracker.name, asset_server.load_state(asset_id));
|
|
||||||
|
|
||||||
// FIXME: hack for now
|
|
||||||
let mut failed = false;// asset_server.load_state(asset_id) == bevy::asset::LoadState::Failed(_error);
|
|
||||||
match asset_server.load_state(asset_id) {
|
|
||||||
bevy::asset::LoadState::Failed(_) => {
|
|
||||||
failed = true
|
|
||||||
},
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
tracker.loaded = loaded || failed;
|
|
||||||
if loaded || failed {
|
|
||||||
loaded_amount += 1;
|
|
||||||
} else {
|
|
||||||
all_loaded = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let progress: f32 = loaded_amount as f32 / total as f32;
|
|
||||||
println!("progress: {}",progress);
|
|
||||||
assets_to_load.progress = progress;
|
|
||||||
|
|
||||||
if all_loaded {
|
|
||||||
assets_to_load.all_loaded = true;
|
|
||||||
println!("done with loading {:?}, inserting components", entity_name);
|
|
||||||
blueprint_events.send(BlueprintEvent::AssetsLoaded {blueprint_name:"".into(), blueprint_path: blueprint_path.0.clone() });
|
|
||||||
|
|
||||||
commands
|
|
||||||
.entity(entity)
|
|
||||||
.insert(BlueprintAssetsLoaded)
|
|
||||||
.remove::<BlueprintAssetsNotLoaded>()
|
|
||||||
.remove::<AssetsToLoad>()
|
|
||||||
;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pub(crate) fn blueprints_spawn(
|
|
||||||
spawn_placeholders: Query<
|
|
||||||
(
|
|
||||||
Entity,
|
|
||||||
&BlueprintName,
|
|
||||||
&BlueprintPath,
|
|
||||||
Option<&Transform>,
|
|
||||||
Option<&Parent>,
|
|
||||||
Option<&AddToGameWorld>,
|
|
||||||
Option<&Name>,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
With<BlueprintAssetsLoaded>,
|
|
||||||
Added<BlueprintAssetsLoaded>,
|
|
||||||
Without<BlueprintAssetsNotLoaded>,
|
|
||||||
),
|
|
||||||
>,
|
|
||||||
|
|
||||||
mut commands: Commands,
|
|
||||||
mut game_world: Query<Entity, With<GameWorldTag>>,
|
|
||||||
|
|
||||||
assets_gltf: Res<Assets<Gltf>>,
|
|
||||||
asset_server: Res<AssetServer>,
|
|
||||||
children: Query<&Children>,
|
|
||||||
) {
|
|
||||||
for (
|
|
||||||
entity,
|
|
||||||
blupeprint_name,
|
|
||||||
blueprint_path,
|
|
||||||
transform,
|
|
||||||
original_parent,
|
|
||||||
add_to_world,
|
|
||||||
name,
|
|
||||||
) in spawn_placeholders.iter()
|
|
||||||
{
|
|
||||||
info!(
|
|
||||||
"attempting to spawn blueprint {:?} for entity {:?}, id: {:?}, parent:{:?}",
|
|
||||||
blupeprint_name.0, name, entity, original_parent
|
|
||||||
);
|
|
||||||
|
|
||||||
// info!("attempting to spawn {:?}", model_path);
|
|
||||||
let model_handle: Handle<Gltf> = asset_server.load(blueprint_path.0.clone()); // FIXME: kinda weird now
|
|
||||||
|
|
||||||
let gltf = assets_gltf.get(&model_handle).unwrap_or_else(|| {
|
|
||||||
panic!(
|
|
||||||
"gltf file {:?} should have been loaded",
|
|
||||||
&blueprint_path.0
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
// WARNING we work under the assumtion that there is ONLY ONE named scene, and that the first one is the right one
|
|
||||||
let main_scene_name = gltf
|
|
||||||
.named_scenes
|
|
||||||
.keys()
|
|
||||||
.next()
|
|
||||||
.expect("there should be at least one named scene in the gltf file to spawn");
|
|
||||||
|
|
||||||
let scene = &gltf.named_scenes[main_scene_name];
|
|
||||||
|
|
||||||
// transforms are optional, but still deal with them correctly
|
|
||||||
let mut transforms: Transform = Transform::default();
|
|
||||||
if transform.is_some() {
|
|
||||||
transforms = *transform.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut original_children: Vec<Entity> = vec![];
|
|
||||||
if let Ok(c) = children.get(entity) {
|
|
||||||
for child in c.iter() {
|
|
||||||
original_children.push(*child);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut named_animations:HashMap<String, Handle<AnimationClip>> = HashMap::new() ;
|
|
||||||
for (key, value) in gltf.named_animations.iter() {
|
|
||||||
named_animations.insert(key.to_string(), value.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
commands.entity(entity).insert((
|
|
||||||
SceneBundle {
|
|
||||||
scene: scene.clone(),
|
|
||||||
transform: transforms,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
Spawned,
|
|
||||||
BlueprintInstanceReady, // FIXME: not sure if this is should be added here or in the post process
|
|
||||||
OriginalChildren(original_children),
|
|
||||||
BlueprintAnimations {
|
|
||||||
// these are animations specific to the inside of the blueprint
|
|
||||||
named_animations: named_animations//gltf.named_animations.clone(),
|
|
||||||
},
|
|
||||||
));
|
|
||||||
|
|
||||||
if add_to_world.is_some() {
|
|
||||||
let world = game_world
|
|
||||||
.get_single_mut()
|
|
||||||
.expect("there should be a game world present");
|
|
||||||
commands.entity(world).add_child(entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,165 +0,0 @@
|
||||||
use std::any::TypeId;
|
|
||||||
|
|
||||||
use bevy::gltf::Gltf;
|
|
||||||
use bevy::prelude::*;
|
|
||||||
use bevy::scene::SceneInstance;
|
|
||||||
// use bevy::utils::hashbrown::HashSet;
|
|
||||||
|
|
||||||
use crate::{BlueprintAnimationPlayerLink, BlueprintAnimations, BlueprintInfo, BlueprintReadyForPostProcess, BlueprintInstanceReady, BlueprintSpawning, SubBlueprintSpawnRoot, SubBlueprintsSpawnTracker};
|
|
||||||
use crate::{SpawnBlueprint, Spawned};
|
|
||||||
use crate::{
|
|
||||||
BlueprintEvent, CopyComponents, InBlueprint, NoInBlueprint, OriginalChildren
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// this system is in charge of doing any necessary post processing after a blueprint scene has been spawned
|
|
||||||
/// - it removes one level of useless nesting
|
|
||||||
/// - it copies the blueprint's root components to the entity it was spawned on (original entity)
|
|
||||||
/// - it copies the children of the blueprint scene into the original entity
|
|
||||||
/// - it add `AnimationLink` components so that animations can be controlled from the original entity
|
|
||||||
/// - it cleans up/ removes a few , by then uneeded components
|
|
||||||
pub(crate) fn spawned_blueprint_post_process( // rename to '
|
|
||||||
unprocessed_entities: Query<
|
|
||||||
(
|
|
||||||
Entity,
|
|
||||||
&Children,
|
|
||||||
&OriginalChildren,
|
|
||||||
&BlueprintAnimations,
|
|
||||||
Option<&NoInBlueprint>,
|
|
||||||
Option<&Name>,
|
|
||||||
&BlueprintInfo,
|
|
||||||
|
|
||||||
// sub blueprint instances tracker
|
|
||||||
Option<&SubBlueprintSpawnRoot>
|
|
||||||
),
|
|
||||||
(With<SpawnBlueprint>, With<SceneInstance>, Added<BlueprintReadyForPostProcess>),
|
|
||||||
>,
|
|
||||||
added_animation_players: Query<(Entity, &Parent), Added<AnimationPlayer>>,
|
|
||||||
all_children: Query<&Children>,
|
|
||||||
|
|
||||||
mut trackers: Query<(Entity, &mut SubBlueprintsSpawnTracker, &BlueprintInfo)>,
|
|
||||||
|
|
||||||
|
|
||||||
mut commands: Commands,
|
|
||||||
mut blueprint_events: EventWriter<BlueprintEvent>,
|
|
||||||
|
|
||||||
) {
|
|
||||||
for (original, children, original_children, animations, no_inblueprint, name, blueprint_info, track_root) in
|
|
||||||
unprocessed_entities.iter()
|
|
||||||
{
|
|
||||||
info!("post processing blueprint for entity {:?}", name);
|
|
||||||
|
|
||||||
if children.len() == 0 {
|
|
||||||
warn!("timing issue ! no children found, please restart your bevy app (bug being investigated)");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// the root node is the first & normally only child inside a scene, it is the one that has all relevant components
|
|
||||||
let mut root_entity = Entity::PLACEHOLDER; //FIXME: and what about childless ones ?? => should not be possible normally
|
|
||||||
// let diff = HashSet::from_iter(original_children.0).difference(HashSet::from_iter(children));
|
|
||||||
// we find the first child that was not in the entity before (aka added during the scene spawning)
|
|
||||||
for c in children.iter() {
|
|
||||||
if !original_children.0.contains(c) {
|
|
||||||
root_entity = *c;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// we flag all children of the blueprint instance with 'InBlueprint'
|
|
||||||
// can be usefull to filter out anything that came from blueprints vs normal children
|
|
||||||
if no_inblueprint.is_none() {
|
|
||||||
for child in all_children.iter_descendants(root_entity) {
|
|
||||||
commands.entity(child).insert(InBlueprint); // we do this here in order to avoid doing it to normal children
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy components into from blueprint instance's root_entity to original entity
|
|
||||||
commands.add(CopyComponents {
|
|
||||||
source: root_entity,
|
|
||||||
destination: original,
|
|
||||||
exclude: vec![TypeId::of::<Parent>(), TypeId::of::<Children>()],
|
|
||||||
stringent: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
// we move all of children of the blueprint instance one level to the original entity
|
|
||||||
if let Ok(root_entity_children) = all_children.get(root_entity) {
|
|
||||||
for child in root_entity_children.iter() {
|
|
||||||
// info!("copying child {:?} upward from {:?} to {:?}", names.get(*child), root_entity, original);
|
|
||||||
commands.entity(original).add_child(*child);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if animations.named_animations.keys().len() > 0 {
|
|
||||||
for (added, parent) in added_animation_players.iter() {
|
|
||||||
if parent.get() == root_entity {
|
|
||||||
// FIXME: stopgap solution: since we cannot use an AnimationPlayer at the root entity level
|
|
||||||
// and we cannot update animation clips so that the EntityPaths point to one level deeper,
|
|
||||||
// BUT we still want to have some marker/control at the root entity level, we add this
|
|
||||||
commands
|
|
||||||
.entity(original)
|
|
||||||
.insert(BlueprintAnimationPlayerLink(added));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
commands.entity(original).remove::<SpawnBlueprint>();
|
|
||||||
commands.entity(original).remove::<Spawned>();
|
|
||||||
// commands.entity(original).remove::<Handle<Scene>>(); // FIXME: if we delete the handle to the scene, things get despawned ! not what we want
|
|
||||||
//commands.entity(original).remove::<BlueprintAssetsLoadState>(); // also clear the sub assets tracker to free up handles, perhaps just freeing up the handles and leave the rest would be better ?
|
|
||||||
//commands.entity(original).remove::<BlueprintAssetsLoaded>();
|
|
||||||
commands.entity(root_entity).despawn_recursive(); // Remove the root entity that comes from the spawned-in scene
|
|
||||||
commands.entity(original).insert( Visibility::Visible
|
|
||||||
);
|
|
||||||
|
|
||||||
commands.entity(original)
|
|
||||||
.insert(BlueprintInstanceReady)
|
|
||||||
.remove::<BlueprintSpawning>()
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
.remove::<BlueprintReadyForPostProcess>()
|
|
||||||
;
|
|
||||||
|
|
||||||
|
|
||||||
if let Some(track_root) = track_root {
|
|
||||||
//println!("got some root");
|
|
||||||
if let Ok((s_entity, mut tracker, bp_info)) = trackers.get_mut(track_root.0) {
|
|
||||||
// println!("found the tracker, setting loaded for {}", entity);
|
|
||||||
tracker.sub_blueprint_instances.entry(original).or_insert(true);
|
|
||||||
tracker.sub_blueprint_instances.insert(original, true);
|
|
||||||
|
|
||||||
// TODO: ugh, my limited rust knowledge, this is bad code
|
|
||||||
let mut all_spawned = true;
|
|
||||||
|
|
||||||
for key in tracker.sub_blueprint_instances.keys() {
|
|
||||||
let val = tracker.sub_blueprint_instances[key];
|
|
||||||
println!("Key: {key}, Spawned {}", val);
|
|
||||||
}
|
|
||||||
|
|
||||||
for val in tracker.sub_blueprint_instances.values() {
|
|
||||||
println!("spawned {}", val);
|
|
||||||
if !val {
|
|
||||||
all_spawned = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if all_spawned { // TODO: move this to an other system, or "notify" the tracked root entity of the fact that all its sub blueprints have been loaded
|
|
||||||
println!("ALLLLL SPAAAAWNED for {}", track_root.0);
|
|
||||||
// commands.entity(track_root.0).insert(bundle)
|
|
||||||
blueprint_events.send(BlueprintEvent::Spawned {entity: track_root.0, blueprint_name: bp_info.name.clone(), blueprint_path: bp_info.path.clone()});
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
if trackers.get(original).is_err() {
|
|
||||||
// if it has no sub blueprint instances
|
|
||||||
blueprint_events.send(BlueprintEvent::Spawned {entity: original, blueprint_name: blueprint_info.name.clone(), blueprint_path: blueprint_info.path.clone()});
|
|
||||||
}
|
|
||||||
|
|
||||||
debug!("DONE WITH POST PROCESS");
|
|
||||||
info!("done instanciating blueprint for entity {:?}", name);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -45,7 +45,7 @@ pub struct AnimationInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stores information about animations, to make things a bit easier api wise:
|
/// Stores information about animations, to make things a bit easier api wise:
|
||||||
/// these components are automatically inserted by `gltf_auto_export` on entities that have animations
|
/// these components are automatically inserted by the `blenvy` Blender add-on on entities that have animations
|
||||||
#[derive(Component, Reflect, Default, Debug)]
|
#[derive(Component, Reflect, Default, Debug)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct AnimationInfos {
|
pub struct AnimationInfos {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{BlueprintAssetsLoadState, BlueprintAssetsLoaded, BlueprintChildrenReady, BlueprintInfo, BlueprintInstanceReady, BlueprintSpawning, InBlueprint, SpawnBlueprint, SubBlueprintsSpawnTracker};
|
use crate::{BlueprintAssetsLoadState, BlueprintAssetsLoaded, BlueprintInfo, BlueprintInstanceReady, BlueprintSpawning, SpawnBlueprint, SubBlueprintsSpawnTracker};
|
||||||
use bevy::asset::{AssetEvent, UntypedAssetId};
|
use bevy::asset::AssetEvent;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy::scene::SceneInstance;
|
use bevy::scene::SceneInstance;
|
||||||
use bevy::utils::hashbrown::HashMap;
|
use bevy::utils::hashbrown::HashMap;
|
||||||
|
@ -75,7 +75,7 @@ pub(crate) fn react_to_asset_changes(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ! retained_candidates.contains(&entity) {
|
if ! retained_candidates.contains(entity) {
|
||||||
retained_candidates.push(**entity);
|
retained_candidates.push(**entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,10 +53,7 @@ pub(crate) fn inject_materials(
|
||||||
material_found = Some(material);
|
material_found = Some(material);
|
||||||
} else {
|
} else {
|
||||||
let model_handle: Handle<Gltf> = asset_server.load(material_info.path.clone()); // FIXME: kinda weird now
|
let model_handle: Handle<Gltf> = asset_server.load(material_info.path.clone()); // FIXME: kinda weird now
|
||||||
let mat_gltf = assets_gltf.get(model_handle.id()).expect(&format!(
|
let mat_gltf = assets_gltf.get(model_handle.id()).unwrap_or_else(|| panic!("materials file {} should have been preloaded", material_info.path));
|
||||||
"materials file {} should have been preloaded",
|
|
||||||
material_info.path
|
|
||||||
));
|
|
||||||
if mat_gltf
|
if mat_gltf
|
||||||
.named_materials
|
.named_materials
|
||||||
.contains_key(&material_info.name as &str)
|
.contains_key(&material_info.name as &str)
|
||||||
|
|
|
@ -66,7 +66,7 @@ fn aabbs_enabled(blenvy_config: Res<BlenvyConfig>) -> bool {
|
||||||
|
|
||||||
fn hot_reload(watching_for_changes: Res<WatchingForChanges>) -> bool {
|
fn hot_reload(watching_for_changes: Res<WatchingForChanges>) -> bool {
|
||||||
// println!("hot reload ? {}", watching_for_changes.0);
|
// println!("hot reload ? {}", watching_for_changes.0);
|
||||||
return watching_for_changes.0
|
watching_for_changes.0
|
||||||
}
|
}
|
||||||
|
|
||||||
trait BlenvyApp {
|
trait BlenvyApp {
|
||||||
|
|
|
@ -37,23 +37,11 @@ impl BlueprintInfo {
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct SpawnBlueprint;
|
pub struct SpawnBlueprint;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Component, Reflect, Default, Debug)]
|
#[derive(Component, Reflect, Default, Debug)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
/// flag component marking any spwaned child of blueprints ..unless the original entity was marked with the `NoInBlueprint` marker component
|
/// flag component marking any spwaned child of blueprints
|
||||||
pub struct InBlueprint;
|
pub struct InBlueprint;
|
||||||
|
|
||||||
#[derive(Component, Reflect, Default, Debug)]
|
|
||||||
#[reflect(Component)]
|
|
||||||
/// flag component preventing any spawned child of blueprints to be marked with the `InBlueprint` component
|
|
||||||
pub struct NoInBlueprint;
|
|
||||||
|
|
||||||
#[derive(Component, Reflect, Default, Debug)]
|
|
||||||
#[reflect(Component)]
|
|
||||||
// this allows overriding the default library path for a given entity/blueprint
|
|
||||||
pub struct Library(pub PathBuf);
|
|
||||||
|
|
||||||
#[derive(Component, Reflect, Default, Debug)]
|
#[derive(Component, Reflect, Default, Debug)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
/// flag component to force adding newly spawned entity as child of game world
|
/// flag component to force adding newly spawned entity as child of game world
|
||||||
|
@ -72,7 +60,7 @@ pub struct HideUntilReady;
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
/// marker component, gets added to all children of a currently spawning blueprint instance, can be usefull to avoid manipulating still in progress entities
|
/// marker component, gets added to all children of a currently spawning blueprint instance, can be usefull to avoid manipulating still in progress entities
|
||||||
pub struct BlueprintDisabled;
|
pub struct BlueprintInstanceDisabled;
|
||||||
|
|
||||||
#[derive(Event, Debug)]
|
#[derive(Event, Debug)]
|
||||||
pub enum BlueprintEvent {
|
pub enum BlueprintEvent {
|
||||||
|
@ -169,12 +157,12 @@ pub(crate) fn blueprints_prepare_spawn(
|
||||||
let gltf = RawGltf::open(format!("assets/{}", blueprint_info.path)).unwrap();
|
let gltf = RawGltf::open(format!("assets/{}", blueprint_info.path)).unwrap();
|
||||||
for scene in gltf.scenes() {
|
for scene in gltf.scenes() {
|
||||||
let scene_extras = scene.extras().clone().unwrap();
|
let scene_extras = scene.extras().clone().unwrap();
|
||||||
let lookup: HashMap<String, Value> = serde_json::from_str(&scene_extras.get()).unwrap();
|
let lookup: HashMap<String, Value> = serde_json::from_str(scene_extras.get()).unwrap();
|
||||||
if lookup.contains_key("BlueprintAssets") {
|
if lookup.contains_key("BlueprintAssets") {
|
||||||
let assets_raw = &lookup["BlueprintAssets"];
|
let assets_raw = &lookup["BlueprintAssets"];
|
||||||
//println!("ASSETS RAW {}", assets_raw);
|
//println!("ASSETS RAW {}", assets_raw);
|
||||||
let all_assets: BlueprintAssets =
|
let all_assets: BlueprintAssets =
|
||||||
ron::from_str(&assets_raw.as_str().unwrap()).unwrap();
|
ron::from_str(assets_raw.as_str().unwrap()).unwrap();
|
||||||
// println!("all_assets {:?}", all_assets);
|
// println!("all_assets {:?}", all_assets);
|
||||||
|
|
||||||
for asset in all_assets.assets.iter() {
|
for asset in all_assets.assets.iter() {
|
||||||
|
@ -228,7 +216,7 @@ pub(crate) fn blueprints_prepare_spawn(
|
||||||
.entity(entity)
|
.entity(entity)
|
||||||
.insert(bevy::prelude::Name::from(blueprint_info.name.clone()));
|
.insert(bevy::prelude::Name::from(blueprint_info.name.clone()));
|
||||||
// add the blueprint spawning marker
|
// add the blueprint spawning marker
|
||||||
commands.entity(entity).insert((BlueprintSpawning));
|
commands.entity(entity).insert(BlueprintSpawning);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,12 +238,8 @@ pub(crate) fn blueprints_check_assets_loading(
|
||||||
let asset_id = tracker.id;
|
let asset_id = tracker.id;
|
||||||
let loaded = asset_server.is_loaded_with_dependencies(asset_id);
|
let loaded = asset_server.is_loaded_with_dependencies(asset_id);
|
||||||
|
|
||||||
// FIXME: hack for now
|
let mut failed = false;
|
||||||
let mut failed = false; // asset_server.load_state(asset_id) == bevy::asset::LoadState::Failed(_error);
|
if let bevy::asset::LoadState::Failed(_) = asset_server.load_state(asset_id) { failed = true }
|
||||||
match asset_server.load_state(asset_id) {
|
|
||||||
bevy::asset::LoadState::Failed(_) => failed = true,
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
tracker.loaded = loaded || failed;
|
tracker.loaded = loaded || failed;
|
||||||
if loaded || failed {
|
if loaded || failed {
|
||||||
loaded_amount += 1;
|
loaded_amount += 1;
|
||||||
|
@ -514,7 +498,7 @@ pub(crate) fn blueprints_scenes_spawned(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Mark all components as "Disabled" (until Bevy gets this as first class feature)
|
// Mark all components as "Disabled" (until Bevy gets this as first class feature)
|
||||||
commands.entity(child).insert(BlueprintDisabled);
|
commands.entity(child).insert(BlueprintInstanceDisabled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -543,7 +527,7 @@ pub struct BlueprintReadyForPostProcess;
|
||||||
/// - it removes one level of useless nesting
|
/// - it removes one level of useless nesting
|
||||||
/// - it copies the blueprint's root components to the entity it was spawned on (original entity)
|
/// - it copies the 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 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
|
/// - it adds an `AnimationLink` component containing the entity that has the `AnimationPlayer` so that animations can be controlled from the original entity
|
||||||
pub(crate) fn blueprints_cleanup_spawned_scene(
|
pub(crate) fn blueprints_cleanup_spawned_scene(
|
||||||
blueprint_scenes: Query<
|
blueprint_scenes: Query<
|
||||||
(
|
(
|
||||||
|
@ -552,7 +536,6 @@ pub(crate) fn blueprints_cleanup_spawned_scene(
|
||||||
&OriginalChildren,
|
&OriginalChildren,
|
||||||
Option<&Name>,
|
Option<&Name>,
|
||||||
&BlueprintAnimations,
|
&BlueprintAnimations,
|
||||||
Option<&NoInBlueprint>,
|
|
||||||
),
|
),
|
||||||
Added<BlueprintChildrenReady>,
|
Added<BlueprintChildrenReady>,
|
||||||
>,
|
>,
|
||||||
|
@ -567,7 +550,7 @@ pub(crate) fn blueprints_cleanup_spawned_scene(
|
||||||
|
|
||||||
all_names: Query<&Name>,
|
all_names: Query<&Name>,
|
||||||
) {
|
) {
|
||||||
for (original, children, original_children, name, animations, no_inblueprint) in
|
for (original, children, original_children, name, animations) in
|
||||||
blueprint_scenes.iter()
|
blueprint_scenes.iter()
|
||||||
{
|
{
|
||||||
info!("YOOO ready !! removing empty nodes {:?}", name);
|
info!("YOOO ready !! removing empty nodes {:?}", name);
|
||||||
|
@ -589,11 +572,10 @@ pub(crate) fn blueprints_cleanup_spawned_scene(
|
||||||
|
|
||||||
// we flag all children of the blueprint instance with 'InBlueprint'
|
// we flag all children of the blueprint instance with 'InBlueprint'
|
||||||
// can be usefull to filter out anything that came from blueprints vs normal children
|
// can be usefull to filter out anything that came from blueprints vs normal children
|
||||||
if no_inblueprint.is_none() {
|
for child in all_children.iter_descendants(blueprint_root_entity) {
|
||||||
for child in all_children.iter_descendants(blueprint_root_entity) {
|
commands.entity(child).insert(InBlueprint); // we do this here in order to avoid doing it to normal children
|
||||||
commands.entity(child).insert(InBlueprint); // we do this here in order to avoid doing it to normal children
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// copy components into from blueprint instance's blueprint_root_entity to original entity
|
// copy components into from blueprint instance's blueprint_root_entity to original entity
|
||||||
commands.add(CopyComponents {
|
commands.add(CopyComponents {
|
||||||
|
@ -762,7 +744,7 @@ pub(crate) fn blueprints_finalize_instances(
|
||||||
}
|
}
|
||||||
|
|
||||||
for child in all_children.iter_descendants(entity) {
|
for child in all_children.iter_descendants(entity) {
|
||||||
commands.entity(child).remove::<BlueprintDisabled>();
|
commands.entity(child).remove::<BlueprintInstanceDisabled>();
|
||||||
}
|
}
|
||||||
|
|
||||||
if hide_until_ready.is_some() {
|
if hide_until_ready.is_some() {
|
||||||
|
|
|
@ -23,7 +23,7 @@ use bevy::{
|
||||||
/// # use bevy::gltf::*;
|
/// # use bevy::gltf::*;
|
||||||
/// # use bevy_gltf_components::ComponentsFromGltfPlugin;
|
/// # use bevy_gltf_components::ComponentsFromGltfPlugin;
|
||||||
///
|
///
|
||||||
/// //too barebones of an example to be meaningfull, please see https://github.com/kaosat-dev/Blender_bevy_components_workflow/examples/basic for a real example
|
/// //too barebones of an example to be meaningfull, please see https://github.com/kaosat-dev/Blenvy/examples/basic for a real example
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// App::new()
|
/// App::new()
|
||||||
/// .add_plugins(DefaultPlugins)
|
/// .add_plugins(DefaultPlugins)
|
||||||
|
|
|
@ -22,6 +22,10 @@ pub struct BlenvyConfig {
|
||||||
pub(crate) aabbs: bool,
|
pub(crate) aabbs: bool,
|
||||||
pub(crate) aabb_cache: HashMap<String, Aabb>, // cache for aabbs
|
pub(crate) aabb_cache: HashMap<String, Aabb>, // cache for aabbs
|
||||||
pub(crate) materials_cache: HashMap<String, Handle<StandardMaterial>>, // cache for materials
|
pub(crate) materials_cache: HashMap<String, Handle<StandardMaterial>>, // cache for materials
|
||||||
|
|
||||||
|
// save & load
|
||||||
|
pub(crate) save_component_filter: SceneFilter,
|
||||||
|
pub(crate) save_resource_filter: SceneFilter,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -34,6 +38,10 @@ pub struct BlenvyPlugin {
|
||||||
|
|
||||||
/// Automatically generate aabbs for the blueprints root objects
|
/// Automatically generate aabbs for the blueprints root objects
|
||||||
pub aabbs: bool,
|
pub aabbs: bool,
|
||||||
|
|
||||||
|
// for save & load
|
||||||
|
pub save_component_filter: SceneFilter,
|
||||||
|
pub save_resource_filter: SceneFilter,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for BlenvyPlugin {
|
impl Default for BlenvyPlugin {
|
||||||
|
@ -43,6 +51,9 @@ impl Default for BlenvyPlugin {
|
||||||
registry_component_filter: SceneFilter::default(),
|
registry_component_filter: SceneFilter::default(),
|
||||||
registry_resource_filter: SceneFilter::default(),
|
registry_resource_filter: SceneFilter::default(),
|
||||||
aabbs: false,
|
aabbs: false,
|
||||||
|
|
||||||
|
save_component_filter: SceneFilter::default(),
|
||||||
|
save_resource_filter: SceneFilter::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,6 +74,9 @@ impl Plugin for BlenvyPlugin {
|
||||||
aabb_cache: HashMap::new(),
|
aabb_cache: HashMap::new(),
|
||||||
|
|
||||||
materials_cache: HashMap::new(),
|
materials_cache: HashMap::new(),
|
||||||
|
|
||||||
|
save_component_filter: self.save_component_filter.clone(),
|
||||||
|
save_resource_filter: self.save_resource_filter.clone()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
[package]
|
[package]
|
||||||
name = "bevy_gltf_blueprints_animation_example"
|
name = "blenvy_animation_example"
|
||||||
version = "0.3.0"
|
version = "0.0.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bevy = { version = "0.13", features = ["dynamic_linking"] }
|
bevy = { version = "0.14", features = ["dynamic_linking"] }
|
||||||
bevy_gltf_blueprints = { path = "../../../crates/bevy_gltf_blueprints" }
|
blenvy = { path = "../../../crates/blenvy" }
|
||||||
bevy_gltf_worlflow_examples_common_rapier = { path = "../../common_rapier" }
|
rand = "0.8.5"
|
||||||
bevy_rapier3d = { version = "0.25.0", features = ["serde-serialize", "debug-render-3d", "enhanced-determinism"] }
|
|
||||||
rand = "0.8.5"
|
|
Binary file not shown.
|
@ -1,6 +0,0 @@
|
||||||
({
|
|
||||||
"world":File (path: "models/Level1.glb"),
|
|
||||||
"models": Folder (
|
|
||||||
path: "models/library",
|
|
||||||
),
|
|
||||||
})
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
@ -60,8 +60,8 @@ pub enum EnumTest {
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ComponentsTestPlugin;
|
pub struct ComponentsExamplesPlugin;
|
||||||
impl Plugin for ComponentsTestPlugin {
|
impl Plugin for ComponentsExamplesPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.register_type::<BasicTest>()
|
app.register_type::<BasicTest>()
|
||||||
.register_type::<UnitTest>()
|
.register_type::<UnitTest>()
|
|
@ -1,12 +0,0 @@
|
||||||
use bevy::prelude::*;
|
|
||||||
use bevy_gltf_blueprints::*;
|
|
||||||
|
|
||||||
pub struct CorePlugin;
|
|
||||||
impl Plugin for CorePlugin {
|
|
||||||
fn build(&self, app: &mut App) {
|
|
||||||
app.add_plugins((BlueprintsPlugin {
|
|
||||||
aabbs: true,
|
|
||||||
..Default::default()
|
|
||||||
},));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,320 +0,0 @@
|
||||||
use bevy_gltf_worlflow_examples_common_rapier::{
|
|
||||||
assets::GameAssets, GameState, InAppRunning, Player,
|
|
||||||
};
|
|
||||||
use bevy_rapier3d::prelude::Velocity;
|
|
||||||
use rand::Rng;
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use bevy::prelude::*;
|
|
||||||
|
|
||||||
use bevy_gltf_blueprints::{
|
|
||||||
BluePrintBundle, BlueprintAnimationPlayerLink, BlueprintAnimations, BlueprintName, GameWorldTag,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{Fox, Robot};
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
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 = 8.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("Fox".to_string()),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
bevy::prelude::Name::from(format!("Spawned{}", name_index)),
|
|
||||||
// BlueprintName("Health_Pickup".to_string()),
|
|
||||||
// SpawnHere,
|
|
||||||
TransformBundle::from_transform(Transform::from_xyz(x, 0.0, y)),
|
|
||||||
Velocity {
|
|
||||||
linvel: Vec3::new(vel_x, vel_y, vel_z),
|
|
||||||
angvel: Vec3::new(0.0, 0.0, 0.0),
|
|
||||||
},
|
|
||||||
))
|
|
||||||
.id();
|
|
||||||
commands.entity(world).add_child(new_entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
&BlueprintAnimationPlayerLink,
|
|
||||||
&BlueprintAnimations,
|
|
||||||
),
|
|
||||||
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 (8.5..10.0).contains(&distance) {
|
|
||||||
anim_name = "Walk";
|
|
||||||
} else if (10.0..15.0).contains(&distance) {
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// example of changing animation of entities based on proximity to the player, this time for the "robot" entities (Tag component)
|
|
||||||
pub fn animation_change_on_proximity_robots(
|
|
||||||
players: Query<&GlobalTransform, With<Player>>,
|
|
||||||
animated_robots: Query<
|
|
||||||
(
|
|
||||||
&GlobalTransform,
|
|
||||||
&BlueprintAnimationPlayerLink,
|
|
||||||
&BlueprintAnimations,
|
|
||||||
),
|
|
||||||
With<Robot>,
|
|
||||||
>,
|
|
||||||
|
|
||||||
mut animation_players: Query<&mut AnimationPlayer>,
|
|
||||||
) {
|
|
||||||
for player_transforms in players.iter() {
|
|
||||||
for (robot_tranforms, link, animations) in animated_robots.iter() {
|
|
||||||
let distance = player_transforms
|
|
||||||
.translation()
|
|
||||||
.distance(robot_tranforms.translation());
|
|
||||||
|
|
||||||
let mut anim_name = "Idle";
|
|
||||||
if distance < 8.5 {
|
|
||||||
anim_name = "Jump";
|
|
||||||
} else if (8.5..10.0).contains(&distance) {
|
|
||||||
anim_name = "Scan";
|
|
||||||
} else if (10.0..15.0).contains(&distance) {
|
|
||||||
anim_name = "Idle";
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn animation_control(
|
|
||||||
animated_enemies: Query<(&BlueprintAnimationPlayerLink, &BlueprintAnimations), With<Robot>>,
|
|
||||||
animated_foxes: Query<(&BlueprintAnimationPlayerLink, &BlueprintAnimations), With<Fox>>,
|
|
||||||
|
|
||||||
mut animation_players: Query<&mut AnimationPlayer>,
|
|
||||||
|
|
||||||
keycode: Res<ButtonInput<KeyCode>>,
|
|
||||||
// mut entities_with_animations : Query<(&mut AnimationPlayer, &mut BlueprintAnimations)>,
|
|
||||||
) {
|
|
||||||
// robots
|
|
||||||
if keycode.just_pressed(KeyCode::KeyB) {
|
|
||||||
for (link, animations) in animated_enemies.iter() {
|
|
||||||
let mut animation_player = animation_players.get_mut(link.0).unwrap();
|
|
||||||
let anim_name = "Scan";
|
|
||||||
animation_player
|
|
||||||
.play_with_transition(
|
|
||||||
animations
|
|
||||||
.named_animations
|
|
||||||
.get(anim_name)
|
|
||||||
.expect("animation name should be in the list")
|
|
||||||
.clone(),
|
|
||||||
Duration::from_secs(5),
|
|
||||||
)
|
|
||||||
.repeat();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// foxes
|
|
||||||
if keycode.just_pressed(KeyCode::KeyW) {
|
|
||||||
for (link, animations) in animated_foxes.iter() {
|
|
||||||
let mut animation_player = animation_players.get_mut(link.0).unwrap();
|
|
||||||
let anim_name = "Walk";
|
|
||||||
animation_player
|
|
||||||
.play_with_transition(
|
|
||||||
animations
|
|
||||||
.named_animations
|
|
||||||
.get(anim_name)
|
|
||||||
.expect("animation name should be in the list")
|
|
||||||
.clone(),
|
|
||||||
Duration::from_secs(5),
|
|
||||||
)
|
|
||||||
.repeat();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if keycode.just_pressed(KeyCode::KeyX) {
|
|
||||||
for (link, animations) in animated_foxes.iter() {
|
|
||||||
let mut animation_player = animation_players.get_mut(link.0).unwrap();
|
|
||||||
let anim_name = "Run";
|
|
||||||
animation_player
|
|
||||||
.play_with_transition(
|
|
||||||
animations
|
|
||||||
.named_animations
|
|
||||||
.get(anim_name)
|
|
||||||
.expect("animation name should be in the list")
|
|
||||||
.clone(),
|
|
||||||
Duration::from_secs(5),
|
|
||||||
)
|
|
||||||
.repeat();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if keycode.just_pressed(KeyCode::KeyC) {
|
|
||||||
for (link, animations) in animated_foxes.iter() {
|
|
||||||
let mut animation_player = animation_players.get_mut(link.0).unwrap();
|
|
||||||
let anim_name = "Survey";
|
|
||||||
animation_player
|
|
||||||
.play_with_transition(
|
|
||||||
animations
|
|
||||||
.named_animations
|
|
||||||
.get(anim_name)
|
|
||||||
.expect("animation name should be in the list")
|
|
||||||
.clone(),
|
|
||||||
Duration::from_secs(5),
|
|
||||||
)
|
|
||||||
.repeat();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Improveement ideas for the future
|
|
||||||
// a bit more ideal API
|
|
||||||
if keycode.just_pressed(KeyCode::B) {
|
|
||||||
for (animation_player, animations) in animated_enemies.iter() {
|
|
||||||
let anim_name = "Scan";
|
|
||||||
if animations.named_animations.contains_key(anim_name) {
|
|
||||||
let clip = animations.named_animations.get(anim_name).unwrap();
|
|
||||||
animation_player.play_with_transition(clip.clone(), Duration::from_secs(5)).repeat();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// even better API
|
|
||||||
if keycode.just_pressed(KeyCode::B) {
|
|
||||||
for (animation_player, animations) in animated_enemies.iter() {
|
|
||||||
animation_player.play_with_transition("Scan", Duration::from_secs(5)).repeat(); // with a merged animationPlayer + animations storage
|
|
||||||
// alternative, perhaps more realistic, and better seperation of concerns
|
|
||||||
animation_player.play_with_transition(animations, "Scan", Duration::from_secs(5)).repeat();
|
|
||||||
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
/*for (mut anim_player, animations) in entities_with_animations.iter_mut(){
|
|
||||||
|
|
||||||
if keycode.just_pressed(KeyCode::W) {
|
|
||||||
let anim_name = "Walk";
|
|
||||||
if animations.named_animations.contains_key(anim_name) {
|
|
||||||
let clip = animations.named_animations.get(anim_name).unwrap();
|
|
||||||
anim_player.play_with_transition(clip.clone(), Duration::from_secs(5)).repeat();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if keycode.just_pressed(KeyCode::X) {
|
|
||||||
let anim_name = "Run";
|
|
||||||
if animations.named_animations.contains_key(anim_name) {
|
|
||||||
let clip = animations.named_animations.get(anim_name).unwrap();
|
|
||||||
anim_player.play_with_transition(clip.clone(), Duration::from_secs(5)).repeat();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if keycode.just_pressed(KeyCode::C) {
|
|
||||||
let anim_name = "Survey";
|
|
||||||
if animations.named_animations.contains_key(anim_name) {
|
|
||||||
let clip = animations.named_animations.get(anim_name).unwrap();
|
|
||||||
anim_player.play_with_transition(clip.clone(), Duration::from_secs(5)).repeat();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if keycode.just_pressed(KeyCode::S) {
|
|
||||||
let anim_name = "Scan";
|
|
||||||
if animations.named_animations.contains_key(anim_name) {
|
|
||||||
let clip = animations.named_animations.get(anim_name).unwrap();
|
|
||||||
anim_player.play_with_transition(clip.clone(), Duration::from_secs(5)).repeat();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if keycode.just_pressed(KeyCode::I) {
|
|
||||||
let anim_name = "Idle";
|
|
||||||
if animations.named_animations.contains_key(anim_name) {
|
|
||||||
let clip = animations.named_animations.get(anim_name).unwrap();
|
|
||||||
anim_player.play_with_transition(clip.clone(), Duration::from_secs(5)).repeat();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
pub mod in_game;
|
|
||||||
pub use in_game::*;
|
|
||||||
|
|
||||||
pub mod in_main_menu;
|
|
||||||
pub use in_main_menu::*;
|
|
||||||
|
|
||||||
use bevy::prelude::*;
|
|
||||||
use bevy_gltf_worlflow_examples_common_rapier::{AppState, GameState};
|
|
||||||
|
|
||||||
#[derive(Component, Reflect, Default, Debug)]
|
|
||||||
#[reflect(Component)]
|
|
||||||
/// Demo marker component
|
|
||||||
pub struct Fox;
|
|
||||||
|
|
||||||
#[derive(Component, Reflect, Default, Debug)]
|
|
||||||
#[reflect(Component)]
|
|
||||||
/// Demo marker component
|
|
||||||
pub struct Robot;
|
|
||||||
|
|
||||||
pub struct GamePlugin;
|
|
||||||
impl Plugin for GamePlugin {
|
|
||||||
fn build(&self, app: &mut App) {
|
|
||||||
app.register_type::<Robot>()
|
|
||||||
.register_type::<Fox>()
|
|
||||||
.add_systems(
|
|
||||||
Update,
|
|
||||||
(
|
|
||||||
spawn_test,
|
|
||||||
animation_control,
|
|
||||||
animation_change_on_proximity_foxes,
|
|
||||||
animation_change_on_proximity_robots,
|
|
||||||
)
|
|
||||||
.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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +1,217 @@
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy_gltf_worlflow_examples_common_rapier::CommonPlugin;
|
use blenvy::{AddToGameWorld, BlenvyPlugin, BluePrintBundle, BlueprintAnimationPlayerLink, BlueprintAnimations, BlueprintInfo, DynamicBlueprintInstance, GameWorldTag, HideUntilReady, SpawnBlueprint};
|
||||||
|
use rand::Rng;
|
||||||
|
|
||||||
mod core;
|
mod component_examples;
|
||||||
use crate::core::*;
|
use component_examples::*;
|
||||||
|
|
||||||
mod game;
|
#[derive(Component, Reflect, Default, Debug)]
|
||||||
use game::*;
|
#[reflect(Component)]
|
||||||
|
/// Demo marker component
|
||||||
|
pub struct Player;
|
||||||
|
|
||||||
|
#[derive(Component, Reflect, Default, Debug)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
/// Demo marker component
|
||||||
|
pub struct Fox;
|
||||||
|
|
||||||
|
#[derive(Component, Reflect, Default, Debug)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
/// Demo marker component
|
||||||
|
pub struct Robot;
|
||||||
|
|
||||||
mod test_components;
|
|
||||||
use test_components::*;
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
.add_plugins((
|
.add_plugins((
|
||||||
DefaultPlugins.set(AssetPlugin::default()),
|
DefaultPlugins.set(AssetPlugin::default()),
|
||||||
// our custom plugins
|
// our custom plugins
|
||||||
CommonPlugin,
|
ComponentsExamplesPlugin, // Showcases different type of components /structs
|
||||||
CorePlugin, // reusable plugins
|
BlenvyPlugin::default()
|
||||||
GamePlugin, // specific to our game
|
|
||||||
ComponentsTestPlugin, // Showcases different type of components /structs
|
|
||||||
))
|
))
|
||||||
|
.register_type::<Player>()
|
||||||
|
.register_type::<Fox>()
|
||||||
|
.register_type::<Robot>()
|
||||||
|
|
||||||
|
.add_systems(Startup, setup_game)
|
||||||
|
.add_systems(Update,
|
||||||
|
(animation_control,)
|
||||||
|
)
|
||||||
.run();
|
.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,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////
|
||||||
|
|
||||||
|
pub fn animation_control(
|
||||||
|
animated_robots: Query<(&BlueprintAnimationPlayerLink, &BlueprintAnimations), With<Robot>>,
|
||||||
|
animated_foxes: Query<(&BlueprintAnimationPlayerLink, &BlueprintAnimations), With<Fox>>,
|
||||||
|
|
||||||
|
mut animation_players: Query<(&mut AnimationPlayer, &mut AnimationTransitions)>,
|
||||||
|
|
||||||
|
keycode: Res<ButtonInput<KeyCode>>,
|
||||||
|
// mut entities_with_animations : Query<(&mut AnimationPlayer, &mut BlueprintAnimations)>,
|
||||||
|
) {
|
||||||
|
// robots
|
||||||
|
if keycode.just_pressed(KeyCode::KeyB) {
|
||||||
|
println!("scan animation for robots");
|
||||||
|
for (link, animations) in animated_robots.iter() {
|
||||||
|
let (mut animation_player, mut animation_transitions) =
|
||||||
|
animation_players.get_mut(link.0).unwrap();
|
||||||
|
println!("got some animations");
|
||||||
|
let anim_name = "Scan";
|
||||||
|
animation_transitions
|
||||||
|
.play(
|
||||||
|
&mut animation_player,
|
||||||
|
animations
|
||||||
|
.named_indices
|
||||||
|
.get(anim_name)
|
||||||
|
.expect("animation name should be in the list")
|
||||||
|
.clone(),
|
||||||
|
Duration::from_secs(5),
|
||||||
|
)
|
||||||
|
.repeat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// foxes
|
||||||
|
if keycode.just_pressed(KeyCode::KeyW) {
|
||||||
|
for (link, animations) in animated_foxes.iter() {
|
||||||
|
let (mut animation_player, mut animation_transitions) =
|
||||||
|
animation_players.get_mut(link.0).unwrap();
|
||||||
|
|
||||||
|
let anim_name = "Walk";
|
||||||
|
animation_transitions
|
||||||
|
.play(
|
||||||
|
&mut animation_player,
|
||||||
|
animations
|
||||||
|
.named_indices
|
||||||
|
.get(anim_name)
|
||||||
|
.expect("animation name should be in the list")
|
||||||
|
.clone(),
|
||||||
|
Duration::from_secs(5),
|
||||||
|
)
|
||||||
|
.repeat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if keycode.just_pressed(KeyCode::KeyX) {
|
||||||
|
for (link, animations) in animated_foxes.iter() {
|
||||||
|
let (mut animation_player, mut animation_transitions) =
|
||||||
|
animation_players.get_mut(link.0).unwrap();
|
||||||
|
|
||||||
|
let anim_name = "Run";
|
||||||
|
animation_transitions
|
||||||
|
.play(
|
||||||
|
&mut animation_player,
|
||||||
|
animations
|
||||||
|
.named_indices
|
||||||
|
.get(anim_name)
|
||||||
|
.expect("animation name should be in the list")
|
||||||
|
.clone(),
|
||||||
|
Duration::from_secs(5),
|
||||||
|
)
|
||||||
|
.repeat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if keycode.just_pressed(KeyCode::KeyC) {
|
||||||
|
for (link, animations) in animated_foxes.iter() {
|
||||||
|
let (mut animation_player, mut animation_transitions) =
|
||||||
|
animation_players.get_mut(link.0).unwrap();
|
||||||
|
|
||||||
|
let anim_name = "Survey";
|
||||||
|
animation_transitions
|
||||||
|
.play(
|
||||||
|
&mut animation_player,
|
||||||
|
animations
|
||||||
|
.named_indices
|
||||||
|
.get(anim_name)
|
||||||
|
.expect("animation name should be in the list")
|
||||||
|
.clone(),
|
||||||
|
Duration::from_secs(5),
|
||||||
|
)
|
||||||
|
.repeat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Improveement ideas for the future
|
||||||
|
// a bit more ideal API
|
||||||
|
if keycode.just_pressed(KeyCode::B) {
|
||||||
|
for (animation_player, animations) in animated_robots.iter() {
|
||||||
|
let anim_name = "Scan";
|
||||||
|
if animations.named_animations.contains_key(anim_name) {
|
||||||
|
let clip = animations.named_animations.get(anim_name).unwrap();
|
||||||
|
animation_player.play_with_transition(clip.clone(), Duration::from_secs(5)).repeat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// even better API
|
||||||
|
if keycode.just_pressed(KeyCode::B) {
|
||||||
|
for (animation_player, animations) in animated_robots.iter() {
|
||||||
|
animation_player.play_with_transition("Scan", Duration::from_secs(5)).repeat(); // with a merged animationPlayer + animations storage
|
||||||
|
// alternative, perhaps more realistic, and better seperation of concerns
|
||||||
|
animation_player.play_with_transition(animations, "Scan", Duration::from_secs(5)).repeat();
|
||||||
|
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/*for (mut anim_player, animations) in entities_with_animations.iter_mut(){
|
||||||
|
|
||||||
|
if keycode.just_pressed(KeyCode::W) {
|
||||||
|
let anim_name = "Walk";
|
||||||
|
if animations.named_animations.contains_key(anim_name) {
|
||||||
|
let clip = animations.named_animations.get(anim_name).unwrap();
|
||||||
|
anim_player.play_with_transition(clip.clone(), Duration::from_secs(5)).repeat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if keycode.just_pressed(KeyCode::X) {
|
||||||
|
let anim_name = "Run";
|
||||||
|
if animations.named_animations.contains_key(anim_name) {
|
||||||
|
let clip = animations.named_animations.get(anim_name).unwrap();
|
||||||
|
anim_player.play_with_transition(clip.clone(), Duration::from_secs(5)).repeat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if keycode.just_pressed(KeyCode::C) {
|
||||||
|
let anim_name = "Survey";
|
||||||
|
if animations.named_animations.contains_key(anim_name) {
|
||||||
|
let clip = animations.named_animations.get(anim_name).unwrap();
|
||||||
|
anim_player.play_with_transition(clip.clone(), Duration::from_secs(5)).repeat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if keycode.just_pressed(KeyCode::S) {
|
||||||
|
let anim_name = "Scan";
|
||||||
|
if animations.named_animations.contains_key(anim_name) {
|
||||||
|
let clip = animations.named_animations.get(anim_name).unwrap();
|
||||||
|
anim_player.play_with_transition(clip.clone(), Duration::from_secs(5)).repeat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if keycode.just_pressed(KeyCode::I) {
|
||||||
|
let anim_name = "Idle";
|
||||||
|
if animations.named_animations.contains_key(anim_name) {
|
||||||
|
let clip = animations.named_animations.get(anim_name).unwrap();
|
||||||
|
anim_player.play_with_transition(clip.clone(), Duration::from_secs(5)).repeat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
|
@ -1,54 +0,0 @@
|
||||||
use bevy::app::AppExit;
|
|
||||||
use bevy::prelude::*;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Default, States)]
|
|
||||||
pub enum AppState {
|
|
||||||
#[default]
|
|
||||||
CoreLoading,
|
|
||||||
MenuRunning,
|
|
||||||
AppLoading,
|
|
||||||
AppRunning,
|
|
||||||
AppEnding,
|
|
||||||
|
|
||||||
// FIXME: not sure
|
|
||||||
LoadingGame,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Default, States)]
|
|
||||||
pub enum GameState {
|
|
||||||
#[default]
|
|
||||||
None,
|
|
||||||
|
|
||||||
InMenu,
|
|
||||||
InGame,
|
|
||||||
|
|
||||||
InGameOver,
|
|
||||||
|
|
||||||
InSaving,
|
|
||||||
InLoading,
|
|
||||||
}
|
|
||||||
|
|
||||||
// tag components for all entities within a certain state (for despawning them if needed) , FIXME: seems kinda hack-ish
|
|
||||||
#[derive(Component)]
|
|
||||||
pub struct InCoreLoading;
|
|
||||||
#[derive(Component, Default)]
|
|
||||||
pub struct InMenuRunning;
|
|
||||||
#[derive(Component)]
|
|
||||||
pub struct InAppLoading;
|
|
||||||
#[derive(Component)]
|
|
||||||
pub struct InAppRunning;
|
|
||||||
|
|
||||||
// components for tagging in game vs in game menu stuff
|
|
||||||
#[derive(Component, Default)]
|
|
||||||
pub struct InMainMenu;
|
|
||||||
#[derive(Component, Default)]
|
|
||||||
pub struct InMenu;
|
|
||||||
#[derive(Component, Default)]
|
|
||||||
pub struct InGame;
|
|
||||||
|
|
||||||
pub struct StatePlugin;
|
|
||||||
impl Plugin for StatePlugin {
|
|
||||||
fn build(&self, app: &mut App) {
|
|
||||||
app.add_state::<AppState>().add_state::<GameState>();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "bevy_gltf_blueprints_basic_example"
|
|
||||||
version = "0.3.0"
|
|
||||||
edition = "2021"
|
|
||||||
license = "MIT OR Apache-2.0"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
bevy = { version = "0.13", features = ["dynamic_linking"] }
|
|
||||||
bevy_gltf_blueprints = { path = "../../../crates/bevy_gltf_blueprints" }
|
|
||||||
bevy_gltf_worlflow_examples_common_rapier = { path = "../../common_rapier" }
|
|
||||||
bevy_rapier3d = { version = "0.25.0", features = ["serde-serialize", "debug-render-3d", "enhanced-determinism"] }
|
|
||||||
rand = "0.8.5"
|
|
|
@ -1 +0,0 @@
|
||||||
({})
|
|
|
@ -1,6 +0,0 @@
|
||||||
({
|
|
||||||
"world":File (path: "models/World.glb"),
|
|
||||||
"models": Folder (
|
|
||||||
path: "models/library",
|
|
||||||
),
|
|
||||||
})
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,12 +0,0 @@
|
||||||
use bevy::prelude::*;
|
|
||||||
use bevy_gltf_blueprints::*;
|
|
||||||
|
|
||||||
pub struct CorePlugin;
|
|
||||||
impl Plugin for CorePlugin {
|
|
||||||
fn build(&self, app: &mut App) {
|
|
||||||
app.add_plugins((BlueprintsPlugin {
|
|
||||||
aabbs: true,
|
|
||||||
..Default::default()
|
|
||||||
},));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,126 +0,0 @@
|
||||||
use bevy::prelude::*;
|
|
||||||
use bevy_gltf_blueprints::{BluePrintBundle, BlueprintName, GameWorldTag};
|
|
||||||
use bevy_gltf_worlflow_examples_common_rapier::{assets::GameAssets, GameState, InAppRunning};
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn spawn_test_unregisted_components(
|
|
||||||
keycode: Res<ButtonInput<KeyCode>>,
|
|
||||||
mut commands: Commands,
|
|
||||||
|
|
||||||
mut game_world: Query<(Entity, &Children), With<GameWorldTag>>,
|
|
||||||
) {
|
|
||||||
if keycode.just_pressed(KeyCode::KeyU) {
|
|
||||||
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),
|
|
||||||
},
|
|
||||||
UnregisteredComponent,
|
|
||||||
))
|
|
||||||
.id();
|
|
||||||
commands.entity(world).add_child(new_entity);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,121 +0,0 @@
|
||||||
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>>,
|
|
||||||
// mut next_game_state: ResMut<NextState<GameState>>,
|
|
||||||
// mut save_requested_events: EventWriter<SaveRequest>,
|
|
||||||
// mut load_requested_events: EventWriter<LoadRequest>,
|
|
||||||
) {
|
|
||||||
if keycode.just_pressed(KeyCode::Enter) {
|
|
||||||
next_app_state.set(AppState::AppLoading);
|
|
||||||
// next_game_state.set(GameState::None);
|
|
||||||
}
|
|
||||||
|
|
||||||
if keycode.just_pressed(KeyCode::KeyL) {
|
|
||||||
next_app_state.set(AppState::AppLoading);
|
|
||||||
// load_requested_events.send(LoadRequest { path: "toto".into() })
|
|
||||||
}
|
|
||||||
|
|
||||||
if keycode.just_pressed(KeyCode::KeyS) {
|
|
||||||
// save_requested_events.send(SaveRequest { path: "toto".into() })
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
pub mod in_game;
|
|
||||||
pub use in_game::*;
|
|
||||||
|
|
||||||
pub mod in_main_menu;
|
|
||||||
pub use in_main_menu::*;
|
|
||||||
|
|
||||||
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_systems(
|
|
||||||
Update,
|
|
||||||
(spawn_test, spawn_test_unregisted_components).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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,80 +0,0 @@
|
||||||
use bevy::prelude::*;
|
|
||||||
|
|
||||||
#[derive(Component, Reflect, Default, Debug)]
|
|
||||||
#[reflect(Component)]
|
|
||||||
struct UnitTest;
|
|
||||||
|
|
||||||
#[derive(Component, Reflect, Default, Debug, Deref, DerefMut)]
|
|
||||||
#[reflect(Component)]
|
|
||||||
struct TupleTestF32(f32);
|
|
||||||
|
|
||||||
#[derive(Component, Reflect, Default, Debug, Deref, DerefMut)]
|
|
||||||
#[reflect(Component)]
|
|
||||||
struct TupleTestU64(u64);
|
|
||||||
|
|
||||||
#[derive(Component, Reflect, Default, Debug, Deref, DerefMut)]
|
|
||||||
#[reflect(Component)]
|
|
||||||
pub struct TupleTestStr(String);
|
|
||||||
|
|
||||||
#[derive(Component, Reflect, Default, Debug)]
|
|
||||||
#[reflect(Component)]
|
|
||||||
struct TupleTest2(f32, u64, String);
|
|
||||||
|
|
||||||
#[derive(Component, Reflect, Default, Debug)]
|
|
||||||
#[reflect(Component)]
|
|
||||||
struct TupleTestBool(bool);
|
|
||||||
|
|
||||||
#[derive(Component, Reflect, Default, Debug)]
|
|
||||||
#[reflect(Component)]
|
|
||||||
struct TupleVec2(Vec2);
|
|
||||||
|
|
||||||
#[derive(Component, Reflect, Default, Debug)]
|
|
||||||
#[reflect(Component)]
|
|
||||||
struct TupleVec3(Vec3);
|
|
||||||
|
|
||||||
#[derive(Component, Reflect, Default, Debug)]
|
|
||||||
#[reflect(Component)]
|
|
||||||
struct TupleVec(Vec<String>);
|
|
||||||
|
|
||||||
#[derive(Component, Reflect, Default, Debug)]
|
|
||||||
#[reflect(Component)]
|
|
||||||
struct TupleTestColor(Color);
|
|
||||||
|
|
||||||
#[derive(Component, Reflect, Default, Debug)]
|
|
||||||
#[reflect(Component)]
|
|
||||||
struct BasicTest {
|
|
||||||
a: f32,
|
|
||||||
b: u64,
|
|
||||||
c: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Component, Reflect, Default, Debug)]
|
|
||||||
#[reflect(Component)]
|
|
||||||
pub enum EnumTest {
|
|
||||||
Metal,
|
|
||||||
Wood,
|
|
||||||
Rock,
|
|
||||||
Cloth,
|
|
||||||
Squishy,
|
|
||||||
#[default]
|
|
||||||
None,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ComponentsTestPlugin;
|
|
||||||
impl Plugin for ComponentsTestPlugin {
|
|
||||||
fn build(&self, app: &mut App) {
|
|
||||||
app.register_type::<BasicTest>()
|
|
||||||
.register_type::<UnitTest>()
|
|
||||||
.register_type::<TupleTestF32>()
|
|
||||||
.register_type::<TupleTestU64>()
|
|
||||||
.register_type::<TupleTestStr>()
|
|
||||||
.register_type::<TupleTestBool>()
|
|
||||||
.register_type::<TupleTest2>()
|
|
||||||
.register_type::<TupleVec2>()
|
|
||||||
.register_type::<TupleVec3>()
|
|
||||||
.register_type::<EnumTest>()
|
|
||||||
.register_type::<TupleTestColor>()
|
|
||||||
.register_type::<TupleVec>()
|
|
||||||
.register_type::<Vec<String>>();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "bevy_gltf_blueprints_basic_xpbd_physics_example"
|
|
||||||
version = "0.3.0"
|
|
||||||
edition = "2021"
|
|
||||||
license = "MIT OR Apache-2.0"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
bevy = { version = "0.13", features = ["dynamic_linking"] }
|
|
||||||
bevy_gltf_blueprints = { path = "../../../crates/bevy_gltf_blueprints" }
|
|
||||||
bevy_gltf_worlflow_examples_common_xpbd = { path = "../../common_xpbd" }
|
|
||||||
bevy_xpbd_3d = "0.4"
|
|
||||||
rand = "0.8.5"
|
|
|
@ -1,15 +0,0 @@
|
||||||
|
|
||||||
# Basic xpbd physics example/demo
|
|
||||||
|
|
||||||
Same as the basic example but using [xpbd](https://github.com/Jondolf/bevy_xpbd) instead of Rapier [rapier](https://github.com/dimforge/bevy_rapier)
|
|
||||||
|
|
||||||
## Running this example
|
|
||||||
|
|
||||||
```
|
|
||||||
cargo run --features bevy/dynamic_linking
|
|
||||||
```
|
|
||||||
|
|
||||||
### Additional notes
|
|
||||||
|
|
||||||
* You usually define either the Components directly or use ```Proxy components``` that get replaced in Bevy systems with the actual Components that you want (usually when for some reason, ie external crates with unregistered components etc) you cannot use the components directly.
|
|
||||||
* this example contains code for future features, not finished yet ! please disregard anything related to saving & loading
|
|
Binary file not shown.
|
@ -1 +0,0 @@
|
||||||
({})
|
|
|
@ -1,6 +0,0 @@
|
||||||
({
|
|
||||||
"world":File (path: "models/World.glb"),
|
|
||||||
"models": Folder (
|
|
||||||
path: "models/library",
|
|
||||||
),
|
|
||||||
})
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,11 +0,0 @@
|
||||||
use bevy::prelude::*;
|
|
||||||
use bevy_gltf_blueprints::*;
|
|
||||||
|
|
||||||
pub struct CorePlugin;
|
|
||||||
impl Plugin for CorePlugin {
|
|
||||||
fn build(&self, app: &mut App) {
|
|
||||||
app.add_plugins((BlueprintsPlugin {
|
|
||||||
..Default::default()
|
|
||||||
},));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,76 +0,0 @@
|
||||||
use bevy::prelude::*;
|
|
||||||
use bevy_gltf_blueprints::{BluePrintBundle, BlueprintName, GameWorldTag};
|
|
||||||
use bevy_gltf_worlflow_examples_common_xpbd::{assets::GameAssets, GameState, InAppRunning};
|
|
||||||
use bevy_xpbd_3d::prelude::*;
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
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)),
|
|
||||||
LinearVelocity(Vec3::new(vel_x, vel_y, vel_z)),
|
|
||||||
AngularVelocity::ZERO,
|
|
||||||
))
|
|
||||||
.id();
|
|
||||||
commands.entity(world).add_child(new_entity);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,107 +0,0 @@
|
||||||
use bevy::prelude::*;
|
|
||||||
use bevy_gltf_worlflow_examples_common_xpbd::{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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
pub mod in_game;
|
|
||||||
pub use in_game::*;
|
|
||||||
|
|
||||||
pub mod in_main_menu;
|
|
||||||
pub use in_main_menu::*;
|
|
||||||
|
|
||||||
use bevy::prelude::*;
|
|
||||||
use bevy_gltf_worlflow_examples_common_xpbd::{AppState, GameState};
|
|
||||||
|
|
||||||
pub struct GamePlugin;
|
|
||||||
impl Plugin for GamePlugin {
|
|
||||||
fn build(&self, app: &mut App) {
|
|
||||||
app.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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
use bevy::prelude::*;
|
|
||||||
use bevy_gltf_worlflow_examples_common_xpbd::CommonPlugin;
|
|
||||||
|
|
||||||
mod core;
|
|
||||||
use crate::core::*;
|
|
||||||
|
|
||||||
mod game;
|
|
||||||
use game::*;
|
|
||||||
|
|
||||||
mod test_components;
|
|
||||||
use test_components::*;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
App::new()
|
|
||||||
.add_plugins((
|
|
||||||
DefaultPlugins.set(AssetPlugin::default()),
|
|
||||||
// our custom plugins
|
|
||||||
CommonPlugin,
|
|
||||||
CorePlugin, // reusable plugins
|
|
||||||
GamePlugin, // specific to our game
|
|
||||||
ComponentsTestPlugin, // Showcases different type of components /structs
|
|
||||||
))
|
|
||||||
.run();
|
|
||||||
}
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
[package]
|
||||||
|
name = "blenvy_blueprints_example"
|
||||||
|
version = "0.0.1"
|
||||||
|
edition = "2021"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bevy = { version = "0.14", features = ["dynamic_linking"] }
|
||||||
|
blenvy = { path = "../../../crates/blenvy" }
|
||||||
|
rand = "0.8.5"
|
|
@ -2,7 +2,7 @@
|
||||||
# Materials example/demo
|
# Materials example/demo
|
||||||
|
|
||||||
Example of materials use & reuse (including textures) to avoid redundant materials in blueprints gltfs that lead to asset & memory bloat
|
Example of materials use & reuse (including textures) to avoid redundant materials in blueprints gltfs that lead to asset & memory bloat
|
||||||
- to be used together with ```gltf_auto_export``` version >0.6 with the "materials library" option for exports
|
- to be used together with ```blenvy``` version >0.6 with the "materials library" option for exports
|
||||||
- It shows you how ou can configure```Bevy_gltf_blueprints``` to support material libraries
|
- It shows you how ou can configure```Bevy_gltf_blueprints``` to support material libraries
|
||||||
- material library is [here](./assets/materials/)
|
- material library is [here](./assets/materials/)
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 243 KiB After Width: | Height: | Size: 243 KiB |
Before Width: | Height: | Size: 174 KiB After Width: | Height: | Size: 174 KiB |
|
@ -56,8 +56,8 @@ pub enum EnumTest {
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ComponentsTestPlugin;
|
pub struct ComponentsExamplesPlugin;
|
||||||
impl Plugin for ComponentsTestPlugin {
|
impl Plugin for ComponentsExamplesPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.register_type::<BasicTest>()
|
app.register_type::<BasicTest>()
|
||||||
.register_type::<TupleTestF32>()
|
.register_type::<TupleTestF32>()
|
|
@ -0,0 +1,76 @@
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use blenvy::{AddToGameWorld, BlenvyPlugin, BluePrintBundle, BlueprintInfo, DynamicBlueprintInstance, GameWorldTag, HideUntilReady, SpawnBlueprint};
|
||||||
|
use rand::Rng;
|
||||||
|
|
||||||
|
mod component_examples;
|
||||||
|
use component_examples::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
App::new()
|
||||||
|
.add_plugins((
|
||||||
|
DefaultPlugins.set(AssetPlugin::default()),
|
||||||
|
// our custom plugins
|
||||||
|
ComponentsExamplesPlugin, // Showcases different type of components /structs
|
||||||
|
BlenvyPlugin::default()
|
||||||
|
))
|
||||||
|
|
||||||
|
.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 spawn our game world/level, which is also a blueprint !
|
||||||
|
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,
|
||||||
|
|
||||||
|
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 name_index: u64 = rng.gen();
|
||||||
|
|
||||||
|
let new_entity = commands
|
||||||
|
.spawn((
|
||||||
|
BluePrintBundle {
|
||||||
|
blueprint: BlueprintInfo {
|
||||||
|
name: "spawned".into(),
|
||||||
|
path: "blueprints/Blueprint 3.gltf".into(),
|
||||||
|
}, // FIXME
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
DynamicBlueprintInstance,
|
||||||
|
bevy::prelude::Name::from(format!("test{}", name_index)),
|
||||||
|
HideUntilReady,
|
||||||
|
AddToGameWorld,
|
||||||
|
TransformBundle::from_transform(Transform::from_xyz(x, 2.0, y)),
|
||||||
|
|
||||||
|
))
|
||||||
|
.id();
|
||||||
|
// commands.entity(world).add_child(new_entity);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
[package]
|
||||||
|
name = "blenvy_components_example"
|
||||||
|
version = "0.0.1"
|
||||||
|
edition = "2021"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bevy = { version = "0.14", features = ["dynamic_linking"] }
|
||||||
|
blenvy = { path = "../../../crates/blenvy" }
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue