chore(crates): updated code from main to v0.14 compatibility for all old crates

This commit is contained in:
kaosat.dev 2024-07-18 13:42:29 +02:00
parent 3a8844b7a1
commit a618e0035e
21 changed files with 510 additions and 783 deletions

View File

@ -15,7 +15,7 @@ workspace = true
[dependencies] [dependencies]
bevy_gltf_components = { version = "0.6", path = "../bevy_gltf_components" } bevy_gltf_components = { version = "0.6", path = "../bevy_gltf_components" }
bevy = { version = "0.14.0-rc.3", default-features = false, features = ["bevy_asset", "bevy_scene", "bevy_gltf", "bevy_animation", "animation"] } bevy = { version = "0.14", default-features = false, features = ["bevy_asset", "bevy_scene", "bevy_gltf", "bevy_animation", "animation"] }
[dev-dependencies] [dev-dependencies]
bevy = { version = "0.14.0-rc.3", default-features = false, features = ["dynamic_linking"] } bevy = { version = "0.14", default-features = false, features = ["dynamic_linking"] }

View File

@ -3,7 +3,9 @@
[![License](https://img.shields.io/crates/l/bevy_gltf_blueprints)](https://github.com/kaosat-dev/Blender_bevy_components_workflow/blob/main/crates/bevy_gltf_blueprints/License.md) [![License](https://img.shields.io/crates/l/bevy_gltf_blueprints)](https://github.com/kaosat-dev/Blender_bevy_components_workflow/blob/main/crates/bevy_gltf_blueprints/License.md)
[![Bevy tracking](https://img.shields.io/badge/Bevy%20tracking-released%20version-lightblue)](https://github.com/bevyengine/bevy/blob/main/docs/plugins_guidelines.md#main-branch-tracking) [![Bevy tracking](https://img.shields.io/badge/Bevy%20tracking-released%20version-lightblue)](https://github.com/bevyengine/bevy/blob/main/docs/plugins_guidelines.md#main-branch-tracking)
# bevy_gltf_blueprints # bevy_gltf_blueprints (deprecated in favor of Blenvy)
> bevy_gltf_blueprints has been deprecated in favor of its successor [Blenvy](https://crates.io/crates/blenvy), part of the [Blenvy project](https://github.com/kaosat-dev/Blenvy). No further development or maintenance will be done for Bevy bevy_gltf_blueprints. See [#194](https://github.com/kaosat-dev/Blenvy/issues/194) for background.
Built on [bevy_gltf_components](https://crates.io/crates/bevy_gltf_components) this crate adds the ability to define Blueprints/Prefabs for [Bevy](https://bevyengine.org/) inside gltf files and spawn them in Bevy. Built on [bevy_gltf_components](https://crates.io/crates/bevy_gltf_components) this crate adds the ability to define Blueprints/Prefabs for [Bevy](https://bevyengine.org/) inside gltf files and spawn them in Bevy.
@ -15,8 +17,9 @@ A blueprint is a set of **overrideable** components + a hierarchy: ie
* just a Gltf file with Gltf_extras specifying components * just a Gltf file with Gltf_extras specifying components
* a component called BlueprintName * a component called BlueprintName
Particularly useful when using [Blender](https://www.blender.org/) as an editor for the [Bevy](https://bevyengine.org/) game engine, combined with the Blender add-on that do a lot of the work for you Particularly useful when using [Blender](https://www.blender.org/) as an editor for the [Bevy](https://bevyengine.org/) game engine, combined with the Blender add-ons that do a lot of the work for you
- [blenvy](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/tools/blenvy) - [gltf_auto_export](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/tools/gltf_auto_export)
- [bevy_components](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/tools/bevy_components)
## Usage ## Usage
@ -26,8 +29,8 @@ Here's a minimal usage example:
```toml ```toml
# Cargo.toml # Cargo.toml
[dependencies] [dependencies]
bevy="0.13" bevy="0.14"
bevy_gltf_blueprints = { version = "0.10"} bevy_gltf_blueprints = { version = "0.11.0"}
``` ```
@ -65,7 +68,7 @@ fn spawn_blueprint(
Add the following to your `[dependencies]` section in `Cargo.toml`: Add the following to your `[dependencies]` section in `Cargo.toml`:
```toml ```toml
bevy_gltf_blueprints = "0.10" bevy_gltf_blueprints = "0.11.0"
``` ```
Or use `cargo add`: Or use `cargo add`:
@ -100,8 +103,11 @@ fn main() {
App::new() App::new()
.add_plugins(( .add_plugins((
BlueprintsPlugin{ BlueprintsPlugin{
library_folder: "advanced/models/library".into() // replace this with your blueprints library path , relative to the assets folder,
format: GltfFormat::GLB,// optional, use either format: GltfFormat::GLB, or format: GltfFormat::GLTF, or ..Default::default() if you want to keep the default .glb extension, this sets what extensions/ gltf files will be looked for by the library
aabbs: true, // defaults to false, enable this to automatically calculate aabb for the scene/blueprint aabbs: true, // defaults to false, enable this to automatically calculate aabb for the scene/blueprint
material_library: true, // defaults to false, enable this to enable automatic injection of materials from material library files material_library: true, // defaults to false, enable this to enable automatic injection of materials from material library files
material_library_folder: "materials".into() //defaults to "materials" the folder to look for for the material files
..Default::default() ..Default::default()
} }
)) ))
@ -220,7 +226,7 @@ Typically , the order of systems should be
***bevy_gltf_components (GltfComponentsSet::Injection)*** => ***bevy_gltf_blueprints (GltfBlueprintsSet::Spawn, GltfBlueprintsSet::AfterSpawn)*** => ***replace_proxies*** ***bevy_gltf_components (GltfComponentsSet::Injection)*** => ***bevy_gltf_blueprints (GltfBlueprintsSet::Spawn, GltfBlueprintsSet::AfterSpawn)*** => ***replace_proxies***
see https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/basic for how to set it up correctly see an example [here](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/basic) for how to set it up correctly
@ -274,9 +280,9 @@ pub fn animation_change_on_proximity_foxes(
} }
``` ```
see https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/animation for how to set it up correctly see [here](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/animation) for how to set it up correctly
particularly from https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/animation/game/in_game.rs particularly from [here](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/animation/src/game/in_game.rs)
## Materials ## Materials
@ -290,28 +296,47 @@ Ie for example without this option, 56 different blueprints using the same mater
you can configure this with the settings: you can configure this with the settings:
```rust ```rust
material_library: true // defaults to false, enable this to enable automatic injection of materials from material library files material_library: true // defaults to false, enable this to enable automatic injection of materials from material library files
material_library_folder: "materials".into() //defaults to "materials" the folder to look for for the material files
``` ```
> Important! you must take care of preloading your material librairy gltf files in advance, using for example ```bevy_asset_loader```since > Important! you must take care of preloading your material librairy gltf files in advance, using for example ```bevy_asset_loader```since
```bevy_gltf_blueprints``` currently does NOT take care of loading those at runtime ```bevy_gltf_blueprints``` currently does NOT take care of loading those at runtime
see https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/materials for how to set it up correctly see an example [here](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/materials) for how to set it up correctly
Generating optimised blueprints and material libraries can be automated using the latests version of the [Blender plugin](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/tools/gltf_auto_export) 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)
## Legacy mode
Starting in version 0.7 there is a new parameter ```legacy_mode``` for backwards compatibility
To disable the legacy mode: (enabled by default)
```rust no_run
BlueprintsPlugin{legacy_mode: false}
```
You **need** to disable legacy mode if you want to use the [```bevy_components```](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/tools_bevy_blueprints/tools/bevy_components) Blender addon + the [```bevy_registry_export crate```](https://crates.io/crates/bevy_registry_export) !
As it create custom properties that are writen in real **ron** file format instead of a simplified version (the one in the legacy mode)
> Note: the legacy mode support will be dropped in future versions, and the default behaviour will be NO legacy mode
## Examples ## Examples
https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/basic * [basic](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/basic)
https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/basic_xpbd_physics * [xbpd](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/basic_xpbd_physics)
https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/animation * [animation](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/animation)
https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/materials * [materials](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/materials)
https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles * [multiple_levels_multiple_blendfiles](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_gltf_blueprints/multiple_levels_multiple_blendfiles)
## Compatible Bevy versions ## Compatible Bevy versions
@ -321,6 +346,7 @@ The main branch is compatible with the latest Bevy release, while the branch `be
Compatibility of `bevy_gltf_blueprints` versions: Compatibility of `bevy_gltf_blueprints` versions:
| `bevy_gltf_blueprints` | `bevy` | | `bevy_gltf_blueprints` | `bevy` |
| :-- | :-- | | :-- | :-- |
| `0.11` | `0.14` |
| `0.9 - 0.10` | `0.13` | | `0.9 - 0.10` | `0.13` |
| `0.3 - 0.8` | `0.12` | | `0.3 - 0.8` | `0.12` |
| `0.1 - 0.2` | `0.11` | | `0.1 - 0.2` | `0.11` |

View File

@ -3,9 +3,11 @@ use bevy::utils::HashMap;
#[derive(Component, Reflect, Default, Debug)] #[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)] #[reflect(Component)]
/// storage for animations for a given entity's BLUEPRINT (ie for example a characters animations), essentially a clone of gltf's `named_animations` /// storage for animations for a given entity (hierarchy), essentially a clone of gltf's `named_animations`
pub struct BlueprintAnimations { pub struct Animations {
pub named_animations: HashMap<String, Handle<AnimationClip>>, pub named_animations: HashMap<String, Handle<AnimationClip>>,
pub named_indices: HashMap<String, AnimationNodeIndex>,
pub graph: Handle<AnimationGraph>,
} }
#[derive(Component, Debug)] #[derive(Component, Debug)]
@ -13,209 +15,4 @@ pub struct BlueprintAnimations {
/// so that the root entity knows which of its children contains an actualy `AnimationPlayer` component /// so that the root entity knows which of its children contains an actualy `AnimationPlayer` component
/// this is for convenience, because currently , Bevy's gltf parsing inserts `AnimationPlayers` "one level down" /// this is for convenience, because currently , Bevy's gltf parsing inserts `AnimationPlayers` "one level down"
/// ie armature/root for animated models, which means more complex queries to trigger animations that we want to avoid /// ie armature/root for animated models, which means more complex queries to trigger animations that we want to avoid
pub struct BlueprintAnimationPlayerLink(pub Entity); pub struct AnimationPlayerLink(pub Entity);
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
/// storage for scene level animations for a given entity (hierarchy), essentially a clone of gltf's `named_animations`
pub struct SceneAnimations {
pub named_animations: HashMap<String, Handle<AnimationClip>>,
}
#[derive(Component, Debug)]
/// Stop gap helper component : this is inserted into a "root" entity (an entity representing a whole gltf file)
/// so that the root entity knows which of its children contains an actualy `AnimationPlayer` component
/// this is for convenience, because currently , Bevy's gltf parsing inserts `AnimationPlayers` "one level down"
/// ie armature/root for animated models, which means more complex queries to trigger animations that we want to avoid
pub struct SceneAnimationPlayerLink(pub Entity);
/// Stores Animation information: name, frame informations etc
#[derive(Reflect, Default, Debug)]
pub struct AnimationInfo {
pub name: String,
pub frame_start: f32,
pub frame_end: f32,
pub frames_length: f32,
pub frame_start_override: f32,
pub frame_end_override: f32,
}
/// 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
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
pub struct AnimationInfos {
pub animations: Vec<AnimationInfo>,
}
#[derive(Reflect, Default, Debug)]
pub struct AnimationMarker {
// pub frame: u32,
pub name: String,
pub handled_for_cycle: bool,
}
/// Stores information about animation markers: practical for adding things like triggering events at specific keyframes etc
/// it is essentiall a hashmap of `AnimationName` => `HashMap`<`FrameNumber`, Vec of marker names>
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
pub struct AnimationMarkers(pub HashMap<String, HashMap<u32, Vec<String>>>);
/// Event that gets triggered once a specific marker inside an animation has been reached (frame based)
/// Provides some usefull information about which entity , wich animation, wich frame & which marker got triggered
#[derive(Event, Debug)]
pub struct AnimationMarkerReached {
pub entity: Entity,
pub animation_name: String,
pub frame: u32,
pub marker_name: String,
}
/////////////////////
/*
/// triggers events when a given animation marker is reached for INSTANCE animations
pub fn trigger_instance_animation_markers_events(
animation_infos: Query<(
Entity,
&AnimationMarkers,
&SceneAnimationPlayerLink,
&SceneAnimations,
&AnimationInfos,
)>,
animation_players: Query<(&AnimationPlayer)>,
animation_clips: Res<Assets<AnimationClip>>,
animation_graphs: Res<Assets<AnimationGraph>>,
mut animation_marker_events: EventWriter<AnimationMarkerReached>,
) {
for (entity, markers, link, animations, animation_infos) in animation_infos.iter() {
let animation_player = animation_players.get(link.0).unwrap();
let animation_clip = animation_clips.get(animation_player.animation_clip());
// animation_player.play(animation)
if animation_clip.is_some() {
// println!("Entity {:?} markers {:?}", entity, markers);
// println!("Player {:?} {}", animation_player.elapsed(), animation_player.completions());
// FIMXE: yikes ! very inneficient ! perhaps add boilerplate to the "start playing animation" code so we know what is playing
let animation_name = animations.named_animations.iter().find_map(|(key, value)| {
if value == animation_player.animation_clip() {
Some(key)
} else {
None
}
});
if animation_name.is_some() {
let animation_name = animation_name.unwrap();
let animation_length_seconds = animation_clip.unwrap().duration();
let animation_length_frames = animation_infos
.animations
.iter()
.find(|anim| &anim.name == animation_name)
.unwrap()
.frames_length;
// TODO: we also need to take playback speed into account
let time_in_animation = animation_player.elapsed()
- (animation_player.completions() as f32) * animation_length_seconds;
let frame_seconds =
(animation_length_frames / animation_length_seconds) * time_in_animation;
let frame = frame_seconds as u32;
let matching_animation_marker = &markers.0[animation_name];
if matching_animation_marker.contains_key(&frame) {
let matching_markers_per_frame = matching_animation_marker.get(&frame).unwrap();
// let timediff = animation_length_seconds - time_in_animation;
// println!("timediff {}", timediff);
// println!("FOUND A MARKER {:?} at frame {}", matching_markers_per_frame, frame);
// emit an event AnimationMarkerReached(entity, animation_name, frame, marker_name)
// FIXME: problem, this can fire multiple times in a row, depending on animation length , speed , etc
for marker in matching_markers_per_frame {
animation_marker_events.send(AnimationMarkerReached {
entity,
animation_name: animation_name.clone(),
frame,
marker_name: marker.clone(),
});
}
}
}
}
}
}
/// triggers events when a given animation marker is reached for BLUEPRINT animations
pub fn trigger_blueprint_animation_markers_events(
animation_infos: Query<(Entity, &BlueprintAnimationPlayerLink, &BlueprintAnimations)>,
// FIXME: annoying hiearchy issue yet again: the Markers & AnimationInfos are stored INSIDE the blueprint, so we need to access them differently
all_animation_infos: Query<(Entity, &AnimationMarkers, &AnimationInfos, &Parent)>,
animation_players: Query<&AnimationPlayer>,
animation_clips: Res<Assets<AnimationClip>>,
mut animation_marker_events: EventWriter<AnimationMarkerReached>,
) {
for (entity, link, animations) in animation_infos.iter() {
let animation_player = animation_players.get(link.0).unwrap();
let animation_clip = animation_clips.get(animation_player.animation_clip());
// FIXME: horrible code
for (_, markers, animation_infos, parent) in all_animation_infos.iter() {
if parent.get() == entity {
if animation_clip.is_some() {
// println!("Entity {:?} markers {:?}", entity, markers);
// println!("Player {:?} {}", animation_player.elapsed(), animation_player.completions());
// FIMXE: yikes ! very inneficient ! perhaps add boilerplate to the "start playing animation" code so we know what is playing
let animation_name =
animations.named_animations.iter().find_map(|(key, value)| {
if value == animation_player.animation_clip() {
Some(key)
} else {
None
}
});
if animation_name.is_some() {
let animation_name = animation_name.unwrap();
let animation_length_seconds = animation_clip.unwrap().duration();
let animation_length_frames = animation_infos
.animations
.iter()
.find(|anim| &anim.name == animation_name)
.unwrap()
.frames_length;
// TODO: we also need to take playback speed into account
let time_in_animation = animation_player.elapsed()
- (animation_player.completions() as f32) * animation_length_seconds;
let frame_seconds = (animation_length_frames / animation_length_seconds)
* time_in_animation;
// println!("frame seconds {}", frame_seconds);
let frame = frame_seconds.ceil() as u32; // FIXME , bad hack
let matching_animation_marker = &markers.0[animation_name];
if matching_animation_marker.contains_key(&frame) {
let matching_markers_per_frame =
matching_animation_marker.get(&frame).unwrap();
// println!("FOUND A MARKER {:?} at frame {}", matching_markers_per_frame, frame);
// emit an event AnimationMarkerReached(entity, animation_name, frame, marker_name)
// FIXME: complete hack-ish solution , otherwise this can fire multiple times in a row, depending on animation length , speed , etc
let diff = frame as f32 - frame_seconds;
println!("diff {}", diff);
if diff < 0.1 {
for marker in matching_markers_per_frame {
animation_marker_events.send(AnimationMarkerReached {
entity,
animation_name: animation_name.clone(),
frame,
marker_name: marker.clone(),
});
}
}
}
}
}
break;
}
}
}
}
*/

View File

@ -1,5 +1,4 @@
use bevy::ecs::world::Command; use bevy::{ecs::world::Command, prelude::*};
use bevy::prelude::*;
use std::any::TypeId; use std::any::TypeId;
// originally based https://github.com/bevyengine/bevy/issues/1515, // originally based https://github.com/bevyengine/bevy/issues/1515,

View File

@ -10,9 +10,6 @@ pub use animation::*;
pub mod aabb; pub mod aabb;
pub use aabb::*; pub use aabb::*;
pub mod assets;
pub use assets::*;
pub mod materials; pub mod materials;
pub use materials::*; pub use materials::*;
@ -22,7 +19,11 @@ pub use copy_components::*;
use core::fmt; use core::fmt;
use std::path::PathBuf; use std::path::PathBuf;
use bevy::{prelude::*, render::primitives::Aabb, utils::HashMap}; use bevy::{
prelude::*,
render::{primitives::Aabb, view::VisibilitySystems},
utils::HashMap,
};
use bevy_gltf_components::{ComponentsFromGltfPlugin, GltfComponentsSet}; use bevy_gltf_components::{ComponentsFromGltfPlugin, GltfComponentsSet};
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)] #[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
@ -35,14 +36,12 @@ pub enum GltfBlueprintsSet {
#[derive(Bundle)] #[derive(Bundle)]
pub struct BluePrintBundle { pub struct BluePrintBundle {
pub blueprint: BlueprintName, pub blueprint: BlueprintName,
pub blueprint_path: BlueprintPath,
pub spawn_here: SpawnHere, pub spawn_here: SpawnHere,
} }
impl Default for BluePrintBundle { impl Default for BluePrintBundle {
fn default() -> Self { fn default() -> Self {
BluePrintBundle { BluePrintBundle {
blueprint: BlueprintName("default".into()), blueprint: BlueprintName("default".into()),
blueprint_path: BlueprintPath("".into()),
spawn_here: SpawnHere, spawn_here: SpawnHere,
} }
} }
@ -50,10 +49,13 @@ impl Default for BluePrintBundle {
#[derive(Clone, Resource)] #[derive(Clone, Resource)]
pub struct BluePrintsConfig { pub struct BluePrintsConfig {
pub(crate) format: GltfFormat,
pub(crate) library_folder: PathBuf,
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) material_library: bool, pub(crate) material_library: bool,
pub(crate) material_library_folder: PathBuf,
pub(crate) material_library_cache: HashMap<String, Handle<StandardMaterial>>, pub(crate) material_library_cache: HashMap<String, Handle<StandardMaterial>>,
} }
@ -80,17 +82,27 @@ impl fmt::Display for GltfFormat {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
/// Plugin for gltf blueprints /// Plugin for gltf blueprints
pub struct BlueprintsPlugin { pub struct BlueprintsPlugin {
pub legacy_mode: bool, // flag that gets passed on to bevy_gltf_components
pub format: GltfFormat,
/// The base folder where library/blueprints assets are loaded from, relative to the executable.
pub library_folder: PathBuf,
/// Automatically generate aabbs for the blueprints root objects /// Automatically generate aabbs for the blueprints root objects
pub aabbs: bool, pub aabbs: bool,
/// ///
pub material_library: bool, pub material_library: bool,
pub material_library_folder: PathBuf,
} }
impl Default for BlueprintsPlugin { impl Default for BlueprintsPlugin {
fn default() -> Self { fn default() -> Self {
Self { Self {
legacy_mode: true,
format: GltfFormat::GLB,
library_folder: PathBuf::from("models/library"),
aabbs: false, aabbs: false,
material_library: false, material_library: false,
material_library_folder: PathBuf::from("materials"),
} }
} }
} }
@ -105,34 +117,25 @@ fn materials_library_enabled(blueprints_config: Res<BluePrintsConfig>) -> bool {
impl Plugin for BlueprintsPlugin { impl Plugin for BlueprintsPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_plugins(ComponentsFromGltfPlugin {}) app.add_plugins(ComponentsFromGltfPlugin {
legacy_mode: self.legacy_mode,
})
.register_type::<BlueprintName>() .register_type::<BlueprintName>()
.register_type::<BlueprintPath>()
.register_type::<MaterialInfo>() .register_type::<MaterialInfo>()
.register_type::<SpawnHere>() .register_type::<SpawnHere>()
.register_type::<BlueprintAnimations>() .register_type::<Animations>()
.register_type::<SceneAnimations>() .register_type::<BlueprintsList>()
.register_type::<AnimationInfo>()
.register_type::<AnimationInfos>()
.register_type::<Vec<AnimationInfo>>()
.register_type::<AnimationMarkers>()
.register_type::<HashMap<u32, Vec<String>>>()
.register_type::<HashMap<String, HashMap<u32, Vec<String>>>>()
.add_event::<AnimationMarkerReached>()
.register_type::<BlueprintAsset>()
.register_type::<Vec<BlueprintAsset>>()
.register_type::<Vec<String>>() .register_type::<Vec<String>>()
.register_type::<LocalAssets>()
.register_type::<BlueprintAssets>()
.register_type::<HashMap<String, Vec<String>>>() .register_type::<HashMap<String, Vec<String>>>()
.insert_resource(BluePrintsConfig { .insert_resource(BluePrintsConfig {
format: self.format,
library_folder: self.library_folder.clone(),
aabbs: self.aabbs, aabbs: self.aabbs,
aabb_cache: HashMap::new(), aabb_cache: HashMap::new(),
material_library: self.material_library, material_library: self.material_library,
material_library_folder: self.material_library_folder.clone(),
material_library_cache: HashMap::new(), material_library_cache: HashMap::new(),
}) })
.configure_sets( .configure_sets(
@ -144,17 +147,13 @@ impl Plugin for BlueprintsPlugin {
.add_systems( .add_systems(
Update, Update,
( (
test_thingy, (
check_for_loaded2,
spawn_from_blueprints2,
/*(
prepare_blueprints, prepare_blueprints,
check_for_loaded, check_for_loaded,
spawn_from_blueprints, spawn_from_blueprints,
apply_deferred, apply_deferred,
) )
.chain(),*/ .chain(),
(compute_scene_aabbs, apply_deferred) (compute_scene_aabbs, apply_deferred)
.chain() .chain()
.run_if(aabbs_enabled), .run_if(aabbs_enabled),
@ -171,18 +170,11 @@ impl Plugin for BlueprintsPlugin {
.in_set(GltfBlueprintsSet::Spawn), .in_set(GltfBlueprintsSet::Spawn),
) )
.add_systems( .add_systems(
Update, PostUpdate,
(spawned_blueprint_post_process, apply_deferred) (spawned_blueprint_post_process, apply_deferred)
.chain() .chain()
.in_set(GltfBlueprintsSet::AfterSpawn), .in_set(GltfBlueprintsSet::AfterSpawn)
) .before(VisibilitySystems::CheckVisibility),
/* .add_systems( );
Update,
(
trigger_instance_animation_markers_events,
trigger_blueprint_animation_markers_events,
),
)*/
;
} }
} }

View File

@ -17,14 +17,14 @@ use bevy::{
render::mesh::Mesh, render::mesh::Mesh,
}; };
use crate::{AssetLoadTracker, AssetsToLoad, BluePrintsConfig, BlueprintInstanceReady}; use crate::{AssetLoadTracker, AssetsToLoad, BluePrintsConfig};
#[derive(Component, Reflect, Default, Debug)] #[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)] #[reflect(Component)]
/// struct containing the name & path of the material to apply /// struct containing the name & source of the material to apply
pub struct MaterialInfo { pub struct MaterialInfo {
pub name: String, pub name: String,
pub path: String, pub source: String,
} }
/// flag component /// flag component
@ -42,8 +42,15 @@ pub(crate) fn materials_inject(
mut commands: Commands, mut commands: Commands,
) { ) {
for (entity, material_info) in material_infos.iter() { for (entity, material_info) in material_infos.iter() {
println!("Entity with material info {:?} {:?}", entity, material_info); let model_file_name = format!(
let material_full_path = format!("{}#{}", material_info.path, material_info.name); "{}_materials_library.{}",
&material_info.source, &blueprints_config.format
);
let materials_path = Path::new(&blueprints_config.material_library_folder)
.join(Path::new(model_file_name.as_str()));
let material_name = &material_info.name;
let material_full_path = materials_path.to_str().unwrap().to_string() + "#" + material_name; // TODO: yikes, cleanup
if blueprints_config if blueprints_config
.material_library_cache .material_library_cache
.contains_key(&material_full_path) .contains_key(&material_full_path)
@ -57,11 +64,10 @@ pub(crate) fn materials_inject(
.entity(entity) .entity(entity)
.insert(BlueprintMaterialAssetsLoaded); .insert(BlueprintMaterialAssetsLoaded);
} else { } else {
let material_file_handle = asset_server.load_untyped(&material_info.path.clone()); // : Handle<Gltf> let material_file_handle: Handle<Gltf> = asset_server.load(materials_path.clone());
let material_file_id = material_file_handle.id(); let material_file_id = material_file_handle.id();
let asset_infos: Vec<AssetLoadTracker<Gltf>> = vec![AssetLoadTracker {
let asset_infos: Vec<AssetLoadTracker> = vec![AssetLoadTracker { name: material_full_path,
name: material_info.name.clone(),
id: material_file_id, id: material_file_id,
loaded: false, loaded: false,
handle: material_file_handle.clone(), handle: material_file_handle.clone(),
@ -75,6 +81,7 @@ pub(crate) fn materials_inject(
..Default::default() ..Default::default()
}) })
.insert(BlueprintMaterialAssetsNotLoaded); .insert(BlueprintMaterialAssetsNotLoaded);
/**/
} }
} }
} }
@ -82,7 +89,7 @@ pub(crate) fn materials_inject(
// TODO, merge with check_for_loaded, make generic ? // TODO, merge with check_for_loaded, make generic ?
pub(crate) fn check_for_material_loaded( pub(crate) fn check_for_material_loaded(
mut blueprint_assets_to_load: Query< mut blueprint_assets_to_load: Query<
(Entity, &mut AssetsToLoad), (Entity, &mut AssetsToLoad<Gltf>),
With<BlueprintMaterialAssetsNotLoaded>, With<BlueprintMaterialAssetsNotLoaded>,
>, >,
asset_server: Res<AssetServer>, asset_server: Res<AssetServer>,
@ -139,7 +146,15 @@ pub(crate) fn materials_inject2(
mut commands: Commands, mut commands: Commands,
) { ) {
for (material_info, children) in material_infos.iter() { for (material_info, children) in material_infos.iter() {
let material_full_path = format!("{}#{}", material_info.path, material_info.name); let model_file_name = format!(
"{}_materials_library.{}",
&material_info.source, &blueprints_config.format
);
let materials_path = Path::new(&blueprints_config.material_library_folder)
.join(Path::new(model_file_name.as_str()));
let material_name = &material_info.name;
let material_full_path = materials_path.to_str().unwrap().to_string() + "#" + material_name; // TODO: yikes, cleanup
let mut material_found: Option<&Handle<StandardMaterial>> = None; let mut material_found: Option<&Handle<StandardMaterial>> = None;
if blueprints_config if blueprints_config
@ -153,17 +168,14 @@ pub(crate) fn materials_inject2(
.expect("we should have the material available"); .expect("we should have the material available");
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(materials_path.clone()); // FIXME: kinda weird now
let mat_gltf = assets_gltf let mat_gltf = assets_gltf
.get(model_handle.id()) .get(model_handle.id())
.expect("material should have been preloaded"); .expect("material should have been preloaded");
if mat_gltf if mat_gltf.named_materials.contains_key(material_name as &str) {
.named_materials
.contains_key(&material_info.name as &str)
{
let material = mat_gltf let material = mat_gltf
.named_materials .named_materials
.get(&material_info.name as &str) .get(material_name as &str)
.expect("this material should have been loaded"); .expect("this material should have been loaded");
blueprints_config blueprints_config
.material_library_cache .material_library_cache
@ -177,8 +189,8 @@ pub(crate) fn materials_inject2(
if with_materials_and_meshes.contains(*child) { if with_materials_and_meshes.contains(*child) {
debug!( debug!(
"injecting material {}, path: {:?}", "injecting material {}, path: {:?}",
material_info.name, material_name,
material_info.path.clone() materials_path.clone()
); );
commands.entity(*child).insert(material.clone()); commands.entity(*child).insert(material.clone());

View File

@ -1,11 +1,8 @@
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use bevy::{gltf::Gltf, prelude::*, utils::hashbrown::HashMap}; use bevy::{gltf::Gltf, prelude::*, utils::HashMap};
use crate::{ use crate::{Animations, BluePrintsConfig};
AssetLoadTracker, AssetsToLoad, BluePrintsConfig, BlueprintAnimations, BlueprintAssets,
BlueprintAssetsLoaded, BlueprintAssetsNotLoaded,
};
/// this is a flag component for our levels/game world /// this is a flag component for our levels/game world
#[derive(Component)] #[derive(Component)]
@ -16,11 +13,6 @@ pub struct GameWorldTag;
#[reflect(Component)] #[reflect(Component)]
pub struct BlueprintName(pub String); 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 /// flag component needed to signify the intent to spawn a Blueprint
#[derive(Component, Reflect, Default, Debug)] #[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)] #[reflect(Component)]
@ -30,12 +22,6 @@ pub struct SpawnHere;
/// flag component for dynamically spawned scenes /// flag component for dynamically spawned scenes
pub struct Spawned; 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)] #[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 ..unless the original entity was marked with the `NoInBlueprint` marker component
@ -60,63 +46,97 @@ pub struct AddToGameWorld;
/// helper component, just to transfer child data /// helper component, just to transfer child data
pub(crate) struct OriginalChildren(pub Vec<Entity>); pub(crate) struct OriginalChildren(pub Vec<Entity>);
pub(crate) fn test_thingy( /// helper component, is used to store the list of sub blueprints to enable automatic loading of dependend blueprints
spawn_placeholders: Query< #[derive(Component, Reflect, Default, Debug)]
(Entity, &BlueprintPath), #[reflect(Component)]
(Added<BlueprintPath>, Without<Spawned>, Without<SpawnHere>), pub struct BlueprintsList(pub HashMap<String, Vec<String>>);
>,
// before 0.14 we have to use a seperate query, after migrating we can query at the root level /// helper component, for tracking loaded assets's loading state, id , handle etc
entities_with_assets: Query< #[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, Entity,
/*&BlueprintName, &BlueprintName,
&BlueprintPath, Option<&Parent>,
Option<&Parent>,*/ Option<&Library>,
Option<&Name>, Option<&Name>,
Option<&BlueprintAssets>, Option<&BlueprintsList>,
), ),
(Added<BlueprintAssets>), // Added<BlueprintAssets> (Added<BlueprintName>, Added<SpawnHere>, Without<Spawned>),
>, >,
bla_bla: Query<
(Entity, &BlueprintName, &BlueprintPath, Option<&Parent>),
(Added<BlueprintPath>),
>,
mut commands: Commands, mut commands: Commands,
asset_server: Res<AssetServer>, asset_server: Res<AssetServer>,
blueprints_config: Res<BluePrintsConfig>,
) { ) {
for (entity, blueprint_path) in spawn_placeholders.iter() { for (entity, blupeprint_name, original_parent, library_override, name, blueprints_list) in
//println!("added blueprint_path {:?}", blueprint_path); spawn_placeholders.iter()
/*commands.entity(entity).insert( {
SceneBundle { debug!(
scene: asset_server.load(format!("{}#Scene0", &blueprint_path.0)), // "levels/World.glb#Scene0"), "requesting to spawn {:?} for entity {:?}, id: {:?}, parent:{:?}",
..default() blupeprint_name.0, name, entity, original_parent
},
);*/
// let model_handle: Handle<Gltf> = asset_server.load(model_path.clone());
}
for (entity, blueprint_name, blueprint_path, parent) in bla_bla.iter() {
println!(
"added blueprint to spawn {:?} {:?}",
blueprint_name, blueprint_path
); );
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![]; // 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(|| &blueprints_config.library_folder, |l| &l.0);
for (blueprint_name, _) in blueprints_list.0.iter() {
let model_file_name = format!("{}.{}", &blueprint_name, &blueprints_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 { if !loaded {
asset_infos.push(AssetLoadTracker { asset_infos.push(AssetLoadTracker {
name: blueprint_name.0.clone(), name: model_path.to_string_lossy().into(),
id: asset_id, id: model_id,
loaded: false, loaded: false,
handle: untyped_handle.clone(), handle: model_handle.clone(),
}); });
} }
}
// now insert load tracker // if not all assets are already loaded, inject a component to signal that we need them to be loaded
if !asset_infos.is_empty() { if !asset_infos.is_empty() {
commands commands
.entity(entity) .entity(entity)
@ -129,107 +149,57 @@ pub(crate) fn test_thingy(
} else { } else {
commands.entity(entity).insert(BlueprintAssetsLoaded); 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 { } else {
commands.entity(child_entity).insert(BlueprintAssetsLoaded); // in case there are no blueprintsList, we revert back to the old behaviour
} commands.entity(entity).insert(BlueprintAssetsLoaded);
} }
} }
} }
pub(crate) fn check_for_loaded2( pub(crate) fn check_for_loaded(
mut blueprint_assets_to_load: Query< mut blueprint_assets_to_load: Query<
(Entity, Option<&Name>, &mut AssetsToLoad), (Entity, &mut AssetsToLoad<Gltf>),
With<BlueprintAssetsNotLoaded>, With<BlueprintAssetsNotLoaded>,
>, >,
asset_server: Res<AssetServer>, asset_server: Res<AssetServer>,
mut commands: Commands, mut commands: Commands,
) { ) {
for (entity, entity_name, mut assets_to_load) in blueprint_assets_to_load.iter_mut() { for (entity, mut assets_to_load) in blueprint_assets_to_load.iter_mut() {
let mut all_loaded = true; let mut all_loaded = true;
let mut loaded_amount = 0; let mut loaded_amount = 0;
let total = assets_to_load.asset_infos.len(); let total = assets_to_load.asset_infos.len();
for tracker in assets_to_load.asset_infos.iter_mut() { for tracker in assets_to_load.asset_infos.iter_mut() {
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);
println!( tracker.loaded = loaded;
"loading {}: // load state: {:?}", if loaded {
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; loaded_amount += 1;
} else { } else {
all_loaded = false; all_loaded = false;
} }
} }
let progress: f32 = loaded_amount as f32 / total as f32; let progress: f32 = loaded_amount as f32 / total as f32;
println!("progress: {}", progress); // println!("progress: {}",progress);
assets_to_load.progress = progress; assets_to_load.progress = progress;
if all_loaded { if all_loaded {
assets_to_load.all_loaded = true; assets_to_load.all_loaded = true;
println!("done with loading {:?}, inserting components", entity_name);
commands commands
.entity(entity) .entity(entity)
.insert(BlueprintAssetsLoaded) .insert(BlueprintAssetsLoaded)
.remove::<BlueprintAssetsNotLoaded>() .remove::<BlueprintAssetsNotLoaded>();
.remove::<AssetsToLoad>();
} }
} }
} }
pub(crate) fn spawn_from_blueprints2( pub(crate) fn spawn_from_blueprints(
spawn_placeholders: Query< spawn_placeholders: Query<
( (
Entity, Entity,
&BlueprintName, &BlueprintName,
&BlueprintPath,
Option<&Transform>, Option<&Transform>,
Option<&Parent>, Option<&Parent>,
Option<&Library>,
Option<&AddToGameWorld>, Option<&AddToGameWorld>,
Option<&Name>, Option<&Name>,
), ),
@ -244,25 +214,44 @@ pub(crate) fn spawn_from_blueprints2(
mut game_world: Query<Entity, With<GameWorldTag>>, mut game_world: Query<Entity, With<GameWorldTag>>,
assets_gltf: Res<Assets<Gltf>>, assets_gltf: Res<Assets<Gltf>>,
mut graphs: ResMut<Assets<AnimationGraph>>,
asset_server: Res<AssetServer>, asset_server: Res<AssetServer>,
blueprints_config: Res<BluePrintsConfig>, blueprints_config: Res<BluePrintsConfig>,
children: Query<&Children>, children: Query<&Children>,
) { ) {
for (entity, blupeprint_name, blueprint_path, transform, original_parent, add_to_world, name) in for (
spawn_placeholders.iter() entity,
blupeprint_name,
transform,
original_parent,
library_override,
add_to_world,
name,
) in spawn_placeholders.iter()
{ {
info!( debug!(
"attempting to spawn blueprint {:?} for entity {:?}, id: {:?}, parent:{:?}", "attempting to spawn {:?} for entity {:?}, id: {:?}, parent:{:?}",
blupeprint_name.0, name, entity, original_parent blupeprint_name.0, name, entity, original_parent
); );
// info!("attempting to spawn {:?}", model_path); let what = &blupeprint_name.0;
let model_handle: Handle<Gltf> = asset_server.load(blueprint_path.0.clone()); // FIXME: kinda weird now let model_file_name = format!("{}.{}", &what, &blueprints_config.format);
let gltf = assets_gltf // library path is either defined at the plugin level or overriden by optional Library components
.get(&model_handle) let library_path =
.unwrap_or_else(|| panic!("gltf file {:?} should have been loaded", &blueprint_path.0)); library_override.map_or_else(|| &blueprints_config.library_folder, |l| &l.0);
let model_path = Path::new(&library_path).join(Path::new(model_file_name.as_str()));
// 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 // 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 let main_scene_name = gltf
@ -286,10 +275,16 @@ pub(crate) fn spawn_from_blueprints2(
} }
} }
let mut graph = AnimationGraph::new();
let mut named_animations: HashMap<String, Handle<AnimationClip>> = HashMap::new(); let mut named_animations: HashMap<String, Handle<AnimationClip>> = HashMap::new();
for (key, value) in gltf.named_animations.iter() { let mut named_indices: HashMap<String, AnimationNodeIndex> = HashMap::new();
named_animations.insert(key.to_string(), value.clone());
for (key, clip) in gltf.named_animations.iter() {
named_animations.insert(key.to_string(), clip.clone());
let animation_index = graph.add_clip(clip.clone(), 1.0, graph.root);
named_indices.insert(key.to_string(), animation_index);
} }
let graph = graphs.add(graph);
commands.entity(entity).insert(( commands.entity(entity).insert((
SceneBundle { SceneBundle {
@ -297,13 +292,13 @@ pub(crate) fn spawn_from_blueprints2(
transform: transforms, transform: transforms,
..Default::default() ..Default::default()
}, },
Spawned, Animations {
BlueprintInstanceReady, // FIXME: not sure if this is should be added here or in the post process named_animations,
OriginalChildren(original_children), named_indices,
BlueprintAnimations { graph
// these are animations specific to the inside of the blueprint
named_animations: named_animations, //gltf.named_animations.clone(),
}, },
Spawned,
OriginalChildren(original_children),
)); ));
if add_to_world.is_some() { if add_to_world.is_some() {

View File

@ -5,7 +5,7 @@ use bevy::prelude::*;
use bevy::scene::SceneInstance; use bevy::scene::SceneInstance;
// use bevy::utils::hashbrown::HashSet; // use bevy::utils::hashbrown::HashSet;
use super::{BlueprintAnimationPlayerLink, BlueprintAnimations}; use super::{AnimationPlayerLink, Animations};
use super::{SpawnHere, Spawned}; use super::{SpawnHere, Spawned};
use crate::{ use crate::{
AssetsToLoad, BlueprintAssetsLoaded, CopyComponents, InBlueprint, NoInBlueprint, AssetsToLoad, BlueprintAssetsLoaded, CopyComponents, InBlueprint, NoInBlueprint,
@ -24,7 +24,7 @@ pub(crate) fn spawned_blueprint_post_process(
Entity, Entity,
&Children, &Children,
&OriginalChildren, &OriginalChildren,
&BlueprintAnimations, &Animations,
Option<&NoInBlueprint>, Option<&NoInBlueprint>,
Option<&Name>, Option<&Name>,
), ),
@ -38,7 +38,7 @@ pub(crate) fn spawned_blueprint_post_process(
for (original, children, original_children, animations, no_inblueprint, name) in for (original, children, original_children, animations, no_inblueprint, name) in
unprocessed_entities.iter() unprocessed_entities.iter()
{ {
info!("post processing blueprint for entity {:?}", name); debug!("post processing blueprint for entity {:?}", name);
if children.len() == 0 { if children.len() == 0 {
warn!("timing issue ! no children found, please restart your bevy app (bug being investigated)"); warn!("timing issue ! no children found, please restart your bevy app (bug being investigated)");
@ -85,19 +85,16 @@ pub(crate) fn spawned_blueprint_post_process(
// FIXME: stopgap solution: since we cannot use an AnimationPlayer at the root entity level // 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, // 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 // BUT we still want to have some marker/control at the root entity level, we add this
commands commands.entity(original).insert(AnimationPlayerLink(added));
.entity(original)
.insert(BlueprintAnimationPlayerLink(added));
} }
} }
} }
commands.entity(original).remove::<SpawnHere>(); commands.entity(original).remove::<SpawnHere>();
commands.entity(original).remove::<Spawned>(); 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::<Handle<Scene>>();
//commands.entity(original).remove::<AssetsToLoad>(); // 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::<AssetsToLoad<Gltf>>(); // 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(original).remove::<BlueprintAssetsLoaded>();
commands.entity(root_entity).despawn_recursive(); commands.entity(root_entity).despawn_recursive();
info!("DONE WITH POST PROCESS");
} }
} }

View File

@ -14,9 +14,9 @@ license = "MIT OR Apache-2.0"
workspace = true workspace = true
[dependencies] [dependencies]
bevy = { version = "0.14.0-rc.3", default-features = false, features = ["bevy_asset", "bevy_scene", "bevy_gltf"] } bevy = { version = "0.14", default-features = false, features = ["bevy_asset", "bevy_scene", "bevy_gltf"] }
serde = "1.0.188" serde = "1.0.188"
ron = "0.8.1" ron = "0.8.1"
[dev-dependencies] [dev-dependencies]
bevy = { version = "0.14.0-rc.3", default-features = false, features = ["dynamic_linking"] } bevy = { version = "0.14", default-features = false, features = ["dynamic_linking"] }

View File

@ -4,7 +4,9 @@
[![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)
# bevy_gltf_components # bevy_gltf_components (deprecated in favor of Blenvy)
> bevy_gltf_components has been deprecated in favor of its successor [Blenvy](https://crates.io/crates/blenvy), part of the [Blenvy project](https://github.com/kaosat-dev/Blenvy). No further development or maintenance will be done for Bevy bevy_gltf_components. See [#194](https://github.com/kaosat-dev/Blenvy/issues/194) for background.
This crate allows you to define [Bevy](https://bevyengine.org/) components direclty inside gltf files and instanciate the components on the Bevy side. This crate allows you to define [Bevy](https://bevyengine.org/) components direclty inside gltf files and instanciate the components on the Bevy side.
@ -23,8 +25,8 @@ Here's a minimal usage example:
```toml ```toml
# Cargo.toml # Cargo.toml
[dependencies] [dependencies]
bevy="0.13" bevy="0.14"
bevy_gltf_components = { version = "0.5"} bevy_gltf_components = { version = "0.6"}
``` ```
@ -60,7 +62,7 @@ bevy_gltf_components = { version = "0.5"}
Add the following to your `[dependencies]` section in `Cargo.toml`: Add the following to your `[dependencies]` section in `Cargo.toml`:
```toml ```toml
bevy_gltf_components = "0.5" bevy_gltf_components = "0.6"
``` ```
Or use `cargo add`: Or use `cargo add`:
@ -78,78 +80,17 @@ Use the default configuration:
ComponentsFromGltfPlugin::default() ComponentsFromGltfPlugin::default()
``` ```
As of v0.6 Legacy mode has been removed , you can emulate it using a system that should run BEFORE bevy_gltf_components Or disable the legacy mode: (enabled by default)
```rust no run
if simplified_types {
if let TypeInfo::TupleStruct(info) = type_registration.type_info() {
// we handle tupple strucs with only one field differently, as Blender's custom properties with custom ui (float, int, bool, etc) always give us a tupple struct
if info.field_len() == 1 {
let field = info
.field_at(0)
.expect("we should always have at least one field here");
let field_name = field.type_path();
let mut formated = parsed_value.clone();
match field_name {
"f32" => {
formated = parsed_value.parse::<f32>().unwrap().to_string();
}
"f64" => {
formated = parsed_value.parse::<f64>().unwrap().to_string();
}
"u8" => {
formated = parsed_value.parse::<u8>().unwrap().to_string();
}
"u16" => {
formated = parsed_value.parse::<u16>().unwrap().to_string();
}
"u32" => {
formated = parsed_value.parse::<u32>().unwrap().to_string();
}
"u64" => {
formated = parsed_value.parse::<u64>().unwrap().to_string();
}
"u128" => {
formated = parsed_value.parse::<u128>().unwrap().to_string();
}
"glam::Vec2" => {
let parsed: Vec<f32> = ron::from_str(&parsed_value).unwrap();
formated = format!("(x:{},y:{})", parsed[0], parsed[1]);
}
"glam::Vec3" => {
let parsed: Vec<f32> = ron::from_str(&parsed_value).unwrap();
formated =
format!("(x:{},y:{},z:{})", parsed[0], parsed[1], parsed[2]);
}
"bevy_render::color::Color" => {
let parsed: Vec<f32> = ron::from_str(&parsed_value).unwrap();
if parsed.len() == 3 {
formated = format!(
"Rgba(red:{},green:{},blue:{}, alpha: 1.0)",
parsed[0], parsed[1], parsed[2]
);
}
if parsed.len() == 4 {
formated = format!(
"Rgba(red:{},green:{},blue:{}, alpha:{})",
parsed[0], parsed[1], parsed[2], parsed[3]
);
}
}
_ => {}
}
parsed_value = format!("({formated})");
}
}
if parsed_value.is_empty() {
parsed_value = "()".to_string();
}
}
```rust no_run
ComponentsFromGltfPlugin{legacy_mode: false}
``` ```
You **need** to disable legacy mode if you want to use the [```bevy_components```](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/tools/bevy_components) Blender addon + the [```bevy_registry_export crate```](https://crates.io/crates/bevy_registry_export) !
As it create custom properties that are writen in real **ron** file format
instead of a simplified version (the one in the legacy mode)
> Note: the legacy mode support will be dropped in future versions, and the default behaviour will be NO legacy mode
## SystemSet ## SystemSet
@ -188,6 +129,7 @@ The main branch is compatible with the latest Bevy release, while the branch `be
Compatibility of `bevy_gltf_components` versions: Compatibility of `bevy_gltf_components` versions:
| `bevy_gltf_components` | `bevy` | | `bevy_gltf_components` | `bevy` |
| :-- | :-- | | :-- | :-- |
| `0.6` | `0.14` |
| `0.5` | `0.13` | | `0.5` | `0.13` |
| `0.2 - 0.4` | `0.12` | | `0.2 - 0.4` | `0.12` |
| `0.1` | `0.11` | | `0.1` | `0.11` |

View File

@ -10,7 +10,13 @@ pub use process_gltfs::*;
pub mod blender_settings; pub mod blender_settings;
use bevy::{ use bevy::{
ecs::{component::Component, reflect::ReflectComponent, system::Resource}, app::Startup,
ecs::{
component::Component,
reflect::ReflectComponent,
system::{Res, Resource},
},
log::warn,
prelude::{App, IntoSystemConfigs, Plugin, SystemSet, Update}, prelude::{App, IntoSystemConfigs, Plugin, SystemSet, Update},
reflect::Reflect, reflect::Reflect,
}; };
@ -60,16 +66,34 @@ pub enum GltfComponentsSet {
} }
#[derive(Clone, Resource)] #[derive(Clone, Resource)]
pub struct GltfComponentsConfig {} pub struct GltfComponentsConfig {
pub(crate) legacy_mode: bool,
}
#[derive(Default)] pub struct ComponentsFromGltfPlugin {
pub struct ComponentsFromGltfPlugin {} pub legacy_mode: bool,
}
impl Default for ComponentsFromGltfPlugin {
fn default() -> Self {
Self { legacy_mode: true }
}
}
fn check_for_legacy_mode(gltf_components_config: Res<GltfComponentsConfig>) {
if gltf_components_config.legacy_mode {
warn!("using simplified component definitions is deprecated since 0.3, prefer defining components with real ron values (use the bevy_components tool for Blender for simplicity) ");
}
}
impl Plugin for ComponentsFromGltfPlugin { impl Plugin for ComponentsFromGltfPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_plugins(blender_settings::plugin) app.add_plugins(blender_settings::plugin)
.register_type::<GltfProcessed>() .register_type::<GltfProcessed>()
.insert_resource(GltfComponentsConfig {}) .insert_resource(GltfComponentsConfig {
legacy_mode: self.legacy_mode,
})
.add_systems(Startup, check_for_legacy_mode)
.add_systems( .add_systems(
Update, Update,
(add_components_from_gltf_extras).in_set(GltfComponentsSet::Injection), (add_components_from_gltf_extras).in_set(GltfComponentsSet::Injection),

View File

@ -6,23 +6,39 @@ use bevy::{
reflect::{AppTypeRegistry, ReflectComponent}, reflect::{AppTypeRegistry, ReflectComponent},
world::World, world::World,
}, },
gltf::{GltfExtras, GltfMaterialExtras, GltfMeshExtras, GltfSceneExtras}, gltf::GltfExtras,
hierarchy::Parent, hierarchy::Parent,
log::debug, log::debug,
reflect::{Reflect, TypeRegistration}, reflect::{Reflect, TypeRegistration},
utils::HashMap, utils::HashMap,
}; };
use crate::{ronstring_to_reflect_component, GltfProcessed}; use crate::{ronstring_to_reflect_component, GltfComponentsConfig, GltfProcessed};
/// main function: injects components into each entity in gltf files that have `gltf_extras`, using reflection
pub fn add_components_from_gltf_extras(world: &mut World) {
let mut extras =
world.query_filtered::<(Entity, &Name, &GltfExtras, &Parent), (Added<GltfExtras>, Without<GltfProcessed>)>();
let mut entity_components: HashMap<Entity, Vec<(Box<dyn Reflect>, TypeRegistration)>> =
HashMap::new();
let gltf_components_config = world.resource::<GltfComponentsConfig>();
for (entity, name, extra, parent) in extras.iter(world) {
debug!(
"Name: {}, entity {:?}, parent: {:?}, extras {:?}",
name, entity, parent, extra
);
let type_registry: &AppTypeRegistry = world.resource();
let type_registry = type_registry.read();
let reflect_components = ronstring_to_reflect_component(
&extra.value,
&type_registry,
gltf_components_config.legacy_mode,
);
// , mut entity_components: HashMap<Entity, Vec<(Box<dyn Reflect>, TypeRegistration)>>
fn find_entity_components(
entity: Entity,
name: &Name,
parent: &Parent,
reflect_components: Vec<(Box<dyn Reflect>, TypeRegistration)>,
entity_components: &HashMap<Entity, Vec<(Box<dyn Reflect>, TypeRegistration)>>,
) -> (Entity, Vec<(Box<dyn Reflect>, TypeRegistration)>) {
// we assign the components specified /xxx_components objects to their parent node // we assign the components specified /xxx_components objects to their parent node
let mut target_entity = entity; let mut target_entity = entity;
// if the node contains "components" or ends with "_pa" (ie add to parent), the components will not be added to the entity itself but to its parent // if the node contains "components" or ends with "_pa" (ie add to parent), the components will not be added to the entity itself but to its parent
@ -46,86 +62,10 @@ fn find_entity_components(
for (component, type_registration) in reflect_components { for (component, type_registration) in reflect_components {
updated_components.push((component.clone_value(), type_registration)); updated_components.push((component.clone_value(), type_registration));
} }
return (target_entity, updated_components); entity_components.insert(target_entity, updated_components);
//entity_components.insert(target_entity, updated_components);
} else { } else {
return (target_entity, reflect_components); entity_components.insert(target_entity, reflect_components);
// entity_components.insert(target_entity, reflect_components);
} }
}
/// main function: injects components into each entity in gltf files that have `gltf_extras`, using reflection
pub fn add_components_from_gltf_extras(world: &mut World) {
let mut extras =
world.query_filtered::<(Entity, &Name, &GltfExtras, &Parent), (Added<GltfExtras>, Without<GltfProcessed>)>();
let mut scene_extras = world.query_filtered::<(Entity, &Name, &GltfSceneExtras, &Parent), (Added<GltfSceneExtras>, Without<GltfProcessed>)>();
let mut mesh_extras = world.query_filtered::<(Entity, &Name, &GltfMeshExtras, &Parent), (Added<GltfMeshExtras>, Without<GltfProcessed>)>();
let mut material_extras = world.query_filtered::<(Entity, &Name, &GltfMaterialExtras, &Parent), (Added<GltfMaterialExtras>, Without<GltfProcessed>)>();
let mut entity_components: HashMap<Entity, Vec<(Box<dyn Reflect>, TypeRegistration)>> =
HashMap::new();
// let gltf_components_config = world.resource::<GltfComponentsConfig>();
for (entity, name, extra, parent) in extras.iter(world) {
debug!(
"Name: {}, entity {:?}, parent: {:?}, extras {:?}",
name, entity, parent, extra
);
let type_registry: &AppTypeRegistry = world.resource();
let type_registry = type_registry.read();
let reflect_components = ronstring_to_reflect_component(&extra.value, &type_registry);
let (target_entity, updated_components) =
find_entity_components(entity, name, parent, reflect_components, &entity_components);
entity_components.insert(target_entity, updated_components);
}
for (entity, name, extra, parent) in scene_extras.iter(world) {
debug!(
"Name: {}, entity {:?}, parent: {:?}, scene_extras {:?}",
name, entity, parent, extra
);
let type_registry: &AppTypeRegistry = world.resource();
let type_registry = type_registry.read();
let reflect_components = ronstring_to_reflect_component(&extra.value, &type_registry);
let (target_entity, updated_components) =
find_entity_components(entity, name, parent, reflect_components, &entity_components);
entity_components.insert(target_entity, updated_components);
}
for (entity, name, extra, parent) in mesh_extras.iter(world) {
debug!(
"Name: {}, entity {:?}, parent: {:?}, mesh_extras {:?}",
name, entity, parent, extra
);
let type_registry: &AppTypeRegistry = world.resource();
let type_registry = type_registry.read();
let reflect_components = ronstring_to_reflect_component(&extra.value, &type_registry);
let (target_entity, updated_components) =
find_entity_components(entity, name, parent, reflect_components, &entity_components);
entity_components.insert(target_entity, updated_components);
}
for (entity, name, extra, parent) in material_extras.iter(world) {
debug!(
"Name: {}, entity {:?}, parent: {:?}, material_extras {:?}",
name, entity, parent, extra
);
let type_registry: &AppTypeRegistry = world.resource();
let type_registry = type_registry.read();
let reflect_components = ronstring_to_reflect_component(&extra.value, &type_registry);
let (target_entity, updated_components) =
find_entity_components(entity, name, parent, reflect_components, &entity_components);
entity_components.insert(target_entity, updated_components);
} }
for (entity, components) in entity_components { for (entity, components) in entity_components {

View File

@ -1,6 +1,6 @@
use bevy::log::{debug, warn}; use bevy::log::{debug, warn};
use bevy::reflect::serde::ReflectDeserializer; use bevy::reflect::serde::ReflectDeserializer;
use bevy::reflect::{Reflect, TypeRegistration, TypeRegistry}; use bevy::reflect::{Reflect, TypeInfo, TypeRegistration, TypeRegistry};
use bevy::utils::HashMap; use bevy::utils::HashMap;
use ron::Value; use ron::Value;
use serde::de::DeserializeSeed; use serde::de::DeserializeSeed;
@ -10,46 +10,92 @@ use super::capitalize_first_letter;
pub fn ronstring_to_reflect_component( pub fn ronstring_to_reflect_component(
ron_string: &str, ron_string: &str,
type_registry: &TypeRegistry, type_registry: &TypeRegistry,
simplified_types: bool,
) -> Vec<(Box<dyn Reflect>, TypeRegistration)> { ) -> Vec<(Box<dyn Reflect>, TypeRegistration)> {
let lookup: HashMap<String, Value> = ron::from_str(ron_string).unwrap(); let lookup: HashMap<String, Value> = ron::from_str(ron_string).unwrap();
let mut components: Vec<(Box<dyn Reflect>, TypeRegistration)> = Vec::new(); let mut components: Vec<(Box<dyn Reflect>, TypeRegistration)> = Vec::new();
// println!("ron_string {:?}", ron_string); for (key, value) in lookup.into_iter() {
for (name, value) in lookup.into_iter() { let type_string = key.replace("component: ", "").trim().to_string();
let parsed_value: String = match value.clone() {
Value::String(str) => str,
_ => ron::to_string(&value).unwrap().to_string(),
};
if name.as_str() == "bevy_components" {
bevy_components_string_to_components(parsed_value, type_registry, &mut components);
} else {
components_string_to_components(
name,
value,
parsed_value,
type_registry,
&mut components,
);
}
}
components
}
fn components_string_to_components(
name: String,
value: Value,
parsed_value: String,
type_registry: &TypeRegistry,
components: &mut Vec<(Box<dyn Reflect>, TypeRegistration)>,
) {
let type_string = name.replace("component: ", "").trim().to_string();
let capitalized_type_name = capitalize_first_letter(type_string.as_str()); let capitalized_type_name = capitalize_first_letter(type_string.as_str());
let mut parsed_value: String;
match value.clone() {
Value::String(str) => {
parsed_value = str;
}
_ => parsed_value = ron::to_string(&value).unwrap().to_string(),
}
if let Some(type_registration) = if let Some(type_registration) =
type_registry.get_with_short_type_path(capitalized_type_name.as_str()) type_registry.get_with_short_type_path(capitalized_type_name.as_str())
{ {
debug!("TYPE INFO {:?}", type_registration.type_info()); debug!("TYPE INFO {:?}", type_registration.type_info());
if simplified_types {
if let TypeInfo::TupleStruct(info) = type_registration.type_info() {
// we handle tupple strucs with only one field differently, as Blender's custom properties with custom ui (float, int, bool, etc) always give us a tupple struct
if info.field_len() == 1 {
let field = info
.field_at(0)
.expect("we should always have at least one field here");
let field_name = field.type_path();
let mut formated = parsed_value.clone();
match field_name {
"f32" => {
formated = parsed_value.parse::<f32>().unwrap().to_string();
}
"f64" => {
formated = parsed_value.parse::<f64>().unwrap().to_string();
}
"u8" => {
formated = parsed_value.parse::<u8>().unwrap().to_string();
}
"u16" => {
formated = parsed_value.parse::<u16>().unwrap().to_string();
}
"u32" => {
formated = parsed_value.parse::<u32>().unwrap().to_string();
}
"u64" => {
formated = parsed_value.parse::<u64>().unwrap().to_string();
}
"u128" => {
formated = parsed_value.parse::<u128>().unwrap().to_string();
}
"glam::Vec2" => {
let parsed: Vec<f32> = ron::from_str(&parsed_value).unwrap();
formated = format!("(x:{},y:{})", parsed[0], parsed[1]);
}
"glam::Vec3" => {
let parsed: Vec<f32> = ron::from_str(&parsed_value).unwrap();
formated =
format!("(x:{},y:{},z:{})", parsed[0], parsed[1], parsed[2]);
}
"bevy_render::color::Color" => {
let parsed: Vec<f32> = ron::from_str(&parsed_value).unwrap();
if parsed.len() == 3 {
formated = format!(
"Rgba(red:{},green:{},blue:{}, alpha: 1.0)",
parsed[0], parsed[1], parsed[2]
);
}
if parsed.len() == 4 {
formated = format!(
"Rgba(red:{},green:{},blue:{}, alpha:{})",
parsed[0], parsed[1], parsed[2], parsed[3]
);
}
}
_ => {}
}
parsed_value = format!("({formated})");
}
}
if parsed_value.is_empty() {
parsed_value = "()".to_string();
}
}
let ron_string = format!( let ron_string = format!(
"{{ \"{}\":{} }}", "{{ \"{}\":{} }}",
type_registration.type_info().type_path(), type_registration.type_info().type_path(),
@ -72,7 +118,7 @@ fn components_string_to_components(
.unwrap_or_else(|_| { .unwrap_or_else(|_| {
panic!( panic!(
"failed to deserialize component {} with value: {:?}", "failed to deserialize component {} with value: {:?}",
name, value key, value
) )
}); });
@ -83,48 +129,6 @@ fn components_string_to_components(
} else { } else {
warn!("no type registration for {}", capitalized_type_name); warn!("no type registration for {}", capitalized_type_name);
} }
}
fn bevy_components_string_to_components(
parsed_value: String,
type_registry: &TypeRegistry,
components: &mut Vec<(Box<dyn Reflect>, TypeRegistration)>,
) {
let lookup: HashMap<String, Value> = ron::from_str(&parsed_value).unwrap();
for (key, value) in lookup.into_iter() {
let parsed_value: String = match value.clone() {
Value::String(str) => str,
_ => ron::to_string(&value).unwrap().to_string(),
};
if let Some(type_registration) = type_registry.get_with_type_path(key.as_str()) {
debug!("TYPE INFO {:?}", type_registration.type_info());
let ron_string = format!(
"{{ \"{}\":{} }}",
type_registration.type_info().type_path(),
parsed_value
);
debug!("component data ron string {}", ron_string);
let mut deserializer = ron::Deserializer::from_str(ron_string.as_str())
.expect("deserialzer should have been generated from string");
let reflect_deserializer = ReflectDeserializer::new(type_registry);
let component = reflect_deserializer
.deserialize(&mut deserializer)
.unwrap_or_else(|_| {
panic!(
"failed to deserialize component {} with value: {:?}",
key, value
)
});
debug!("component {:?}", component);
debug!("real type {:?}", component.get_represented_type_info());
components.push((component, type_registration.clone()));
debug!("found type registration for {}", key);
} else {
warn!("no type registration for {}", key);
}
} }
components
} }

View File

@ -1,6 +1,6 @@
[package] [package]
name = "bevy_gltf_save_load" name = "bevy_gltf_save_load"
version = "0.4.1" version = "0.5.0"
authors = ["Mark 'kaosat-dev' Moissette"] authors = ["Mark 'kaosat-dev' Moissette"]
description = "Save & load your bevy games" description = "Save & load your bevy games"
homepage = "https://github.com/kaosat-dev/Blender_bevy_components_workflow" homepage = "https://github.com/kaosat-dev/Blender_bevy_components_workflow"
@ -14,8 +14,8 @@ license = "MIT OR Apache-2.0"
workspace = true workspace = true
[dependencies] [dependencies]
bevy = { version = "0.14.0-rc.3", default-features = false, features = ["bevy_asset", "bevy_scene", "bevy_gltf"] } bevy = { version = "0.14", default-features = false, features = ["bevy_asset", "bevy_scene", "bevy_gltf"] }
bevy_gltf_blueprints = { version = "0.11", path = "../bevy_gltf_blueprints" } bevy_gltf_blueprints = { version = "0.11", path = "../bevy_gltf_blueprints" }
[dev-dependencies] [dev-dependencies]
bevy = { version = "0.14.0-rc.3", default-features = false, features = ["dynamic_linking"] } bevy = { version = "0.14", default-features = false, features = ["dynamic_linking"] }

View File

@ -3,7 +3,9 @@
[![License](https://img.shields.io/crates/l/bevy_gltf_save_load)](https://github.com/kaosat-dev/Blender_bevy_components_workflow/blob/main/crates/bevy_gltf_save_load/License.md) [![License](https://img.shields.io/crates/l/bevy_gltf_save_load)](https://github.com/kaosat-dev/Blender_bevy_components_workflow/blob/main/crates/bevy_gltf_save_load/License.md)
[![Bevy tracking](https://img.shields.io/badge/Bevy%20tracking-released%20version-lightblue)](https://github.com/bevyengine/bevy/blob/main/docs/plugins_guidelines.md#main-branch-tracking) [![Bevy tracking](https://img.shields.io/badge/Bevy%20tracking-released%20version-lightblue)](https://github.com/bevyengine/bevy/blob/main/docs/plugins_guidelines.md#main-branch-tracking)
# bevy_gltf_save_load # bevy_gltf_save_load (deprecated in favor of Blenvy)
> bevy_gltf_save_load has been deprecated in favor of its successor [Blenvy](https://crates.io/crates/blenvy), part of the [Blenvy project](https://github.com/kaosat-dev/Blenvy). No further development or maintenance will be done for Bevy bevy_gltf_save_load. See [#194](https://github.com/kaosat-dev/Blenvy/issues/194) for background.
Built upon [bevy_gltf_blueprints](https://crates.io/crates/bevy_gltf_blueprints) this crate adds the ability to easilly **save** and **load** your game worlds for [Bevy](https://bevyengine.org/) . Built upon [bevy_gltf_blueprints](https://crates.io/crates/bevy_gltf_blueprints) this crate adds the ability to easilly **save** and **load** your game worlds for [Bevy](https://bevyengine.org/) .
@ -34,9 +36,9 @@ Here's a minimal usage example:
```toml ```toml
# Cargo.toml # Cargo.toml
[dependencies] [dependencies]
bevy="0.13" bevy="0.14"
bevy_gltf_save_load = "0.4" bevy_gltf_save_load = "0.5"
bevy_gltf_blueprints = "0.10" // also needed bevy_gltf_blueprints = "0.11" // also needed
``` ```
```rust no_run ```rust no_run
@ -191,6 +193,7 @@ fn main() {
}, },
// you need to configure the blueprints plugin as well (might be pre_configured in the future, but for now you need to do it manually) // you need to configure the blueprints plugin as well (might be pre_configured in the future, but for now you need to do it manually)
BlueprintsPlugin { BlueprintsPlugin {
library_folder: "models/library".into(),
format: GltfFormat::GLB, format: GltfFormat::GLB,
aabbs: true, aabbs: true,
..Default::default() ..Default::default()
@ -297,6 +300,7 @@ The main branch is compatible with the latest Bevy release, while the branch `be
Compatibility of `bevy_gltf_save_load` versions: Compatibility of `bevy_gltf_save_load` versions:
| `bevy_gltf_save_load` | `bevy` | | `bevy_gltf_save_load` | `bevy` |
| :-- | :-- | | :-- | :-- |
| `0.5 ` | `0.14` |
| `0.4 ` | `0.13` | | `0.4 ` | `0.13` |
| `0.1 -0.3` | `0.12` | | `0.1 -0.3` | `0.12` |
| branch `main` | `0.12` | | branch `main` | `0.12` |

View File

@ -1,6 +1,6 @@
[package] [package]
name = "bevy_registry_export" name = "bevy_registry_export"
version = "0.3.1" version = "0.4.0"
authors = ["Mark 'kaosat-dev' Moissette", "Pascal 'Killercup' Hertleif"] authors = ["Mark 'kaosat-dev' Moissette", "Pascal 'Killercup' Hertleif"]
description = "Allows you to create a Json export of all your components/ registered types of your Bevy app/game" description = "Allows you to create a Json export of all your components/ registered types of your Bevy app/game"
homepage = "https://github.com/kaosat-dev/Blender_bevy_components_workflow" homepage = "https://github.com/kaosat-dev/Blender_bevy_components_workflow"
@ -11,11 +11,11 @@ edition = "2021"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
[dependencies] [dependencies]
bevy = { version = "0.14.0-rc.3", default-features = false, features = ["bevy_scene"] } bevy = { version = "0.14", default-features = false, features = ["bevy_scene"] }
bevy_reflect = { version = "0.14.0-rc.3", default-features = false } bevy_reflect = { version = "0.14", default-features = false }
bevy_app = { version = "0.14.0-rc.3", default-features = false, features = ["bevy_reflect"] } bevy_app = { version = "0.14", default-features = false, features = ["bevy_reflect"] }
bevy_ecs = { version = "0.14.0-rc.3", default-features = false, features = ["bevy_reflect"] } bevy_ecs = { version = "0.14", default-features = false, features = ["bevy_reflect"] }
serde_json = "1.0.108" serde_json = "1.0.108"
[dev-dependencies] [dev-dependencies]
bevy = { version = "0.14.0-rc.3", default-features = false, features = ["dynamic_linking"] } bevy = { version = "0.14", default-features = false, features = ["dynamic_linking"] }

View File

@ -3,10 +3,13 @@
[![License](https://img.shields.io/crates/l/bevy_registry_export)](https://github.com/kaosat-dev/Blender_bevy_components_workflow/blob/main/crates/bevy_registry_export/License.md) [![License](https://img.shields.io/crates/l/bevy_registry_export)](https://github.com/kaosat-dev/Blender_bevy_components_workflow/blob/main/crates/bevy_registry_export/License.md)
[![Bevy tracking](https://img.shields.io/badge/Bevy%20tracking-released%20version-lightblue)](https://github.com/bevyengine/bevy/blob/main/docs/plugins_guidelines.md#main-branch-tracking) [![Bevy tracking](https://img.shields.io/badge/Bevy%20tracking-released%20version-lightblue)](https://github.com/bevyengine/bevy/blob/main/docs/plugins_guidelines.md#main-branch-tracking)
# bevy_registry_export # bevy_registry_export (deprecated in favor of Blenvy)
> bevy_registry_export has been deprecated in favor of its successor [Blenvy](https://crates.io/crates/blenvy), part of the [Blenvy project](https://github.com/kaosat-dev/Blenvy). No further development or maintenance will be done for Bevy bevy_registry_export. See [#194](https://github.com/kaosat-dev/Blenvy/issues/194) for background.
This plugin allows you to create a Json export of all your components/ registered types. This plugin allows you to create a Json export of all your components/ registered types.
Its main use case is as a backbone for the [```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 Its main use case is as a backbone for the [```bevy_components``` Blender add-on](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/tools/bevy_components), that allows you to add & edit components directly in Blender, using the actual type definitions from Bevy
(and any of your custom types & components that you register in Bevy). (and any of your custom types & components that you register in Bevy).
@ -17,8 +20,8 @@ Here's a minimal usage example:
```toml ```toml
# Cargo.toml # Cargo.toml
[dependencies] [dependencies]
bevy="0.13" bevy="0.14"
bevy_registry_export = "0.3" bevy_registry_export = "0.4"
``` ```
```rust no_run ```rust no_run
@ -44,7 +47,7 @@ take a look at the [example](https://github.com/kaosat-dev/Blender_bevy_componen
Add the following to your `[dependencies]` section in `Cargo.toml`: Add the following to your `[dependencies]` section in `Cargo.toml`:
```toml ```toml
bevy_registry_export = "0.3" bevy_registry_export = "0.4"
``` ```
@ -100,6 +103,8 @@ fn main() {
All examples are here: All examples are here:
> the examples use ```bevy_gltf_blueprints``` with the **legacy_mode** set to **FALSE** as the new custom properties generated by the Blender add-on require newer/ non legacy logic.
- https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_registry_export/basic - https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/examples/bevy_registry_export/basic
@ -110,6 +115,7 @@ The main branch is compatible with the latest Bevy release, while the branch `be
Compatibility of `bevy_registry_export` versions: Compatibility of `bevy_registry_export` versions:
| `bevy_registry_export` | `bevy` | `bevy_components (Blender add-on)` | | `bevy_registry_export` | `bevy` | `bevy_components (Blender add-on)` |
| :-- | :-- |:-- | | :-- | :-- |:-- |
| `0.4 ` | `0.14` | `0.3` |
| `0.3 ` | `0.13` | `0.3` | | `0.3 ` | `0.13` | `0.3` |
| `0.2 ` | `0.12` | `0.3` | | `0.2 ` | `0.12` | `0.3` |
| `0.1 ` | `0.12` | `0.1 -0.2` | | `0.1 ` | `0.12` | `0.1 -0.2` |

View File

@ -17,28 +17,18 @@ pub fn export_types(world: &mut World) {
let asset_root = world.resource::<AssetRoot>(); let asset_root = world.resource::<AssetRoot>();
let registry_save_path = Path::join(&asset_root.0, &config.save_path); let registry_save_path = Path::join(&asset_root.0, &config.save_path);
println!("registry_save_path {}", registry_save_path.display());
let writer = File::create(registry_save_path).expect("should have created schema file"); let writer = File::create(registry_save_path).expect("should have created schema file");
let components_to_filter_out = &config.component_filter.clone();
let resources_to_filter_out = &config.resource_filter.clone();
let types = world.resource_mut::<AppTypeRegistry>(); let types = world.resource_mut::<AppTypeRegistry>();
let types = types.read(); let types = types.read();
let schemas = types let schemas = types.iter().map(export_type).collect::<Map<_, _>>();
.iter()
.filter(|type_info| {
let type_id = type_info.type_id();
components_to_filter_out.is_allowed_by_id(type_id)
&& resources_to_filter_out.is_allowed_by_id(type_id)
})
.map(export_type)
.collect::<Map<_, _>>();
serde_json::to_writer_pretty( serde_json::to_writer_pretty(
writer, writer,
&json!({ &json!({
"$schema": "https://json-schema.org/draft/2020-12/schema", "$schema": "https://json-schema.org/draft/2020-12/schema",
"long_name": "bevy component registry schema", "title": "bevy component registry schema",
"$defs": schemas, "$defs": schemas,
}), }),
) )
@ -67,7 +57,7 @@ pub fn export_type(reg: &TypeRegistration) -> (String, Value) {
json!({ json!({
"type": "object", "type": "object",
"typeInfo": "Struct", "typeInfo": "Struct",
"long_name": t.type_path(), "title": t.type_path(),
"properties": properties, "properties": properties,
"additionalProperties": false, "additionalProperties": false,
"required": info "required": info
@ -85,7 +75,7 @@ pub fn export_type(reg: &TypeRegistration) -> (String, Value) {
json!({ json!({
"type": "string", "type": "string",
"typeInfo": "Enum", "typeInfo": "Enum",
"long_name": t.type_path(), "title": t.type_path(),
"oneOf": info "oneOf": info
.iter() .iter()
.map(|variant| match variant { .map(|variant| match variant {
@ -104,12 +94,12 @@ pub fn export_type(reg: &TypeRegistration) -> (String, Value) {
VariantInfo::Struct(v) => json!({ VariantInfo::Struct(v) => json!({
"type": "object", "type": "object",
"typeInfo": "Struct", "typeInfo": "Struct",
"long_name": v.name(), "title": v.name(),
"short_name": v.name().split("::").last().unwrap_or(v.name()), "short_name": v.name().split("::").last().unwrap_or(v.name()),
"properties": v "properties": v
.iter() .iter()
.enumerate() .enumerate()
.map(|(variant_idx, field)| (field.name().to_owned(), add_min_max(json!({"type": typ(field.type_path()), "long_name": field.name()}), reg, field_idx, Some(variant_idx)))) .map(|(variant_idx, field)| (field.name().to_owned(), add_min_max(json!({"type": typ(field.type_path()), "title": field.name()}), reg, field_idx, Some(variant_idx))))
.collect::<Map<_, _>>(), .collect::<Map<_, _>>(),
"additionalProperties": false, "additionalProperties": false,
"required": v "required": v
@ -121,7 +111,7 @@ pub fn export_type(reg: &TypeRegistration) -> (String, Value) {
VariantInfo::Tuple(v) => json!({ VariantInfo::Tuple(v) => json!({
"type": "array", "type": "array",
"typeInfo": "Tuple", "typeInfo": "Tuple",
"long_name": v.name(), "title": v.name(),
"short_name":v.name(), "short_name":v.name(),
"prefixItems": v "prefixItems": v
.iter() .iter()
@ -131,7 +121,7 @@ pub fn export_type(reg: &TypeRegistration) -> (String, Value) {
"items": false, "items": false,
}), }),
VariantInfo::Unit(v) => json!({ VariantInfo::Unit(v) => json!({
"long_name": v.name(), "title": v.name(),
}), }),
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -139,13 +129,13 @@ pub fn export_type(reg: &TypeRegistration) -> (String, Value) {
json!({ json!({
"type": "object", "type": "object",
"typeInfo": "Enum", "typeInfo": "Enum",
"long_name": t.type_path(), "title": t.type_path(),
"oneOf": variants, "oneOf": variants,
}) })
} }
} }
TypeInfo::TupleStruct(info) => json!({ TypeInfo::TupleStruct(info) => json!({
"long_name": t.type_path(), "title": t.type_path(),
"type": "array", "type": "array",
"typeInfo": "TupleStruct", "typeInfo": "TupleStruct",
"prefixItems": info "prefixItems": info
@ -157,27 +147,26 @@ pub fn export_type(reg: &TypeRegistration) -> (String, Value) {
}), }),
TypeInfo::List(info) => { TypeInfo::List(info) => {
json!({ json!({
"long_name": t.type_path(), "title": t.type_path(),
"type": "array", "type": "array",
"typeInfo": "List", "typeInfo": "List",
"items": json!({"type": typ(info.item_type_path_table().path())}), "items": json!({"type": typ(info.item_type_path_table().path())}),
}) })
} }
TypeInfo::Array(info) => json!({ TypeInfo::Array(info) => json!({
"long_name": t.type_path(), "title": t.type_path(),
"type": "array", "type": "array",
"typeInfo": "Array", "typeInfo": "Array",
"items": json!({"type": typ(info.item_type_path_table().path())}), "items": json!({"type": typ(info.item_type_path_table().path())}),
}), }),
TypeInfo::Map(info) => json!({ TypeInfo::Map(info) => json!({
"long_name": t.type_path(), "title": t.type_path(),
"type": "object", "type": "object",
"typeInfo": "Map", "typeInfo": "Map",
"valueType": json!({"type": typ(info.value_type_path_table().path())}), "additionalProperties": json!({"type": typ(info.value_type_path_table().path())}),
"keyType": json!({"type": typ(info.key_type_path_table().path())}),
}), }),
TypeInfo::Tuple(info) => json!({ TypeInfo::Tuple(info) => json!({
"long_name": t.type_path(), "title": t.type_path(),
"type": "array", "type": "array",
"typeInfo": "Tuple", "typeInfo": "Tuple",
"prefixItems": info "prefixItems": info
@ -188,7 +177,7 @@ pub fn export_type(reg: &TypeRegistration) -> (String, Value) {
"items": false, "items": false,
}), }),
TypeInfo::Value(info) => json!({ TypeInfo::Value(info) => json!({
"long_name": t.type_path(), "title": t.type_path(),
"type": map_json_type(info.type_path()), "type": map_json_type(info.type_path()),
"typeInfo": "Value", "typeInfo": "Value",
}), }),

View File

@ -16,9 +16,9 @@ use bevy::{
pub struct ExportComponentsConfig { pub struct ExportComponentsConfig {
pub(crate) save_path: PathBuf, pub(crate) save_path: PathBuf,
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) component_filter: SceneFilter, pub(crate) component_filter: SceneFilter, // unused for now
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) resource_filter: SceneFilter, pub(crate) resource_filter: SceneFilter, // unused for now
} }
pub struct ExportRegistryPlugin { pub struct ExportRegistryPlugin {
@ -30,8 +30,8 @@ pub struct ExportRegistryPlugin {
impl Default for ExportRegistryPlugin { impl Default for ExportRegistryPlugin {
fn default() -> Self { fn default() -> Self {
Self { Self {
component_filter: SceneFilter::default(), component_filter: SceneFilter::default(), // unused for now
resource_filter: SceneFilter::default(), resource_filter: SceneFilter::default(), // unused for now
save_path: PathBuf::from("registry.json"), // relative to assets folder save_path: PathBuf::from("registry.json"), // relative to assets folder
} }
} }

View File

@ -19,7 +19,7 @@ This crate allows you to
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 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
- [blenvy](https://github.com/kaosat-dev/Blenvy/tree/main/tools/blenvy) - [blenvy](https://github.com/kaosat-dev/Blenvy/tree/main/tools/blenvy)
- allows you to create a Json export of all your components/ registered types. - 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 Its main use case is as a backbone for the [```blenvy``` Blender add-on](https://github.com/kaosat-dev/Blenvy/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). (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/) . - adds the ability to easilly **save** and **load** your game worlds for [Bevy](https://bevyengine.org/) .
@ -33,7 +33,7 @@ Its main use case is as a backbone for the [```blenvy``` Blender add-on](https:/
* ability to specify **which resources** 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) * 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) Particularly useful when using [Blender](https://www.blender.org/) as an editor for the [Bevy](https://bevyengine.org/) game engine, combined with the [Blender plugin](https://github.com/kaosat-dev/Blenvy/tree/main/tools/blenvy) that does a lot of the work for you (including spliting generating seperate gltf files for your static vs dynamic assets)
## Usage ## Usage

View File

@ -354,12 +354,12 @@ pub(crate) fn blueprints_assets_loaded(
// prepare data for animations // prepare data for animations
let mut graph = AnimationGraph::new(); let mut graph = AnimationGraph::new();
let mut named_animations: HashMap<String, Handle<AnimationClip>> = HashMap::new(); let mut named_animations: HashMap<String, Handle<AnimationClip>> = HashMap::new();
let mut animation_indices: HashMap<String, AnimationNodeIndex> = HashMap::new(); let mut named_indices: HashMap<String, AnimationNodeIndex> = HashMap::new();
for (key, clip) in blueprint_gltf.named_animations.iter() { for (key, clip) in blueprint_gltf.named_animations.iter() {
named_animations.insert(key.to_string(), clip.clone()); named_animations.insert(key.to_string(), clip.clone());
let animation_index = graph.add_clip(clip.clone(), 1.0, graph.root); let animation_index = graph.add_clip(clip.clone(), 1.0, graph.root);
animation_indices.insert(key.to_string(), animation_index); named_indices.insert(key.to_string(), animation_index);
} }
let graph = graphs.add(graph); let graph = graphs.add(graph);
@ -377,7 +377,7 @@ pub(crate) fn blueprints_assets_loaded(
// TODO: perhaps swap this out with SceneAnimations depending on whether we are spawning a level or a simple blueprint // TODO: perhaps swap this out with SceneAnimations depending on whether we are spawning a level or a simple blueprint
// these are animations specific to the blueprint // these are animations specific to the blueprint
named_animations, named_animations,
named_indices: animation_indices, named_indices,
graph, graph,
}, },
)); ));