Compare commits

..

2 Commits

Author SHA1 Message Date
kaosat.dev a618e0035e chore(crates): updated code from main to v0.14 compatibility for all old crates 2024-07-18 13:42:29 +02:00
kaosat.dev 3a8844b7a1 chore(tests): minor tweaks 2024-07-18 11:46:38 +02:00
24 changed files with 515 additions and 787 deletions

View File

@ -15,7 +15,7 @@ workspace = true
[dependencies]
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]
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)
[![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.
@ -15,8 +17,9 @@ A blueprint is a set of **overrideable** components + a hierarchy: ie
* just a Gltf file with Gltf_extras specifying components
* a component called BlueprintName
Particularly useful when using [Blender](https://www.blender.org/) as an editor for the [Bevy](https://bevyengine.org/) game engine, combined with the Blender add-on that do a lot of the work for you
- [blenvy](https://github.com/kaosat-dev/Blender_bevy_components_workflow/tree/main/tools/blenvy)
Particularly useful when using [Blender](https://www.blender.org/) as an editor for the [Bevy](https://bevyengine.org/) game engine, combined with the Blender add-ons that do a lot of the work for you
- [gltf_auto_export](https://github.com/kaosat-dev/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
@ -26,8 +29,8 @@ Here's a minimal usage example:
```toml
# Cargo.toml
[dependencies]
bevy="0.13"
bevy_gltf_blueprints = { version = "0.10"}
bevy="0.14"
bevy_gltf_blueprints = { version = "0.11.0"}
```
@ -65,7 +68,7 @@ fn spawn_blueprint(
Add the following to your `[dependencies]` section in `Cargo.toml`:
```toml
bevy_gltf_blueprints = "0.10"
bevy_gltf_blueprints = "0.11.0"
```
Or use `cargo add`:
@ -100,8 +103,11 @@ fn main() {
App::new()
.add_plugins((
BlueprintsPlugin{
library_folder: "advanced/models/library".into() // replace this with your blueprints library path , relative to the assets folder,
format: GltfFormat::GLB,// optional, use either format: GltfFormat::GLB, or format: GltfFormat::GLTF, or ..Default::default() if you want to keep the default .glb extension, this sets what extensions/ gltf files will be looked for by the library
aabbs: true, // defaults to false, enable this to automatically calculate aabb for the scene/blueprint
material_library: true, // defaults to false, enable this to enable automatic injection of materials from material library files
material_library_folder: "materials".into() //defaults to "materials" the folder to look for for the material files
..Default::default()
}
))
@ -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***
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
@ -290,28 +296,47 @@ Ie for example without this option, 56 different blueprints using the same mater
you can configure this with the settings:
```rust
material_library: true // defaults to false, enable this to enable automatic injection of materials from material library files
material_library_folder: "materials".into() //defaults to "materials" the folder to look for for the material files
```
> Important! you must take care of preloading your material librairy gltf files in advance, using for example ```bevy_asset_loader```since
```bevy_gltf_blueprints``` currently does NOT take care of loading those at runtime
see 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)
## 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
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
@ -321,6 +346,7 @@ The main branch is compatible with the latest Bevy release, while the branch `be
Compatibility of `bevy_gltf_blueprints` versions:
| `bevy_gltf_blueprints` | `bevy` |
| :-- | :-- |
| `0.11` | `0.14` |
| `0.9 - 0.10` | `0.13` |
| `0.3 - 0.8` | `0.12` |
| `0.1 - 0.2` | `0.11` |

View File

@ -3,9 +3,11 @@ use bevy::utils::HashMap;
#[derive(Component, Reflect, Default, Debug)]
#[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`
pub struct BlueprintAnimations {
/// storage for animations for a given entity (hierarchy), essentially a clone of gltf's `named_animations`
pub struct Animations {
pub named_animations: HashMap<String, Handle<AnimationClip>>,
pub named_indices: HashMap<String, AnimationNodeIndex>,
pub graph: Handle<AnimationGraph>,
}
#[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
/// 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 BlueprintAnimationPlayerLink(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;
}
}
}
}
*/
pub struct AnimationPlayerLink(pub Entity);

View File

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

View File

@ -10,9 +10,6 @@ pub use animation::*;
pub mod aabb;
pub use aabb::*;
pub mod assets;
pub use assets::*;
pub mod materials;
pub use materials::*;
@ -22,7 +19,11 @@ pub use copy_components::*;
use core::fmt;
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};
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
@ -35,14 +36,12 @@ pub enum GltfBlueprintsSet {
#[derive(Bundle)]
pub struct BluePrintBundle {
pub blueprint: BlueprintName,
pub blueprint_path: BlueprintPath,
pub spawn_here: SpawnHere,
}
impl Default for BluePrintBundle {
fn default() -> Self {
BluePrintBundle {
blueprint: BlueprintName("default".into()),
blueprint_path: BlueprintPath("".into()),
spawn_here: SpawnHere,
}
}
@ -50,10 +49,13 @@ impl Default for BluePrintBundle {
#[derive(Clone, Resource)]
pub struct BluePrintsConfig {
pub(crate) format: GltfFormat,
pub(crate) library_folder: PathBuf,
pub(crate) aabbs: bool,
pub(crate) aabb_cache: HashMap<String, Aabb>, // cache for aabbs
pub(crate) material_library: bool,
pub(crate) material_library_folder: PathBuf,
pub(crate) material_library_cache: HashMap<String, Handle<StandardMaterial>>,
}
@ -80,17 +82,27 @@ impl fmt::Display for GltfFormat {
#[derive(Debug, Clone)]
/// Plugin for gltf blueprints
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
pub aabbs: bool,
///
pub material_library: bool,
pub material_library_folder: PathBuf,
}
impl Default for BlueprintsPlugin {
fn default() -> Self {
Self {
legacy_mode: true,
format: GltfFormat::GLB,
library_folder: PathBuf::from("models/library"),
aabbs: false,
material_library: false,
material_library_folder: PathBuf::from("materials"),
}
}
}
@ -105,84 +117,64 @@ fn materials_library_enabled(blueprints_config: Res<BluePrintsConfig>) -> bool {
impl Plugin for BlueprintsPlugin {
fn build(&self, app: &mut App) {
app.add_plugins(ComponentsFromGltfPlugin {})
.register_type::<BlueprintName>()
.register_type::<BlueprintPath>()
.register_type::<MaterialInfo>()
.register_type::<SpawnHere>()
.register_type::<BlueprintAnimations>()
.register_type::<SceneAnimations>()
.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::<LocalAssets>()
.register_type::<BlueprintAssets>()
app.add_plugins(ComponentsFromGltfPlugin {
legacy_mode: self.legacy_mode,
})
.register_type::<BlueprintName>()
.register_type::<MaterialInfo>()
.register_type::<SpawnHere>()
.register_type::<Animations>()
.register_type::<BlueprintsList>()
.register_type::<Vec<String>>()
.register_type::<HashMap<String, Vec<String>>>()
.insert_resource(BluePrintsConfig {
format: self.format,
library_folder: self.library_folder.clone(),
aabbs: self.aabbs,
aabb_cache: HashMap::new(),
.register_type::<HashMap<String, Vec<String>>>()
.insert_resource(BluePrintsConfig {
aabbs: self.aabbs,
aabb_cache: HashMap::new(),
material_library: self.material_library,
material_library_cache: HashMap::new(),
})
.configure_sets(
Update,
(GltfBlueprintsSet::Spawn, GltfBlueprintsSet::AfterSpawn)
.chain()
.after(GltfComponentsSet::Injection),
)
.add_systems(
Update,
material_library: self.material_library,
material_library_folder: self.material_library_folder.clone(),
material_library_cache: HashMap::new(),
})
.configure_sets(
Update,
(GltfBlueprintsSet::Spawn, GltfBlueprintsSet::AfterSpawn)
.chain()
.after(GltfComponentsSet::Injection),
)
.add_systems(
Update,
(
(
test_thingy,
check_for_loaded2,
spawn_from_blueprints2,
/*(
prepare_blueprints,
check_for_loaded,
spawn_from_blueprints,
apply_deferred,
)
.chain(),*/
(compute_scene_aabbs, apply_deferred)
.chain()
.run_if(aabbs_enabled),
prepare_blueprints,
check_for_loaded,
spawn_from_blueprints,
apply_deferred,
(
materials_inject,
check_for_material_loaded,
materials_inject2,
)
.chain()
.run_if(materials_library_enabled),
)
.chain(),
(compute_scene_aabbs, apply_deferred)
.chain()
.run_if(aabbs_enabled),
apply_deferred,
(
materials_inject,
check_for_material_loaded,
materials_inject2,
)
.chain()
.in_set(GltfBlueprintsSet::Spawn),
.run_if(materials_library_enabled),
)
.add_systems(
Update,
(spawned_blueprint_post_process, apply_deferred)
.chain()
.in_set(GltfBlueprintsSet::AfterSpawn),
)
/* .add_systems(
Update,
(
trigger_instance_animation_markers_events,
trigger_blueprint_animation_markers_events,
),
)*/
;
.chain()
.in_set(GltfBlueprintsSet::Spawn),
)
.add_systems(
PostUpdate,
(spawned_blueprint_post_process, apply_deferred)
.chain()
.in_set(GltfBlueprintsSet::AfterSpawn)
.before(VisibilitySystems::CheckVisibility),
);
}
}

View File

@ -17,14 +17,14 @@ use bevy::{
render::mesh::Mesh,
};
use crate::{AssetLoadTracker, AssetsToLoad, BluePrintsConfig, BlueprintInstanceReady};
use crate::{AssetLoadTracker, AssetsToLoad, BluePrintsConfig};
#[derive(Component, Reflect, Default, Debug)]
#[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 name: String,
pub path: String,
pub source: String,
}
/// flag component
@ -42,8 +42,15 @@ pub(crate) fn materials_inject(
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);
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
if blueprints_config
.material_library_cache
.contains_key(&material_full_path)
@ -57,11 +64,10 @@ pub(crate) fn materials_inject(
.entity(entity)
.insert(BlueprintMaterialAssetsLoaded);
} 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 asset_infos: Vec<AssetLoadTracker> = vec![AssetLoadTracker {
name: material_info.name.clone(),
let asset_infos: Vec<AssetLoadTracker<Gltf>> = vec![AssetLoadTracker {
name: material_full_path,
id: material_file_id,
loaded: false,
handle: material_file_handle.clone(),
@ -75,6 +81,7 @@ pub(crate) fn materials_inject(
..Default::default()
})
.insert(BlueprintMaterialAssetsNotLoaded);
/**/
}
}
}
@ -82,7 +89,7 @@ pub(crate) fn materials_inject(
// TODO, merge with check_for_loaded, make generic ?
pub(crate) fn check_for_material_loaded(
mut blueprint_assets_to_load: Query<
(Entity, &mut AssetsToLoad),
(Entity, &mut AssetsToLoad<Gltf>),
With<BlueprintMaterialAssetsNotLoaded>,
>,
asset_server: Res<AssetServer>,
@ -139,7 +146,15 @@ pub(crate) fn materials_inject2(
mut commands: Commands,
) {
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;
if blueprints_config
@ -153,17 +168,14 @@ pub(crate) fn materials_inject2(
.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 model_handle: Handle<Gltf> = asset_server.load(materials_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)
{
if mat_gltf.named_materials.contains_key(material_name as &str) {
let material = mat_gltf
.named_materials
.get(&material_info.name as &str)
.get(material_name as &str)
.expect("this material should have been loaded");
blueprints_config
.material_library_cache
@ -177,8 +189,8 @@ pub(crate) fn materials_inject2(
if with_materials_and_meshes.contains(*child) {
debug!(
"injecting material {}, path: {:?}",
material_info.name,
material_info.path.clone()
material_name,
materials_path.clone()
);
commands.entity(*child).insert(material.clone());

View File

@ -1,11 +1,8 @@
use std::path::{Path, PathBuf};
use bevy::{gltf::Gltf, prelude::*, utils::hashbrown::HashMap};
use bevy::{gltf::Gltf, prelude::*, utils::HashMap};
use crate::{
AssetLoadTracker, AssetsToLoad, BluePrintsConfig, BlueprintAnimations, BlueprintAssets,
BlueprintAssetsLoaded, BlueprintAssetsNotLoaded,
};
use crate::{Animations, BluePrintsConfig};
/// this is a flag component for our levels/game world
#[derive(Component)]
@ -16,11 +13,6 @@ pub struct GameWorldTag;
#[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)]
@ -30,12 +22,6 @@ pub struct SpawnHere;
/// 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
@ -60,105 +46,100 @@ pub struct AddToGameWorld;
/// helper component, just to transfer child data
pub(crate) struct OriginalChildren(pub Vec<Entity>);
pub(crate) fn test_thingy(
spawn_placeholders: Query<
(Entity, &BlueprintPath),
(Added<BlueprintPath>, Without<Spawned>, Without<SpawnHere>),
>,
/// helper component, is used to store the list of sub blueprints to enable automatic loading of dependend blueprints
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
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
entities_with_assets: Query<
/// 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,
&BlueprintPath,
Option<&Parent>,*/
&BlueprintName,
Option<&Parent>,
Option<&Library>,
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,
asset_server: Res<AssetServer>,
blueprints_config: Res<BluePrintsConfig>,
) {
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) in bla_bla.iter() {
println!(
"added blueprint to spawn {:?} {:?}",
blueprint_name, blueprint_path
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
);
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(),
});
}
// 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()));
// 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);
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: asset.name.clone(),
id: asset_id,
name: model_path.to_string_lossy().into(),
id: model_id,
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() {
commands
.entity(child_entity)
.entity(entity)
.insert(AssetsToLoad {
all_loaded: false,
asset_infos,
@ -166,70 +147,59 @@ pub(crate) fn test_thingy(
})
.insert(BlueprintAssetsNotLoaded);
} else {
commands.entity(child_entity).insert(BlueprintAssetsLoaded);
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 check_for_loaded2(
pub(crate) fn check_for_loaded(
mut blueprint_assets_to_load: Query<
(Entity, Option<&Name>, &mut AssetsToLoad),
(Entity, &mut AssetsToLoad<Gltf>),
With<BlueprintAssetsNotLoaded>,
>,
asset_server: Res<AssetServer>,
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 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 {
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);
// 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);
commands
.entity(entity)
.insert(BlueprintAssetsLoaded)
.remove::<BlueprintAssetsNotLoaded>()
.remove::<AssetsToLoad>();
.remove::<BlueprintAssetsNotLoaded>();
}
}
}
pub(crate) fn spawn_from_blueprints2(
pub(crate) fn spawn_from_blueprints(
spawn_placeholders: Query<
(
Entity,
&BlueprintName,
&BlueprintPath,
Option<&Transform>,
Option<&Parent>,
Option<&Library>,
Option<&AddToGameWorld>,
Option<&Name>,
),
@ -244,25 +214,44 @@ pub(crate) fn spawn_from_blueprints2(
mut game_world: Query<Entity, With<GameWorldTag>>,
assets_gltf: Res<Assets<Gltf>>,
mut graphs: ResMut<Assets<AnimationGraph>>,
asset_server: Res<AssetServer>,
blueprints_config: Res<BluePrintsConfig>,
children: Query<&Children>,
) {
for (entity, blupeprint_name, blueprint_path, transform, original_parent, add_to_world, name) in
spawn_placeholders.iter()
for (
entity,
blupeprint_name,
transform,
original_parent,
library_override,
add_to_world,
name,
) in spawn_placeholders.iter()
{
info!(
"attempting to spawn blueprint {:?} for entity {:?}, id: {:?}, parent:{:?}",
debug!(
"attempting to spawn {:?} 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 what = &blupeprint_name.0;
let model_file_name = format!("{}.{}", &what, &blueprints_config.format);
let gltf = assets_gltf
.get(&model_handle)
.unwrap_or_else(|| panic!("gltf file {:?} should have been loaded", &blueprint_path.0));
// library path is either defined at the plugin level or overriden by optional Library components
let library_path =
library_override.map_or_else(|| &blueprints_config.library_folder, |l| &l.0);
let model_path = Path::new(&library_path).join(Path::new(model_file_name.as_str()));
// 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
@ -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();
for (key, value) in gltf.named_animations.iter() {
named_animations.insert(key.to_string(), value.clone());
let mut named_indices: HashMap<String, AnimationNodeIndex> = HashMap::new();
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((
SceneBundle {
@ -297,13 +292,13 @@ pub(crate) fn spawn_from_blueprints2(
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(),
Animations {
named_animations,
named_indices,
graph
},
Spawned,
OriginalChildren(original_children),
));
if add_to_world.is_some() {

View File

@ -5,7 +5,7 @@ use bevy::prelude::*;
use bevy::scene::SceneInstance;
// use bevy::utils::hashbrown::HashSet;
use super::{BlueprintAnimationPlayerLink, BlueprintAnimations};
use super::{AnimationPlayerLink, Animations};
use super::{SpawnHere, Spawned};
use crate::{
AssetsToLoad, BlueprintAssetsLoaded, CopyComponents, InBlueprint, NoInBlueprint,
@ -24,7 +24,7 @@ pub(crate) fn spawned_blueprint_post_process(
Entity,
&Children,
&OriginalChildren,
&BlueprintAnimations,
&Animations,
Option<&NoInBlueprint>,
Option<&Name>,
),
@ -38,7 +38,7 @@ pub(crate) fn spawned_blueprint_post_process(
for (original, children, original_children, animations, no_inblueprint, name) in
unprocessed_entities.iter()
{
info!("post processing blueprint for entity {:?}", name);
debug!("post processing blueprint for entity {:?}", name);
if children.len() == 0 {
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
// 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).insert(AnimationPlayerLink(added));
}
}
}
commands.entity(original).remove::<SpawnHere>();
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::<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::<BlueprintAssetsLoaded>();
commands.entity(original).remove::<Handle<Scene>>();
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(root_entity).despawn_recursive();
info!("DONE WITH POST PROCESS");
}
}

View File

@ -14,9 +14,9 @@ license = "MIT OR Apache-2.0"
workspace = true
[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"
ron = "0.8.1"
[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_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.
@ -23,8 +25,8 @@ Here's a minimal usage example:
```toml
# Cargo.toml
[dependencies]
bevy="0.13"
bevy_gltf_components = { version = "0.5"}
bevy="0.14"
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`:
```toml
bevy_gltf_components = "0.5"
bevy_gltf_components = "0.6"
```
Or use `cargo add`:
@ -78,78 +80,17 @@ Use the default configuration:
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
```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();
}
}
Or disable the legacy mode: (enabled by default)
```rust no_run
ComponentsFromGltfPlugin{legacy_mode: false}
```
You **need** to disable legacy mode if you want to use the [```bevy_components```](https://github.com/kaosat-dev/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
@ -188,6 +129,7 @@ The main branch is compatible with the latest Bevy release, while the branch `be
Compatibility of `bevy_gltf_components` versions:
| `bevy_gltf_components` | `bevy` |
| :-- | :-- |
| `0.6` | `0.14` |
| `0.5` | `0.13` |
| `0.2 - 0.4` | `0.12` |
| `0.1` | `0.11` |

View File

@ -10,7 +10,13 @@ pub use process_gltfs::*;
pub mod blender_settings;
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},
reflect::Reflect,
};
@ -60,16 +66,34 @@ pub enum GltfComponentsSet {
}
#[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 {
fn build(&self, app: &mut App) {
app.add_plugins(blender_settings::plugin)
.register_type::<GltfProcessed>()
.insert_resource(GltfComponentsConfig {})
.insert_resource(GltfComponentsConfig {
legacy_mode: self.legacy_mode,
})
.add_systems(Startup, check_for_legacy_mode)
.add_systems(
Update,
(add_components_from_gltf_extras).in_set(GltfComponentsSet::Injection),

View File

@ -6,67 +6,23 @@ use bevy::{
reflect::{AppTypeRegistry, ReflectComponent},
world::World,
},
gltf::{GltfExtras, GltfMaterialExtras, GltfMeshExtras, GltfSceneExtras},
gltf::GltfExtras,
hierarchy::Parent,
log::debug,
reflect::{Reflect, TypeRegistration},
utils::HashMap,
};
use crate::{ronstring_to_reflect_component, GltfProcessed};
// , 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
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
// this is mostly used for Blender collections
if name.as_str().contains("components") || name.as_str().ends_with("_pa") {
debug!("adding components to parent");
target_entity = parent.get();
}
debug!("adding to {:?}", target_entity);
// if there where already components set to be added to this entity (for example when entity_data was refering to a parent), update the vec of entity_components accordingly
// this allows for example blender collection to provide basic ecs data & the instances to override/ define their own values
if entity_components.contains_key(&target_entity) {
let mut updated_components: Vec<(Box<dyn Reflect>, TypeRegistration)> = Vec::new();
let current_components = &entity_components[&target_entity];
// first inject the current components
for (component, type_registration) in current_components {
updated_components.push((component.clone_value(), type_registration.clone()));
}
// then inject the new components: this also enables overwrite components set in the collection
for (component, type_registration) in reflect_components {
updated_components.push((component.clone_value(), type_registration));
}
return (target_entity, updated_components);
//entity_components.insert(target_entity, updated_components);
} else {
return (target_entity, reflect_components);
// entity_components.insert(target_entity, reflect_components);
}
}
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 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>();
let gltf_components_config = world.resource::<GltfComponentsConfig>();
for (entity, name, extra, parent) in extras.iter(world) {
debug!(
@ -76,56 +32,40 @@ pub fn add_components_from_gltf_extras(world: &mut World) {
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 reflect_components = ronstring_to_reflect_component(
&extra.value,
&type_registry,
gltf_components_config.legacy_mode,
);
let type_registry: &AppTypeRegistry = world.resource();
let type_registry = type_registry.read();
let reflect_components = ronstring_to_reflect_component(&extra.value, &type_registry);
// we assign the components specified /xxx_components objects to their parent node
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
// this is mostly used for Blender collections
if name.as_str().contains("components") || name.as_str().ends_with("_pa") {
debug!("adding components to parent");
target_entity = parent.get();
}
debug!("adding to {:?}", target_entity);
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);
// if there where already components set to be added to this entity (for example when entity_data was refering to a parent), update the vec of entity_components accordingly
// this allows for example blender collection to provide basic ecs data & the instances to override/ define their own values
if entity_components.contains_key(&target_entity) {
let mut updated_components: Vec<(Box<dyn Reflect>, TypeRegistration)> = Vec::new();
let current_components = &entity_components[&target_entity];
// first inject the current components
for (component, type_registration) in current_components {
updated_components.push((component.clone_value(), type_registration.clone()));
}
// then inject the new components: this also enables overwrite components set in the collection
for (component, type_registration) in reflect_components {
updated_components.push((component.clone_value(), type_registration));
}
entity_components.insert(target_entity, updated_components);
} else {
entity_components.insert(target_entity, reflect_components);
}
}
for (entity, components) in entity_components {

View File

@ -1,6 +1,6 @@
use bevy::log::{debug, warn};
use bevy::reflect::serde::ReflectDeserializer;
use bevy::reflect::{Reflect, TypeRegistration, TypeRegistry};
use bevy::reflect::{Reflect, TypeInfo, TypeRegistration, TypeRegistry};
use bevy::utils::HashMap;
use ron::Value;
use serde::de::DeserializeSeed;
@ -10,102 +10,105 @@ use super::capitalize_first_letter;
pub fn ronstring_to_reflect_component(
ron_string: &str,
type_registry: &TypeRegistry,
simplified_types: bool,
) -> Vec<(Box<dyn Reflect>, TypeRegistration)> {
let lookup: HashMap<String, Value> = ron::from_str(ron_string).unwrap();
let mut components: Vec<(Box<dyn Reflect>, TypeRegistration)> = Vec::new();
// println!("ron_string {:?}", ron_string);
for (name, value) in lookup.into_iter() {
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());
if let Some(type_registration) =
type_registry.get_with_short_type_path(capitalized_type_name.as_str())
{
debug!("TYPE INFO {:?}", type_registration.type_info());
let ron_string = format!(
"{{ \"{}\":{} }}",
type_registration.type_info().type_path(),
parsed_value
);
// usefull to determine what an entity looks like Serialized
/*let test_struct = CameraRenderGraph::new("name");
let serializer = ReflectSerializer::new(&test_struct, &type_registry);
let serialized =
ron::ser::to_string_pretty(&serializer, ron::ser::PrettyConfig::default()).unwrap();
println!("serialized Component {}", serialized);*/
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: {:?}",
name, value
)
});
debug!("component {:?}", component);
debug!("real type {:?}", component.get_represented_type_info());
components.push((component, type_registration.clone()));
debug!("found type registration for {}", capitalized_type_name);
} else {
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(),
};
let type_string = key.replace("component: ", "").trim().to_string();
let capitalized_type_name = capitalize_first_letter(type_string.as_str());
if let Some(type_registration) = type_registry.get_with_type_path(key.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) =
type_registry.get_with_short_type_path(capitalized_type_name.as_str())
{
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!(
"{{ \"{}\":{} }}",
type_registration.type_info().type_path(),
parsed_value
);
// usefull to determine what an entity looks like Serialized
/*let test_struct = CameraRenderGraph::new("name");
let serializer = ReflectSerializer::new(&test_struct, &type_registry);
let serialized =
ron::ser::to_string_pretty(&serializer, ron::ser::PrettyConfig::default()).unwrap();
println!("serialized Component {}", serialized);*/
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");
@ -122,9 +125,10 @@ fn bevy_components_string_to_components(
debug!("component {:?}", component);
debug!("real type {:?}", component.get_represented_type_info());
components.push((component, type_registration.clone()));
debug!("found type registration for {}", key);
debug!("found type registration for {}", capitalized_type_name);
} else {
warn!("no type registration for {}", key);
warn!("no type registration for {}", capitalized_type_name);
}
}
components
}

View File

@ -1,6 +1,6 @@
[package]
name = "bevy_gltf_save_load"
version = "0.4.1"
version = "0.5.0"
authors = ["Mark 'kaosat-dev' Moissette"]
description = "Save & load your bevy games"
homepage = "https://github.com/kaosat-dev/Blender_bevy_components_workflow"
@ -14,8 +14,8 @@ license = "MIT OR Apache-2.0"
workspace = true
[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" }
[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)
[![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/) .
@ -34,9 +36,9 @@ Here's a minimal usage example:
```toml
# Cargo.toml
[dependencies]
bevy="0.13"
bevy_gltf_save_load = "0.4"
bevy_gltf_blueprints = "0.10" // also needed
bevy="0.14"
bevy_gltf_save_load = "0.5"
bevy_gltf_blueprints = "0.11" // also needed
```
```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)
BlueprintsPlugin {
library_folder: "models/library".into(),
format: GltfFormat::GLB,
aabbs: true,
..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:
| `bevy_gltf_save_load` | `bevy` |
| :-- | :-- |
| `0.5 ` | `0.14` |
| `0.4 ` | `0.13` |
| `0.1 -0.3` | `0.12` |
| branch `main` | `0.12` |

View File

@ -1,6 +1,6 @@
[package]
name = "bevy_registry_export"
version = "0.3.1"
version = "0.4.0"
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"
homepage = "https://github.com/kaosat-dev/Blender_bevy_components_workflow"
@ -11,11 +11,11 @@ edition = "2021"
license = "MIT OR Apache-2.0"
[dependencies]
bevy = { version = "0.14.0-rc.3", default-features = false, features = ["bevy_scene"] }
bevy_reflect = { version = "0.14.0-rc.3", default-features = false }
bevy_app = { version = "0.14.0-rc.3", default-features = false, features = ["bevy_reflect"] }
bevy_ecs = { version = "0.14.0-rc.3", default-features = false, features = ["bevy_reflect"] }
bevy = { version = "0.14", default-features = false, features = ["bevy_scene"] }
bevy_reflect = { version = "0.14", default-features = false }
bevy_app = { version = "0.14", default-features = false, features = ["bevy_reflect"] }
bevy_ecs = { version = "0.14", default-features = false, features = ["bevy_reflect"] }
serde_json = "1.0.108"
[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)
[![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.
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).
@ -17,8 +20,8 @@ Here's a minimal usage example:
```toml
# Cargo.toml
[dependencies]
bevy="0.13"
bevy_registry_export = "0.3"
bevy="0.14"
bevy_registry_export = "0.4"
```
```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`:
```toml
bevy_registry_export = "0.3"
bevy_registry_export = "0.4"
```
@ -100,6 +103,8 @@ fn main() {
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
@ -110,6 +115,7 @@ The main branch is compatible with the latest Bevy release, while the branch `be
Compatibility of `bevy_registry_export` versions:
| `bevy_registry_export` | `bevy` | `bevy_components (Blender add-on)` |
| :-- | :-- |:-- |
| `0.4 ` | `0.14` | `0.3` |
| `0.3 ` | `0.13` | `0.3` |
| `0.2 ` | `0.12` | `0.3` |
| `0.1 ` | `0.12` | `0.1 -0.2` |

View File

@ -17,28 +17,18 @@ pub fn export_types(world: &mut World) {
let asset_root = world.resource::<AssetRoot>();
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 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 = types.read();
let schemas = types
.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<_, _>>();
let schemas = types.iter().map(export_type).collect::<Map<_, _>>();
serde_json::to_writer_pretty(
writer,
&json!({
"$schema": "https://json-schema.org/draft/2020-12/schema",
"long_name": "bevy component registry schema",
"title": "bevy component registry schema",
"$defs": schemas,
}),
)
@ -67,7 +57,7 @@ pub fn export_type(reg: &TypeRegistration) -> (String, Value) {
json!({
"type": "object",
"typeInfo": "Struct",
"long_name": t.type_path(),
"title": t.type_path(),
"properties": properties,
"additionalProperties": false,
"required": info
@ -85,7 +75,7 @@ pub fn export_type(reg: &TypeRegistration) -> (String, Value) {
json!({
"type": "string",
"typeInfo": "Enum",
"long_name": t.type_path(),
"title": t.type_path(),
"oneOf": info
.iter()
.map(|variant| match variant {
@ -104,12 +94,12 @@ pub fn export_type(reg: &TypeRegistration) -> (String, Value) {
VariantInfo::Struct(v) => json!({
"type": "object",
"typeInfo": "Struct",
"long_name": v.name(),
"title": v.name(),
"short_name": v.name().split("::").last().unwrap_or(v.name()),
"properties": v
.iter()
.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<_, _>>(),
"additionalProperties": false,
"required": v
@ -121,7 +111,7 @@ pub fn export_type(reg: &TypeRegistration) -> (String, Value) {
VariantInfo::Tuple(v) => json!({
"type": "array",
"typeInfo": "Tuple",
"long_name": v.name(),
"title": v.name(),
"short_name":v.name(),
"prefixItems": v
.iter()
@ -131,7 +121,7 @@ pub fn export_type(reg: &TypeRegistration) -> (String, Value) {
"items": false,
}),
VariantInfo::Unit(v) => json!({
"long_name": v.name(),
"title": v.name(),
}),
})
.collect::<Vec<_>>();
@ -139,13 +129,13 @@ pub fn export_type(reg: &TypeRegistration) -> (String, Value) {
json!({
"type": "object",
"typeInfo": "Enum",
"long_name": t.type_path(),
"title": t.type_path(),
"oneOf": variants,
})
}
}
TypeInfo::TupleStruct(info) => json!({
"long_name": t.type_path(),
"title": t.type_path(),
"type": "array",
"typeInfo": "TupleStruct",
"prefixItems": info
@ -157,27 +147,26 @@ pub fn export_type(reg: &TypeRegistration) -> (String, Value) {
}),
TypeInfo::List(info) => {
json!({
"long_name": t.type_path(),
"title": t.type_path(),
"type": "array",
"typeInfo": "List",
"items": json!({"type": typ(info.item_type_path_table().path())}),
})
}
TypeInfo::Array(info) => json!({
"long_name": t.type_path(),
"title": t.type_path(),
"type": "array",
"typeInfo": "Array",
"items": json!({"type": typ(info.item_type_path_table().path())}),
}),
TypeInfo::Map(info) => json!({
"long_name": t.type_path(),
"title": t.type_path(),
"type": "object",
"typeInfo": "Map",
"valueType": json!({"type": typ(info.value_type_path_table().path())}),
"keyType": json!({"type": typ(info.key_type_path_table().path())}),
"additionalProperties": json!({"type": typ(info.value_type_path_table().path())}),
}),
TypeInfo::Tuple(info) => json!({
"long_name": t.type_path(),
"title": t.type_path(),
"type": "array",
"typeInfo": "Tuple",
"prefixItems": info
@ -188,7 +177,7 @@ pub fn export_type(reg: &TypeRegistration) -> (String, Value) {
"items": false,
}),
TypeInfo::Value(info) => json!({
"long_name": t.type_path(),
"title": t.type_path(),
"type": map_json_type(info.type_path()),
"typeInfo": "Value",
}),

View File

@ -16,9 +16,9 @@ use bevy::{
pub struct ExportComponentsConfig {
pub(crate) save_path: PathBuf,
#[allow(dead_code)]
pub(crate) component_filter: SceneFilter,
pub(crate) component_filter: SceneFilter, // unused for now
#[allow(dead_code)]
pub(crate) resource_filter: SceneFilter,
pub(crate) resource_filter: SceneFilter, // unused for now
}
pub struct ExportRegistryPlugin {
@ -30,8 +30,8 @@ pub struct ExportRegistryPlugin {
impl Default for ExportRegistryPlugin {
fn default() -> Self {
Self {
component_filter: SceneFilter::default(),
resource_filter: SceneFilter::default(),
component_filter: SceneFilter::default(), // unused for now
resource_filter: SceneFilter::default(), // unused for now
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
- [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
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).
- 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
* 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

View File

@ -354,12 +354,12 @@ pub(crate) fn blueprints_assets_loaded(
// prepare data for animations
let mut graph = AnimationGraph::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() {
named_animations.insert(key.to_string(), clip.clone());
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);
@ -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
// these are animations specific to the blueprint
named_animations,
named_indices: animation_indices,
named_indices,
graph,
},
));

View File

@ -15,7 +15,7 @@ pub fn setup_game(
) {
// here we actually spawn our game world/level
commands.spawn((
BlueprintInfo::from_path("levels/World.gltf"),
BlueprintInfo::from_path("levels/World.glb"),
HideUntilReady,
SpawnBlueprint,
GameWorldTag,

View File

@ -53,7 +53,7 @@ def test_export_external_blueprints(setup_data):
blenvy.auto_export.auto_export = True
blenvy.auto_export.export_scene_settings = True
blenvy.auto_export.export_blueprints = True
blenvy.auto_export.export_materials_library = True
#blenvy.auto_export.export_materials_library = True
print("SCENES", bpy.data.scenes)
for scene in bpy.data.scenes:

View File

@ -123,9 +123,10 @@ def test_export_complex(setup_data):
user_asset.path = "audio/fake.mp3"'''
# we have to cheat, since we cannot rely on the data injected when saving the library file (since we are not saving it as part of the tests)
'''bpy.data.collections["External_blueprint"]["export_path"] = "blueprints/External_blueprint.glb"
bpy.data.collections["External_blueprint"]["export_path"] = "blueprints/External_blueprint.glb"
bpy.data.collections["External_blueprint2"]["export_path"] = "blueprints/External_blueprint2.glb"
bpy.data.collections["External_blueprint3"]["export_path"] = "blueprints/External_blueprint3.glb"'''
bpy.data.collections["External_blueprint3"]["export_path"] = "blueprints/External_blueprint3.glb"
#materials/testing_library_materials.glb
# do the actual exporting
prepare_and_export()