From ed0c85b66efbb97c69fa09798ac1a86d66be95a7 Mon Sep 17 00:00:00 2001 From: "kaosat.dev" Date: Mon, 10 Jun 2024 00:31:23 +0200 Subject: [PATCH] feat(Blenvy): more work done on materials handling overhaul (wip) * split out materials scan from injection of materialInfo into objects * added material Asset injection into list of assets at scene level * related tweaks & cleanups * continued overhaul on the bevy side --- crates/bevy_gltf_blueprints/src/materials.rs | 42 +++++++++---------- .../src/spawn_from_blueprints.rs | 8 ++++ tools/blenvy/TODO.md | 6 ++- .../auto_export/common/export_materials.py | 11 ++--- .../auto_export/common/settings_diff.py | 1 - .../auto_export/levels/export_main_scenes.py | 16 +++++-- tools/blenvy/add_ons/auto_export/settings.py | 7 ---- tools/blenvy/add_ons/auto_export/ui.py | 1 - tools/blenvy/blueprints/blueprints_scan.py | 6 +-- tools/blenvy/materials/materials_helpers.py | 27 ++++++++---- tools/blenvy/tests/test_bevy_integration.py | 2 +- .../tests/test_bevy_integration_prepare.py | 2 +- tools/blenvy/tests/test_export_parameters.py | 1 - 13 files changed, 76 insertions(+), 54 deletions(-) diff --git a/crates/bevy_gltf_blueprints/src/materials.rs b/crates/bevy_gltf_blueprints/src/materials.rs index d11ffde..7ca2cae 100644 --- a/crates/bevy_gltf_blueprints/src/materials.rs +++ b/crates/bevy_gltf_blueprints/src/materials.rs @@ -17,14 +17,14 @@ use bevy::{ render::mesh::Mesh, }; -use crate::{AssetLoadTracker, AssetsToLoad, BluePrintsConfig}; +use crate::{AssetLoadTracker, AssetsToLoad, BluePrintsConfig, BlueprintInstanceReady}; #[derive(Component, Reflect, Default, Debug)] #[reflect(Component)] -/// struct containing the name & source of the material to apply +/// struct containing the name & path of the material to apply pub struct MaterialInfo { pub name: String, - pub source: String, + pub path: String, } /// flag component @@ -37,21 +37,21 @@ pub(crate) struct BlueprintMaterialAssetsNotLoaded; /// system that injects / replaces materials from material library pub(crate) fn materials_inject( blueprints_config: ResMut, - material_infos: Query<(Entity, &MaterialInfo), Added>, + + ready_blueprints: Query<(Entity, &Children), (With)>, + material_infos: Query<(Entity, &MaterialInfo, &Parent), Added>, asset_server: Res, mut commands: Commands, ) { - for (entity, material_info) in material_infos.iter() { - 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 + /*for(entity, children) in ready_blueprints.iter() { + println!("Blueprint ready !"); + } */ + for (entity, material_info, parent) in material_infos.iter() { + println!("Entity with material info {:?} {:?}", entity, material_info); + let parent_blueprint = ready_blueprints.get(parent.get()); + println!("Parent blueprint {:?}", parent_blueprint) + /*if blueprints_config .material_library_cache .contains_key(&material_full_path) { @@ -68,7 +68,7 @@ pub(crate) fn materials_inject( let material_file_id = material_file_handle.id(); // FIXME: fix this stuff - /*let asset_infos: Vec = vec![AssetLoadTracker { + let asset_infos: Vec = vec![AssetLoadTracker { name: material_full_path, id: material_file_id, loaded: false, @@ -83,17 +83,17 @@ 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), + (Entity, &mut AssetsToLoad), With, >, asset_server: Res, @@ -125,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, @@ -152,7 +152,7 @@ pub(crate) fn materials_inject2( for (material_info, children) in material_infos.iter() { let model_file_name = format!( "{}_materials_library.{}", - &material_info.source, &blueprints_config.format + &material_info.path, &blueprints_config.format ); let materials_path = Path::new(&blueprints_config.material_library_folder) .join(Path::new(model_file_name.as_str())); diff --git a/crates/bevy_gltf_blueprints/src/spawn_from_blueprints.rs b/crates/bevy_gltf_blueprints/src/spawn_from_blueprints.rs index 42c9e30..bf2d236 100644 --- a/crates/bevy_gltf_blueprints/src/spawn_from_blueprints.rs +++ b/crates/bevy_gltf_blueprints/src/spawn_from_blueprints.rs @@ -27,6 +27,13 @@ pub struct SpawnHere; /// flag component for dynamically spawned scenes pub struct Spawned; + +#[derive(Component)] +/// 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 @@ -298,6 +305,7 @@ pub(crate) fn spawn_from_blueprints2( ..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 diff --git a/tools/blenvy/TODO.md b/tools/blenvy/TODO.md index 4d8a5c8..7d62a76 100644 --- a/tools/blenvy/TODO.md +++ b/tools/blenvy/TODO.md @@ -113,10 +113,14 @@ General issues: - [ ] add option to 'split out' meshes from blueprints ? - [ ] ie considering meshletts etc , it would make sense to keep blueprints seperate from purely mesh gltfs -- [ ] remove 'export_marked_assets' it should be a default setting +- [x] 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 - [x] fix level asets UI +- [ ] persist exported materials path in blueprints so that it can be read from library file users + - [ ] just like "export_path" write it into each blueprint's collection + - [ ] scan for used materials per blueprint ! + - [ ] for scenes, scan for used materials of all non instance objects (TODO: what about overrides ?) - [x] remove BlueprintsList & replace is with assets list 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 3ce04a0..2577e00 100644 --- a/tools/blenvy/add_ons/auto_export/common/export_materials.py +++ b/tools/blenvy/add_ons/auto_export/common/export_materials.py @@ -4,7 +4,7 @@ from pathlib import Path from blenvy.core.helpers_collections import (traverse_tree) from blenvy.core.object_makers import make_cube -from blenvy.materials.materials_helpers import get_all_materials +from blenvy.materials.materials_helpers import add_material_info_to_objects, get_all_materials from .generate_temporary_scene_and_export import generate_temporary_scene_and_export from .export_gltf import (generate_gltf_export_settings) @@ -69,8 +69,8 @@ def export_materials(collections, library_scenes, settings): gltf_export_settings = generate_gltf_export_settings(settings) materials_path_full = getattr(settings,"materials_path_full") - used_material_names = get_all_materials(collections, library_scenes) - current_project_name = Path(bpy.context.blend_data.filepath).stem + (used_material_names, materials_per_object) = get_all_materials(collections, library_scenes) + add_material_info_to_objects(materials_per_object, settings) gltf_export_settings = { **gltf_export_settings, 'use_active_scene': True, @@ -80,8 +80,9 @@ def export_materials(collections, library_scenes, settings): 'use_renderable': False, 'export_apply':True } - - gltf_output_path = os.path.join(materials_path_full, current_project_name + "_materials_library") + + current_project_name = Path(bpy.context.blend_data.filepath).stem + gltf_output_path = os.path.join(materials_path_full, current_project_name + "_materials") print(" exporting Materials to", gltf_output_path, ".gltf/glb") diff --git a/tools/blenvy/add_ons/auto_export/common/settings_diff.py b/tools/blenvy/add_ons/auto_export/common/settings_diff.py index 9ece74c..40e26db 100644 --- a/tools/blenvy/add_ons/auto_export/common/settings_diff.py +++ b/tools/blenvy/add_ons/auto_export/common/settings_diff.py @@ -21,7 +21,6 @@ parameter_names_whitelist_auto_export = [ 'export_separate_dynamic_and_static_objects', 'export_materials_library', 'collection_instances_combine_mode', - 'export_marked_assets' ] def get_setting_changes(): diff --git a/tools/blenvy/add_ons/auto_export/levels/export_main_scenes.py b/tools/blenvy/add_ons/auto_export/levels/export_main_scenes.py index c807536..b9d1da2 100644 --- a/tools/blenvy/add_ons/auto_export/levels/export_main_scenes.py +++ b/tools/blenvy/add_ons/auto_export/levels/export_main_scenes.py @@ -1,6 +1,9 @@ import json import os +from pathlib import Path from types import SimpleNamespace + +import bpy from blenvy.blueprints.blueprint_helpers import inject_blueprints_list_into_main_scene, remove_blueprints_list_from_main_scene 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 @@ -49,9 +52,9 @@ def export_main_scene(scene, settings, blueprints_data): auto_assets = [] all_assets = [] + export_gltf_extension = getattr(settings.auto_export, "export_gltf_extension", ".glb") blueprints_path = getattr(settings, "blueprints_path") - export_gltf_extension = getattr(settings.auto_export, "export_gltf_extension", ".glb") for blueprint in blueprints_in_scene: if blueprint.local: blueprint_exported_path = os.path.join(blueprints_path, f"{blueprint.name}{export_gltf_extension}") @@ -69,8 +72,15 @@ def export_main_scene(scene, settings, blueprints_data): """for asset in auto_assets: print(" generated asset", asset.name, asset.path)""" - scene["local_assets"] = assets_to_fake_ron([{"name": asset.name, "path": asset.path} for asset in scene.user_assets] + auto_assets) - scene["AllAssets"] = assets_to_fake_ron(all_assets + [{"name": asset.name, "path": asset.path} for asset in scene.user_assets] + auto_assets) + materials_path = getattr(settings, "materials_path") + current_project_name = Path(bpy.context.blend_data.filepath).stem + materials_library_name = f"{current_project_name}_materials" + materials_exported_path = os.path.join(materials_path, f"{materials_library_name}{export_gltf_extension}") + material_assets = [{"name": materials_library_name, "path": materials_exported_path}] # we also add the material library as an asset + + + scene["local_assets"] = assets_to_fake_ron([{"name": asset.name, "path": asset.path} for asset in scene.user_assets] + auto_assets + material_assets) + scene["AllAssets"] = assets_to_fake_ron(all_assets + [{"name": asset.name, "path": asset.path} for asset in scene.user_assets] + auto_assets + material_assets) if export_separate_dynamic_and_static_objects: diff --git a/tools/blenvy/add_ons/auto_export/settings.py b/tools/blenvy/add_ons/auto_export/settings.py index 1b4237e..f2ccea2 100644 --- a/tools/blenvy/add_ons/auto_export/settings.py +++ b/tools/blenvy/add_ons/auto_export/settings.py @@ -101,13 +101,6 @@ class AutoExportSettings(PropertyGroup): update=save_settings ) # type: ignore - export_marked_assets: BoolProperty( - name='Auto export marked assets', - description='Collections that have been marked as assets will be systematically exported, even if not in use in another scene', - default=True, - update=save_settings - ) # type: ignore - dry_run: EnumProperty( name="dry run", description="debug/ develop helper to enable everything but the actual exporting of files", diff --git a/tools/blenvy/add_ons/auto_export/ui.py b/tools/blenvy/add_ons/auto_export/ui.py index 6c84d1a..d41772e 100644 --- a/tools/blenvy/add_ons/auto_export/ui.py +++ b/tools/blenvy/add_ons/auto_export/ui.py @@ -51,7 +51,6 @@ def draw_settings_ui(layout, auto_export_settings): # collections/blueprints section.prop(auto_export_settings, "collection_instances_combine_mode") - section.prop(auto_export_settings, "export_marked_assets") section.separator() section.prop(auto_export_settings, "export_separate_dynamic_and_static_objects") diff --git a/tools/blenvy/blueprints/blueprints_scan.py b/tools/blenvy/blueprints/blueprints_scan.py index 07fef8f..cd7fa91 100644 --- a/tools/blenvy/blueprints/blueprints_scan.py +++ b/tools/blenvy/blueprints/blueprints_scan.py @@ -8,8 +8,6 @@ from .blueprint import Blueprint # - with the "auto_export" flag # https://blender.stackexchange.com/questions/167878/how-to-get-all-collections-of-the-current-scene def blueprints_scan(main_scenes, library_scenes, settings): - export_marked_assets = getattr(settings.auto_export, "export_marked_assets") - blueprints = {} blueprints_from_objects = {} blueprint_name_from_instances = {} @@ -84,12 +82,12 @@ def blueprints_scan(main_scenes, library_scenes, settings): if ( 'AutoExport' in collection and collection['AutoExport'] == True # get marked collections - or export_marked_assets and collection.asset_data is not None # or if you have marked collections as assets you can auto export them too + or collection.asset_data is not None # or if you have marked collections as assets you can auto export them too or collection.name in list(internal_collection_instances.keys()) # or if the collection has an instance in one of the main scenes ): blueprint = Blueprint(collection.name) blueprint.local = True - blueprint.marked = 'AutoExport' in collection and collection['AutoExport'] == True or export_marked_assets and collection.asset_data is not None + blueprint.marked = 'AutoExport' in collection and collection['AutoExport'] == True or collection.asset_data is not None blueprint.objects = [object.name for object in collection.all_objects if not object.instance_type == 'COLLECTION'] # inneficient, double loop blueprint.nested_blueprints = [object.instance_collection.name for object in collection.all_objects if object.instance_type == 'COLLECTION'] # FIXME: not precise enough, aka "what is a blueprint" blueprint.collection = collection diff --git a/tools/blenvy/materials/materials_helpers.py b/tools/blenvy/materials/materials_helpers.py index 78adbb4..152ee48 100644 --- a/tools/blenvy/materials/materials_helpers.py +++ b/tools/blenvy/materials/materials_helpers.py @@ -4,32 +4,43 @@ from pathlib import Path from ..core.helpers_collections import (traverse_tree) # get materials per object, and injects the materialInfo component -def get_materials(object): +def get_materials(object, materials_per_object): material_slots = object.material_slots used_materials_names = [] - #materials_per_object = {} - current_project_name = Path(bpy.context.blend_data.filepath).stem for m in material_slots: material = m.material # print(" slot", m, "material", material) used_materials_names.append(material.name) # TODO:, also respect slots & export multiple materials if applicable ! - # TODO: do NOT modify objects like this !! do it in a different function - object['MaterialInfo'] = '(name: "'+material.name+'", source: "'+current_project_name + '")' - + materials_per_object[object] = material return used_materials_names def get_all_materials(collection_names, library_scenes): used_material_names = [] + materials_per_object = {} + for scene in library_scenes: root_collection = scene.collection for cur_collection in traverse_tree(root_collection): if cur_collection.name in collection_names: for object in cur_collection.all_objects: - used_material_names = used_material_names + get_materials(object) + used_material_names = used_material_names + get_materials(object, materials_per_object) # we only want unique names used_material_names = list(set(used_material_names)) - return used_material_names \ No newline at end of file + return (used_material_names, materials_per_object) + +def add_material_info_to_objects(materials_per_object, settings): + materials_path = getattr(settings, "materials_path") + export_gltf_extension = getattr(settings.auto_export, "export_gltf_extension", ".glb") + + current_project_name = Path(bpy.context.blend_data.filepath).stem + materials_library_name = f"{current_project_name}_materials" + materials_exported_path = os.path.join(materials_path, f"{materials_library_name}{export_gltf_extension}") + for object in materials_per_object.keys(): + material = materials_per_object[object] + # TODO: switch to using actual components ? + materials_exported_path = os.path.join(materials_path, f"{materials_library_name}{export_gltf_extension}") + object['MaterialInfo'] = f'(name: "{material.name}", path: "{materials_exported_path}")' \ No newline at end of file diff --git a/tools/blenvy/tests/test_bevy_integration.py b/tools/blenvy/tests/test_bevy_integration.py index ac66a53..532c613 100644 --- a/tools/blenvy/tests/test_bevy_integration.py +++ b/tools/blenvy/tests/test_bevy_integration.py @@ -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 = False # TODO: switch back + blenvy.auto_export.export_materials_library = True 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 4ea7cd0..fc59799 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 = False # TODO switch back + blenvy.auto_export.export_materials_library = True print("SCENES", bpy.data.scenes) for scene in bpy.data.scenes: diff --git a/tools/blenvy/tests/test_export_parameters.py b/tools/blenvy/tests/test_export_parameters.py index 447da9d..693e426 100644 --- a/tools/blenvy/tests/test_export_parameters.py +++ b/tools/blenvy/tests/test_export_parameters.py @@ -223,7 +223,6 @@ def test_export_do_not_export_marked_assets(setup_data): export_output_folder="./models", export_scene_settings=True, export_blueprints=True, - export_marked_assets = False ) assert os.path.exists(os.path.join(setup_data["levels_path"], "World.glb")) == True assert os.path.exists(os.path.join(setup_data["blueprints_path"], "Blueprint1.glb")) == True