From 73441f34a43cafc19e3240d1960394bdefefd9ca Mon Sep 17 00:00:00 2001 From: "kaosat.dev" Date: Tue, 2 Apr 2024 16:42:24 +0200 Subject: [PATCH] feat(auto_export): restructured & improved changed parameters detection * now all centralized in the operator's did_export_settings_change() function * now works correctly ! ie only compares the parameters for auto & gltf at the time of exporting ! ie it does not matter anymore how many parameters you changed, until you save/export * this solves a lot of randomness bugs in change detection * related & various cleanups --- tools/gltf_auto_export/__init__.py | 46 +++++++- .../gltf_auto_export/auto_export/operators.py | 77 ++++++++++--- tools/gltf_auto_export/auto_export/tracker.py | 103 +----------------- .../helpers/to_remove_later.py | 89 ++++++++++++++- 4 files changed, 194 insertions(+), 121 deletions(-) diff --git a/tools/gltf_auto_export/__init__.py b/tools/gltf_auto_export/__init__.py index 19f8cc4..4cabfc3 100644 --- a/tools/gltf_auto_export/__init__.py +++ b/tools/gltf_auto_export/__init__.py @@ -10,6 +10,9 @@ bl_info = { "tracker_url": "https://github.com/kaosat-dev/Blender_bevy_components_workflow/issues/new", "category": "Import-Export" } +import os +from pathlib import Path +import json import bpy from bpy.types import Context from bpy.props import (StringProperty, BoolProperty, PointerProperty) @@ -174,9 +177,7 @@ def register(): # add our addon to the toolbar bpy.types.TOPBAR_MT_file_export.append(menu_func_import) - - bpy.types.WindowManager.gltf_exporter_running = BoolProperty(default=False) - bpy.types.WindowManager.gltf_settings_changed = BoolProperty(default=False) + bpy.types.WindowManager.gltf_settings_backup = StringProperty(default="") def unregister(): for cls in classes: @@ -185,8 +186,45 @@ def unregister(): bpy.app.handlers.depsgraph_update_post.remove(post_update) bpy.app.handlers.save_post.remove(post_save) - del bpy.types.WindowManager.gltf_exporter_running +global gltf_settings_backup + +def glTF2_pre_export_callback(data): + pass #print("pre_export", data) + # we backup any existing gltf export settings, if there where any + scene = bpy.context.scene + if "glTF2ExportSettings" in scene: + existing_setting = scene["glTF2ExportSettings"] + gltf_settings_backup = existing_setting + +def glTF2_post_export_callback(data): + gltf_settings_backup = "" + #print("post_export", data) + gltf_filepath = data["gltf_filepath"] + filename = Path(gltf_filepath).stem + + if filename == "dummy": # TODO: potentially use a hidden gltf exporter extension with an additional parameter instead of this hack + if os.path.exists(gltf_filepath): + print("removing dummy file") + os.unlink(gltf_filepath) + + # get the parameters + scene = bpy.context.scene + if "glTF2ExportSettings" in scene: + settings = scene["glTF2ExportSettings"] + export_settings = bpy.data.texts[".gltf_auto_export_gltf_settings"] if ".gltf_auto_export_gltf_settings" in bpy.data.texts else bpy.data.texts.new(".gltf_auto_export_gltf_settings") + # now write new settings + export_settings.clear() + export_settings.write(json.dumps(dict(settings))) + # now reset the original gltf_settings + if gltf_settings_backup != "": + print("resetting original gltf settings") + #scene["glTF2ExportSettings"] = gltf_settings_backup + else: + print("no pre_existing settings") + if "glTF2ExportSettings" in scene: + del scene["glTF2ExportSettings"] + gltf_settings_backup = "" if "gltf_auto_export" == "__main__": register() diff --git a/tools/gltf_auto_export/auto_export/operators.py b/tools/gltf_auto_export/auto_export/operators.py index eb13327..b9c3d74 100644 --- a/tools/gltf_auto_export/auto_export/operators.py +++ b/tools/gltf_auto_export/auto_export/operators.py @@ -68,7 +68,7 @@ class AutoExportGLTF(Operator, AutoExportGltfAddonPreferences, ExportHelper): except: return True - def save_settings(self, context): + def format_settings(self): # find all props to save exceptional = [ # options that don't start with 'export_' @@ -93,6 +93,10 @@ class AutoExportGLTF(Operator, AutoExportGltfAddonPreferences, ExportHelper): export_props['main_scene_names'] = list(map(lambda scene_data: scene_data.name, getattr(self,"main_scenes"))) export_props['library_scene_names'] = list(map(lambda scene_data: scene_data.name, getattr(self,"library_scenes"))) + return export_props + + def save_settings(self, context): + export_props = self.format_settings() self.properties['main_scene_names'] = export_props['main_scene_names'] self.properties['library_scene_names'] = export_props['library_scene_names'] @@ -142,28 +146,65 @@ class AutoExportGLTF(Operator, AutoExportGltfAddonPreferences, ExportHelper): self.report({"ERROR"}, "Loading export settings failed. Removed corrupted settings") bpy.data.texts.remove(bpy.data.texts[".gltf_auto_export_settings"]) + """ + This should ONLY be run when actually doing exports/aka calling auto_export function, because we only care about the difference in settings between EXPORTS + """ def did_export_settings_change(self): + print("comparing settings") # compare both the auto export settings & the gltf settings - previous_export_settings = bpy.data.texts[".gltf_auto_export_settings"] if ".gltf_auto_export_settings" in bpy.data.texts else None - changed_gltf_settings = bpy.context.window_manager.gltf_settings_changed - # if there was no setting before, it is new, we need export - print("changed settings", changed_gltf_settings, previous_export_settings.as_string()) - if previous_export_settings == None: - return changed_gltf_settings - else: - export_settings = {} - for (k, v) in self.properties.items(): - if k in self.white_list or k not in AutoExportGltfPreferenceNames: - export_settings[k] = v + previous_auto_settings = bpy.data.texts[".gltf_auto_export_settings_previous"] if ".gltf_auto_export_settings_previous" in bpy.data.texts else None + previous_gltf_settings = bpy.data.texts[".gltf_auto_export_gltf_settings_previous"] if ".gltf_auto_export_gltf_settings_previous" in bpy.data.texts else None + current_auto_settings = bpy.data.texts[".gltf_auto_export_settings"] if ".gltf_auto_export_settings" in bpy.data.texts else None + current_gltf_settings = bpy.data.texts[".gltf_auto_export_gltf_settings"] if ".gltf_auto_export_gltf_settings" in bpy.data.texts else None + + #check if params have changed + + # if there were no setting before, it is new, we need export + changed = False + if previous_auto_settings == None or previous_gltf_settings == None: + print("previous settings missing, exporting") + changed = True + else: + auto_settings_changed = sorted(json.loads(previous_auto_settings.as_string()).items()) != sorted(json.loads(current_auto_settings.as_string()).items()) if current_auto_settings != None else False + gltf_settings_changed = sorted(json.loads(previous_gltf_settings.as_string()).items()) != sorted(json.loads(current_gltf_settings.as_string()).items()) if current_gltf_settings != None else False + + print("auto settings previous", sorted(json.loads(previous_auto_settings.as_string()).items())) + print("auto settings current", sorted(json.loads(current_auto_settings.as_string()).items())) + print("auto_settings_changed", auto_settings_changed) + + print("gltf settings previous", sorted(json.loads(previous_gltf_settings.as_string()).items())) + print("gltf settings current", sorted(json.loads(current_gltf_settings.as_string()).items())) + print("gltf_settings_changed", gltf_settings_changed) + + changed = auto_settings_changed or gltf_settings_changed + # now write the current settings to the "previous settings" + previous_auto_settings = bpy.data.texts[".gltf_auto_export_settings_previous"] if ".gltf_auto_export_settings_previous" in bpy.data.texts else bpy.data.texts.new(".gltf_auto_export_settings_previous") + previous_auto_settings.clear() + previous_auto_settings.write(current_auto_settings.as_string()) # TODO : check if this is always valid + + previous_gltf_settings = bpy.data.texts[".gltf_auto_export_gltf_settings_previous"] if ".gltf_auto_export_gltf_settings_previous" in bpy.data.texts else bpy.data.texts.new(".gltf_auto_export_gltf_settings_previous") + previous_gltf_settings.clear() + previous_gltf_settings.write(current_gltf_settings.as_string()) + + print("changed", changed) + return changed + + """# if there was no setting before, it is new, we need export + print("changed settings IN OPERATOR", changed_gltf_settings, previous_export_settings) + if previous_export_settings == None: + return True # we can disregard the gltf settings, we need to save either way + else: + export_settings = self.format_settings() if len(export_settings.keys()) == 0: # first time after we already used the addon, since we already have export settings, but they have not yet been applied return changed_gltf_settings - # print("foo", json.loads(previous_export_settings.as_string()).items()) + print("previous", sorted(json.loads(previous_export_settings.as_string()).items())) + print("current", sorted(export_settings.items())) changed = sorted(json.loads(previous_export_settings.as_string()).items()) != sorted(export_settings.items()) - print("changed final", changed and changed_gltf_settings) - return changed and changed_gltf_settings + print("changed FINAL: auto_settings", changed, "gltf_settings", changed_gltf_settings, "combo", changed or changed_gltf_settings) + return changed and changed_gltf_settings""" def execute(self, context): # disable change detection while the operator runs @@ -171,13 +212,15 @@ class AutoExportGLTF(Operator, AutoExportGltfAddonPreferences, ExportHelper): if self.direct_mode: self.load_settings(context) if self.will_save_settings: + print("SAVING SETTINGS") self.save_settings(context) changes_per_scene = context.window_manager.auto_export_tracker.changed_objects_per_scene - #determine changed parameters - params_changed = self.did_export_settings_change() + #& do the export if self.direct_mode: #Do not auto export when applying settings in the menu, do it on save only + #determine changed parameters + params_changed = self.did_export_settings_change() auto_export(changes_per_scene, params_changed, self) # cleanup bpy.app.timers.register(bpy.context.window_manager.auto_export_tracker.enable_change_detection, first_interval=1) diff --git a/tools/gltf_auto_export/auto_export/tracker.py b/tools/gltf_auto_export/auto_export/tracker.py index 45d98db..d083fb5 100644 --- a/tools/gltf_auto_export/auto_export/tracker.py +++ b/tools/gltf_auto_export/auto_export/tracker.py @@ -48,113 +48,18 @@ class AutoExportTracker(PropertyGroup): # all our logic is done, mark this as done print("EXPORT DONE") - - @classmethod - def gltf_exporter_handler(cls): - # FOr some reason, the active operator here is always None, so using a workaround - # active_operator = bpy.context.active_operator - print("here", bpy.context.window_manager.gltf_exporter_running) - - if bpy.context.window_manager.gltf_exporter_running: - try: - dummy_file_path = "/home/ckaos/projects/bevy/Blender_bevy_components_worklflow/testing/bevy_example/assets/dummy.glb" - - import os - if os.path.exists(dummy_file_path): - print("dummy file exists, assuming it worked") - os.unlink(dummy_file_path) - - # get the parameters - scene = bpy.context.scene - if "glTF2ExportSettings" in scene: - settings = scene["glTF2ExportSettings"] - formatted_settings = dict(settings) - - export_settings = bpy.data.texts[".gltf_auto_export_gltf_settings"] if ".gltf_auto_export_gltf_settings" in bpy.data.texts else bpy.data.texts.new(".gltf_auto_export_gltf_settings") - - #check if params have changed - bpy.context.window_manager.gltf_settings_changed = sorted(json.loads(export_settings.as_string()).items()) != sorted(formatted_settings.items()) - - print("gltf NEW settings", formatted_settings, "OLD settings", export_settings, "CHANGED ?", bpy.context.window_manager.gltf_settings_changed) - - # now write new settings - export_settings.clear() - export_settings.write(json.dumps(formatted_settings)) - - - # now reset the original gltf_settings - if getattr(cls, "existing_gltf_settings", None) != None: - print("resetting original gltf settings") - scene["glTF2ExportSettings"] = cls.existing_gltf_settings - else: - print("no pre_existing settings") - if "glTF2ExportSettings" in scene: - del scene["glTF2ExportSettings"] - cls.existing_gltf_settings = None - except:pass - bpy.context.window_manager.gltf_exporter_running = False - return None - - - else: - try: - bpy.app.timers.unregister(cls.gltf_exporter_handler) - except:pass - return None - return 1 - @classmethod def deps_update_handler(cls, scene, depsgraph): # print("change detection enabled", cls.change_detection_enabled) active_operator = bpy.context.active_operator if active_operator: - print("Operator", active_operator.bl_label, active_operator.bl_idname, "bla", bpy.context.window_manager.gltf_exporter_running) - if active_operator.bl_idname == "EXPORT_SCENE_OT_gltf" and not bpy.context.window_manager.gltf_exporter_running: - print("matching") - try: - bpy.app.timers.unregister(cls.gltf_exporter_handler) - except:pass - bpy.app.timers.register(cls.gltf_exporter_handler, first_interval=3) - + # print("Operator", active_operator.bl_label, active_operator.bl_idname) + if active_operator.bl_idname == "EXPORT_SCENE_OT_gltf" : # we force saving params active_operator.will_save_settings = True - - # we backup any existing gltf export settings, if there where any - scene = bpy.context.scene - if "glTF2ExportSettings" in scene: - existing_setting = scene["glTF2ExportSettings"] - cls.existing_gltf_settings = existing_setting - bpy.context.window_manager.gltf_exporter_running = True - - - else: - if bpy.context.window_manager.gltf_exporter_running: - bpy.context.window_manager.gltf_exporter_running = False - """if active_operator.bl_idname == "EXPORT_SCENE_OT_gltf": - scene = bpy.context.scene - if "glTF2ExportSettings" in scene: - existing_setting = scene["glTF2ExportSettings"] - cls.existing_gltf_settings = existing_setting - print("we just executed the correct operator") + if active_operator.bl_idname == "EXPORT_SCENES_OT_auto_gltf": + # we force saving params active_operator.will_save_settings = True - else: - import os - dummy_file_path = "/home/ckaos/projects/bevy/Blender_bevy_components_worklflow/testing/bevy_example/assets/dummy.glb" - if os.path.exists(dummy_file_path): - print("dummy file exists") - os.unlink(dummy_file_path) - # get the parameters - scene = bpy.context.scene - settings = scene["glTF2ExportSettings"] - print("gltf settings", dict(settings)) - - # now reset the original gltf_settings - if hasattr(cls, "existing_gltf_settings"): - print("resetting original gltf settings") - scene["glTF2ExportSettings"] = cls.existing_gltf_settings - else: - del scene["glTF2ExportSettings"]""" - if scene.name != "temp_scene": # print("depsgraph_update_post", scene.name) diff --git a/tools/gltf_auto_export/helpers/to_remove_later.py b/tools/gltf_auto_export/helpers/to_remove_later.py index e012e4e..08090d5 100644 --- a/tools/gltf_auto_export/helpers/to_remove_later.py +++ b/tools/gltf_auto_export/helpers/to_remove_later.py @@ -240,4 +240,91 @@ def duplicate_object2(object, original_name): print("OJECT ANIMATION") new_obj.animation_data.action = object.animation_data.action.copy() - return new_obj \ No newline at end of file + return new_obj + + + + + + if active_operator: + # print("Operator", active_operator.bl_label, active_operator.bl_idname, "bla", bpy.context.window_manager.gltf_exporter_running) + if active_operator.bl_idname == "EXPORT_SCENE_OT_gltf" : #and not bpy.context.window_manager.gltf_exporter_running: + # we force saving params + active_operator.will_save_settings = True + if active_operator.bl_idname == "EXPORT_SCENES_OT_auto_gltf": + # we force saving params + active_operator.will_save_settings = True + + + """ + print("matching") + try: + bpy.app.timers.unregister(cls.gltf_exporter_handler) + except:pass + bpy.app.timers.register(cls.gltf_exporter_handler, first_interval=3) + # we backup any existing gltf export settings, if there where any + scene = bpy.context.scene + if "glTF2ExportSettings" in scene: + existing_setting = scene["glTF2ExportSettings"] + cls.existing_gltf_settings = existing_setting + bpy.context.window_manager.gltf_exporter_running = True + + + else: + if bpy.context.window_manager.gltf_exporter_running: + bpy.context.window_manager.gltf_exporter_running = False""" + + + """@classmethod + def gltf_exporter_handler(cls): + # FOr some reason, the active operator here is always None, so using a workaround + # active_operator = bpy.context.active_operator + print("here", bpy.context.window_manager.gltf_exporter_running) + + if bpy.context.window_manager.gltf_exporter_running: + try: + dummy_file_path = "/home/ckaos/projects/bevy/Blender_bevy_components_worklflow/testing/bevy_example/assets/dummy.glb" + + import os + if os.path.exists(dummy_file_path): + print("dummy file exists, assuming it worked") + os.unlink(dummy_file_path) + + # get the parameters + scene = bpy.context.scene + if "glTF2ExportSettings" in scene: + settings = scene["glTF2ExportSettings"] + formatted_settings = dict(settings) + + export_settings = bpy.data.texts[".gltf_auto_export_gltf_settings"] if ".gltf_auto_export_gltf_settings" in bpy.data.texts else bpy.data.texts.new(".gltf_auto_export_gltf_settings") + + #check if params have changed + bpy.context.window_manager.gltf_settings_changed = sorted(json.loads(export_settings.as_string()).items()) != sorted(formatted_settings.items()) + + print("gltf NEW settings", formatted_settings, "OLD settings", export_settings, "CHANGED ?", bpy.context.window_manager.gltf_settings_changed) + + # now write new settings + export_settings.clear() + export_settings.write(json.dumps(formatted_settings)) + + + # now reset the original gltf_settings + if getattr(cls, "existing_gltf_settings", None) != None: + print("resetting original gltf settings") + scene["glTF2ExportSettings"] = cls.existing_gltf_settings + else: + print("no pre_existing settings") + if "glTF2ExportSettings" in scene: + del scene["glTF2ExportSettings"] + cls.existing_gltf_settings = None + except:pass + bpy.context.window_manager.gltf_exporter_running = False + return None + + + else: + try: + bpy.app.timers.unregister(cls.gltf_exporter_handler) + except:pass + return None + return 1""" \ No newline at end of file