From 1fdb45bab686cca73debad7a9a4d0a0eb459c884 Mon Sep 17 00:00:00 2001 From: "kaosat.dev" Date: Sun, 9 Jun 2024 23:14:49 +0200 Subject: [PATCH] feat(Blenvy): basic working loading of levels & assets * removed/ obsoleted blueprintsList * now using AllAssets (meh naming) * modified example & internals to enable loading levels as Blueprints as well * internals quite messy for now, needs a big cleanup still * disabled materials library for now, needs to be overhauled to make use of asset logic as well * added more mock assets for testing * related changes to blender & bevy side --- crates/bevy_gltf_blueprints/src/assets.rs | 36 ++- crates/bevy_gltf_blueprints/src/lib.rs | 11 +- crates/bevy_gltf_blueprints/src/materials.rs | 10 +- crates/bevy_gltf_blueprints/src/old.rs | 248 ++++++++++++++++++ .../src/spawn_from_blueprints.rs | 177 +++++++------ .../src/spawn_post_process.rs | 4 +- .../assets_sounds_breakout_collision.ogg | Bin 0 -> 4930 bytes testing/bevy_example/assets/audio/fake.mp3 | 0 testing/bevy_example/assets/text/test.txt | 1 + testing/bevy_example/src/game/in_game.rs | 13 +- testing/bevy_example/src/game/mod.rs | 14 +- tools/blenvy/TODO.md | 4 +- .../auto_export/common/duplicate_object.py | 7 - .../auto_export/common/export_materials.py | 6 + tools/blenvy/assets/ui.py | 5 +- tools/blenvy/tests/test_bevy_integration.py | 4 +- .../tests/test_bevy_integration_prepare.py | 2 +- 17 files changed, 418 insertions(+), 124 deletions(-) create mode 100644 crates/bevy_gltf_blueprints/src/old.rs create mode 100644 testing/bevy_example/assets/audio/assets_sounds_breakout_collision.ogg create mode 100644 testing/bevy_example/assets/audio/fake.mp3 create mode 100644 testing/bevy_example/assets/text/test.txt diff --git a/crates/bevy_gltf_blueprints/src/assets.rs b/crates/bevy_gltf_blueprints/src/assets.rs index 8a60cba..c57d204 100644 --- a/crates/bevy_gltf_blueprints/src/assets.rs +++ b/crates/bevy_gltf_blueprints/src/assets.rs @@ -1,6 +1,6 @@ use std::path::{Path, PathBuf}; -use bevy::{gltf::Gltf, prelude::*, utils::HashMap}; +use bevy::{asset::LoadedUntypedAsset, gltf::Gltf, prelude::*, utils::HashMap}; use crate::{BluePrintsConfig, BlueprintAnimations}; @@ -24,30 +24,35 @@ pub struct AllAssets(pub Vec); -/// 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>); +//////////////////////// +/// +/// flag component, usually added when a blueprint is loaded +#[derive(Component)] +pub(crate) struct BlueprintAssetsLoaded; +/// flag component +#[derive(Component)] +pub(crate) struct BlueprintAssetsNotLoaded; /// helper component, for tracking loaded assets's loading state, id , handle etc -#[derive(Default, Debug)] -pub(crate) struct AssetLoadTracker { +#[derive(Debug)] +pub(crate) struct AssetLoadTracker { #[allow(dead_code)] pub name: String, - pub id: AssetId, + pub id: AssetId, pub loaded: bool, #[allow(dead_code)] - pub handle: Handle, + pub handle: Handle, } + /// helper component, for tracking loaded assets #[derive(Component, Debug)] -pub(crate) struct AssetsToLoad { +pub(crate) struct AssetsToLoad { pub all_loaded: bool, - pub asset_infos: Vec>, + pub asset_infos: Vec, pub progress: f32, } -impl Default for AssetsToLoad { +impl Default for AssetsToLoad { fn default() -> Self { Self { all_loaded: Default::default(), @@ -56,10 +61,3 @@ impl Default for AssetsToLoad { } } } - -/// flag component, usually added when a blueprint is loaded -#[derive(Component)] -pub(crate) struct BlueprintAssetsLoaded; -/// flag component -#[derive(Component)] -pub(crate) struct BlueprintAssetsNotLoaded; diff --git a/crates/bevy_gltf_blueprints/src/lib.rs b/crates/bevy_gltf_blueprints/src/lib.rs index b7a0bf2..abb9405 100644 --- a/crates/bevy_gltf_blueprints/src/lib.rs +++ b/crates/bevy_gltf_blueprints/src/lib.rs @@ -129,13 +129,13 @@ impl Plugin for BlueprintsPlugin { .register_type::>>() .register_type::>>>() .add_event::() - .register_type::() .register_type::() .register_type::>() .register_type::>() .register_type::() .register_type::() + .register_type::>>() .insert_resource(BluePrintsConfig { format: self.format, @@ -158,20 +158,23 @@ impl Plugin for BlueprintsPlugin { Update, ( test_thingy, - ( + check_for_loaded2, + spawn_from_blueprints2, + + /*( prepare_blueprints, check_for_loaded, spawn_from_blueprints, apply_deferred, ) - .chain(), + .chain(),*/ (compute_scene_aabbs, apply_deferred) .chain() .run_if(aabbs_enabled), apply_deferred, ( materials_inject, - check_for_material_loaded, + // check_for_material_loaded, materials_inject2, ) .chain() diff --git a/crates/bevy_gltf_blueprints/src/materials.rs b/crates/bevy_gltf_blueprints/src/materials.rs index fe440d4..d11ffde 100644 --- a/crates/bevy_gltf_blueprints/src/materials.rs +++ b/crates/bevy_gltf_blueprints/src/materials.rs @@ -66,7 +66,9 @@ pub(crate) fn materials_inject( } else { let material_file_handle: Handle = asset_server.load(materials_path.clone()); let material_file_id = material_file_handle.id(); - let asset_infos: Vec> = vec![AssetLoadTracker { + + // FIXME: fix this stuff + /*let asset_infos: Vec = vec![AssetLoadTracker { name: material_full_path, id: material_file_id, loaded: false, @@ -81,12 +83,14 @@ pub(crate) fn materials_inject( ..Default::default() }) .insert(BlueprintMaterialAssetsNotLoaded); - /**/ + */ } } } // TODO, merge with check_for_loaded, make generic ? +// FIXME: fix this: +/* pub(crate) fn check_for_material_loaded( mut blueprint_assets_to_load: Query< (Entity, &mut AssetsToLoad), @@ -121,7 +125,7 @@ pub(crate) fn check_for_material_loaded( } } } - +*/ /// system that injects / replaces materials from material library pub(crate) fn materials_inject2( mut blueprints_config: ResMut, diff --git a/crates/bevy_gltf_blueprints/src/old.rs b/crates/bevy_gltf_blueprints/src/old.rs new file mode 100644 index 0000000..5cf01a0 --- /dev/null +++ b/crates/bevy_gltf_blueprints/src/old.rs @@ -0,0 +1,248 @@ + + +/// helper component, for tracking loaded assets's loading state, id , handle etc +#[derive(Default, Debug)] +pub(crate) struct AssetLoadTracker { + #[allow(dead_code)] + pub name: String, + pub id: AssetId, + pub loaded: bool, + #[allow(dead_code)] + pub handle: Handle, +} + +/// helper component, for tracking loaded assets +#[derive(Component, Debug)] +pub(crate) struct AssetsToLoad { + pub all_loaded: bool, + pub asset_infos: Vec>, + pub progress: f32, +} +impl Default for AssetsToLoad { + fn default() -> Self { + Self { + all_loaded: Default::default(), + asset_infos: Default::default(), + progress: Default::default(), + } + } +} + +/// flag component, usually added when a blueprint is loaded +#[derive(Component)] +pub(crate) struct BlueprintAssetsLoaded; +/// flag component +#[derive(Component)] +pub(crate) struct BlueprintAssetsNotLoaded; + + + +/// spawning prepare function, +/// * also takes into account the already exisiting "override" components, ie "override components" > components from blueprint +pub(crate) fn prepare_blueprints( + spawn_placeholders: Query< + ( + Entity, + &BlueprintName, + Option<&Parent>, + Option<&Library>, + Option<&Name>, + Option<&BlueprintsList>, + ), + (Added, Added, Without), + >, + + mut commands: Commands, + asset_server: Res, + blueprints_config: Res, +) { + for (entity, blupeprint_name, original_parent, library_override, name, blueprints_list) in + spawn_placeholders.iter() + { + debug!( + "requesting to spawn {:?} for entity {:?}, id: {:?}, parent:{:?}", + blupeprint_name.0, name, entity, original_parent + ); + + // println!("main model path {:?}", model_path); + if blueprints_list.is_some() { + let blueprints_list = blueprints_list.unwrap(); + // println!("blueprints list {:?}", blueprints_list.0.keys()); + let mut asset_infos: Vec> = 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 = asset_server.load(model_path.clone()); + let model_id = model_handle.id(); + let loaded = asset_server.is_loaded_with_dependencies(model_id); + if !loaded { + asset_infos.push(AssetLoadTracker { + name: model_path.to_string_lossy().into(), + id: model_id, + loaded: false, + handle: model_handle.clone(), + }); + } + } + // if not all assets are already loaded, inject a component to signal that we need them to be loaded + if !asset_infos.is_empty() { + commands + .entity(entity) + .insert(AssetsToLoad { + all_loaded: false, + asset_infos, + ..Default::default() + }) + .insert(BlueprintAssetsNotLoaded); + } else { + commands.entity(entity).insert(BlueprintAssetsLoaded); + } + } else { + // in case there are no blueprintsList, we revert back to the old behaviour + commands.entity(entity).insert(BlueprintAssetsLoaded); + } + } +} + +pub(crate) fn check_for_loaded( + mut blueprint_assets_to_load: Query< + (Entity, &mut AssetsToLoad), + With, + >, + asset_server: Res, + mut commands: Commands, +) { + for (entity, mut assets_to_load) in blueprint_assets_to_load.iter_mut() { + let mut all_loaded = true; + let mut loaded_amount = 0; + let total = assets_to_load.asset_infos.len(); + for tracker in assets_to_load.asset_infos.iter_mut() { + let asset_id = tracker.id; + let loaded = asset_server.is_loaded_with_dependencies(asset_id); + tracker.loaded = loaded; + if loaded { + loaded_amount += 1; + } else { + all_loaded = false; + } + } + let progress: f32 = loaded_amount as f32 / total as f32; + // println!("progress: {}",progress); + assets_to_load.progress = progress; + + if all_loaded { + assets_to_load.all_loaded = true; + commands + .entity(entity) + .insert(BlueprintAssetsLoaded) + .remove::(); + } + } +} + +pub(crate) fn spawn_from_blueprints( + spawn_placeholders: Query< + ( + Entity, + &BlueprintName, + Option<&Transform>, + Option<&Parent>, + Option<&Library>, + Option<&AddToGameWorld>, + Option<&Name>, + ), + ( + With, + Added, + Without, + ), + >, + + mut commands: Commands, + mut game_world: Query>, + + assets_gltf: Res>, + asset_server: Res, + blueprints_config: Res, + + children: Query<&Children>, +) { + for ( + entity, + blupeprint_name, + transform, + original_parent, + library_override, + add_to_world, + name, + ) in spawn_placeholders.iter() + { + debug!( + "attempting to spawn {:?} for entity {:?}, id: {:?}, parent:{:?}", + blupeprint_name.0, name, entity, original_parent + ); + + let what = &blupeprint_name.0; + let model_file_name = format!("{}.{}", &what, &blueprints_config.format); + + // library path is either defined at the plugin level or overriden by optional Library components + let library_path = + library_override.map_or_else(|| &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 = asset_server.load(model_path.clone()); // FIXME: kinda weird now + + let gltf = assets_gltf.get(&model_handle).unwrap_or_else(|| { + panic!( + "gltf file {:?} should have been loaded", + model_path.to_str() + ) + }); + + // WARNING we work under the assumtion that there is ONLY ONE named scene, and that the first one is the right one + let main_scene_name = gltf + .named_scenes + .keys() + .next() + .expect("there should be at least one named scene in the gltf file to spawn"); + + let scene = &gltf.named_scenes[main_scene_name]; + + // transforms are optional, but still deal with them correctly + let mut transforms: Transform = Transform::default(); + if transform.is_some() { + transforms = *transform.unwrap(); + } + + let mut original_children: Vec = vec![]; + if let Ok(c) = children.get(entity) { + for child in c.iter() { + original_children.push(*child); + } + } + commands.entity(entity).insert(( + SceneBundle { + scene: scene.clone(), + transform: transforms, + ..Default::default() + }, + Spawned, + OriginalChildren(original_children), + BlueprintAnimations { + // these are animations specific to the inside of the blueprint + named_animations: gltf.named_animations.clone(), + }, + )); + + if add_to_world.is_some() { + let world = game_world + .get_single_mut() + .expect("there should be a game world present"); + commands.entity(world).add_child(entity); + } + } +} diff --git a/crates/bevy_gltf_blueprints/src/spawn_from_blueprints.rs b/crates/bevy_gltf_blueprints/src/spawn_from_blueprints.rs index 5c54bb8..42c9e30 100644 --- a/crates/bevy_gltf_blueprints/src/spawn_from_blueprints.rs +++ b/crates/bevy_gltf_blueprints/src/spawn_from_blueprints.rs @@ -2,7 +2,7 @@ use std::path::{Path, PathBuf}; use bevy::{gltf::Gltf, prelude::*, utils::HashMap}; -use crate::{BluePrintsConfig, BlueprintAnimations, BlueprintsList, AssetLoadTracker, AssetsToLoad, BlueprintAssetsNotLoaded, BlueprintAssetsLoaded}; +use crate::{AllAssets, AssetsToLoad, AssetLoadTracker, BluePrintsConfig, BlueprintAnimations, BlueprintAssetsLoaded, BlueprintAssetsNotLoaded}; /// this is a flag component for our levels/game world #[derive(Component)] @@ -59,81 +59,103 @@ pub(crate) fn test_thingy( Entity, &BlueprintPath, ), - (Added, Without, Without), ->, + (Added, Without, Without)>, + + // before 0.14 we have to use a seperate query, after migrating we can query at the root level + entities_with_assets: Query< + ( + Entity, + /*&BlueprintName, + &BlueprintPath, + Option<&Parent>,*/ + Option<&Name>, + Option<&AllAssets>, + ), + (Added), // Added + >, + + + bla_bla : Query< + (Entity, + &BlueprintName, + &BlueprintPath, + Option<&Parent>,),(Added) + >, mut commands: Commands, asset_server: Res, ) { for (entity, blueprint_path) in spawn_placeholders.iter() { - println!("added blueprint_path {:?}", blueprint_path); - commands.entity(entity).insert( + //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 = 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); -/// spawning prepare function, -/// * also takes into account the already exisiting "override" components, ie "override components" > components from blueprint -pub(crate) fn prepare_blueprints( - spawn_placeholders: Query< - ( - Entity, - &BlueprintName, - Option<&Parent>, - Option<&Library>, - Option<&Name>, - Option<&BlueprintsList>, - ), - (Added, Added, Without), - >, + let mut asset_infos: Vec = vec![]; + if !loaded { + asset_infos.push(AssetLoadTracker { + name: blueprint_name.0.clone(), + id: asset_id, + loaded: false, + handle: untyped_handle.clone(), + }); + } - mut commands: Commands, - asset_server: Res, - blueprints_config: Res, -) { - 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 - ); + // 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); + } + } - // 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> = 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())); + 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 = vec![]; - let model_handle: Handle = asset_server.load(model_path.clone()); - let model_id = model_handle.id(); - let loaded = asset_server.is_loaded_with_dependencies(model_id); + 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: model_path.to_string_lossy().into(), - id: model_id, + name: asset.name.clone(), + id: asset_id, loaded: false, - handle: model_handle.clone(), + handle: untyped_handle.clone(), }); } } - // if not all assets are already loaded, inject a component to signal that we need them to be loaded + + // now insert load tracker if !asset_infos.is_empty() { commands - .entity(entity) + .entity(child_entity) .insert(AssetsToLoad { all_loaded: false, asset_infos, @@ -141,59 +163,66 @@ pub(crate) fn prepare_blueprints( }) .insert(BlueprintAssetsNotLoaded); } else { - commands.entity(entity).insert(BlueprintAssetsLoaded); + commands.entity(child_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_loaded( +pub(crate) fn check_for_loaded2( mut blueprint_assets_to_load: Query< - (Entity, &mut AssetsToLoad), + (Entity, Option<&Name>, &mut AssetsToLoad), With, >, asset_server: Res, mut commands: Commands, ) { - for (entity, mut assets_to_load) in blueprint_assets_to_load.iter_mut() { + for (entity,entity_name, mut assets_to_load) in blueprint_assets_to_load.iter_mut() { let mut all_loaded = true; let mut loaded_amount = 0; let total = assets_to_load.asset_infos.len(); for tracker in assets_to_load.asset_infos.iter_mut() { let asset_id = tracker.id; let loaded = asset_server.is_loaded_with_dependencies(asset_id); - tracker.loaded = loaded; - if loaded { + println!("loading {}: // load state: {:?}", tracker.name, asset_server.load_state(asset_id)); + + // FIXME: hack for now + let failed = asset_server.load_state(asset_id) == bevy::asset::LoadState::Failed; + + tracker.loaded = loaded || failed; + if loaded || failed { loaded_amount += 1; } else { all_loaded = false; } } let progress: f32 = loaded_amount as f32 / total as f32; - // println!("progress: {}",progress); + 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::(); + .remove::() + .remove::() + ; } } } -pub(crate) fn spawn_from_blueprints( + + +pub(crate) fn spawn_from_blueprints2( spawn_placeholders: Query< ( Entity, &BlueprintName, + &BlueprintPath, Option<&Transform>, Option<&Parent>, - Option<&Library>, Option<&AddToGameWorld>, Option<&Name>, ), @@ -216,33 +245,28 @@ pub(crate) fn spawn_from_blueprints( for ( entity, blupeprint_name, + blueprint_path, transform, original_parent, - library_override, add_to_world, name, ) in spawn_placeholders.iter() { - debug!( - "attempting to spawn {:?} for entity {:?}, id: {:?}, parent:{:?}", + info!( + "attempting to spawn blueprint {:?} for entity {:?}, id: {:?}, parent:{:?}", blupeprint_name.0, name, entity, original_parent ); let what = &blupeprint_name.0; let model_file_name = format!("{}.{}", &what, &blueprints_config.format); - // library path is either defined at the plugin level or overriden by optional Library components - let library_path = - library_override.map_or_else(|| &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 = asset_server.load(model_path.clone()); // FIXME: kinda weird now + let model_handle: Handle = asset_server.load(blueprint_path.0.clone()); // FIXME: kinda weird now let gltf = assets_gltf.get(&model_handle).unwrap_or_else(|| { panic!( "gltf file {:?} should have been loaded", - model_path.to_str() + &blueprint_path.0 ) }); @@ -289,3 +313,8 @@ pub(crate) fn spawn_from_blueprints( } } } + + + + + diff --git a/crates/bevy_gltf_blueprints/src/spawn_post_process.rs b/crates/bevy_gltf_blueprints/src/spawn_post_process.rs index a88e950..62eaac6 100644 --- a/crates/bevy_gltf_blueprints/src/spawn_post_process.rs +++ b/crates/bevy_gltf_blueprints/src/spawn_post_process.rs @@ -95,8 +95,8 @@ pub(crate) fn spawned_blueprint_post_process( commands.entity(original).remove::(); commands.entity(original).remove::(); commands.entity(original).remove::>(); - commands.entity(original).remove::>(); // 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::(); + //commands.entity(original).remove::(); // 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::(); commands.entity(root_entity).despawn_recursive(); } } diff --git a/testing/bevy_example/assets/audio/assets_sounds_breakout_collision.ogg b/testing/bevy_example/assets/audio/assets_sounds_breakout_collision.ogg new file mode 100644 index 0000000000000000000000000000000000000000..0211d70cfb845fc9a88e32b0caf038f435a98d29 GIT binary patch literal 4930 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 EKT_>GLI3~& literal 0 HcmV?d00001 diff --git a/testing/bevy_example/assets/audio/fake.mp3 b/testing/bevy_example/assets/audio/fake.mp3 new file mode 100644 index 0000000..e69de29 diff --git a/testing/bevy_example/assets/text/test.txt b/testing/bevy_example/assets/text/test.txt new file mode 100644 index 0000000..b649a9b --- /dev/null +++ b/testing/bevy_example/assets/text/test.txt @@ -0,0 +1 @@ +some text \ No newline at end of file diff --git a/testing/bevy_example/src/game/in_game.rs b/testing/bevy_example/src/game/in_game.rs index a8eb49f..86746ff 100644 --- a/testing/bevy_example/src/game/in_game.rs +++ b/testing/bevy_example/src/game/in_game.rs @@ -1,5 +1,5 @@ use bevy::prelude::*; -use bevy_gltf_blueprints::{BluePrintBundle, BlueprintName, GameWorldTag}; +use bevy_gltf_blueprints::{BluePrintBundle, BlueprintName, BlueprintPath, GameWorldTag}; use bevy_gltf_worlflow_examples_common_rapier::{GameState, InAppRunning}; use bevy_rapier3d::prelude::Velocity; @@ -11,7 +11,7 @@ pub fn setup_game( mut next_game_state: ResMut>, ) { // here we actually spawn our game world/level - commands.spawn(( + /*commands.spawn(( SceneBundle { scene: asset_server.load("levels/World.glb#Scene0"), ..default() @@ -19,7 +19,16 @@ pub fn setup_game( bevy::prelude::Name::from("world"), GameWorldTag, InAppRunning, + ));*/ + + commands.spawn(( + BlueprintName("World".into()), + BlueprintPath("levels/World.glb".into()), + bevy::prelude::Name::from("world"), + GameWorldTag, + InAppRunning, )); + next_game_state.set(GameState::InGame) } diff --git a/testing/bevy_example/src/game/mod.rs b/testing/bevy_example/src/game/mod.rs index b35febf..186af92 100644 --- a/testing/bevy_example/src/game/mod.rs +++ b/testing/bevy_example/src/game/mod.rs @@ -6,7 +6,7 @@ pub use in_game::*; use std::{collections::HashMap, fs, time::Duration}; use bevy_gltf_blueprints::{ - BlueprintAnimationPlayerLink, BlueprintName, BlueprintsList, GltfBlueprintsSet, SceneAnimations, + AllAssets, BlueprintAnimationPlayerLink, BlueprintName, GltfBlueprintsSet, SceneAnimations }; use bevy::{ @@ -23,7 +23,7 @@ fn start_game(mut next_app_state: ResMut>) { // if the export from Blender worked correctly, we should have animations (simplified here by using AnimationPlayerLink) // if the export from Blender worked correctly, we should have an Entity called "Blueprint4_nested" that has a child called "Blueprint3" that has a "BlueprintName" component with value Blueprint3 -// if the export from Blender worked correctly, we should have a blueprints_list +// if the export from Blender worked correctly, we should have an assets_list // if the export from Blender worked correctly, we should have the correct tree of entities #[allow(clippy::too_many_arguments)] #[allow(clippy::type_complexity)] @@ -36,7 +36,7 @@ fn validate_export( scene_animations: Query<(Entity, &SceneAnimations)>, empties_candidates: Query<(Entity, &Name, &GlobalTransform)>, - blueprints_list: Query<(Entity, &BlueprintsList)>, + assets_list: Query<(Entity, &AllAssets)>, root: Query<(Entity, &Name, &Children), (Without, With)>, ) { let animations_found = @@ -66,8 +66,8 @@ fn validate_export( break; } } - // check if there are blueprints_list components - let blueprints_list_found = !blueprints_list.is_empty(); + // check if there are assets_list components + let assets_list_found = !assets_list.is_empty(); // there should be no entity named xxx____bak as it means an error in the Blender side export process let mut exported_names_correct = true; @@ -104,8 +104,8 @@ fn validate_export( fs::write( "bevy_diagnostics.json", format!( - "{{ \"animations\": {}, \"nested_blueprint_found\": {}, \"empty_found\": {}, \"blueprints_list_found\": {}, \"exported_names_correct\": {} }}", - animations_found, nested_blueprint_found, empty_found, blueprints_list_found, exported_names_correct + "{{ \"animations\": {}, \"nested_blueprint_found\": {}, \"empty_found\": {}, \"assets_list_found\": {}, \"exported_names_correct\": {} }}", + animations_found, nested_blueprint_found, empty_found, assets_list_found, exported_names_correct ), ) .expect("Unable to write file"); diff --git a/tools/blenvy/TODO.md b/tools/blenvy/TODO.md index 750d9f3..4d8a5c8 100644 --- a/tools/blenvy/TODO.md +++ b/tools/blenvy/TODO.md @@ -116,6 +116,8 @@ General issues: - [ ] remove 'export_marked_assets' it should be a default setting - [x] disable/ hide asset editing ui for external assets - [ ] inject_export_path_into_internal_blueprints should be called on every asset/blueprint scan !! Not just on export -- [ ] fix level asets UI +- [x] fix level asets UI + +- [x] remove BlueprintsList & replace is with assets list clear && pytest -svv --blender-template ../../testing/bevy_example/art/testing_library.blend --blender-executable /home/ckaos/tools/blender/blender-4.1.0-linux-x64/blender tests/test_bevy_integration_prepare.py && pytest -svv --blender-executable /home/ckaos/tools/blender/blender-4.1.0-linux-x64/blender tests/test_bevy_integration.py \ No newline at end of file diff --git a/tools/blenvy/add_ons/auto_export/common/duplicate_object.py b/tools/blenvy/add_ons/auto_export/common/duplicate_object.py index ec07b87..f72329b 100644 --- a/tools/blenvy/add_ons/auto_export/common/duplicate_object.py +++ b/tools/blenvy/add_ons/auto_export/common/duplicate_object.py @@ -108,13 +108,6 @@ def duplicate_object(object, parent, combine_mode, destination_collection, bluep empty_obj['BlueprintName'] = f'("{blueprint_name}")' empty_obj["BlueprintPath"] = f'("{blueprint_path}")' empty_obj['SpawnHere'] = '()' - - # we also inject a list of all sub blueprints, so that the bevy side can preload them - children_per_blueprint = {} - blueprint = blueprints_data.blueprints_per_name.get(blueprint_name, None) - if blueprint: - children_per_blueprint[blueprint_name] = blueprint.nested_blueprints - empty_obj["BlueprintsList"] = f"({json.dumps(dict(children_per_blueprint))})" # we copy custom properties over from our original object to our empty for component_name, component_value in object.items(): diff --git a/tools/blenvy/add_ons/auto_export/common/export_materials.py b/tools/blenvy/add_ons/auto_export/common/export_materials.py index 1f993b9..3ce04a0 100644 --- a/tools/blenvy/add_ons/auto_export/common/export_materials.py +++ b/tools/blenvy/add_ons/auto_export/common/export_materials.py @@ -8,6 +8,12 @@ from blenvy.materials.materials_helpers import get_all_materials from .generate_temporary_scene_and_export import generate_temporary_scene_and_export from .export_gltf import (generate_gltf_export_settings) +# material library logic +# To avoid redundant materials (can be very costly, mostly when using high res textures) +# - we explore a gltf file containing all materials from a blend file +# - we add materialInfo component to each object that uses one of the materials, so that "what material is used by which object" is preserved +# + def clear_material_info(collection_names, library_scenes): for scene in library_scenes: root_collection = scene.collection diff --git a/tools/blenvy/assets/ui.py b/tools/blenvy/assets/ui.py index d1c3754..cde9e6c 100644 --- a/tools/blenvy/assets/ui.py +++ b/tools/blenvy/assets/ui.py @@ -9,7 +9,7 @@ def draw_assets(layout, name, title, asset_registry, target_type, target_name, e number_of_generated_assets = len(generated_assets) header, panel = layout.panel(f"assets{name}", default_closed=True) - header.label(text=title + f"({number_of_user_assets + number_of_generated_assets})", icon="ASSET_MANAGER") + header.label(text=title + f"({number_of_user_assets})", icon="ASSET_MANAGER") blueprint_assets = target_type == 'BLUEPRINT' @@ -42,8 +42,9 @@ def draw_assets(layout, name, title, asset_registry, target_type, target_name, e if editable: row = panel.row() #panel.separator() - + print("here", user_assets) for asset in user_assets: + print("asset", asset) row = panel.row() split = row.split(factor=nesting_indent) col = split.column() diff --git a/tools/blenvy/tests/test_bevy_integration.py b/tools/blenvy/tests/test_bevy_integration.py index fd0dc94..ac66a53 100644 --- a/tools/blenvy/tests/test_bevy_integration.py +++ b/tools/blenvy/tests/test_bevy_integration.py @@ -59,7 +59,7 @@ def setup_data(request): if os.path.exists(screenshot_observed_path): os.remove(screenshot_observed_path) - request.addfinalizer(finalizer) + #request.addfinalizer(finalizer) return None @@ -105,7 +105,7 @@ def test_export_complex(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 = False # TODO: switch back bpy.data.scenes['World'].blenvy_scene_type = 'Level' # set scene as main/level scene bpy.data.scenes['Library'].blenvy_scene_type = 'Library' # set scene as Library scene diff --git a/tools/blenvy/tests/test_bevy_integration_prepare.py b/tools/blenvy/tests/test_bevy_integration_prepare.py index fc59799..4ea7cd0 100644 --- a/tools/blenvy/tests/test_bevy_integration_prepare.py +++ b/tools/blenvy/tests/test_bevy_integration_prepare.py @@ -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 = False # TODO switch back print("SCENES", bpy.data.scenes) for scene in bpy.data.scenes: