feat(Blenvy): experimenting with injection of assets data into exported scenes/gltf files

* added extraction of local/all (wip) assets into auto export
 * added injection of LocalAssets & AllAssets (unsure)
 * related tweaks & experiments
 * also cleaned up asset ui for external assets
 * started updating the bevy integration tests
This commit is contained in:
kaosat.dev 2024-06-08 11:52:13 +02:00
parent 6f6b813474
commit ba25c3cb20
9 changed files with 114 additions and 83 deletions

View File

@ -111,4 +111,9 @@ General issues:
- [x] fix asset file selection - [x] fix asset file selection
- [x] change "assets" tab to "levels"/worlds tab & modify UI accordingly - [x] change "assets" tab to "levels"/worlds tab & modify UI accordingly
- [ ] add option to 'split out' meshes from blueprints ? - [ ] add option to 'split out' meshes from blueprints ?
- [ ] ie considering meshletts etc , it would make sense to keep blueprints seperate from purely mesh gltfs - [ ] 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] disable/ hide asset editing ui for external assets
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

View File

@ -105,7 +105,7 @@ def auto_export(changes_per_scene, changed_export_parameters, settings):
print("export MAIN scenes") print("export MAIN scenes")
for scene_name in main_scenes_to_export: for scene_name in main_scenes_to_export:
print(" exporting scene:", scene_name) print(" exporting scene:", scene_name)
export_main_scene(bpy.data.scenes[scene_name], blend_file_path, settings, blueprints_data) export_main_scene(bpy.data.scenes[scene_name], settings, blueprints_data)
# now deal with blueprints/collections # now deal with blueprints/collections
do_export_library_scene = not change_detection or changed_export_parameters or len(blueprints_to_export) > 0 do_export_library_scene = not change_detection or changed_export_parameters or len(blueprints_to_export) > 0
@ -124,7 +124,7 @@ def auto_export(changes_per_scene, changed_export_parameters, settings):
else: else:
for scene in settings.main_scenes: for scene in settings.main_scenes:
export_main_scene(scene, blend_file_path, settings, []) export_main_scene(scene, settings, [])

View File

@ -1,8 +1,10 @@
import json
import bpy import bpy
from blenvy.core.helpers_collections import (set_active_collection) from blenvy.core.helpers_collections import (set_active_collection)
from blenvy.core.object_makers import (make_empty) from blenvy.core.object_makers import (make_empty)
from .duplicate_object import duplicate_object from .duplicate_object import duplicate_object
from .export_gltf import export_gltf from .export_gltf import export_gltf
from blenvy.core.scene_helpers import add_scene_property
""" """
generates a temporary scene, fills it with data, cleans up after itself generates a temporary scene, fills it with data, cleans up after itself
@ -12,11 +14,26 @@ generates a temporary scene, fills it with data, cleans up after itself
* cleaned up using tempScene_cleaner * cleaned up using tempScene_cleaner
""" """
def generate_temporary_scene_and_export(settings, gltf_export_settings, gltf_output_path, temp_scene_name="__temp_scene", tempScene_filler=None, tempScene_cleaner=None): def generate_temporary_scene_and_export(settings, gltf_export_settings, gltf_output_path, temp_scene_name="__temp_scene", tempScene_filler=None, tempScene_cleaner=None, additional_data=None):
temp_scene = bpy.data.scenes.new(name=temp_scene_name) temp_scene = bpy.data.scenes.new(name=temp_scene_name)
temp_root_collection = temp_scene.collection temp_root_collection = temp_scene.collection
print("additional_dataAAAAAAAAAAAAAAAH", additional_data)
if additional_data is not None: # FIXME not a fan of having this here
for entry in dict(additional_data):
print("entry in additional data", entry)
if entry == "local_assets":
temp_scene["local_assets"] = additional_data[entry] # this is for bevy 0.14
temp_root_collection["local_assets"] = additional_data[entry] # for previous bevy versions, remove when migration done
bla = "[(name: \"test_asset\", path: \"audio/fake.mp3\")]"
add_scene_property(temp_scene, 'assets_components', {"LocalAssets": f"LocalAssets({additional_data[entry]})".replace("'", '')})
if entry == entry == "AllAssets":
temp_scene["AllAssets"] = additional_data[entry]
temp_root_collection["AllAssets"] = additional_data[entry] # for previous bevy versions, remove when migration done
add_scene_property(temp_scene, 'assets_components', {"AllAssets": f"AllAssets({additional_data[entry]})".replace("'", '')})
# save active scene # save active scene
original_scene = bpy.context.window.scene original_scene = bpy.context.window.scene
# and selected collection # and selected collection
@ -43,6 +60,7 @@ def generate_temporary_scene_and_export(settings, gltf_export_settings, gltf_out
scene_filler_data = tempScene_filler(temp_root_collection) scene_filler_data = tempScene_filler(temp_root_collection)
# export the temporary scene # export the temporary scene
try: try:
print("dry_run MODE", settings.auto_export.dry_run)
if settings.auto_export.dry_run == "DISABLED": if settings.auto_export.dry_run == "DISABLED":
export_gltf(gltf_output_path, gltf_export_settings) export_gltf(gltf_output_path, gltf_export_settings)
except Exception as error: except Exception as error:

View File

@ -18,7 +18,7 @@ def prepare_and_export():
setting_changes = get_setting_changes() setting_changes = get_setting_changes()
print("setting_changes", setting_changes) print("setting_changes", setting_changes)
# do the actual export # do the actual export
blenvy.auto_export.dry_run = 'NO_EXPORT'#'DISABLED'# # blenvy.auto_export.dry_run = 'NO_EXPORT'#'DISABLED'#
auto_export(per_scene_changes, setting_changes, blenvy) auto_export(per_scene_changes, setting_changes, blenvy)
# cleanup # cleanup

View File

@ -1,11 +1,20 @@
import json
import os import os
from types import SimpleNamespace
from blenvy.blueprints.blueprint_helpers import inject_blueprints_list_into_main_scene, remove_blueprints_list_from_main_scene from blenvy.blueprints.blueprint_helpers import inject_blueprints_list_into_main_scene, remove_blueprints_list_from_main_scene
from ..constants import TEMPSCENE_PREFIX 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.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 ..common.export_gltf import (generate_gltf_export_settings, export_gltf)
from .is_object_dynamic import is_object_dynamic, is_object_static from .is_object_dynamic import is_object_dynamic, is_object_static
def export_main_scene(scene, blend_file_path, settings, blueprints_data): def assets_to_fake_ron(list_like):
result = []
for item in list_like:
result.append(f"(name: \"{item['name']}\", path: \"{item['path']}\")")
return result#.join(", ")
def export_main_scene(scene, settings, blueprints_data):
gltf_export_settings = generate_gltf_export_settings(settings) gltf_export_settings = generate_gltf_export_settings(settings)
assets_path_full = getattr(settings,"assets_path_full") assets_path_full = getattr(settings,"assets_path_full")
levels_path_full = getattr(settings,"levels_path_full") levels_path_full = getattr(settings,"levels_path_full")
@ -25,7 +34,44 @@ def export_main_scene(scene, blend_file_path, settings, blueprints_data):
if export_blueprints : if export_blueprints :
gltf_output_path = os.path.join(levels_path_full, scene.name) gltf_output_path = os.path.join(levels_path_full, scene.name)
#inject_blueprints_list_into_main_scene(scene, blueprints_data, settings) inject_blueprints_list_into_main_scene(scene, blueprints_data, settings)
print("main 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_main_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)
auto_assets = []
all_assets = []
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}")
else:
# get the injected path of the external blueprints
blueprint_exported_path = blueprint.collection['export_path'] if 'export_path' in blueprint.collection else None
if blueprint_exported_path is not None: # and not does_asset_exist(assets_list, blueprint_exported_path):
auto_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 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)
if export_separate_dynamic_and_static_objects: if export_separate_dynamic_and_static_objects:
#print("SPLIT STATIC AND DYNAMIC") #print("SPLIT STATIC AND DYNAMIC")
# first export static objects # first export static objects
@ -54,19 +100,21 @@ def export_main_scene(scene, blend_file_path, settings, blueprints_data):
generate_temporary_scene_and_export( generate_temporary_scene_and_export(
settings, settings,
temp_scene_name=TEMPSCENE_PREFIX, temp_scene_name=TEMPSCENE_PREFIX,
additional_data = scene,
gltf_export_settings=gltf_export_settings, gltf_export_settings=gltf_export_settings,
gltf_output_path=gltf_output_path, gltf_output_path=gltf_output_path,
tempScene_filler= lambda temp_collection: copy_hollowed_collection_into(scene.collection, temp_collection, blueprints_data=blueprints_data, settings=settings), tempScene_filler= lambda temp_collection: copy_hollowed_collection_into(scene.collection, temp_collection, blueprints_data=blueprints_data, settings=settings),
tempScene_cleaner= lambda temp_scene, params: clear_hollow_scene(original_root_collection=scene.collection, temp_scene=temp_scene, **params) tempScene_cleaner= lambda temp_scene, params: clear_hollow_scene(original_root_collection=scene.collection, temp_scene=temp_scene, **params)
) )
remove_blueprints_list_from_main_scene(scene)
else: else:
gltf_output_path = os.path.join(assets_path_full, scene.name) gltf_output_path = os.path.join(assets_path_full, scene.name)
print(" exporting gltf to", gltf_output_path, ".gltf/glb") print(" exporting gltf to", gltf_output_path, ".gltf/glb")
if settings.auto_export.dry_run == "DISABLED": if settings.auto_export.dry_run == "DISABLED":
export_gltf(gltf_output_path, gltf_export_settings) export_gltf(gltf_output_path, gltf_export_settings)
remove_blueprints_list_from_main_scene(scene)

View File

@ -20,7 +20,7 @@ def draw_assets(layout, name, title, asset_registry, target_type, target_name, e
add_possible = does_asset_exist(target, {"path": asset_registry.asset_path_selector}) #"name": asset_registry.asset_name_selector, add_possible = does_asset_exist(target, {"path": asset_registry.asset_path_selector}) #"name": asset_registry.asset_name_selector,
if header: if header and editable:
row = header.row() row = header.row()
row.alert = add_possible row.alert = add_possible
@ -41,23 +41,6 @@ def draw_assets(layout, name, title, asset_registry, target_type, target_name, e
if panel: if panel:
if editable: if editable:
row = panel.row() row = panel.row()
"""row.alert = add_possible
row.prop(asset_registry, "asset_name_selector", text="")
row.label(text=asset_registry.asset_path_selector)
asset_selector = row.operator(operator="asset.open_filebrowser", text="", icon="FILE_FOLDER")
add_asset_layout = row.column()
add_asset_layout.enabled = not add_possible
add_asset = add_asset_layout.operator(operator="bevyassets.add", text="", icon="ADD")
add_asset.target_type = target_type
add_asset.target_name = target_name
add_asset.asset_name = asset_registry.asset_name_selector
add_asset.asset_type = asset_registry.asset_type_selector
add_asset.asset_path = asset_registry.asset_path_selector"""
#panel.separator() #panel.separator()
for asset in user_assets: for asset in user_assets:
@ -77,27 +60,6 @@ def draw_assets(layout, name, title, asset_registry, target_type, target_name, e
remove_asset.target_type = target_type remove_asset.target_type = target_type
remove_asset.target_name = target_name remove_asset.target_name = target_name
remove_asset.asset_path = asset.path remove_asset.asset_path = asset.path
'''for asset in generated_assets:
row = panel.row()
#row.label(text=asset.name)
#row.label(text=asset.path)
split = row.split(factor=nesting_indent)
col = split.column()
col.label(text=" ")
col = split.column()
sub_header, sub_panel = col.panel(f"assets_sub{asset.name}", default_closed=False)
sub_header.label(text=f"{asset.name} ({asset.path})", icon="XRAY")
if sub_panel:
sub_panel.label(text=" some stuff")
"""remove_asset = row.operator(operator="bevyassets.remove", text="", icon="TRASH")
remove_asset.target_type = target_type
remove_asset.target_name = target_name
remove_asset.asset_path = asset.path"""'''
return panel return panel
class Blenvy_assets(bpy.types.Panel): class Blenvy_assets(bpy.types.Panel):

View File

@ -77,4 +77,4 @@ class GLTF_PT_auto_export_blueprints_list(bpy.types.Panel):
generated_assets = get_generated_assets(blueprint.collection) generated_assets = get_generated_assets(blueprint.collection)
draw_assets(layout=col, name=blueprint.name, title="Assets", asset_registry=asset_registry, user_assets=user_assets, generated_assets=generated_assets, target_type="BLUEPRINT", target_name=blueprint.name, editable=False) draw_assets(layout=col, name=blueprint.name, title="Assets", asset_registry=asset_registry, user_assets=user_assets, generated_assets=generated_assets, target_type="BLUEPRINT", target_name=blueprint.name, editable=False)
panel.label(text="External") panel.label(text="External blueprint, assets are not editable")

View File

@ -9,7 +9,7 @@ import filecmp
from PIL import Image from PIL import Image
from pixelmatch.contrib.PIL import pixelmatch from pixelmatch.contrib.PIL import pixelmatch
from blenvy.auto_export.export.prepare_and_export import prepare_and_export from blenvy.add_ons.auto_export.common.prepare_and_export import prepare_and_export
@pytest.fixture @pytest.fixture
def setup_data(request): def setup_data(request):
@ -73,14 +73,11 @@ def setup_data(request):
""" """
def test_export_complex(setup_data): def test_export_complex(setup_data):
root_path = setup_data["root_path"] root_path = setup_data["root_path"]
auto_export_operator = bpy.ops.export_scenes.auto_gltf
# with change detection # with change detection
# first, configure things # first, configure things
# we use the global settings for that # we use the global settings for that
export_props = { export_props = {
"main_scene_names" : ['World'],
"library_scene_names": ['Library'],
} }
gltf_settings = { gltf_settings = {
"export_animations": True, "export_animations": True,
@ -102,26 +99,29 @@ def test_export_complex(setup_data):
# move the cube in the library # move the cube in the library
# TODO: add back bpy.data.objects["Blueprint1_mesh"].location = [1, 2, 1] # TODO: add back bpy.data.objects["Blueprint1_mesh"].location = [1, 2, 1]
registry = bpy.context.window_manager.components_registry
blenvy = bpy.context.window_manager.blenvy blenvy = bpy.context.window_manager.blenvy
main_scene = blenvy.main_scenes.add() #blenvy.project_root_path =
main_scene.name = "World" #blenvy.blueprints_path
blenvy.auto_export.auto_export = True 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 = 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
# scene asset
user_asset = bpy.data.scenes['World'].user_assets.add()
user_asset.name = "test_asset"
user_asset.path = "audio/fake.mp3"
# blueprint asset
user_asset = bpy.data.collections['Blueprint4_nested'].user_assets.add()
user_asset.name = "yoho_audio"
user_asset.path = "audio/fake.mp3"
prepare_and_export() prepare_and_export()
"""auto_export_operator(
auto_export=True,
direct_mode=True,
project_root_path = os.path.abspath(root_path),
#blueprints_path = os.path.join("assets", "models", "library"),
export_output_folder = os.path.join("assets", "models"), #"./models",
#levels_path = os.path.join("assets", "models"),
export_scene_settings=True,
export_blueprints=True,
export_materials_library=True
)"""
# blueprint1 => has an instance, got changed, should export # blueprint1 => has an instance, got changed, should export
# blueprint2 => has NO instance, but marked as asset, should export # blueprint2 => has NO instance, but marked as asset, should export
# blueprint3 => has NO instance, not marked as asset, used inside blueprint 4: should export # blueprint3 => has NO instance, not marked as asset, used inside blueprint 4: should export

View File

@ -2,6 +2,7 @@ import os
import json import json
import pytest import pytest
import bpy import bpy
from blenvy.add_ons.auto_export.common.prepare_and_export import prepare_and_export
@pytest.fixture @pytest.fixture
def setup_data(request): def setup_data(request):
@ -25,14 +26,11 @@ def setup_data(request):
# this runs the external blueprints file # this runs the external blueprints file
def test_export_external_blueprints(setup_data): def test_export_external_blueprints(setup_data):
root_path = setup_data["root_path"] root_path = setup_data["root_path"]
auto_export_operator = bpy.ops.export_scenes.auto_gltf
# with change detection # with change detection
# first, configure things # first, configure things
# we use the global settings for that # we use the global settings for that
export_props = { export_props = {
"main_scene_names" : [],
"library_scene_names": ['Library'],
} }
gltf_settings = { gltf_settings = {
"export_animations": True, "export_animations": True,
@ -49,20 +47,20 @@ def test_export_external_blueprints(setup_data):
stored_gltf_settings.clear() stored_gltf_settings.clear()
stored_gltf_settings.write(json.dumps(gltf_settings)) stored_gltf_settings.write(json.dumps(gltf_settings))
blenvy = bpy.context.window_manager.blenvy
#blenvy.project_root_path =
#blenvy.blueprints_path
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 = True
auto_export_operator( print("SCENES", bpy.data.scenes)
auto_export=True, for scene in bpy.data.scenes:
direct_mode=True, print("SCNE", scene)
project_root_path = os.path.abspath(root_path), bpy.data.scenes['Library'].blenvy_scene_type = 'Library' # set scene as Library scene
#blueprints_path = os.path.join("assets", "models", "library"), # do the actual export
#export_output_folder = os.path.join("assets", "models"), #"./models", prepare_and_export()
#levels_path = os.path.join("assets", "models"),
export_scene_settings=False,
export_blueprints=True,
export_materials_library=True,
export_marked_assets= True
)
assert os.path.exists(os.path.join(setup_data["blueprints_path"], "External_blueprint.glb")) == True assert os.path.exists(os.path.join(setup_data["blueprints_path"], "External_blueprint.glb")) == True
assert os.path.exists(os.path.join(setup_data["blueprints_path"], "External_blueprint2.glb")) == True assert os.path.exists(os.path.join(setup_data["blueprints_path"], "External_blueprint2.glb")) == True