From ee873b06f174639af731b8e0d530f2b9878b6d73 Mon Sep 17 00:00:00 2001 From: "kaosat.dev" Date: Mon, 29 Jul 2024 22:32:23 +0200 Subject: [PATCH] feat(Blenvy): big overhaul, cleanup & fixed to the assets system * now the blender side exports .meta.ron files in addition to the gltf files * these ron files contain the list of assets that are then preloaded on the Bevy side * removed the double loading of gltf files on the Bevy side, replaced with use of the new metadata/asset files * added bevy_common_assets/ ron as dependency for the file loading * big cleanup & partial restructure of the spawning steps * fixed premature removal of the BlueprintAssetsLoadState component that was leading to missing material gltf files in setups withouth hot reload * added OriginalVisibility component & logic to correctly reset the visibility of entities to what they where before the blueprint spawning * fixed a few not so visible issues with some components staying around after the blueprint instance has become "ready" * moved a number of component insertions to the new "get the assets list from the meta file" * also, loading speed feels faster ! (thanks to now loading the gltf files only once) * minor various tweaks & cleanups --- TODO.md | 1 + crates/blenvy/Cargo.toml | 14 +- crates/blenvy/src/blueprints/aabb.rs | 2 +- crates/blenvy/src/blueprints/assets.rs | 23 ++ crates/blenvy/src/blueprints/materials.rs | 10 +- crates/blenvy/src/blueprints/mod.rs | 10 +- .../src/blueprints/spawn_from_blueprints.rs | 352 ++++++++++-------- examples/demo/src/core/mod.rs | 3 +- testing/bevy_example/src/core/mod.rs | 1 - .../blueprints/export_blueprints.py | 7 +- .../generate_temporary_scene_and_export.py | 5 +- .../auto_export/levels/export_levels.py | 4 +- tools/blenvy/add_ons/auto_export/utils.py | 43 +++ 13 files changed, 295 insertions(+), 180 deletions(-) diff --git a/TODO.md b/TODO.md index 5be78d4..d2cc308 100644 --- a/TODO.md +++ b/TODO.md @@ -339,5 +339,6 @@ Bevy Side: - [ ] solving problems with scene renames - [ ] the ability to map external TEXT files to data in BLender (git-able, hand editable) - [x] make aabbs calculation non configurable, getting rid of the last setting (for now) +- [ ] add information & screenshots about adding assets to the Blender add-on docs clear && pytest -svv --blender-template ../../testing/bevy_example/art/testing_library.blend --blender-executable /home/ckaos/tools/blender/blender-4.1.0-linux-x64/blender tests/test_bevy_integration_prepare.py && pytest -svv --blender-executable /home/ckaos/tools/blender/blender-4.1.0-linux-x64/blender tests/test_bevy_integration.py \ No newline at end of file diff --git a/crates/blenvy/Cargo.toml b/crates/blenvy/Cargo.toml index c66ec47..37bbab0 100644 --- a/crates/blenvy/Cargo.toml +++ b/crates/blenvy/Cargo.toml @@ -18,19 +18,7 @@ bevy = { version = "0.14", default-features = false, features = ["bevy_asset", " serde = "1.0.188" ron = "0.8.1" serde_json = "1.0.108" -gltf = { version = "1.4.0", default-features = false, features = [ - "KHR_lights_punctual", - "KHR_materials_transmission", - "KHR_materials_ior", - "KHR_materials_volume", - "KHR_materials_unlit", - "KHR_materials_emissive_strength", - "KHR_texture_transform", - "extras", - "extensions", - "names", - "utils", -] } +bevy_common_assets = {version = "0.11", features = ["ron"]} [dev-dependencies] diff --git a/crates/blenvy/src/blueprints/aabb.rs b/crates/blenvy/src/blueprints/aabb.rs index 3c3baed..289292e 100644 --- a/crates/blenvy/src/blueprints/aabb.rs +++ b/crates/blenvy/src/blueprints/aabb.rs @@ -29,7 +29,7 @@ pub fn compute_scene_aabbs( } else { let aabb = compute_descendant_aabb(root_entity, &children, &existing_aabbs); blenvy_config.aabb_cache.insert(name.to_string(), aabb); - info!("generating aabb for {:?}", name); + info!("Step 7: generating aabb for {:?}", name); commands .entity(root_entity) .insert(aabb) diff --git a/crates/blenvy/src/blueprints/assets.rs b/crates/blenvy/src/blueprints/assets.rs index 3659e4e..9b97f56 100644 --- a/crates/blenvy/src/blueprints/assets.rs +++ b/crates/blenvy/src/blueprints/assets.rs @@ -74,3 +74,26 @@ impl Default for BlueprintAssetsLoadState { } } } + +// for preloading asset files +#[derive(serde::Deserialize, bevy::asset::Asset, bevy::reflect::TypePath, Debug)] +pub(crate) struct File{ + pub(crate) path: String, +} + +#[derive(serde::Deserialize, bevy::asset::Asset, bevy::reflect::TypePath, Debug)] +pub(crate) struct BlueprintPreloadAssets{ + pub(crate) assets: Vec<(String, File)> +} + + +#[derive(Component)] +pub(crate) struct BlueprintMetaHandle(pub Handle); + +/// flag component, usually added when a blueprint is loaded +#[derive(Component)] +pub(crate) struct BlueprintMetaLoaded; + + +#[derive(Component)] +pub(crate) struct BlueprintMetaLoading; \ No newline at end of file diff --git a/crates/blenvy/src/blueprints/materials.rs b/crates/blenvy/src/blueprints/materials.rs index eacaa94..4c5008e 100644 --- a/crates/blenvy/src/blueprints/materials.rs +++ b/crates/blenvy/src/blueprints/materials.rs @@ -57,12 +57,17 @@ pub(crate) fn inject_materials( material_found = Some(material); } else { let model_handle: Handle = asset_server.load(material_info.path.clone()); // FIXME: kinda weird now - let mat_gltf = assets_gltf.get(model_handle.id()).unwrap_or_else(|| { + let Some(mat_gltf) = assets_gltf.get(model_handle.id()) else { + warn!("materials file {} should have been preloaded skipping",material_info.path); + continue; + + }; + /*let mat_gltf = assets_gltf.get(model_handle.id()).unwrap_or_else(|| { panic!( "materials file {} should have been preloaded", material_info.path ) - }); + });*/ if mat_gltf .named_materials .contains_key(&material_info.name as &str) @@ -79,6 +84,7 @@ pub(crate) fn inject_materials( } if let Some(material) = material_found { + info!("Step 6: injecting/replacing materials"); for (child_index, child) in children.iter().enumerate() { if child_index == material_index { if with_materials_and_meshes.contains(*child) { diff --git a/crates/blenvy/src/blueprints/mod.rs b/crates/blenvy/src/blueprints/mod.rs index 278a5dc..c3d75bf 100644 --- a/crates/blenvy/src/blueprints/mod.rs +++ b/crates/blenvy/src/blueprints/mod.rs @@ -1,4 +1,5 @@ pub mod spawn_from_blueprints; +use bevy_common_assets::ron::RonAssetPlugin; pub use spawn_from_blueprints::*; pub mod animation; @@ -110,8 +111,10 @@ impl Plugin for BlueprintsPlugin { .register_type::>() .register_type::() .register_type::>>() - .init_asset::() - .init_asset_loader::() + //.init_asset::() + //.init_asset_loader::() + .add_plugins(RonAssetPlugin::::new(&["meta.ron"]),) + .configure_sets( Update, (GltfBlueprintsSet::Spawn, GltfBlueprintsSet::AfterSpawn) @@ -121,7 +124,8 @@ impl Plugin for BlueprintsPlugin { .add_systems( Update, ( - load_raw_gltf, + blueprints_prepare_metadata_file_for_spawn, + blueprints_check_assets_metadata_files_loading, blueprints_prepare_spawn, blueprints_check_assets_loading, blueprints_assets_loaded, diff --git a/crates/blenvy/src/blueprints/spawn_from_blueprints.rs b/crates/blenvy/src/blueprints/spawn_from_blueprints.rs index 6232d0c..d9a27d6 100644 --- a/crates/blenvy/src/blueprints/spawn_from_blueprints.rs +++ b/crates/blenvy/src/blueprints/spawn_from_blueprints.rs @@ -1,21 +1,17 @@ use std::path::Path; use bevy::{ - asset::{io::Reader, AssetLoader, AsyncReadExt, LoadContext}, gltf::Gltf, prelude::*, scene::SceneInstance, - utils::hashbrown::HashMap, + utils::{hashbrown::HashMap, warn}, }; -use serde_json::Value; use crate::{ - AnimationInfos, AssetLoadTracker, AssetToBlueprintInstancesMapper, BlueprintAnimationInfosLink, - BlueprintAnimationPlayerLink, BlueprintAnimations, BlueprintAssets, BlueprintAssetsLoadState, - BlueprintAssetsLoaded, BlueprintAssetsNotLoaded, InstanceAnimationInfosLink, - InstanceAnimationPlayerLink, InstanceAnimations, WatchingForChanges, + AnimationInfos, AssetLoadTracker, AssetToBlueprintInstancesMapper, BlueprintAnimationInfosLink, BlueprintAnimationPlayerLink, BlueprintAnimations, BlueprintAssets, BlueprintAssetsLoadState, BlueprintAssetsLoaded, BlueprintMetaLoading, BlueprintAssetsNotLoaded, BlueprintPreloadAssets, InstanceAnimationInfosLink, InstanceAnimationPlayerLink, InstanceAnimations, WatchingForChanges }; + /// this is a flag component for our levels/game world #[derive(Component)] pub struct GameWorldTag; @@ -72,6 +68,13 @@ pub(crate) struct OriginalChildren(pub Vec); /// as it would first become invisible before re-appearing again pub struct HideUntilReady; +#[derive(Component, Reflect, Default, Debug)] +#[reflect(Component)] +/// Companion to the `HideUntilReady` component: this stores the visibility of the entity before the blueprint was inserted into it +pub(crate) struct OriginalVisibility(Visibility); + + + #[derive(Component, Reflect, Default, Debug)] #[reflect(Component)] /// marker component, gets added to all children of a currently spawning blueprint instance, can be usefull to avoid manipulating still in progress entities @@ -103,8 +106,6 @@ pub enum BlueprintEvent { /// component gets added when a blueprint starts spawning, removed when spawning is completely done pub struct BlueprintSpawning; -use gltf::Gltf as RawGltf; - /* Overview of the Blueprint Spawning process - Blueprint Load Assets @@ -121,62 +122,139 @@ Overview of the Blueprint Spawning process => distinguish between blueprint instances inside blueprint instances vs blueprint instances inside blueprints ?? */ -#[derive(Asset, TypePath, Debug)] -pub struct RawGltfAsset(pub RawGltf); +pub(super) fn blueprints_prepare_metadata_file_for_spawn( + blueprint_instances_to_spawn: Query<( + Entity, + &BlueprintInfo, + Option<&Name>, + Option<&Parent>, + Option<&HideUntilReady>, + Option<&Visibility>, + Option<&AddToGameWorld>, -#[derive(Default)] -pub(super) struct RawGltfAssetLoader; - -impl AssetLoader for RawGltfAssetLoader { - type Asset = RawGltfAsset; - type Settings = (); - type Error = gltf::Error; - - async fn load<'a>( - &'a self, - reader: &'a mut Reader<'_>, - _settings: &'a (), - _load_context: &'a mut LoadContext<'_>, - ) -> Result { - let mut bytes = Vec::new(); - reader.read_to_end(&mut bytes).await?; - let gltf = RawGltf::from_slice_without_validation(&bytes)?; - Ok(RawGltfAsset(gltf)) - } -} - -#[derive(Debug, Component, Deref, DerefMut)] -#[component(storage = "SparseSet")] -pub(super) struct AssociatedRawGltfHandle(Handle); - -pub(super) fn load_raw_gltf( - blueprint_instances_to_spawn: Query<(Entity, &BlueprintInfo), Added>, + ), (Without, Without, Without)>, + mut game_world: Query>, asset_server: Res, mut commands: Commands, ) { - for (entity, blueprint_info) in blueprint_instances_to_spawn.iter() { - let gltf_handle: Handle = asset_server.load(&blueprint_info.path); - commands - .entity(entity) - .insert(AssociatedRawGltfHandle(gltf_handle)); + for (entity, blueprint_info, entity_name, original_parent, hide_until_ready, original_visibility, add_to_world) in blueprint_instances_to_spawn.iter() { + // get path to assets / metadata file + info!("Step 1: spawn request detected: loading metadata file for {:?}", blueprint_info); + let blueprint_path = blueprint_info.path.clone(); + let metadata_path = blueprint_path.replace(".glb", ".meta.ron").replace(".gltf", ".meta.ron"); // FIXME: horrible + let mut asset_infos: Vec = vec![]; + //let foo_handle:Handle = asset_server.load(metadata_path); + let untyped_handle = asset_server.load_untyped(metadata_path.clone()); + let asset_id = untyped_handle.id(); + + asset_infos.push(AssetLoadTracker { + name: metadata_path.clone(), + path: metadata_path.clone(), + id: asset_id, + loaded: false, + handle: untyped_handle.clone(), + }); + + // add the blueprint spawning marker & co + commands.entity(entity).insert(( + BlueprintAssetsLoadState { + all_loaded: false, + asset_infos, + ..Default::default() + }, + BlueprintMetaLoading, + BlueprintSpawning + )); + + // if the entity has no name, add one based on the blueprint's + if entity_name.is_none(){ + commands + .entity(entity) + .insert(bevy::prelude::Name::from(blueprint_info.name.clone())); + } + + if original_parent.is_none() { + // only allow hiding until ready when the entity does not have a parent (?) + if hide_until_ready.is_some() { + + // if there is already a set visibility, save it for later + if let Some(original_visibility) = original_visibility { + commands.entity(entity).insert(OriginalVisibility(*original_visibility)); + } + // & now hide the instance until it is ready + commands.entity(entity) + .insert(Visibility::Hidden); + } + + // only allow automatically adding a newly spawned blueprint instance to the "world", if the entity does not have a parent + 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); + } + } } } +// TODO: merge with other asset loading checker ? +pub(crate) fn blueprints_check_assets_metadata_files_loading( + mut blueprint_assets_to_load: Query< + (Entity, &BlueprintInfo, &mut BlueprintAssetsLoadState), + With, + >, + asset_server: Res, + mut commands: Commands, +) { + + for (entity, blueprint_info, mut assets_to_load) in blueprint_assets_to_load.iter_mut() { + + let mut all_loaded = true; + let mut loaded_amount = 0; + let total = assets_to_load.asset_infos.len(); + 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); + + let mut failed = false; + if let bevy::asset::LoadState::Failed(_) = asset_server.load_state(asset_id) { + failed = true + } + tracker.loaded = loaded || failed; + if loaded || failed { + loaded_amount += 1; + } else { + all_loaded = false; + } + if all_loaded { + commands.entity(entity).insert(BlueprintMetaHandle(asset_server.load(tracker.path.clone()))).remove::(); + break; + } + + } + let progress: f32 = loaded_amount as f32 / total as f32; + assets_to_load.progress = progress; + // println!("LOADING: in progress for ALL assets of {:?} (instance of {}): {} ",entity_name, blueprint_info.path, progress * 100.0); + } +} + + pub(super) fn blueprints_prepare_spawn( - blueprint_instances_to_spawn: Query<(Entity, &BlueprintInfo, &AssociatedRawGltfHandle)>, + blueprint_instances_to_spawn: Query<(Entity, &BlueprintInfo, &BlueprintMetaHandle, Option<&Name>), Added>, mut commands: Commands, asset_server: Res, // for hot reload watching_for_changes: Res, mut assets_to_blueprint_instances: ResMut, - raw_gltf_assets: Res>, // for debug // all_names: Query<&Name> + blueprint_metas: Res>, + ) { - for (entity, blueprint_info, raw_gltf_handle) in blueprint_instances_to_spawn.iter() { + for (entity, blueprint_info, blueprint_meta_handle, entity_name) in blueprint_instances_to_spawn.iter() { info!( - "BLUEPRINT: to spawn detected: {:?} path:{:?}", - blueprint_info.name, blueprint_info.path + "Step 2: metadata loaded: loading assets for {:?}", + blueprint_info, ); // we add the asset of the blueprint itself // TODO: add detection of already loaded data @@ -197,67 +275,57 @@ pub(super) fn blueprints_prepare_spawn( // and we also add all its assets /* prefetch attempt */ - let Some(RawGltfAsset(gltf)) = raw_gltf_assets.get(&raw_gltf_handle.0) else { - continue; - }; - for scene in gltf.scenes() { - if let Some(scene_extras) = scene.extras().clone() { - let lookup: HashMap = - serde_json::from_str(scene_extras.get()).unwrap(); - if lookup.contains_key("BlueprintAssets") { - let assets_raw = &lookup["BlueprintAssets"]; - //println!("ASSETS RAW {}", assets_raw); - let all_assets: BlueprintAssets = - ron::from_str(assets_raw.as_str().unwrap()).unwrap(); - // println!("all_assets {:?}", all_assets); + if let Some(blenvy_metadata) = blueprint_metas.get(&blueprint_meta_handle.0) { + for asset in blenvy_metadata.assets.iter() { + let asset_path = asset.1.path.clone(); + let asset_name = asset.0.clone(); - for asset in all_assets.assets.iter() { - let untyped_handle = asset_server.load_untyped(&asset.path); - let asset_id = untyped_handle.id(); - let loaded = asset_server.is_loaded_with_dependencies(asset_id); - if !loaded { - asset_infos.push(AssetLoadTracker { - name: asset.name.clone(), - path: asset.path.clone(), - id: asset_id, - loaded: false, - handle: untyped_handle.clone(), - }); - } + let untyped_handle = asset_server.load_untyped(&asset_path); + let asset_id = untyped_handle.id(); + let loaded = asset_server.is_loaded_with_dependencies(asset_id); + if !loaded { + asset_infos.push(AssetLoadTracker { + name: asset_name.clone(), + path: asset_path.clone(), + id: asset_id, + loaded: false, + handle: untyped_handle.clone(), + }); + } - // FIXME: dang, too early, asset server has not yet started loading yet - // let path_id = asset_server.get_path_id(&asset.path).expect("we should have alread checked for this asset"); - let path_id = asset.path.clone(); + // FIXME: dang, too early, asset server has not yet started loading yet + // let path_id = asset_server.get_path_id(&asset.path).expect("we should have alread checked for this asset"); + let path_id = asset_path.clone(); - // Only do this if hot reload is enabled - if watching_for_changes.0 { - if !assets_to_blueprint_instances - .untyped_id_to_blueprint_entity_ids - .contains_key(&path_id) - { - assets_to_blueprint_instances - .untyped_id_to_blueprint_entity_ids - .insert(path_id.clone(), vec![]); - } + // Only do this if hot reload is enabled + if watching_for_changes.0 { + if !assets_to_blueprint_instances + .untyped_id_to_blueprint_entity_ids + .contains_key(&path_id) + { + assets_to_blueprint_instances + .untyped_id_to_blueprint_entity_ids + .insert(path_id.clone(), vec![]); + } - // only insert if not already present in mapping - if !assets_to_blueprint_instances.untyped_id_to_blueprint_entity_ids - [&path_id] - .contains(&entity) - { - // println!("adding mapping between {} and entity {:?}", path_id, all_names.get(entity)); - assets_to_blueprint_instances - .untyped_id_to_blueprint_entity_ids - .get_mut(&path_id) - .unwrap() - .push(entity); - } - } - } + // only insert if not already present in mapping + if !assets_to_blueprint_instances.untyped_id_to_blueprint_entity_ids + [&path_id] + .contains(&entity) + { + // println!("adding mapping between {} and entity {:?}", path_id, all_names.get(entity)); + assets_to_blueprint_instances + .untyped_id_to_blueprint_entity_ids + .get_mut(&path_id) + .unwrap() + .push(entity); } } } - + }else { + warn!("no asset metadata found for {}, please make sure to generate them using the Blender add-on, or preload your assets manually", blueprint_info.path); + } + // Only do this if hot reload is enabled // TODO: should this be added to the list of "all assets" on the blender side instead if watching_for_changes.0 { @@ -299,40 +367,35 @@ pub(super) fn blueprints_prepare_spawn( commands.entity(entity).insert(BlueprintAssetsLoaded); } - // if the entity has no name, add one based on the blueprint's - commands - .entity(entity) - .insert(bevy::prelude::Name::from(blueprint_info.name.clone())); - // add the blueprint spawning marker - commands - .entity(entity) - .insert(BlueprintSpawning) - .remove::(); + commands.entity(entity) + .remove::() + .remove::(); } } /// This system tracks & updates the loading state of all blueprints assets pub(crate) fn blueprints_check_assets_loading( mut blueprint_assets_to_load: Query< - (Entity, &BlueprintInfo, &mut BlueprintAssetsLoadState), + (Entity, &BlueprintInfo, &mut BlueprintAssetsLoadState, Option<&Name>), With, >, asset_server: Res, mut commands: Commands, mut blueprint_events: EventWriter, - // for hot reload - watching_for_changes: Res, ) { - for (entity, blueprint_info, mut assets_to_load) in blueprint_assets_to_load.iter_mut() { + for (entity, blueprint_info, mut assets_to_load, entity_name) in blueprint_assets_to_load.iter_mut() { let mut all_loaded = true; let mut loaded_amount = 0; let total = assets_to_load.asset_infos.len(); 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); - + if loaded { + debug!("LOADED {}", tracker.path.clone()); + } let mut failed = false; if let bevy::asset::LoadState::Failed(_) = asset_server.load_state(asset_id) { + warn!("FAILED TO LOAD {}", tracker.path.clone()); failed = true } tracker.loaded = loaded || failed; @@ -344,7 +407,7 @@ pub(crate) fn blueprints_check_assets_loading( } let progress: f32 = loaded_amount as f32 / total as f32; assets_to_load.progress = progress; - // println!("LOADING: in progress for ALL assets of {:?} (instance of {}): {} ",entity_name, blueprint_info.path, progress * 100.0); + //println!("LOADING: in progress for ALL assets of {:?} (instance of {}): {} ",entity_name, blueprint_info.path, progress * 100.0); if all_loaded { assets_to_load.all_loaded = true; @@ -359,10 +422,6 @@ pub(crate) fn blueprints_check_assets_loading( .entity(entity) .insert(BlueprintAssetsLoaded) .remove::(); - - if !watching_for_changes.0 { - commands.entity(entity).remove::(); //we REMOVE this component when in hot reload is OFF, as we - } } } } @@ -373,20 +432,15 @@ pub(crate) fn blueprints_assets_loaded( Entity, &BlueprintInfo, Option<&Transform>, - Option<&Parent>, - Option<&AddToGameWorld>, Option<&Name>, - Option<&HideUntilReady>, Option<&AnimationInfos>, ), ( - With, Added, Without, ), >, all_children: Query<&Children>, - mut game_world: Query>, assets_gltf: Res>, asset_server: Res, @@ -398,10 +452,7 @@ pub(crate) fn blueprints_assets_loaded( entity, blueprint_info, transform, - original_parent, - add_to_world, name, - hide_until_ready, animation_infos, ) in spawn_placeholders.iter() { @@ -411,8 +462,8 @@ pub(crate) fn blueprints_assets_loaded( );*/ info!( - "BLUEPRINT: all assets loaded, attempting to spawn blueprint SCENE {:?} for entity {:?}, id: {}", - blueprint_info.name, name, entity + "Step 3: all assets loaded, attempting to spawn blueprint scene {:?} for entity {:?}, id: {}", + blueprint_info, name, entity ); // info!("attempting to spawn {:?}", model_path); @@ -479,20 +530,7 @@ pub(crate) fn blueprints_assets_loaded( }, )); - if original_parent.is_none() { - // only allow hiding until ready when the entity does not have a parent (?) - if hide_until_ready.is_some() { - commands.entity(entity).insert(Visibility::Hidden); // visibility: - } - - // only allow automatically adding a newly spawned blueprint instance to the "world", if the entity does not have a parent - 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); - } - } + } } @@ -536,7 +574,7 @@ pub(crate) fn blueprints_scenes_spawned( ) { for (entity, name, children, track_root) in spawned_blueprint_scene_instances.iter() { info!( - "Done spawning blueprint scene for entity named {:?} (track root: {:?})", + "Step 4: Done spawning blueprint scene for entity named {:?} (track root: {:?})", name, track_root ); let mut sub_blueprint_instances: Vec = vec![]; @@ -614,6 +652,8 @@ pub(crate) fn blueprints_scenes_spawned( use crate::CopyComponents; use std::any::TypeId; +use super::{BlueprintMetaHandle, BlueprintMetaLoaded}; + #[derive(Component, Reflect, Debug)] #[reflect(Component)] pub struct BlueprintReadyForPostProcess; @@ -646,7 +686,7 @@ pub(crate) fn blueprints_cleanup_spawned_scene( all_names: Query<&Name>, ) { for (original, children, original_children, name, animations) in blueprint_scenes.iter() { - info!("Cleaning up spawned scene {:?}", name); + info!("Step 5: Cleaning up spawned scene {:?}", name); if children.len() == 0 { // TODO: investigate, Honestly not sure if this issue from Bevy 0.12 is still present at all anymore @@ -783,6 +823,7 @@ pub(crate) fn blueprints_finalize_instances( &BlueprintInfo, Option<&SubBlueprintSpawnRoot>, Option<&HideUntilReady>, + Option<&OriginalVisibility>, ), (With, With), >, @@ -793,10 +834,10 @@ pub(crate) fn blueprints_finalize_instances( mut commands: Commands, // all_names: Query<&Name> ) { - for (entity, name, blueprint_info, parent_blueprint, hide_until_ready) in + for (entity, name, blueprint_info, parent_blueprint, hide_until_ready, original_visibility) in blueprint_instances.iter() { - info!("Finalizing blueprint instance {:?}", name); + info!("Step 8: Finalizing blueprint instance {:?}", name); commands .entity(entity) .remove::() @@ -804,8 +845,9 @@ pub(crate) fn blueprints_finalize_instances( .remove::() .remove::() //.remove::>(); // FIXME: if we delete the handle to the scene, things get despawned ! not what we want - //.remove::(); // also clear the sub assets tracker to free up handles, perhaps just freeing up the handles and leave the rest would be better ? - //.remove::(); + .remove::() // also clear the sub assets tracker to free up handles, perhaps just freeing up the handles and leave the rest would be better ? + .remove::() + .remove::() // we do not need to keep the original children information .insert(BlueprintInstanceReady); // Deal with sub blueprints @@ -839,12 +881,18 @@ pub(crate) fn blueprints_finalize_instances( } } + + commands.entity(entity).remove::(); for child in all_children.iter_descendants(entity) { commands.entity(child).remove::(); } if hide_until_ready.is_some() { - commands.entity(entity).insert(Visibility::Inherited); + if let Some(original_visibility) = original_visibility { + commands.entity(entity).insert(original_visibility.0); + }else { + commands.entity(entity).insert(Visibility::Inherited); + } } blueprint_events.send(BlueprintEvent::InstanceReady { diff --git a/examples/demo/src/core/mod.rs b/examples/demo/src/core/mod.rs index d194048..925ce20 100644 --- a/examples/demo/src/core/mod.rs +++ b/examples/demo/src/core/mod.rs @@ -4,8 +4,7 @@ use blenvy::*; pub struct CorePlugin; impl Plugin for CorePlugin { fn build(&self, app: &mut App) { - app.add_plugins((BlueprintsPlugin { - material_library: true, + app.add_plugins((BlenvyPlugin { ..Default::default() },)); } diff --git a/testing/bevy_example/src/core/mod.rs b/testing/bevy_example/src/core/mod.rs index f26f2de..13b669c 100644 --- a/testing/bevy_example/src/core/mod.rs +++ b/testing/bevy_example/src/core/mod.rs @@ -13,7 +13,6 @@ impl Plugin for CorePlugin { fn build(&self, app: &mut App) { app.add_plugins( BlenvyPlugin { - aabbs: true, registry_component_filter: SceneFilter::Denylist(HashSet::from([ // this is using Bevy's build in SceneFilter, you can compose what components you want to allow/deny TypeId::of::(), diff --git a/tools/blenvy/add_ons/auto_export/blueprints/export_blueprints.py b/tools/blenvy/add_ons/auto_export/blueprints/export_blueprints.py index fb044ec..a09ad53 100644 --- a/tools/blenvy/add_ons/auto_export/blueprints/export_blueprints.py +++ b/tools/blenvy/add_ons/auto_export/blueprints/export_blueprints.py @@ -1,12 +1,10 @@ import os import bpy -from blenvy.assets.assets_scan import get_blueprint_asset_tree -from blenvy.assets.generate_asset_file import write_ron_assets_file from ....materials.materials_helpers import add_material_info_to_objects, get_blueprint_materials from ..constants import TEMPSCENE_PREFIX from ..common.generate_temporary_scene_and_export import generate_temporary_scene_and_export, copy_hollowed_collection_into, clear_hollow_scene from ..common.export_gltf import generate_gltf_export_settings -from ..utils import upsert_blueprint_assets +from ..utils import upsert_blueprint_assets, write_blueprint_metadata_file def export_blueprints(blueprints, settings, blueprints_data): blueprints_path_full = getattr(settings, "blueprints_path_full") @@ -33,6 +31,9 @@ def export_blueprints(blueprints, settings, blueprints_data): (_, materials_per_object) = get_blueprint_materials(blueprint) add_material_info_to_objects(materials_per_object, settings) + write_blueprint_metadata_file(blueprint=blueprint, blueprints_data=blueprints_data, settings=settings) + + # do the actual export generate_temporary_scene_and_export( settings, diff --git a/tools/blenvy/add_ons/auto_export/common/generate_temporary_scene_and_export.py b/tools/blenvy/add_ons/auto_export/common/generate_temporary_scene_and_export.py index 2615970..2665d5f 100644 --- a/tools/blenvy/add_ons/auto_export/common/generate_temporary_scene_and_export.py +++ b/tools/blenvy/add_ons/auto_export/common/generate_temporary_scene_and_export.py @@ -65,8 +65,9 @@ def generate_temporary_scene_and_export(settings, gltf_export_settings, gltf_out except Exception as error: print("failed to export gltf !", error) raise error - # restore everything - tempScene_cleaner(temp_scene, scene_filler_data) + finally: + # restore everything + tempScene_cleaner(temp_scene, scene_filler_data) # reset active scene bpy.context.window.scene = active_scene diff --git a/tools/blenvy/add_ons/auto_export/levels/export_levels.py b/tools/blenvy/add_ons/auto_export/levels/export_levels.py index 0ce9c5c..20ec344 100644 --- a/tools/blenvy/add_ons/auto_export/levels/export_levels.py +++ b/tools/blenvy/add_ons/auto_export/levels/export_levels.py @@ -1,9 +1,10 @@ import os + from ..constants import TEMPSCENE_PREFIX from ..common.generate_temporary_scene_and_export import generate_temporary_scene_and_export, copy_hollowed_collection_into, clear_hollow_scene from ..common.export_gltf import (generate_gltf_export_settings, export_gltf) from .is_object_dynamic import is_object_dynamic, is_object_static -from ..utils import upsert_scene_assets +from ..utils import upsert_scene_assets, write_level_metadata_file def export_level_scene(scene, settings, blueprints_data): @@ -28,6 +29,7 @@ def export_level_scene(scene, settings, blueprints_data): # we inject assets into the scene before it gets exported # TODO: this should be done in the temporary scene ! upsert_scene_assets(scene, blueprints_data=blueprints_data, settings=settings) + write_level_metadata_file(scene=scene, blueprints_data=blueprints_data, settings=settings) if export_separate_dynamic_and_static_objects: #print("SPLIT STATIC AND DYNAMIC") diff --git a/tools/blenvy/add_ons/auto_export/utils.py b/tools/blenvy/add_ons/auto_export/utils.py index 661aae2..b2bc3cc 100644 --- a/tools/blenvy/add_ons/auto_export/utils.py +++ b/tools/blenvy/add_ons/auto_export/utils.py @@ -4,6 +4,7 @@ from pathlib import Path from blenvy.assets.assets_scan import get_blueprint_asset_tree, get_level_scene_assets_tree2 from blenvy.add_ons.bevy_components.utils import is_component_valid_and_enabled from .constants import custom_properties_to_filter_out +from ...assets.assets_scan import get_level_scene_assets_tree2 def remove_unwanted_custom_properties(object): to_remove = [] @@ -42,3 +43,45 @@ def upsert_blueprint_assets(blueprint, blueprints_data, settings): print("all_assets_raw", all_assets_raw) print("local assets", local_assets) blueprint.collection["BlueprintAssets"] = assets_to_fake_ron(local_assets) + +import os +def write_level_metadata_file(scene, blueprints_data, settings): + levels_path_full = getattr(settings,"levels_path_full") + all_assets_raw = get_level_scene_assets_tree2(level_scene=scene, blueprints_data=blueprints_data, settings=settings) + + formated_assets = [] + for asset in all_assets_raw: + #if asset["internal"] : + formated_asset = f'\n ("{asset["name"]}", File ( path: "{asset["path"]}" )),' + formated_assets.append(formated_asset) + + metadata_file_path_full = os.path.join(levels_path_full, scene.name+".meta.ron") + os.makedirs(os.path.dirname(metadata_file_path_full), exist_ok=True) + + with open(metadata_file_path_full, "w") as assets_file: + assets_file.write("(\n ") + assets_file.write(" assets:\n [ ") + assets_file.writelines(formated_assets) + assets_file.write("\n ]\n") + assets_file.write(")") + +def write_blueprint_metadata_file(blueprint, blueprints_data, settings): + blueprints_path_full = getattr(settings,"blueprints_path_full") + all_assets_raw = get_blueprint_asset_tree(blueprint=blueprint, blueprints_data=blueprints_data, settings=settings) + + formated_assets = [] + for asset in all_assets_raw: + #if asset["internal"] : + formated_asset = f'\n ("{asset["name"]}", File ( path: "{asset["path"]}" )),' + formated_assets.append(formated_asset) + + + metadata_file_path_full = os.path.join(blueprints_path_full, blueprint.name+".meta.ron") + os.makedirs(os.path.dirname(metadata_file_path_full), exist_ok=True) + + with open(metadata_file_path_full, "w") as assets_file: + assets_file.write("(\n ") + assets_file.write(" assets:\n [ ") + assets_file.writelines(formated_assets) + assets_file.write("\n ]\n") + assets_file.write(")") \ No newline at end of file