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
This commit is contained in:
kaosat.dev 2024-06-10 00:31:23 +02:00
parent 1fdb45bab6
commit ed0c85b66e
13 changed files with 76 additions and 54 deletions

View File

@ -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<BluePrintsConfig>,
material_infos: Query<(Entity, &MaterialInfo), Added<MaterialInfo>>,
ready_blueprints: Query<(Entity, &Children), (With<BlueprintInstanceReady>)>,
material_infos: Query<(Entity, &MaterialInfo, &Parent), Added<MaterialInfo>>,
asset_server: Res<AssetServer>,
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<AssetLoadTracker> = vec![AssetLoadTracker {
let asset_infos: Vec<AssetLoadTracker> = 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<Gltf>),
(Entity, &mut AssetsToLoad),
With<BlueprintMaterialAssetsNotLoaded>,
>,
asset_server: Res<AssetServer>,
@ -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<BluePrintsConfig>,
@ -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()));

View File

@ -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

View File

@ -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

View File

@ -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,
@ -81,7 +81,8 @@ def export_materials(collections, library_scenes, settings):
'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")

View File

@ -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():

View File

@ -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:

View File

@ -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",

View File

@ -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")

View File

@ -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

View File

@ -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
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}")'

View File

@ -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

View File

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

View File

@ -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