mirror of
https://github.com/kaosat-dev/Blender_bevy_components_workflow.git
synced 2024-11-22 20:00:53 +00:00
feat(auto_export): a ton of finicky annoying workarounds !
* added workaround for backing up & restoring gltf export settings if there where any, because it turns out in the glTF2_pre_export_callback, they already get created ! * added workaround for deleting the temporary gltf file, aka it needs an additional timer, because the post export callback fires too early ! * added workaround to deal with the fact that operator uis have global state !! aka make sure the settings used when calling the gltf exporter do not "contaminate" the normal exporter * it all works, but sigh ...
This commit is contained in:
parent
73441f34a4
commit
2dae2c41b4
@ -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)
|
from io_scene_gltf2 import (ExportGLTF2, GLTF_PT_export_main, GLTF_PT_export_include)
|
||||||
|
|
||||||
|
|
||||||
class Testing_PT_MainPanel(bpy.types.Panel):
|
class Auto_Export_SidePanel(bpy.types.Panel):
|
||||||
bl_idname = "Testing_PT_MainPanel"
|
bl_idname = "Auto_Export_SidePanel"
|
||||||
bl_label = ""
|
bl_label = ""
|
||||||
bl_space_type = 'VIEW_3D'
|
bl_space_type = 'VIEW_3D'
|
||||||
bl_region_type = 'UI'
|
bl_region_type = 'UI'
|
||||||
bl_category = "Gltf auto_export"
|
bl_category = "Auto Export"
|
||||||
bl_context = "objectmode"
|
bl_context = "objectmode"
|
||||||
|
|
||||||
|
|
||||||
@ -81,15 +81,64 @@ class Testing_PT_MainPanel(bpy.types.Panel):
|
|||||||
op.use_active_collection = True
|
op.use_active_collection = True
|
||||||
op.use_active_collection_with_nested=True
|
op.use_active_collection_with_nested=True
|
||||||
op.use_active_scene = 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 = 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)
|
#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
|
#see here for original gltf exporter infos https://github.com/KhronosGroup/glTF-Blender-IO/blob/main/addons/io_scene_gltf2/__init__.py
|
||||||
classes = [
|
classes = [
|
||||||
SceneLink,
|
SceneLink,
|
||||||
@ -111,17 +160,64 @@ classes = [
|
|||||||
GLTF_PT_auto_export_general,
|
GLTF_PT_auto_export_general,
|
||||||
GLTF_PT_auto_export_scenes,
|
GLTF_PT_auto_export_scenes,
|
||||||
GLTF_PT_auto_export_blueprints,
|
GLTF_PT_auto_export_blueprints,
|
||||||
|
GLTF_PT_auto_export_collections_list,
|
||||||
#GLTF_PT_auto_export_collections_list,
|
|
||||||
#GLTF_PT_auto_export_gltf,
|
|
||||||
#GLTF_PT_export_data,
|
|
||||||
#GLTF_PT_export_data_scene,
|
|
||||||
|
|
||||||
AutoExportTracker,
|
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):
|
def menu_func_import(self, context):
|
||||||
self.layout.operator(AutoExportGLTF.bl_idname, text="glTF auto Export (.glb/gltf)")
|
self.layout.operator(AutoExportGLTF.bl_idname, text="glTF auto Export (.glb/gltf)")
|
||||||
from bpy.app.handlers import persistent
|
from bpy.app.handlers import persistent
|
||||||
@ -134,40 +230,6 @@ def post_update(scene, depsgraph):
|
|||||||
def post_save(scene, depsgraph):
|
def post_save(scene, depsgraph):
|
||||||
bpy.context.window_manager.auto_export_tracker.save_handler( 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():
|
def register():
|
||||||
for cls in classes:
|
for cls in classes:
|
||||||
bpy.utils.register_class(cls)
|
bpy.utils.register_class(cls)
|
||||||
@ -179,6 +241,9 @@ def register():
|
|||||||
bpy.types.TOPBAR_MT_file_export.append(menu_func_import)
|
bpy.types.TOPBAR_MT_file_export.append(menu_func_import)
|
||||||
bpy.types.WindowManager.gltf_settings_backup = StringProperty(default="")
|
bpy.types.WindowManager.gltf_settings_backup = StringProperty(default="")
|
||||||
|
|
||||||
|
bpy.utils.register_class(AutoExportExtensionProperties)
|
||||||
|
bpy.types.Scene.AutoExportExtensionProperties = bpy.props.PointerProperty(type=AutoExportExtensionProperties)
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
for cls in classes:
|
for cls in classes:
|
||||||
bpy.utils.unregister_class(cls)
|
bpy.utils.unregister_class(cls)
|
||||||
@ -187,44 +252,7 @@ def unregister():
|
|||||||
bpy.app.handlers.depsgraph_update_post.remove(post_update)
|
bpy.app.handlers.depsgraph_update_post.remove(post_update)
|
||||||
bpy.app.handlers.save_post.remove(post_save)
|
bpy.app.handlers.save_post.remove(post_save)
|
||||||
|
|
||||||
global gltf_settings_backup
|
bpy.utils.unregister_class(AutoExportExtensionProperties)
|
||||||
|
|
||||||
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__":
|
if "gltf_auto_export" == "__main__":
|
||||||
register()
|
register()
|
||||||
|
@ -163,8 +163,7 @@ class glTF2ExportUserExtension:
|
|||||||
extension={"auto_export_blueprints": self.properties.auto_export_blueprints},
|
extension={"auto_export_blueprints": self.properties.auto_export_blueprints},
|
||||||
required=extension_is_required
|
required=extension_is_required
|
||||||
)
|
)
|
||||||
def pre_export_hook(self):
|
|
||||||
print("pre export callback")
|
|
||||||
|
|
||||||
def glTF2_pre_export_callback(data):
|
def glTF2_pre_export_callback(data):
|
||||||
print("pre_export", data)
|
print("pre_export", data)
|
||||||
|
@ -11,6 +11,10 @@ class AutoExportTracker(PropertyGroup):
|
|||||||
change_detection_enabled = True
|
change_detection_enabled = True
|
||||||
export_params_changed = False
|
export_params_changed = False
|
||||||
|
|
||||||
|
gltf_settings_backup = None
|
||||||
|
last_operator = None
|
||||||
|
dummy_file_path = ""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def register(cls):
|
def register(cls):
|
||||||
bpy.types.WindowManager.auto_export_tracker = PointerProperty(type=AutoExportTracker)
|
bpy.types.WindowManager.auto_export_tracker = PointerProperty(type=AutoExportTracker)
|
||||||
@ -54,9 +58,18 @@ class AutoExportTracker(PropertyGroup):
|
|||||||
active_operator = bpy.context.active_operator
|
active_operator = bpy.context.active_operator
|
||||||
if active_operator:
|
if active_operator:
|
||||||
# print("Operator", active_operator.bl_label, active_operator.bl_idname)
|
# 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
|
# we force saving params
|
||||||
active_operator.will_save_settings = True
|
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":
|
if active_operator.bl_idname == "EXPORT_SCENES_OT_auto_gltf":
|
||||||
# we force saving params
|
# we force saving params
|
||||||
active_operator.will_save_settings = True
|
active_operator.will_save_settings = True
|
||||||
|
@ -328,3 +328,36 @@ def duplicate_object2(object, original_name):
|
|||||||
except:pass
|
except:pass
|
||||||
return None
|
return None
|
||||||
return 1"""
|
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'}
|
||||||
|
Loading…
Reference in New Issue
Block a user