diff --git a/tools/gltf_auto_export/__init__.py b/tools/gltf_auto_export/__init__.py index 4cabfc3..2be2b6c 100644 --- a/tools/gltf_auto_export/__init__.py +++ b/tools/gltf_auto_export/__init__.py @@ -56,12 +56,12 @@ This is a workaround needed because of the way the settings are stored , perhaps from io_scene_gltf2 import (ExportGLTF2, GLTF_PT_export_main, GLTF_PT_export_include) -class Testing_PT_MainPanel(bpy.types.Panel): - bl_idname = "Testing_PT_MainPanel" +class Auto_Export_SidePanel(bpy.types.Panel): + bl_idname = "Auto_Export_SidePanel" bl_label = "" bl_space_type = 'VIEW_3D' bl_region_type = 'UI' - bl_category = "Gltf auto_export" + bl_category = "Auto Export" bl_context = "objectmode" @@ -81,15 +81,64 @@ class Testing_PT_MainPanel(bpy.types.Panel): op.use_active_collection = True op.use_active_collection_with_nested=True op.use_active_scene = True - op.filepath="dummy" - + op.filepath="____dummy____" + op.gltf_export_id = "gltf_auto_export" # we specify that we are in a special case op = layout.operator("EXPORT_SCENES_OT_auto_gltf", text="Auto Export Settings") - op.will_save_settings=True + #op.will_save_settings=True #print("GLTF_PT_export_main", GLTF_PT_export_main.bl_parent_id) +# glTF extensions are named following a convention with known prefixes. +# See: https://github.com/KhronosGroup/glTF/tree/main/extensions#about-gltf-extensions +# also: https://github.com/KhronosGroup/glTF/blob/main/extensions/Prefixes.md +glTF_extension_name = "EXT_auto_export" + +# Support for an extension is "required" if a typical glTF viewer cannot be expected +# to load a given model without understanding the contents of the extension. +# For example, a compression scheme or new image format (with no fallback included) +# would be "required", but physics metadata or app-specific settings could be optional. +extension_is_required = False + +class AutoExportExtensionProperties(bpy.types.PropertyGroup): + enabled: bpy.props.BoolProperty( + name=bl_info["name"], + description='Include this extension in the exported glTF file.', + default=True + ) # type: ignore + +class glTF2ExportUserExtension: + + def __init__(self): + print("init extension", self) + # We need to wait until we create the gltf2UserExtension to import the gltf2 modules + # Otherwise, it may fail because the gltf2 may not be loaded yet + from io_scene_gltf2.io.com.gltf2_io_extensions import Extension + self.Extension = Extension + self.properties = bpy.context.scene.AutoExportExtensionProperties + + def gather_node_hook(self, gltf2_object, blender_object, export_settings): + print("fooo", self) + if self.properties.enabled: + if gltf2_object.extensions is None: + gltf2_object.extensions = {} + print("bla bla") + gltf2_object.extensions[glTF_extension_name] = self.Extension( + name=glTF_extension_name, + extension={"auto_export_blueprints": self.properties.auto_export_blueprints}, + required=extension_is_required + ) + def gather_animation_hook(): + pass + + def gather_gltf_hook(self, active_scene_idx, scenes, animations, export_settings): + if self.properties.enabled: + print("extension enabled") + + #print("gather_gltf_hook", self, active_scene_idx, scenes, animations, export_settings) + + #see here for original gltf exporter infos https://github.com/KhronosGroup/glTF-Blender-IO/blob/main/addons/io_scene_gltf2/__init__.py classes = [ SceneLink, @@ -111,17 +160,64 @@ classes = [ GLTF_PT_auto_export_general, GLTF_PT_auto_export_scenes, GLTF_PT_auto_export_blueprints, - - #GLTF_PT_auto_export_collections_list, - #GLTF_PT_auto_export_gltf, - #GLTF_PT_export_data, - #GLTF_PT_export_data_scene, + GLTF_PT_auto_export_collections_list, AutoExportTracker, - Testing_PT_MainPanel, + Auto_Export_SidePanel, ] +def glTF2_pre_export_callback(data): + #print("pre_export", data) + pass + +def cleanup_file(): + gltf_filepath = "/home/ckaos/projects/bevy/Blender_bevy_components_worklflow/testing/bevy_example/assets/____dummy____.glb" + if os.path.exists(gltf_filepath): + print("removing dummy file", gltf_filepath) + os.remove(gltf_filepath) + return None + else: + return 1 + +def glTF2_post_export_callback(data): + #print("post_export", data) + gltf_settings_backup = bpy.context.window_manager.gltf_settings_backup + print("gltf_settings_backup", gltf_settings_backup) + gltf_filepath = data["gltf_filepath"] + gltf_export_id = data['gltf_export_id'] + if gltf_export_id == "gltf_auto_export": + # some more absurdity: apparently the file is not QUITE done when the export callback is called, so we have to introduce this timer to remove the temporary file correctly + bpy.context.window_manager.auto_export_tracker.dummy_file_path = gltf_filepath + try: + bpy.app.timers.unregister(cleanup_file) + except:pass + bpy.app.timers.register(cleanup_file, first_interval=1) + + # 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"] = json.loads(gltf_settings_backup) + else: + print("no pre_existing settings") + if "glTF2ExportSettings" in scene: + del scene["glTF2ExportSettings"] + bpy.context.window_manager.gltf_settings_backup = "" + + # the absurd length one has to go through to RESET THE OPERATOR because it has global state !!!!! AAAAAHHH + last_operator = bpy.context.window_manager.auto_export_tracker.last_operator + last_operator.filepath = "" + last_operator.gltf_export_id = "" + + def menu_func_import(self, context): self.layout.operator(AutoExportGLTF.bl_idname, text="glTF auto Export (.glb/gltf)") from bpy.app.handlers import persistent @@ -134,40 +230,6 @@ def post_update(scene, depsgraph): def post_save(scene, depsgraph): bpy.context.window_manager.auto_export_tracker.save_handler( scene, depsgraph) - -def invoke_override(self, context, event): - settings = context.scene.get(self.scene_key) - self.will_save_settings = False - if settings: - try: - for (k, v) in settings.items(): - setattr(self, k, v) - self.will_save_settings = True - - # Update filter if user saved settings - if hasattr(self, 'export_format'): - self.filter_glob = '*.glb' if self.export_format == 'GLB' else '*.gltf' - - except (AttributeError, TypeError): - self.report({"ERROR"}, "Loading export settings failed. Removed corrupted settings") - del context.scene[self.scene_key] - - import sys - preferences = bpy.context.preferences - for addon_name in preferences.addons.keys(): - try: - if hasattr(sys.modules[addon_name], 'glTF2ExportUserExtension') or hasattr(sys.modules[addon_name], 'glTF2ExportUserExtensions'): - pass #exporter_extension_panel_unregister_functors.append(sys.modules[addon_name].register_panel()) - except Exception: - pass - - # self.has_active_exporter_extensions = len(exporter_extension_panel_unregister_functors) > 0 - print("ovverride") - wm = context.window_manager - wm.fileselect_add(self) - return {'RUNNING_MODAL'} - - def register(): for cls in classes: bpy.utils.register_class(cls) @@ -179,6 +241,9 @@ def register(): bpy.types.TOPBAR_MT_file_export.append(menu_func_import) bpy.types.WindowManager.gltf_settings_backup = StringProperty(default="") + bpy.utils.register_class(AutoExportExtensionProperties) + bpy.types.Scene.AutoExportExtensionProperties = bpy.props.PointerProperty(type=AutoExportExtensionProperties) + def unregister(): for cls in classes: bpy.utils.unregister_class(cls) @@ -187,44 +252,7 @@ def unregister(): bpy.app.handlers.depsgraph_update_post.remove(post_update) bpy.app.handlers.save_post.remove(post_save) -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 = "" + bpy.utils.unregister_class(AutoExportExtensionProperties) if "gltf_auto_export" == "__main__": register() diff --git a/tools/gltf_auto_export/__init___extension.py b/tools/gltf_auto_export/__init___extension.py index 4461985..ac5fa9e 100644 --- a/tools/gltf_auto_export/__init___extension.py +++ b/tools/gltf_auto_export/__init___extension.py @@ -163,8 +163,7 @@ class glTF2ExportUserExtension: extension={"auto_export_blueprints": self.properties.auto_export_blueprints}, required=extension_is_required ) - def pre_export_hook(self): - print("pre export callback") + def glTF2_pre_export_callback(data): print("pre_export", data) diff --git a/tools/gltf_auto_export/auto_export/tracker.py b/tools/gltf_auto_export/auto_export/tracker.py index d083fb5..1c8150d 100644 --- a/tools/gltf_auto_export/auto_export/tracker.py +++ b/tools/gltf_auto_export/auto_export/tracker.py @@ -11,6 +11,10 @@ class AutoExportTracker(PropertyGroup): change_detection_enabled = True export_params_changed = False + gltf_settings_backup = None + last_operator = None + dummy_file_path = "" + @classmethod def register(cls): bpy.types.WindowManager.auto_export_tracker = PointerProperty(type=AutoExportTracker) @@ -54,9 +58,18 @@ class AutoExportTracker(PropertyGroup): active_operator = bpy.context.active_operator if active_operator: # print("Operator", active_operator.bl_label, active_operator.bl_idname) - if active_operator.bl_idname == "EXPORT_SCENE_OT_gltf" : + if active_operator.bl_idname == "EXPORT_SCENE_OT_gltf" and active_operator.gltf_export_id == "gltf_auto_export": + # we backup any existing gltf export settings, if there were any + scene = bpy.context.scene + if "glTF2ExportSettings" in scene: + existing_setting = scene["glTF2ExportSettings"] + bpy.context.window_manager.gltf_settings_backup = json.dumps(dict(existing_setting)) + # we force saving params active_operator.will_save_settings = True + # we set the last operator here so we can clear the specific settings (yeah for overly complex logic) + cls.last_operator = active_operator + print("active_operator", active_operator.has_active_exporter_extensions, active_operator.__annotations__.keys(), active_operator.filepath, active_operator.gltf_export_id) if active_operator.bl_idname == "EXPORT_SCENES_OT_auto_gltf": # we force saving params active_operator.will_save_settings = True diff --git a/tools/gltf_auto_export/helpers/to_remove_later.py b/tools/gltf_auto_export/helpers/to_remove_later.py index 08090d5..ecbe545 100644 --- a/tools/gltf_auto_export/helpers/to_remove_later.py +++ b/tools/gltf_auto_export/helpers/to_remove_later.py @@ -327,4 +327,37 @@ def duplicate_object2(object, original_name): bpy.app.timers.unregister(cls.gltf_exporter_handler) except:pass return None - return 1""" \ No newline at end of file + return 1""" + + +def invoke_override(self, context, event): + settings = context.scene.get(self.scene_key) + self.will_save_settings = False + if settings: + try: + for (k, v) in settings.items(): + setattr(self, k, v) + self.will_save_settings = True + + # Update filter if user saved settings + if hasattr(self, 'export_format'): + self.filter_glob = '*.glb' if self.export_format == 'GLB' else '*.gltf' + + except (AttributeError, TypeError): + self.report({"ERROR"}, "Loading export settings failed. Removed corrupted settings") + del context.scene[self.scene_key] + + import sys + preferences = bpy.context.preferences + for addon_name in preferences.addons.keys(): + try: + if hasattr(sys.modules[addon_name], 'glTF2ExportUserExtension') or hasattr(sys.modules[addon_name], 'glTF2ExportUserExtensions'): + pass #exporter_extension_panel_unregister_functors.append(sys.modules[addon_name].register_panel()) + except Exception: + pass + + # self.has_active_exporter_extensions = len(exporter_extension_panel_unregister_functors) > 0 + print("ovverride") + wm = context.window_manager + wm.fileselect_add(self) + return {'RUNNING_MODAL'}