feat(Blenvy:Blender):

* fixed issue with parenting due to Blender's very weird matrix_parent_inverse ... solves all issues with children of empties
blueprint instances within blueprint instances etc having the wrong transforms in some cases
 * fixed bad gltf format propagation: semi ok solution, but a cleaner one would be better
 * added additional custom properties to the blacklist , level/blueprint exports are now using the one in the constants
instead of a local copy
 * minor tweaks & cleanups
This commit is contained in:
kaosat.dev 2024-07-07 22:06:16 +02:00
parent fbcd025dc1
commit 478be88a55
12 changed files with 62 additions and 27 deletions

View File

@ -199,7 +199,8 @@ Blender side:
- [ ] disabled components - [ ] disabled components
- [ ] blueprint instances as children of blueprint instances - [ ] blueprint instances as children of blueprint instances
- [ ] blueprint instances as children of empties - [ ] blueprint instances as children of empties
- [x] update testing files - [x] update testing blend files
- [ ] disable 'export_hierarchy_full_collections' for all cases: not reliable and redudant
- [ ] add option to 'split out' meshes from blueprints ? - [ ] add option to 'split out' meshes from blueprints ?

View File

@ -21,16 +21,11 @@ def auto_export(changes_per_scene, changed_export_parameters, settings):
# have the export parameters (not auto export, just gltf export) have changed: if yes (for example switch from glb to gltf, compression or not, animations or not etc), we need to re-export everything # have the export parameters (not auto export, just gltf export) have changed: if yes (for example switch from glb to gltf, compression or not, animations or not etc), we need to re-export everything
print ("changed_export_parameters", changed_export_parameters) print ("changed_export_parameters", changed_export_parameters)
try: try:
# path to the current blend file
file_path = bpy.data.filepath
# Get the folder
blend_file_path = os.path.dirname(file_path)
#should we use change detection or not #should we use change detection or not
change_detection = getattr(settings.auto_export, "change_detection") change_detection = getattr(settings.auto_export, "change_detection")
export_scene_settings = getattr(settings.auto_export,"export_scene_settings") export_scene_settings = getattr(settings.auto_export, "export_scene_settings")
do_export_blueprints = getattr(settings.auto_export,"export_blueprints") do_export_blueprints = getattr(settings.auto_export, "export_blueprints")
export_materials_library = getattr(settings.auto_export,"export_materials_library") export_materials_library = getattr(settings.auto_export, "export_materials_library")
# standard gltf export settings are stored differently # standard gltf export settings are stored differently
standard_gltf_exporter_settings = get_standard_exporter_settings() standard_gltf_exporter_settings = get_standard_exporter_settings()
@ -47,7 +42,10 @@ def auto_export(changes_per_scene, changed_export_parameters, settings):
# we inject the blueprints export path # we inject the blueprints export path
blueprints_path = getattr(settings,"blueprints_path") blueprints_path = getattr(settings,"blueprints_path")
inject_export_path_into_internal_blueprints(internal_blueprints=blueprints_data.internal_blueprints, blueprints_path=blueprints_path, gltf_extension=gltf_extension) # inject the "export_path" and "material_path" properties into the internal blueprints
inject_export_path_into_internal_blueprints(internal_blueprints=blueprints_data.internal_blueprints, blueprints_path=blueprints_path, gltf_extension=gltf_extension, settings=settings)
for blueprint in blueprints_data.blueprints: for blueprint in blueprints_data.blueprints:
bpy.context.window_manager.blueprints_registry.add_blueprint(blueprint) bpy.context.window_manager.blueprints_registry.add_blueprint(blueprint)
#bpy.context.window_manager.blueprints_registry.refresh_blueprints() #bpy.context.window_manager.blueprints_registry.refresh_blueprints()

View File

@ -119,6 +119,10 @@ def duplicate_object(object, parent, combine_mode, destination_collection, bluep
# do this both for empty replacements & normal copies # do this both for empty replacements & normal copies
if parent is not None: if parent is not None:
copy.parent = parent copy.parent = parent
# without this, the copy"s offset from parent (if any ) will not be set correctly !
# see here for example https://blender.stackexchange.com/questions/3763/parenting-messes-up-transforms-where-is-the-offset-stored
copy.matrix_parent_inverse = object.matrix_parent_inverse
remove_unwanted_custom_properties(copy) remove_unwanted_custom_properties(copy)
copy_animation_data(object, copy) copy_animation_data(object, copy)

View File

@ -24,6 +24,7 @@ def generate_gltf_export_settings(settings):
export_cameras=True, export_cameras=True,
export_extras=True, # For custom exported properties. export_extras=True, # For custom exported properties.
export_lights=True, export_lights=True,
export_hierarchy_full_collections=False
#export_texcoords=True, #export_texcoords=True,
#export_normals=True, #export_normals=True,
@ -54,6 +55,7 @@ def generate_gltf_export_settings(settings):
standard_gltf_exporter_settings = get_standard_exporter_settings() standard_gltf_exporter_settings = get_standard_exporter_settings()
# these essential params should NEVER be overwritten , no matter the settings of the standard exporter
constant_keys = [ constant_keys = [
'use_selection', 'use_selection',
'use_visible', 'use_visible',
@ -63,9 +65,10 @@ def generate_gltf_export_settings(settings):
'export_cameras', 'export_cameras',
'export_extras', # For custom exported properties. 'export_extras', # For custom exported properties.
'export_lights', 'export_lights',
'export_hierarchy_full_collections'
] ]
# a certain number of essential params should NEVER be overwritten , no matter the settings of the standard exporter #
for key in standard_gltf_exporter_settings.keys(): for key in standard_gltf_exporter_settings.keys():
if str(key) not in constant_keys: if str(key) not in constant_keys:
gltf_export_settings[key] = standard_gltf_exporter_settings.get(key) gltf_export_settings[key] = standard_gltf_exporter_settings.get(key)

View File

@ -5,7 +5,7 @@ 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 from blenvy.core.scene_helpers import add_scene_property
from ..constants import custom_properties_to_filter_out
""" """
generates a temporary scene, fills it with data, cleans up after itself generates a temporary scene, fills it with data, cleans up after itself
* named using temp_scene_name * named using temp_scene_name
@ -19,7 +19,7 @@ def generate_temporary_scene_and_export(settings, gltf_export_settings, gltf_out
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
properties_black_list = ['glTF2ExportSettings', 'assets', 'user_assets', 'components_meta', 'Components_meta', 'Generated_assets', 'generated_assets'] properties_black_list = custom_properties_to_filter_out
if additional_data is not None: # FIXME not a fan of having this here if additional_data is not None: # FIXME not a fan of having this here
for entry in dict(additional_data): for entry in dict(additional_data):
# we copy everything over except those on the black list # we copy everything over except those on the black list
@ -75,7 +75,6 @@ def generate_temporary_scene_and_export(settings, gltf_export_settings, gltf_out
# copies the contents of a collection into another one while replacing library instances with empties # copies the contents of a collection into another one while replacing library instances with empties
def copy_hollowed_collection_into(source_collection, destination_collection, parent_empty=None, filter=None, blueprints_data=None, settings={}): def copy_hollowed_collection_into(source_collection, destination_collection, parent_empty=None, filter=None, blueprints_data=None, settings={}):
collection_instances_combine_mode = getattr(settings.auto_export, "collection_instances_combine_mode") collection_instances_combine_mode = getattr(settings.auto_export, "collection_instances_combine_mode")
for object in source_collection.objects: for object in source_collection.objects:
if object.name.endswith("____bak"): # some objects could already have been handled, ignore them if object.name.endswith("____bak"): # some objects could already have been handled, ignore them
continue continue

View File

@ -1,4 +1,11 @@
TEMPSCENE_PREFIX = "__temp_scene" TEMPSCENE_PREFIX = "__temp_scene"
#hard coded custom properties to ignore #hard coded custom properties to ignore on export
custom_properties_to_filter_out = ['_combine', 'template', 'components_meta'] custom_properties_to_filter_out = [
'glTF2ExportSettings',
'assets', 'user_assets', 'Generated_assets', 'generated_assets',
'components_meta', 'Components_meta',
'_combine', 'template',
'Blenvy_scene_type', 'blenvy_scene_type'
]
#['_combine', 'template', 'components_meta', 'Components_meta', 'Blenvy_scene_type']

View File

@ -55,7 +55,7 @@ def export_main_scene(scene, settings, blueprints_data):
auto_assets = [] auto_assets = []
all_assets = [] all_assets = []
export_gltf_extension = getattr(settings.auto_export, "export_gltf_extension", ".glb") export_gltf_extension = getattr(settings, "export_gltf_extension", ".glb")
blueprints_path = getattr(settings, "blueprints_path") blueprints_path = getattr(settings, "blueprints_path")
for blueprint in blueprints_in_scene: for blueprint in blueprints_in_scene:
@ -64,6 +64,11 @@ def export_main_scene(scene, settings, blueprints_data):
else: else:
# get the injected path of the external blueprints # get the injected path of the external blueprints
blueprint_exported_path = blueprint.collection['export_path'] if 'export_path' in blueprint.collection else None 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
auto_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): 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}) auto_assets.append({"name": blueprint.name, "path": blueprint_exported_path})#, "generated": True, "internal":blueprint.local, "parent": None})
@ -80,7 +85,7 @@ def export_main_scene(scene, settings, blueprints_data):
materials_library_name = f"{current_project_name}_materials" materials_library_name = f"{current_project_name}_materials"
materials_exported_path = os.path.join(materials_path, f"{materials_library_name}{export_gltf_extension}") 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 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)
scene["BlueprintAssets"] = assets_to_fake_ron(all_assets + [{"name": asset.name, "path": asset.path} for asset in scene.user_assets] + auto_assets + material_assets) scene["BlueprintAssets"] = assets_to_fake_ron(all_assets + [{"name": asset.name, "path": asset.path} for asset in scene.user_assets] + auto_assets + material_assets)
#scene["BlueprintAssets"] = assets_to_fake_ron([{'name':'foo', 'path':'bar'}]) #scene["BlueprintAssets"] = assets_to_fake_ron([{'name':'foo', 'path':'bar'}])

View File

@ -62,7 +62,7 @@ def get_userTextures():
def get_blueprint_assets_tree(blueprint, blueprints_data, parent, settings): def get_blueprint_assets_tree(blueprint, blueprints_data, parent, settings):
blueprints_path = getattr(settings, "blueprints_path") blueprints_path = getattr(settings, "blueprints_path")
export_gltf_extension = getattr(settings.auto_export, "export_gltf_extension", ".glb") export_gltf_extension = getattr(settings, "export_gltf_extension", ".glb")
assets_list = [] assets_list = []
@ -91,7 +91,7 @@ def get_blueprint_assets_tree(blueprint, blueprints_data, parent, settings):
def get_main_scene_assets_tree(main_scene, blueprints_data, settings): def get_main_scene_assets_tree(main_scene, blueprints_data, settings):
blueprints_path = getattr(settings, "blueprints_path") blueprints_path = getattr(settings, "blueprints_path")
export_gltf_extension = getattr(settings.auto_export, "export_gltf_extension", ".glb") export_gltf_extension = getattr(settings, "export_gltf_extension", ".glb")
blueprint_instance_names_for_scene = blueprints_data.blueprint_instances_per_main_scene.get(main_scene.name, None) blueprint_instance_names_for_scene = blueprints_data.blueprint_instances_per_main_scene.get(main_scene.name, None)
assets_list = get_user_assets_as_list(main_scene) assets_list = get_user_assets_as_list(main_scene)
@ -123,7 +123,7 @@ def get_main_scene_assets_tree(main_scene, blueprints_data, settings):
def get_blueprint_asset_tree(blueprint, blueprints_data, settings): def get_blueprint_asset_tree(blueprint, blueprints_data, settings):
blueprints_path = getattr(settings, "blueprints_path") blueprints_path = getattr(settings, "blueprints_path")
export_gltf_extension = getattr(settings.auto_export, "export_gltf_extension", ".glb") export_gltf_extension = getattr(settings, "export_gltf_extension", ".glb")
assets_list = get_user_assets_as_list(blueprint.collection) assets_list = get_user_assets_as_list(blueprint.collection)

View File

@ -2,6 +2,8 @@
import os import os
import json import json
import bpy import bpy
from pathlib import Path
from ..core.scene_helpers import add_scene_property from ..core.scene_helpers import add_scene_property
def find_blueprints_not_on_disk(blueprints, folder_path, extension): def find_blueprints_not_on_disk(blueprints, folder_path, extension):
@ -19,12 +21,23 @@ def check_if_blueprint_on_disk(scene_name, folder_path, extension):
found = os.path.exists(gltf_output_path) and os.path.isfile(gltf_output_path) found = os.path.exists(gltf_output_path) and os.path.isfile(gltf_output_path)
return found return found
def inject_export_path_into_internal_blueprints(internal_blueprints, blueprints_path, gltf_extension): 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 = os.path.join(materials_path, f"{materials_library_name}{export_gltf_extension}")
for blueprint in internal_blueprints: for blueprint in internal_blueprints:
blueprint_exported_path = os.path.join(blueprints_path, f"{blueprint.name}{gltf_extension}") blueprint_exported_path = os.path.join(blueprints_path, f"{blueprint.name}{gltf_extension}")
# print("injecting blueprint path", blueprint_exported_path, "for", blueprint.name) # print("injecting blueprint path", blueprint_exported_path, "for", blueprint.name)
print("blueprint_exported_path", blueprint_exported_path)
blueprint.collection["export_path"] = blueprint_exported_path blueprint.collection["export_path"] = blueprint_exported_path
if export_materials_library:
blueprint.collection["materials_path"] = materials_exported_path
def inject_blueprints_list_into_main_scene(scene, blueprints_data, settings): def inject_blueprints_list_into_main_scene(scene, blueprints_data, settings):
project_root_path = getattr(settings, "project_root_path") project_root_path = getattr(settings, "project_root_path")

View File

@ -34,7 +34,7 @@ def get_all_materials(collection_names, library_scenes):
def add_material_info_to_objects(materials_per_object, settings): def add_material_info_to_objects(materials_per_object, settings):
materials_path = getattr(settings, "materials_path") materials_path = getattr(settings, "materials_path")
export_gltf_extension = getattr(settings.auto_export, "export_gltf_extension", ".glb") export_gltf_extension = getattr(settings, "export_gltf_extension", ".glb")
current_project_name = Path(bpy.context.blend_data.filepath).stem current_project_name = Path(bpy.context.blend_data.filepath).stem
materials_library_name = f"{current_project_name}_materials" materials_library_name = f"{current_project_name}_materials"

View File

@ -122,9 +122,13 @@ def test_export_complex(setup_data):
user_asset.path = "audio/fake.mp3" user_asset.path = "audio/fake.mp3"
# we have to cheat, since we cannot rely on the data injected when saving the library file (since we are not saving it as part of the tests) # we have to cheat, since we cannot rely on the data injected when saving the library file (since we are not saving it as part of the tests)
#bpy.data.collections["External_blueprint"]["export_path"] = "blueprints/External_blueprint.glb" bpy.data.collections["External_blueprint"]["export_path"] = "blueprints/External_blueprint.glb"
#bpy.data.collections["External_blueprint2"]["export_path"] = "blueprints/External_blueprint2.glb" bpy.data.collections["External_blueprint2"]["export_path"] = "blueprints/External_blueprint2.glb"
#bpy.data.collections["External_blueprint3"]["export_path"] = "blueprints/External_blueprint3.glb" bpy.data.collections["External_blueprint3"]["export_path"] = "blueprints/External_blueprint3.glb"
bpy.data.collections["External_blueprint"]["materials_path"] = "materials/testing_library_materials.glb"
bpy.data.collections["External_blueprint2"]["materials_path"] = "materials/testing_library_materials.glb"
bpy.data.collections["External_blueprint3"]["materials_path"] = "materials/testing_library_materials.glb"
# do the actual exporting # do the actual exporting
prepare_and_export() prepare_and_export()

View File

@ -80,6 +80,7 @@ def test_export_complex(setup_data):
export_props = { export_props = {
} }
gltf_settings = { gltf_settings = {
"export_format":"GLTF_SEPARATE",
"export_animations": True, "export_animations": True,
"export_optimize_animation_size": False, "export_optimize_animation_size": False,
"export_apply":True "export_apply":True