diff --git a/tools/gltf_auto_export/auto_export/auto_export.py b/tools/gltf_auto_export/auto_export/auto_export.py index c997cf5..4929412 100644 --- a/tools/gltf_auto_export/auto_export/auto_export.py +++ b/tools/gltf_auto_export/auto_export/auto_export.py @@ -1,9 +1,15 @@ +import copy import json import os +from types import SimpleNamespace import bpy import traceback +from .preferences import AutoExportGltfAddonPreferences + from .get_collections_to_export import get_collections_to_export +from .get_levels_to_export import get_levels_to_export +from .get_standard_exporter_settings import get_standard_exporter_settings from .export_main_scenes import export_main_scene from .export_blueprints import check_if_blueprint_on_disk, check_if_blueprints_exist, export_blueprints_from_collections @@ -29,10 +35,37 @@ def auto_export(changes_per_scene, changed_export_parameters, addon_prefs): export_blueprints = getattr(addon_prefs,"export_blueprints") export_output_folder = getattr(addon_prefs,"export_output_folder") + export_models_path = os.path.join(folder_path, export_output_folder) export_materials_library = getattr(addon_prefs,"export_materials_library") export_scene_settings = getattr(addon_prefs,"export_scene_settings") + # standard gltf export settings are stored differently + standard_gltf_exporter_settings = get_standard_exporter_settings() + gltf_extension = standard_gltf_exporter_settings.get("export_format", 'GLB') + gltf_extension = '.glb' if gltf_extension == 'GLB' else '.gltf' + + # here we do a bit of workaround by creating an override # TODO: do this at the "UI" level + export_blueprints_path = os.path.join(folder_path, export_output_folder, getattr(addon_prefs,"export_blueprints_path")) if getattr(addon_prefs,"export_blueprints_path") != '' else folder_path + #print('addon_prefs', AutoExportGltfAddonPreferences.__annotations__)#)addon_prefs.__annotations__) + + if hasattr(addon_prefs, "__annotations__") : + tmp = {} + for k in AutoExportGltfAddonPreferences.__annotations__: + item = AutoExportGltfAddonPreferences.__annotations__[k] + print("tutu",k, item.keywords.get('default', None) ) + default = item.keywords.get('default', None) + tmp[k] = default + + for (k, v) in addon_prefs.properties.items(): + tmp[k] = v + + addon_prefs = SimpleNamespace(**tmp) #copy.deepcopy(addon_prefs) + addon_prefs.__annotations__ = tmp + addon_prefs.export_blueprints_path = export_blueprints_path + addon_prefs.export_gltf_extension = gltf_extension + addon_prefs.export_models_path = export_models_path + [main_scene_names, level_scenes, library_scene_names, library_scenes] = get_scenes(addon_prefs) print("main scenes", main_scene_names, "library_scenes", library_scene_names) @@ -49,14 +82,34 @@ def auto_export(changes_per_scene, changed_export_parameters, addon_prefs): # export if export_blueprints: print("EXPORTING") - # create parent relations for all collections - (collections, collections_to_export, main_scenes_to_export, library_collections, collections_per_scene, blueprint_hierarchy, export_levels_path, gltf_extension) = get_collections_to_export(folder_path, export_output_folder, changes_per_scene, changed_export_parameters, addon_prefs) + # get blueprints/collections infos + (collections, collections_to_export, library_collections, collections_per_scene) = get_collections_to_export(changes_per_scene, changed_export_parameters, addon_prefs) + + # get level/main scenes infos + (main_scenes_to_export) = get_levels_to_export(changes_per_scene, changed_export_parameters, addon_prefs) # since materials export adds components we need to call this before blueprints are exported # export materials & inject materials components into relevant objects if export_materials_library: export_materials(collections, library_scenes, folder_path, addon_prefs) + # update the list of tracked exports + exports_total = len(collections_to_export) + len(main_scenes_to_export) + (1 if export_materials_library else 0) + bpy.context.window_manager.auto_export_tracker.exports_total = exports_total + bpy.context.window_manager.auto_export_tracker.exports_count = exports_total + + print("-------------------------------") + #print("collections: all:", collections) + #print("collections: changed:", changed_collections) + #print("collections: not found on disk:", collections_not_on_disk) + print("collections: in library:", library_collections) + print("collections: to export:", collections_to_export) + print("collections: per_scene:", collections_per_scene) + print("-------------------------------") + print("BLUEPRINTS: to export:", collections_to_export) + print("-------------------------------") + print("MAIN SCENES: to export:", main_scenes_to_export) + print("-------------------------------") # backup current active scene old_current_scene = bpy.context.scene # backup current selections @@ -65,24 +118,19 @@ def auto_export(changes_per_scene, changed_export_parameters, addon_prefs): # first export any main/level/world scenes if len(main_scenes_to_export) > 0: print("export MAIN scenes") - for scene_name in main_scene_names: - # we have more relaxed rules to determine if the main scenes have changed : any change is ok, (allows easier handling of changes, render settings etc) - do_export_main_scene = not export_change_detection or changed_export_parameters or scene_name in changes_per_scene.keys() or not check_if_blueprint_on_disk(scene_name, export_levels_path, gltf_extension) - if do_export_main_scene: - print(" exporting scene:", scene_name) - export_main_scene(bpy.data.scenes[scene_name], folder_path, addon_prefs, library_collections) - + for scene_name in main_scenes_to_export: + print(" exporting scene:", scene_name) + export_main_scene(bpy.data.scenes[scene_name], folder_path, addon_prefs, library_collections) # now deal with blueprints/collections - do_export_library_scene = not export_change_detection or changed_export_parameters or len(collections_to_export) > 0 # export_library_scene_name in changes_per_scene.keys() + do_export_library_scene = not export_change_detection or changed_export_parameters or len(collections_to_export) > 0 if do_export_library_scene: print("export LIBRARY") # we only want to go through the library scenes where our collections to export are present for (scene_name, collections_to_export) in collections_per_scene.items(): print(" exporting collections from scene:", scene_name) print(" collections to export", collections_to_export) - library_scene = bpy.data.scenes[scene_name] - export_blueprints_from_collections(collections_to_export, library_scene, folder_path, addon_prefs, blueprint_hierarchy, collections) + export_blueprints_from_collections(collections_to_export, folder_path, addon_prefs, collections) # reset current scene from backup bpy.context.window.scene = old_current_scene diff --git a/tools/gltf_auto_export/auto_export/export_blueprints.py b/tools/gltf_auto_export/auto_export/export_blueprints.py index f6bfdb5..126fa4f 100644 --- a/tools/gltf_auto_export/auto_export/export_blueprints.py +++ b/tools/gltf_auto_export/auto_export/export_blueprints.py @@ -7,7 +7,7 @@ from .export_gltf import (generate_gltf_export_preferences) from ..helpers.helpers_scenes import clear_hollow_scene, copy_hollowed_collection_into # export collections: all the collections that have an instance in the main scene AND any marked collections, even if they do not have instances -def export_collections(collections, folder_path, library_scene, addon_prefs, gltf_export_preferences, blueprint_hierarchy, library_collections): +def export_collections(collections, folder_path, addon_prefs, gltf_export_preferences, library_collections): # save current active collection active_collection = bpy.context.view_layer.active_layer_collection @@ -35,13 +35,13 @@ def export_collections(collections, folder_path, library_scene, addon_prefs, glt bpy.context.view_layer.active_layer_collection = active_collection -def export_blueprints_from_collections(collections, library_scene, folder_path, addon_prefs, blueprint_hierarchy, library_collections): +def export_blueprints_from_collections(collections, folder_path, addon_prefs, library_collections): export_output_folder = getattr(addon_prefs,"export_output_folder") gltf_export_preferences = generate_gltf_export_preferences(addon_prefs) export_blueprints_path = os.path.join(folder_path, export_output_folder, getattr(addon_prefs,"export_blueprints_path")) if getattr(addon_prefs,"export_blueprints_path") != '' else folder_path try: - export_collections(collections, export_blueprints_path, library_scene, addon_prefs, gltf_export_preferences, blueprint_hierarchy, library_collections) + export_collections(collections, export_blueprints_path, addon_prefs, gltf_export_preferences, library_collections) except Exception as error: print("failed to export collections to gltf: ", error) raise error diff --git a/tools/gltf_auto_export/auto_export/get_collections_to_export.py b/tools/gltf_auto_export/auto_export/get_collections_to_export.py index 667ad63..1187176 100644 --- a/tools/gltf_auto_export/auto_export/get_collections_to_export.py +++ b/tools/gltf_auto_export/auto_export/get_collections_to_export.py @@ -1,59 +1,59 @@ import os import bpy -from .get_standard_exporter_settings import get_standard_exporter_settings from .export_blueprints import check_if_blueprint_on_disk, check_if_blueprints_exist, export_blueprints_from_collections from ..helpers.helpers_collections import get_exportable_collections from ..helpers.helpers_collections import (get_collections_in_library, get_exportable_collections, get_collections_per_scene, find_collection_ascendant_target_collection) from ..helpers.helpers_scenes import (get_scenes, ) -def get_collections_to_export(folder_path, export_output_folder, changes_per_scene, changed_export_parameters, addon_prefs): +def get_collections_to_export(changes_per_scene, changed_export_parameters, addon_prefs): export_change_detection = getattr(addon_prefs, "export_change_detection") - export_materials_library = getattr(addon_prefs,"export_materials_library") + export_gltf_extension = getattr(addon_prefs, "export_gltf_extension", ".glb") + export_blueprints_path = getattr(addon_prefs,"export_blueprints_path", "") - # standard gltf export settings are stored differently - standard_gltf_exporter_settings = get_standard_exporter_settings() [main_scene_names, level_scenes, library_scene_names, library_scenes] = get_scenes(addon_prefs) - - collection_parents = dict() - for collection in bpy.data.collections: - collection_parents[collection.name] = None - for collection in bpy.data.collections: - for ch in collection.children: - collection_parents[ch.name] = collection.name - # get a list of all collections actually in use (collections, blueprint_hierarchy) = get_exportable_collections(level_scenes, library_scenes, addon_prefs) + collections_to_export = collections # just for clarity - # first check if all collections have already been exported before (if this is the first time the exporter is run - # in your current Blender session for example) - export_blueprints_path = os.path.join(folder_path, export_output_folder, getattr(addon_prefs,"export_blueprints_path")) if getattr(addon_prefs,"export_blueprints_path") != '' else folder_path - export_levels_path = os.path.join(folder_path, export_output_folder) + print("export_change_detection", export_change_detection, export_gltf_extension, export_blueprints_path) + + # if the export parameters have changed, bail out early + # we need to re_export everything if the export parameters have been changed + if export_change_detection and not changed_export_parameters: + changed_collections = [] - gltf_extension = standard_gltf_exporter_settings.get("export_format", 'GLB') - gltf_extension = '.glb' if gltf_extension == 'GLB' else '.gltf' - collections_not_on_disk = check_if_blueprints_exist(collections, export_blueprints_path, gltf_extension) - changed_collections = [] + # first check if all collections have already been exported before (if this is the first time the exporter is run + # in your current Blender session for example) + collections_not_on_disk = check_if_blueprints_exist(collections, export_blueprints_path, export_gltf_extension) - for scene, objects in changes_per_scene.items(): - print(" changed scene", scene) - for obj_name, obj in objects.items(): - object_collections = list(obj.users_collection) if hasattr(obj, 'users_collection') else [] - object_collection_names = list(map(lambda collection: collection.name, object_collections)) + # create parent relations for all collections # TODO: optimise this + collection_parents = dict() + for collection in bpy.data.collections: + collection_parents[collection.name] = None + for collection in bpy.data.collections: + for ch in collection.children: + collection_parents[ch.name] = collection.name - if len(object_collection_names) > 1: - print("ERRROR for",obj_name,"objects in multiple collections not supported") - else: - object_collection_name = object_collection_names[0] if len(object_collection_names) > 0 else None - #recurse updwards until we find one of our collections (or not) - matching_collection = find_collection_ascendant_target_collection(collection_parents, collections, object_collection_name) - if matching_collection is not None: - changed_collections.append(matching_collection) + # determine which collections have changed + for scene, objects in changes_per_scene.items(): + print(" changed scene", scene) + for obj_name, obj in objects.items(): + object_collections = list(obj.users_collection) if hasattr(obj, 'users_collection') else [] + object_collection_names = list(map(lambda collection: collection.name, object_collections)) - collections_to_export = list(set(changed_collections + collections_not_on_disk)) if export_change_detection else collections + if len(object_collection_names) > 1: + print("ERRROR for",obj_name,"objects in multiple collections not supported") + else: + object_collection_name = object_collection_names[0] if len(object_collection_names) > 0 else None + #recurse updwards until we find one of our collections (or not) + matching_collection = find_collection_ascendant_target_collection(collection_parents, collections, object_collection_name) + if matching_collection is not None: + changed_collections.append(matching_collection) - # we need to re_export everything if the export parameters have been changed # TODO: perhaps do this BEFORE the rest above for better perfs - collections_to_export = collections if changed_export_parameters else collections_to_export + collections_to_export = list(set(changed_collections + collections_not_on_disk)) + + # this needs to be done based on all previously collected collections, not the ones that we filter out based on their presence in the library scenes collections_per_scene = get_collections_per_scene(collections_to_export, library_scenes) # collections that do not come from a library should not be exported as seperate blueprints @@ -61,27 +61,5 @@ def get_collections_to_export(folder_path, export_output_folder, changes_per_sce library_collections = get_collections_in_library(library_scenes) collections_to_export = list(set(collections_to_export).intersection(set(library_collections))) - - main_scenes_to_export = [scene_name for scene_name in main_scene_names if not export_change_detection or changed_export_parameters or scene_name in changes_per_scene.keys() or not check_if_blueprint_on_disk(scene_name, export_levels_path, gltf_extension)] - - # update the list of tracked exports - exports_total = len(collections_to_export) + len(main_scenes_to_export) + (1 if export_materials_library else 0) - bpy.context.window_manager.auto_export_tracker.exports_total = exports_total - bpy.context.window_manager.auto_export_tracker.exports_count = exports_total - - - - print("-------------------------------") - print("collections: all:", collections) - print("collections: changed:", changed_collections) - print("collections: not found on disk:", collections_not_on_disk) - print("collections: in library:", library_collections) - print("collections: to export:", collections_to_export) - print("collections: per_scene:", collections_per_scene) - print("-------------------------------") - print("BLUEPRINTS: to export:", collections_to_export) - print("-------------------------------") - print("MAIN SCENES: to export:", main_scenes_to_export) - print("-------------------------------") - - return (collections, collections_to_export, main_scenes_to_export, library_collections, collections_per_scene, blueprint_hierarchy, export_levels_path, gltf_extension) \ No newline at end of file + # all collections, collections to export + return (collections, collections_to_export, library_collections, collections_per_scene) \ No newline at end of file diff --git a/tools/gltf_auto_export/auto_export/get_levels_to_export.py b/tools/gltf_auto_export/auto_export/get_levels_to_export.py new file mode 100644 index 0000000..78adf54 --- /dev/null +++ b/tools/gltf_auto_export/auto_export/get_levels_to_export.py @@ -0,0 +1,15 @@ +import bpy +from .export_blueprints import check_if_blueprint_on_disk +from ..helpers.helpers_scenes import (get_scenes, ) + +def get_levels_to_export(changes_per_scene, changed_export_parameters, addon_prefs): + export_change_detection = getattr(addon_prefs, "export_change_detection") + export_gltf_extension = getattr(addon_prefs, "export_gltf_extension") + export_models_path = getattr(addon_prefs, "export_models_path") + + [main_scene_names, level_scenes, library_scene_names, library_scenes] = get_scenes(addon_prefs) + + # determine list of main scenes to export + # we have more relaxed rules to determine if the main scenes have changed : any change is ok, (allows easier handling of changes, render settings etc) + main_scenes_to_export = [scene_name for scene_name in main_scene_names if not export_change_detection or changed_export_parameters or scene_name in changes_per_scene.keys() or not check_if_blueprint_on_disk(scene_name, export_models_path, export_gltf_extension)] + return (main_scenes_to_export) \ No newline at end of file diff --git a/tools/gltf_auto_export/auto_export/tracker.py b/tools/gltf_auto_export/auto_export/tracker.py index 4fd38d0..b0f8f6a 100644 --- a/tools/gltf_auto_export/auto_export/tracker.py +++ b/tools/gltf_auto_export/auto_export/tracker.py @@ -1,13 +1,18 @@ import json +import os from types import SimpleNamespace import bpy + from bpy.types import (PropertyGroup) from bpy.props import (PointerProperty, IntProperty, StringProperty) +from .get_collections_to_export import get_collections_to_export + from ..constants import TEMPSCENE_PREFIX from .internals import CollectionsToExport from ..helpers.helpers_scenes import (get_scenes) from ..helpers.helpers_collections import (get_exportable_collections) +from .preferences import AutoExportGltfAddonPreferences class AutoExportTracker(PropertyGroup): @@ -135,16 +140,44 @@ class AutoExportTracker(PropertyGroup): # get a list of exportable collections for display # keep it simple, just use Simplenamespace for compatibility with the rest of our code - addon_prefs = SimpleNamespace(**get_auto_exporter_settings()) - print("addon prefs", addon_prefs) - addon_prefs.export_marked_assets = True - [_, level_scenes, _, library_scenes] = get_scenes(addon_prefs) - (collections, _) = get_exportable_collections(level_scenes, library_scenes, addon_prefs) + + tmp = {} + for k in AutoExportGltfAddonPreferences.__annotations__: + item = AutoExportGltfAddonPreferences.__annotations__[k] + print("tutu",k, item.keywords.get('default', None) ) + default = item.keywords.get('default', None) + tmp[k] = default + auto_settings = get_auto_exporter_settings() + for k in auto_settings: + print("k", k, auto_settings[k]) + tmp[k] = auto_settings[k] + tmp['__annotations__'] = tmp + + # path to the current blend file + file_path = bpy.data.filepath + # Get the folder + folder_path = os.path.dirname(file_path) + export_output_folder =tmp["export_output_folder"] + export_models_path = os.path.join(folder_path, export_output_folder) + export_blueprints_path = os.path.join(folder_path, export_output_folder, tmp["export_blueprints_path"]) if tmp["export_blueprints_path"] != '' else folder_path + tmp["export_blueprints_path"] = export_blueprints_path + tmp["export_models_path"] = export_models_path + addon_prefs = SimpleNamespace(**tmp) + + # + + #addon_prefs.export_blueprints_path = export_blueprints_path + #addon_prefs.export_gltf_extension = gltf_extension + #addon_prefs.export_models_path = export_models_path + + + (collections, collections_to_export, library_collections, collections_per_scene) = get_collections_to_export(cls.changed_objects_per_scene, False, addon_prefs) + print("collections to export", collections_to_export) try: # we save this list of collections in the context bpy.context.window_manager.exportedCollections.clear() #TODO: add error handling for this - for collection_name in collections: + for collection_name in collections_to_export: ui_info = bpy.context.window_manager.exportedCollections.add() ui_info.name = collection_name except Exception as error: diff --git a/tools/gltf_auto_export/helpers/helpers_collections.py b/tools/gltf_auto_export/helpers/helpers_collections.py index 219add2..9744d7a 100644 --- a/tools/gltf_auto_export/helpers/helpers_collections.py +++ b/tools/gltf_auto_export/helpers/helpers_collections.py @@ -138,25 +138,16 @@ def get_collections_per_scene(collection_names, library_scenes): return collections_per_scene def get_collections_in_library(library_scenes): - """all_collections = [] - all_collection_names = [] - for main_scene in main_scenes: - (collection_names, collections) = get_used_collections(main_scene) - all_collection_names = all_collection_names + list(collection_names) - all_collections = all_collections + collections""" - # now that we have the collections that are in use by collection instances, check if those collections are actully present in the library scenes collections = [] collection_names = [] for library_scene in library_scenes: root_collection = library_scene.collection - for collection in traverse_tree(root_collection): collections.append(collection) collection_names.append(collection.name) return collection_names - def get_collection_hierarchy(root_col, levels=1): """Read hierarchy of the collections in the scene""" level_lookup = {}