feat(Blenvy):changed materials system to generate one gltf file per material

* modified materials detection & export accordingly
 * modified material paths & co accordingly (all much simpler !)
 * modified assets_scan helpers to also include material assets
 * modified & massively cleaned up BlueprintAsset injection
 * further cleanups & fixes for materials handling
 * also removed a lot of obsolete code dealing with assets
 * ever more cleanups !
This commit is contained in:
kaosat.dev 2024-07-27 16:59:57 +02:00
parent 4865d432d9
commit ce17f723b1
11 changed files with 32 additions and 170 deletions

11
TODO.md
View File

@ -226,10 +226,13 @@ Blender side:
- [ ] materials_path custom property should be ignored both in the list of fixable component AND on export
- [ ] if we want to add material_infos & others as normal components they should not be editable, so we need another attribute, and adapt the UI for that
- [ ] if material library is toggled, then changes to materials should not change the blueprints that are using them => not really: as the name & co might change
- [x] if material library is toggled, then changes to materials should not change the blueprints that are using them => not really: as the name & co might change
- [ ] material assets seem to be added to list regardless of whether material exports are enabled or not
- [ ] review & upgrade overall logic of material libraries, their names & output path
- [ ] persist exported materials path in blueprints so that it can be read from library file users
- [x] review & upgrade overall logic of material libraries, their names & output path
- [x] change materials logic to work with multiple materials per mesh
- [x] the index of the generated gltf files is reliable, and can be used both in Blender & Bevy
- [x] change MaterialInfo to MaterialInfos & turn it into a vec/list & updated logic both on Blender & Bevy side
- [ ] persist exported materials paths 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 ?)
@ -323,7 +326,7 @@ Bevy Side:
- [x] fix "remove component" operator from the rename/fix/update components panel
- [ ] replace string in BlueprintInfo path with PathBuf ?
- [ ] update main docs
- [x] update main docs
- [x] rename project to Blenvy
- [x] replace all references to the old 2 add-ons with those to Blenvy
- [x] rename repo to "Blenvy"

View File

@ -2,8 +2,7 @@ use bevy::prelude::*;
use crate::BlenvyConfig;
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
#[derive(Reflect, Default, Debug)]
/// struct containing the name & path of the material to apply
pub struct MaterialInfo {
pub name: String,
@ -12,6 +11,7 @@ pub struct MaterialInfo {
#[derive(Component, Reflect, Default, Debug)]
#[reflect(Component)]
/// component containing the full list of MaterialInfos for a given entity/object
pub struct MaterialInfos(Vec<MaterialInfo>);
#[derive(Component, Default, Debug)]
@ -45,7 +45,6 @@ pub(crate) fn inject_materials(
for (material_index, material_info) in material_infos.0.iter().enumerate() {
let material_full_path = format!("{}#{}", material_info.path, material_info.name);
let mut material_found: Option<&Handle<StandardMaterial>> = None;
if blenvy_config
.materials_cache
.contains_key(&material_full_path)

Binary file not shown.

View File

@ -8,4 +8,4 @@ custom_properties_to_filter_out = [
'_combine', 'template',
'Blenvy_scene_type', 'blenvy_scene_type',
'materials_path', 'export_path',
]
]

View File

@ -1,5 +1,4 @@
import os
from blenvy.blueprints.blueprint_helpers import inject_blueprints_list_into_level_scene, remove_blueprints_list_from_level_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
from ..common.export_gltf import (generate_gltf_export_settings, export_gltf)
@ -26,8 +25,8 @@ def export_level_scene(scene, settings, blueprints_data):
if export_blueprints :
gltf_output_path = os.path.join(levels_path_full, scene.name)
inject_blueprints_list_into_level_scene(scene, blueprints_data, settings)
# 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)
if export_separate_dynamic_and_static_objects:
@ -67,8 +66,6 @@ def export_level_scene(scene, settings, blueprints_data):
tempScene_cleaner= lambda temp_scene, params: clear_hollow_scene(original_root_collection=scene.collection, temp_scene=temp_scene, **params)
)
remove_blueprints_list_from_level_scene(scene)
else:
gltf_output_path = os.path.join(assets_path_full, scene.name)
print(" exporting gltf to", gltf_output_path, ".gltf/glb")

View File

@ -90,8 +90,6 @@ def export_materials(materials_to_export, settings, blueprints_data):
'export_apply':True
}
for material in materials_to_export:
print("exporting material", material.name)
gltf_output_path = os.path.join(materials_path_full, material.name)
@ -105,21 +103,6 @@ def export_materials(materials_to_export, settings, blueprints_data):
tempScene_cleaner= lambda temp_scene, params: clear_materials_scene(temp_scene=temp_scene)
)
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")
generate_temporary_scene_and_export(
settings=settings,
gltf_export_settings=gltf_export_settings,
temp_scene_name="__materials_scene",
gltf_output_path=gltf_output_path,
tempScene_filler= lambda temp_collection: generate_materials_scene_content(temp_collection, used_material_names),
tempScene_cleaner= lambda temp_scene, params: clear_materials_scene(temp_scene=temp_scene)
)
def cleanup_materials(collections, library_scenes):
# remove temporary components

View File

@ -24,63 +24,14 @@ def assets_to_fake_ron(list_like):
# TODO : move to assets
def upsert_scene_assets(scene, blueprints_data, settings):
"""print("level scene", scene)
for asset in scene.user_assets:
print(" user asset", asset.name, asset.path)
for asset in scene.generated_assets:
print(" generated asset", asset)"""
"""for blueprint in blueprints_data.blueprints_per_scenes[scene.name]:
print("BLUEPRINT", blueprint)"""
blueprint_instances_in_scene = blueprints_data.blueprint_instances_per_level_scene.get(scene.name, {}).keys()
blueprints_in_scene = [blueprints_data.blueprints_per_name[blueprint_name] for blueprint_name in blueprint_instances_in_scene]
#yala = [blueprint.collection.user_assets for blueprint in blueprints_in_scene]
#print("dsfsdf", yala)
level_assets = []
all_assets = []
export_gltf_extension = getattr(settings, "export_gltf_extension", ".glb")
blueprints_path = getattr(settings, "blueprints_path")
for blueprint in blueprints_in_scene:
if blueprint.local:
blueprint_exported_path = posixpath.join(blueprints_path, f"{blueprint.name}{export_gltf_extension}")
else:
# get the injected path of the external blueprints
blueprint_exported_path = blueprint.collection['export_path'] if 'export_path' in blueprint.collection else None
# add their material path
materials_exported_path = blueprint.collection['materials_path'] if 'materials_path' in blueprint.collection else None
level_assets.append({"name": blueprint.name+"_material", "path": materials_exported_path})#, "generated": True, "internal":blueprint.local, "parent": None})
if blueprint_exported_path is not None: # and not does_asset_exist(assets_list, blueprint_exported_path):
level_assets.append({"name": blueprint.name, "path": blueprint_exported_path})#, "generated": True, "internal":blueprint.local, "parent": None})
# now also add the assets of the blueprints # TODO: wait no , these should not be a part of the (scene) local assets
for asset in blueprint.collection.user_assets:
#print("adding assets of blueprint", asset.name)
all_assets.append({"name": asset.name, "path": asset.path})
"""for asset in level_assets:
print(" generated asset", asset.name, asset.path)"""
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 = posixpath.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
print("material_assets", material_assets, "extension", export_gltf_extension)
all_assets_raw = get_level_scene_assets_tree2(level_scene=scene, blueprints_data=blueprints_data, settings=settings)
local_assets = [{"name": asset["name"], "path": asset["path"]} for asset in all_assets_raw if asset['parent'] is None and asset["path"] != "" ]
all_assets = [{"name": asset["name"], "path": asset["path"]} for asset in all_assets_raw if asset["path"] != "" ]
print("all_assets_raw", all_assets_raw)
print("all_assets", all_assets)
print("local assets", local_assets + material_assets)
scene["BlueprintAssets"] = assets_to_fake_ron(local_assets + material_assets)
#scene["BlueprintAssets"] = assets_to_fake_ron(all_assets + [{"name": asset.name, "path": asset.path} for asset in scene.user_assets] + level_assets + material_assets)
#scene["BlueprintAssets"] = assets_to_fake_ron([{'name':'foo', 'path':'bar'}])
print("local assets", local_assets)
scene["BlueprintAssets"] = assets_to_fake_ron(all_assets) #local_assets
def upsert_blueprint_assets(blueprint, blueprints_data, settings):
all_assets_raw = get_blueprint_asset_tree(blueprint=blueprint, blueprints_data=blueprints_data, settings=settings)

View File

@ -3,6 +3,7 @@ import json
import posixpath
import bpy
from ..materials.materials_helpers import get_blueprint_materials
from .asset_helpers import does_asset_exist, get_user_assets, get_user_assets_as_list
def scan_assets(scene, blueprints_data, settings):
@ -62,6 +63,7 @@ def get_userTextures():
print("textures", textures)
def get_blueprint_assets_tree(blueprint, blueprints_data, parent, settings):
print("blueprint", blueprint.name)
blueprints_path = getattr(settings, "blueprints_path")
export_gltf_extension = getattr(settings, "export_gltf_extension", ".glb")
assets_list = []
@ -88,6 +90,15 @@ def get_blueprint_assets_tree(blueprint, blueprints_data, parent, settings):
asset["parent"] = parent
asset["internal"] = blueprint.local
assets_list += direct_assets
# now get materials used by this blueprint
(blueprint_materials_names, materials_per_object) = get_blueprint_materials(blueprint=blueprint)
print("blueprint_materials", blueprint_materials_names)
for material_name in blueprint_materials_names:
materials_path = getattr(settings, "materials_path")
materials_exported_path = posixpath.join(materials_path, f"{material_name}{export_gltf_extension}")
assets_list.append({"name": material_name, "path": materials_exported_path, "type": "MATERIAL", "generated": True,"internal":blueprint.local, "parent": blueprint.name})
return assets_list
def get_level_scene_assets_tree(level_scene, blueprints_data, settings):

View File

@ -70,11 +70,6 @@ class BLENVY_OT_assets_add(Operator):
context.window_manager.assets_registry.asset_type_selector = "MODEL"
context.window_manager.assets_registry.asset_path_selector = ""
"""if blueprint_assets:
bpy.data.collections[self.target_name]["assets"] = json.dumps(assets)
else:
bpy.data.scenes[self.target_name]["assets"] = json.dumps(assets)"""
return {'FINISHED'}

View File

@ -5,8 +5,6 @@ import bpy
from pathlib import Path
import posixpath
from ..core.scene_helpers import add_scene_property
def find_blueprints_not_on_disk(blueprints, folder_path, extension):
not_found_blueprints = []
for blueprint in blueprints:
@ -23,64 +21,9 @@ def check_if_blueprint_on_disk(scene_name, folder_path, extension):
return found
def inject_export_path_into_internal_blueprints(internal_blueprints, blueprints_path, gltf_extension, settings):
export_materials_library = getattr(settings.auto_export, "export_materials_library")
# FIXME: duplicate of materials stuff
export_gltf_extension = getattr(settings, "export_gltf_extension", ".glb")
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 = posixpath.join(materials_path, f"{materials_library_name}{export_gltf_extension}")
for blueprint in internal_blueprints:
blueprint_exported_path = posixpath.join(blueprints_path, f"{blueprint.name}{gltf_extension}")
# print("injecting blueprint path", blueprint_exported_path, "for", blueprint.name)
blueprint.collection["export_path"] = blueprint_exported_path
if export_materials_library:
blueprint.collection["materials_path"] = materials_exported_path
def inject_blueprints_list_into_level_scene(scene, blueprints_data, settings):
project_root_path = getattr(settings, "project_root_path")
assets_path = getattr(settings,"assets_path")
levels_path = getattr(settings,"levels_path")
blueprints_path = getattr(settings, "blueprints_path")
export_gltf_extension = getattr(settings, "export_gltf_extension")
# print("injecting assets/blueprints data into scene")
assets_list_name = f"assets_list_{scene.name}_components"
assets_list_data = {}
blueprint_instance_names_for_scene = blueprints_data.blueprint_instances_per_level_scene.get(scene.name, None)
blueprint_assets_list = []
if blueprint_instance_names_for_scene:
for blueprint_name in blueprint_instance_names_for_scene:
blueprint = blueprints_data.blueprints_per_name.get(blueprint_name, None)
if blueprint is not None:
#print("BLUEPRINT", blueprint)
blueprint_exported_path = None
if blueprint.local:
blueprint_exported_path = posixpath.join(blueprints_path, f"{blueprint.name}{export_gltf_extension}")
else:
# get the injected path of the external blueprints
blueprint_exported_path = blueprint.collection['Export_path'] if 'Export_path' in blueprint.collection else None
#print("foo", dict(blueprint.collection))
if blueprint_exported_path is not None:
blueprint_assets_list.append({"name": blueprint.name, "path": blueprint_exported_path, "type": "MODEL", "internal": True})
assets_list_name = f"assets_{scene.name}"
scene["assets"] = json.dumps(blueprint_assets_list)
#print("blueprint assets", blueprint_assets_list)
def remove_blueprints_list_from_level_scene(scene):
assets_list = None
assets_list_name = f"assets_list_{scene.name}_components"
for object in scene.objects:
if object.name == assets_list_name:
assets_list = object
if assets_list is not None:
bpy.data.objects.remove(assets_list, do_unlink=True)
"""if export_materials_library:
blueprint.collection["materials_path"] = materials_exported_path"""

View File

@ -1,28 +1,15 @@
import os
import posixpath
import bpy
from pathlib import Path
from ..core.helpers_collections import (traverse_tree)
def find_materials_not_on_disk(materials, materials_path_full, extension):
not_found_materials = []
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_full, f"{materials_library_name}{extension}")
found = os.path.exists(materials_exported_path) and os.path.isfile(materials_exported_path)
for material in materials:
if not found:
not_found_materials.append(material)
"""for material in materials:
gltf_output_path = os.path.join(materials_path_full, material.name + extension)
# print("gltf_output_path", gltf_output_path)
found = os.path.exists(gltf_output_path) and os.path.isfile(gltf_output_path)
if not found:
not_found_materials.append(material)"""
not_found_materials.append(material)
return not_found_materials
def check_if_material_on_disk(scene_name, folder_path, extension):
@ -64,22 +51,15 @@ def get_all_materials(collection_names, library_scenes):
def add_material_info_to_objects(materials_per_object, settings):
materials_path = getattr(settings, "materials_path")
export_gltf_extension = getattr(settings, "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 = posixpath.join(materials_path, f"{materials_library_name}{export_gltf_extension}")
#print("ADDING MAERIAL INFOS")
for object in materials_per_object.keys():
material_infos = []
for material in materials_per_object[object]:
materials_exported_path = posixpath.join(materials_path, f"{material.name}{export_gltf_extension}")
material_info = f'(name: "{material.name}", path: "{materials_exported_path}")'
material_infos.append(material_info)
# problem with using actual components: you NEED the type registry/component infos, so if there is none , or it is not loaded yet, it does not work
# for a few components we could hardcode this
material_info = f'(name: "{material.name}", path: "{materials_exported_path}")'
#bpy.ops.blenvy.component_add(target_item_name=object.name, target_item_type="OBJECT", component_type="blenvy::blueprints::materials::MaterialInfo", component_value=component_value)
materials_exported_path = posixpath.join(materials_path, f"{materials_library_name}{export_gltf_extension}")
#object['MaterialInfo'] = component_value
material_infos.append(material_info)
#bpy.ops.blenvy.component_add(target_item_name=object.name, target_item_type="OBJECT", component_type="blenvy::blueprints::materials::MaterialInfos", component_value=component_value)
object['MaterialInfos'] = f"({material_infos})".replace("'","")
print("adding materialInfos to object", object, "material infos", material_infos)