feat(auto_export): exploration, changes, debug etc for change detection weirdness

This commit is contained in:
kaosat.dev 2024-04-08 00:08:06 +02:00
parent 11e8786b59
commit 64fd308fd3
5 changed files with 95 additions and 38 deletions

View File

@ -116,7 +116,6 @@ classes = [
GLTF_PT_auto_export_SidePanel,
AutoExportTracker,
]
def glTF2_pre_export_callback(data):
@ -191,10 +190,6 @@ def register():
bpy.types.TOPBAR_MT_file_export.append(menu_func_import)
bpy.types.WindowManager.gltf_settings_backup = StringProperty(default="")
# FIXME: perhaps move this to tracker
bpy.types.WindowManager.exports_count = IntProperty(default=0)
"""bpy.utils.register_class(AutoExportExtensionProperties)
bpy.types.Scene.AutoExportExtensionProperties = bpy.props.PointerProperty(type=AutoExportExtensionProperties)"""
@ -207,7 +202,6 @@ def unregister():
bpy.app.handlers.save_post.remove(post_save)
"""bpy.utils.unregister_class(AutoExportExtensionProperties)"""
del bpy.types.WindowManager.exports_count
if "gltf_auto_export" == "__main__":
register()

View File

@ -109,20 +109,23 @@ def auto_export(changes_per_scene, changed_export_parameters, addon_prefs):
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)]
bpy.context.window_manager.exports_count = len(collections_to_export)
bpy.context.window_manager.exports_count += len(main_scenes_to_export)
bpy.context.window_manager.auto_export_tracker.exports_count = len(collections_to_export)
bpy.context.window_manager.auto_export_tracker.exports_count += len(main_scenes_to_export)
if export_materials_library:
bpy.context.window_manager.exports_count += 1
bpy.context.window_manager.auto_export_tracker.exports_count += 1
print("--------------")
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("MAIN SCENES TO EXPORT", main_scenes_to_export)
print("-------------------------------")
print("BLUEPRINTS: to export:", collections_to_export)
print("-------------------------------")
print("MAIN SCENES: to export:", main_scenes_to_export)
print("-------------------------------")
# backup current active scene
@ -131,19 +134,20 @@ def auto_export(changes_per_scene, changed_export_parameters, addon_prefs):
old_selections = bpy.context.selected_objects
# first export any main/level/world scenes
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)
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)
# 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()
print("export LIBRARY")
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)
@ -164,6 +168,8 @@ def auto_export(changes_per_scene, changed_export_parameters, addon_prefs):
for scene_name in main_scene_names:
export_main_scene(bpy.data.scenes[scene_name], folder_path, addon_prefs, [])
print("we are done with all export work",bpy.context.window_manager.auto_export_tracker.change_detection_enabled)
except Exception as error:
print(traceback.format_exc())

View File

@ -168,13 +168,13 @@ class AutoExportGLTF(Operator, AutoExportGltfAddonPreferences, ExportHelper):
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 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)
print("gltf_settings_changed", gltf_settings_changed)"""
changed = auto_settings_changed or gltf_settings_changed
# now write the current settings to the "previous settings"
@ -191,6 +191,7 @@ class AutoExportGLTF(Operator, AutoExportGltfAddonPreferences, ExportHelper):
return changed
def execute(self, context):
print("execute")
# disable change detection while the operator runs
bpy.context.window_manager.auto_export_tracker.disable_change_detection()
if self.direct_mode:
@ -206,12 +207,14 @@ class AutoExportGLTF(Operator, AutoExportGltfAddonPreferences, ExportHelper):
params_changed = self.did_export_settings_change()
auto_export(changes_per_scene, params_changed, self)
# cleanup
if bpy.context.window_manager.exports_count == 0: # we need this in case there was nothing to export, to make sure change detection is enabled again
print("YOLOOO")
bpy.context.window_manager.auto_export_tracker.enable_change_detection()
print("AUTO EXPORT DONE")
if bpy.context.window_manager.auto_export_tracker.exports_count == 0: # we need this in case there was nothing to export, to make sure change detection is enabled again
pass #print("YOLOOO")
#py.context.window_manager.auto_export_tracker.enable_change_detection()
#bpy.app.timers.register(bpy.context.window_manager.auto_export_tracker.enable_change_detection, first_interval=1)
#bpy.context.window_manager.auto_export_tracker.enable_change_detection()
# FIXME: wrong logic, this should be called only in an glTF2_post_export_callback
bpy.app.timers.register(bpy.context.window_manager.auto_export_tracker.enable_change_detection, first_interval=1)
#bpy.app.timers.register(bpy.context.window_manager.auto_export_tracker.enable_change_detection, first_interval=1)
else:
print("auto export disabled, skipping")
return {'FINISHED'}

View File

@ -1,7 +1,7 @@
import json
import bpy
from bpy.types import (PropertyGroup)
from bpy.props import (PointerProperty)
from bpy.props import (PointerProperty, IntProperty)
from .internals import CollectionsToExport
@ -15,6 +15,12 @@ class AutoExportTracker(PropertyGroup):
last_operator = None
dummy_file_path = ""
exports_count : IntProperty(
name='exports_count',
description='Number of exports in progress',
default=0
) # type: ignore
@classmethod
def register(cls):
bpy.types.WindowManager.auto_export_tracker = PointerProperty(type=AutoExportTracker)
@ -53,7 +59,7 @@ class AutoExportTracker(PropertyGroup):
@classmethod
def deps_update_handler(cls, scene, depsgraph):
print("change detection enabled", cls.change_detection_enabled)
print("change detection enabled", cls.change_detection_enabled, bpy.context.window_manager.auto_export_tracker.change_detection_enabled)
active_operator = bpy.context.active_operator
if active_operator:
# print("Operator", active_operator.bl_label, active_operator.bl_idname)
@ -74,8 +80,8 @@ class AutoExportTracker(PropertyGroup):
active_operator.will_save_settings = True
active_operator.auto_export = True
if scene.name != "temp_scene":
# print("depsgraph_update_post", scene.name)
if not scene.name.startswith("__temp_scene"):
print("depsgraph_update_post", scene.name)
changed_scene = scene.name or ""
# only deal with changes if we are no in the mids of saving/exporting
@ -90,7 +96,8 @@ class AutoExportTracker(PropertyGroup):
if isinstance(obj.id, bpy.types.Object):
# get the actual object
object = bpy.data.objects[obj.id.name]
#print("changed object", obj.id.name)
print("changed object", obj.id.name)
print("FOO","transforms", obj.is_updated_transform, "geometry", obj.is_updated_geometry)
cls.changed_objects_per_scene[scene.name][obj.id.name] = object
elif isinstance(obj.id, bpy.types.Material): # or isinstance(obj.id, bpy.types.ShaderNodeTree):
# print("changed material", obj.id, "scene", scene.name,)
@ -114,19 +121,27 @@ class AutoExportTracker(PropertyGroup):
for update in depsgraph.updates:
print("update", update)"""
def disable_change_detection(self,):
def disable_change_detection(self):
print("disable change detection")
self.change_detection_enabled = False
self.__class__.change_detection_enabled = False
return None
def enable_change_detection(self):
print("enable change detection")
self.change_detection_enabled = True
self.__class__.change_detection_enabled = True
# bpy.context.window_manager.auto_export_tracker.change_detection_enabled = True
print("bpy.context.window_manager.auto_export_tracker.change_detection_enabled", bpy.context.window_manager.auto_export_tracker.change_detection_enabled)
return None
def export_finished(self):
print("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAHHHHHHHH export_finished")
bpy.context.window_manager.exports_count -= 1
if bpy.context.window_manager.exports_count == 0:
print("YOLOOO")
bpy.context.window_manager.auto_export_tracker.exports_count -= 1
if bpy.context.window_manager.auto_export_tracker.exports_count == 0:
#print("preparing to reset change detection")
#bpy.app.timers.register(bpy.context.window_manager.auto_export_tracker.enable_change_detection, first_interval=1)
self.enable_change_detection()
return None

View File

@ -1,8 +1,10 @@
import bpy
import os
import json
import mathutils
import pytest
import shutil
import pathlib
@pytest.fixture
def setup_data(request):
@ -133,9 +135,20 @@ def test_export_changed_parameters(setup_data):
models_library_path = os.path.join(models_path, "library")
model_library_file_paths = list(map(lambda file_name: os.path.join(models_library_path, file_name), sorted(os.listdir(models_library_path))))
modification_times_first = list(map(lambda file_path: os.path.getmtime(file_path), model_library_file_paths + [world_file_path]))
#print("files", model_library_file_paths)
mapped_files_to_timestamps_and_index = {}
for (index, file_path) in enumerate(model_library_file_paths+ [world_file_path]):
file_path = pathlib.Path(file_path).stem
mapped_files_to_timestamps_and_index[file_path] = (modification_times_first[index], index)
print("files", mapped_files_to_timestamps_and_index)
#print("mod times", modification_times_first)
# export again with no changes
print("----------------")
print("no changes")
print("----------------")
bpy.context.window_manager.auto_export_tracker.enable_change_detection() # FIXME: should not be needed, but ..
auto_export_operator(
auto_export=True,
direct_mode=True,
@ -154,7 +167,7 @@ def test_export_changed_parameters(setup_data):
print("main scene change")
print("----------------")
#py.context.window_manager.auto_export_tracker.enable_change_detection() # FIXME: should not be needed, but ..
bpy.context.window_manager.auto_export_tracker.enable_change_detection() # FIXME: should not be needed, but ..
bpy.data.objects["Cube"].location = [1, 0, 0]
auto_export_operator(
@ -169,6 +182,14 @@ def test_export_changed_parameters(setup_data):
modification_times = list(map(lambda file_path: os.path.getmtime(file_path), model_library_file_paths + [world_file_path]))
assert modification_times != modification_times_first
# only the "world" file should have changed
world_file_index = mapped_files_to_timestamps_and_index["World"][1]
other_files_modification_times = [value for index, value in enumerate(modification_times) if index not in [world_file_index]]
other_files_modification_times_first = [value for index, value in enumerate(modification_times_first) if index not in [world_file_index]]
assert modification_times[world_file_index] != modification_times_first[world_file_index]
assert other_files_modification_times == other_files_modification_times_first
# reset the comparing
modification_times_first = modification_times
@ -176,6 +197,7 @@ def test_export_changed_parameters(setup_data):
print("----------------")
print("library change")
print("----------------")
bpy.context.window_manager.auto_export_tracker.enable_change_detection() # FIXME: should not be needed, but ..
bpy.data.objects["Blueprint1_mesh"].location = [1, 2, 1]
auto_export_operator(
@ -190,14 +212,31 @@ def test_export_changed_parameters(setup_data):
modification_times = list(map(lambda file_path: os.path.getmtime(file_path), model_library_file_paths + [world_file_path]))
assert modification_times != modification_times_first
# only the "world" file should have changed
blueprint1_file_index = mapped_files_to_timestamps_and_index["Blueprint1"][1]
other_files_modification_times = [value for index, value in enumerate(modification_times) if index not in [blueprint1_file_index]]
other_files_modification_times_first = [value for index, value in enumerate(modification_times_first) if index not in [blueprint1_file_index]]
assert modification_times[blueprint1_file_index] != modification_times_first[blueprint1_file_index]
assert other_files_modification_times == other_files_modification_times_first
# reset the comparing
modification_times_first = modification_times
# now same, but using an operator
print("----------------")
print("change using operator")
print("----------------")
bpy.context.window_manager.auto_export_tracker.enable_change_detection() # FIXME: should not be needed, but ..
with bpy.context.temp_override(active_object=bpy.data.objects["Cube"]):
print("translate using operator")
bpy.ops.transform.translate(value=mathutils.Vector((2.0, 1.0, -5.0)))
bpy.ops.transform.rotate(value=0.378874, constraint_axis=(False, False, True), mirror=False, proportional_edit_falloff='SMOOTH', proportional_size=1)
bpy.ops.object.transform_apply()
bpy.ops.transform.translate(value=(0.5, 0, 0), constraint_axis=(True, False, False))
bpy.ops.transform.translate(value=(20.0, 0.0, 0.0))
auto_export_operator(
auto_export=True,