diff --git a/crates/bevy_gltf_blueprints/src/lib.rs b/crates/bevy_gltf_blueprints/src/lib.rs index 366f05f..0b8e2f6 100644 --- a/crates/bevy_gltf_blueprints/src/lib.rs +++ b/crates/bevy_gltf_blueprints/src/lib.rs @@ -120,6 +120,10 @@ impl Plugin for BlueprintsPlugin { .register_type::() .register_type::() .register_type::() + .register_type::() + .register_type::>() + .register_type::>>() + .insert_resource(BluePrintsConfig { format: self.format, library_folder: self.library_folder.clone(), @@ -140,7 +144,10 @@ impl Plugin for BlueprintsPlugin { .add_systems( Update, ( - spawn_from_blueprints, + (spawn_from_blueprints, + check_for_loaded, + actually_spawn_stuff, apply_deferred).chain(), + compute_scene_aabbs.run_if(aabbs_enabled), apply_deferred.run_if(aabbs_enabled), apply_deferred, diff --git a/crates/bevy_gltf_blueprints/src/spawn_from_blueprints.rs b/crates/bevy_gltf_blueprints/src/spawn_from_blueprints.rs index 3701fa5..f896723 100644 --- a/crates/bevy_gltf_blueprints/src/spawn_from_blueprints.rs +++ b/crates/bevy_gltf_blueprints/src/spawn_from_blueprints.rs @@ -1,6 +1,6 @@ use std::path::{Path, PathBuf}; -use bevy::{gltf::Gltf, prelude::*}; +use bevy::{gltf::Gltf, prelude::*, utils::HashMap}; use crate::{Animations, BluePrintsConfig}; @@ -46,22 +46,168 @@ pub struct AddToGameWorld; /// helper component, just to transfer child data pub(crate) struct OriginalChildren(pub Vec); -/// main spawning functions, + +#[derive(Component, Reflect, Default, Debug)] +#[reflect(Component)] +pub struct BlueprintsList(pub HashMap>); + + +#[derive(Reflect, Default, Debug)] +pub(crate) struct BlueprintLoadTracker{ + pub name: String, + pub id: AssetId, + pub loaded: bool, + pub handle: Handle +} +#[derive(Component, Default, Debug)] +pub(crate) struct BlueprintAssetsToLoad{ + pub all_loaded: bool, + pub asset_infos: Vec, + pub progress: f32 +} + +/// flag component +#[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 spawn_from_blueprints( spawn_placeholders: Query< ( Entity, &BlueprintName, - Option<&Transform>, Option<&Parent>, Option<&Library>, - Option<&AddToGameWorld>, Option<&Name>, + + Option<&BlueprintsList>, + // Option<&BlueprintAssetsPreloaded> + ), (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!( + "preparing 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![]; + for (blueprint_name, _) in blueprints_list.0.iter() { + /*if blueprint_name == what { + println!("WHOLY MOLLY !") + }*/ + // println!("library path {:?}", library_path); + let mut library_path = &blueprints_config.library_folder; // TODO: we cannot use the overriden library path + // FIXME: hack + if blueprint_name == "World" { + library_path= &library_override.unwrap().0; + } + + 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(BlueprintLoadTracker{ + 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.len() > 0 { + commands + .entity(entity) + .insert(BlueprintAssetsToLoad{ + all_loaded: false, + asset_infos: asset_infos, + ..Default::default() + }) + .insert(BlueprintAssetsNotLoaded); + }else { + commands + .entity(entity) + .insert(BlueprintAssetsLoaded); + } + } + } +} + + +pub(crate) fn check_for_loaded( + mut blueprint_assets_to_load: Query<(Entity, &mut BlueprintAssetsToLoad),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 actually_spawn_stuff( + 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>, @@ -70,7 +216,9 @@ pub(crate) fn spawn_from_blueprints( blueprints_config: Res, children: Query<&Children>, -) { +){ + + for ( entity, blupeprint_name, @@ -79,20 +227,14 @@ pub(crate) fn spawn_from_blueprints( library_override, add_to_world, name, + ) in spawn_placeholders.iter() { - debug!( + info!( "need to spawn {:?} for entity {:?}, id: {:?}, parent:{:?}", blupeprint_name.0, name, entity, original_parent ); - let mut original_children: Vec = vec![]; - if let Ok(c) = children.get(entity) { - for child in c.iter() { - original_children.push(*child); - } - } - let what = &blupeprint_name.0; let model_file_name = format!("{}.{}", &what, &blueprints_config.format); @@ -101,7 +243,7 @@ pub(crate) fn spawn_from_blueprints( 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())); - debug!("attempting to spawn {:?}", model_path); + info!("attempting to spawn {:?}", model_path); let model_handle: Handle = asset_server.load(model_path); let gltf = assets_gltf @@ -123,6 +265,12 @@ pub(crate) fn spawn_from_blueprints( 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(), @@ -143,4 +291,4 @@ pub(crate) fn spawn_from_blueprints( commands.entity(world).add_child(entity); } } -} +} \ No newline at end of file diff --git a/crates/bevy_gltf_blueprints/src/spawn_post_process.rs b/crates/bevy_gltf_blueprints/src/spawn_post_process.rs index 2caa625..ef12350 100644 --- a/crates/bevy_gltf_blueprints/src/spawn_post_process.rs +++ b/crates/bevy_gltf_blueprints/src/spawn_post_process.rs @@ -6,7 +6,7 @@ use bevy::scene::SceneInstance; use super::{AnimationPlayerLink, Animations}; use super::{SpawnHere, Spawned}; -use crate::{CopyComponents, InBlueprint, NoInBlueprint, OriginalChildren}; +use crate::{BlueprintAssetsToLoad, CopyComponents, InBlueprint, NoInBlueprint, OriginalChildren}; /// this system is in charge of doing any necessary post processing after a blueprint scene has been spawned /// - it removes one level of useless nesting @@ -89,6 +89,7 @@ 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(root_entity).despawn_recursive(); } } diff --git a/tools/gltf_auto_export/auto_export/export_main_scenes.py b/tools/gltf_auto_export/auto_export/export_main_scenes.py index 8bdb018..25cfd37 100644 --- a/tools/gltf_auto_export/auto_export/export_main_scenes.py +++ b/tools/gltf_auto_export/auto_export/export_main_scenes.py @@ -4,7 +4,7 @@ import bpy from ..helpers.generate_and_export import generate_and_export from .export_gltf import (generate_gltf_export_preferences, export_gltf) from ..modules.bevy_dynamic import is_object_dynamic, is_object_static -from ..helpers.helpers_scenes import clear_hollow_scene, copy_hollowed_collection_into +from ..helpers.helpers_scenes import clear_hollow_scene, copy_hollowed_collection_into, inject_blueprints_list_into_main_scene # export all main scenes @@ -29,6 +29,8 @@ def export_main_scene(scene, folder_path, addon_prefs, library_collections): } if export_blueprints : + inject_blueprints_list_into_main_scene(scene) + if export_separate_dynamic_and_static_objects: #print("SPLIT STATIC AND DYNAMIC") # first export static objects diff --git a/tools/gltf_auto_export/helpers/helpers_collections.py b/tools/gltf_auto_export/helpers/helpers_collections.py index edb3214..a1cd927 100644 --- a/tools/gltf_auto_export/helpers/helpers_collections.py +++ b/tools/gltf_auto_export/helpers/helpers_collections.py @@ -36,16 +36,23 @@ def get_marked_collections(scene, addon_prefs): return (collection_names, marked_collections) # gets all collections within collections that might also be relevant -def get_sub_collections(collections, parent, children_per_collection): +def get_sub_collections(collections, parent=None, children_per_collection=None): + if parent == None: + parent = CollectionNode() + if children_per_collection == None: + children_per_collection = {} + collection_names = set() used_collections = [] for root_collection in collections: - node = Node(name=root_collection.name, parent=parent) + print("collections", collections) + node = CollectionNode(name=root_collection.name, parent=parent) parent.children.append(node) #print("root collection", root_collection.name) for collection in traverse_tree(root_collection): # TODO: filter out COLLECTIONS that have the flatten flag (unlike the flatten flag on colleciton instances themselves) + print("sub", collection) node_name = collection.name children_per_collection[node_name] = [] #print(" scanning", collection.name) @@ -53,12 +60,17 @@ def get_sub_collections(collections, parent, children_per_collection): #print("FLATTEN", object.name, 'Flatten' in object) if object.instance_type == 'COLLECTION' : # and not 'Flatten' in object: collection_name = object.instance_collection.name + print("sub obj", collection_name) + # FIXME: not sure: + children_per_collection[node_name].append(collection_name) + (sub_names, sub_collections) = get_sub_collections([object.instance_collection], node, children_per_collection) + print("gna", sub_names, sub_collections) if len(list(sub_names)) > 0: + print("toto") children_per_collection[node_name] += (list(sub_names)) #print(" found sub collection in use", object.name, object.instance_collection) - if not collection_name in collection_names: collection_names.add(collection_name) used_collections.append(object.instance_collection) @@ -77,7 +89,7 @@ def flatten_collection_tree(node, children_per_collection): children_per_collection[node.name] = list(set( children_per_collection[node.name])) -class Node : +class CollectionNode : def __init__(self, name="", parent=None): self.name = name self.children = [] @@ -93,7 +105,7 @@ def get_exportable_collections(main_scenes, library_scenes, addon_prefs): all_collections = [] all_collection_names = [] - root_node = Node() + root_node = CollectionNode() root_node.name = "root" children_per_collection = {} diff --git a/tools/gltf_auto_export/helpers/helpers_scenes.py b/tools/gltf_auto_export/helpers/helpers_scenes.py index e5b4dfb..5d49357 100644 --- a/tools/gltf_auto_export/helpers/helpers_scenes.py +++ b/tools/gltf_auto_export/helpers/helpers_scenes.py @@ -1,5 +1,6 @@ +import json import bpy -from .helpers_collections import (set_active_collection) +from .helpers_collections import (CollectionNode, get_sub_collections, get_used_collections, set_active_collection) from .object_makers import (make_empty) @@ -149,3 +150,40 @@ def get_scenes(addon_prefs): return [level_scene_names, level_scenes, library_scene_names, library_scenes] + + +def inject_blueprints_list_into_main_scene(scene): + print("injecting assets data") + root_collection = scene.collection + assets_list = None + for object in scene.objects: + if object.name == "assets_list"+scene.name: + assets_list = object + break + + if assets_list is None: + assets_list = make_empty('assets_list_'+scene.name, [0,0,0], [0,0,0], [0,0,0], root_collection) + + + # find all blueprints used in a scene + # TODO: export a tree rather than a flat list ? because you could have potential clashing items in flat lists (amongst other issues) + (collection_names, collections) = get_used_collections(scene) + root_node = CollectionNode() + root_node.name = "root" + children_per_collection = {} + + #print("collection_names", collection_names, "collections", collections) + (bla, bli ) = get_sub_collections(collections, root_node, children_per_collection) + #print("sfdsfsdf", bla, bli, "root", root_node, "children_per_collection", children_per_collection) + # with sub collections + # (collection_names, collections) = get_sub_collections(all_collections, root_node, children_per_collection) + # + # what about marked assets ? + #assets_list["blueprints_direct"] = list(collection_names) + assets_list["BlueprintsList"] = f"({json.dumps(dict(children_per_collection))})" + #'({"a":[]})' + #'([])' + # + # + + print("assets list", assets_list["BlueprintsList"], children_per_collection) diff --git a/tools/gltf_auto_export/tests/test_bevy_integration.py b/tools/gltf_auto_export/tests/test_bevy_integration.py index afd5255..d91c148 100644 --- a/tools/gltf_auto_export/tests/test_bevy_integration.py +++ b/tools/gltf_auto_export/tests/test_bevy_integration.py @@ -20,7 +20,7 @@ def setup_data(request): #other_materials_path = os.path.join("../../testing", "other_materials") print("\nPerforming teardown...") - if os.path.exists(models_path): + '''if os.path.exists(models_path): shutil.rmtree(models_path) """if os.path.exists(materials_path): @@ -34,7 +34,7 @@ def setup_data(request): screenshot_observed_path = os.path.join(root_path, "screenshot.png") if os.path.exists(screenshot_observed_path): - os.remove(screenshot_observed_path) + os.remove(screenshot_observed_path)''' request.addfinalizer(finalizer)