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
This commit is contained in:
kaosat.dev 2024-04-02 16:42:24 +02:00
parent 73e81c2b64
commit 73441f34a4
4 changed files with 194 additions and 121 deletions

View File

@ -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()

View File

@ -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)

View File

@ -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)

View File

@ -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
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"""