From 4a0479fbf0e1df93a4cf524b3b88d32a5df24878 Mon Sep 17 00:00:00 2001 From: "kaosat.dev" Date: Tue, 2 Apr 2024 00:46:09 +0200 Subject: [PATCH] feat(auto_export): further experimenting with standard gltf exporter integration --- tools/gltf_auto_export/__init__.py | 326 ++++++++---------- tools/gltf_auto_export/__init__old.py | 189 ++++++++++ .../gltf_auto_export/auto_export/operators.py | 4 +- .../helpers/to_remove_later.py | 1 + tools/gltf_auto_export/ui/main.py | 40 ++- tools/gltf_auto_export/ui2/main.py | 265 ++++++++++++++ tools/gltf_auto_export/ui2/operators.py | 83 +++++ 7 files changed, 724 insertions(+), 184 deletions(-) create mode 100644 tools/gltf_auto_export/__init__old.py create mode 100644 tools/gltf_auto_export/ui2/main.py create mode 100644 tools/gltf_auto_export/ui2/operators.py diff --git a/tools/gltf_auto_export/__init__.py b/tools/gltf_auto_export/__init__.py index a37db05..4461985 100644 --- a/tools/gltf_auto_export/__init__.py +++ b/tools/gltf_auto_export/__init__.py @@ -1,209 +1,173 @@ -bl_info = { - "name": "gltf_auto_export", - "author": "kaosigh", - "version": (0, 16, 0), - "blender": (3, 4, 0), - "location": "File > Import-Export", - "description": "glTF/glb auto-export", - "warning": "", - "wiki_url": "https://github.com/kaosat-dev/Blender_bevy_components_workflow", - "tracker_url": "https://github.com/kaosat-dev/Blender_bevy_components_workflow/issues/new", - "category": "Import-Export" -} import bpy -from bpy.types import Context -from bpy.props import (StringProperty, BoolProperty, PointerProperty) +from bpy.props import (BoolProperty, + IntProperty, + StringProperty, + EnumProperty, + CollectionProperty + ) +bl_info = { + "name": "auto_export", + "category": "Generic", + "version": (1, 0, 0), + "blender": (2, 80, 0), + 'location': 'File > Export > glTF 2.0', + 'description': 'Example addon to add a custom extension to an exported glTF file.', + 'tracker_url': "https://github.com/KhronosGroup/glTF-Blender-IO/issues/", # Replace with your issue tracker + 'isDraft': False, + 'developer': "(Your name here)", # Replace this + 'url': 'https://your_url_here', # Replace this +} -from .auto_export.operators import AutoExportGLTF -from .auto_export.tracker import AutoExportTracker -from .auto_export.preferences import (AutoExportGltfAddonPreferences) - -from .auto_export.internals import (SceneLink, - SceneLinks, - CollectionToExport, - CollectionsToExport, - CUSTOM_PG_sceneName - ) -from .ui.main import (GLTF_PT_auto_export_main, - GLTF_PT_auto_export_root, - GLTF_PT_auto_export_blueprints, - GLTF_PT_auto_export_collections_list, - GLTF_PT_auto_export_gltf, - SCENE_UL_GLTF_auto_export, - ) -from .ui.operators import (SCENES_LIST_OT_actions) - - -###################################################### -""" there are two places where we load settings for auto_export from: -- in ui/main AutoExportGLTF -> invoke -- in auto_export.py -> auto_export -This is a workaround needed because of the way the settings are stored , perhaps there is a better way to deal with it ? ie by calling the AutoExportGLTF operator from the auto_export function ? -""" +# 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 from io_scene_gltf2 import (GLTF_PT_export_main, GLTF_PT_export_include) -class Testing_PT_MainPanel(bpy.types.Panel): - bl_idname = "Testing_PT_MainPanel" - bl_label = "" - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' - bl_category = "Gltf auto_export" - bl_context = "objectmode" +class ExampleExtensionProperties(bpy.types.PropertyGroup): + enabled: bpy.props.BoolProperty( + name=bl_info["name"], + description='Include this extension in the exported glTF file.', + default=True + ) + + auto_export_main_scene_name: StringProperty( + name='Main scene', + description='The name of the main scene/level/world to auto export', + default='Scene' + ) + auto_export_output_folder: StringProperty( + name='Export folder (relative)', + description='The root folder for all exports(relative to current file) Defaults to current folder', + default='' + ) + auto_export_library_scene_name: StringProperty( + name='Library scene', + description='The name of the library scene to auto export', + default='Library' + ) + # scene components + auto_export_scene_settings: BoolProperty( + name='Export scene settings', + description='Export scene settings ie AmbientLighting, Bloom, AO etc', + default=False + ) + + # blueprint settings + auto_export_blueprints: BoolProperty( + name='Export Blueprints', + description='Replaces collection instances with an Empty with a BlueprintName custom property', + default=True + ) + auto_export_blueprints_path: StringProperty( + name='Blueprints path', + description='path to export the blueprints to (relative to the Export folder)', + default='library' + ) + + auto_export_materials_library: BoolProperty( + name='Export materials library', + description='remove materials from blueprints and use the material library instead', + default=False + ) + auto_export_materials_path: StringProperty( + name='Materials path', + description='path to export the materials libraries to (relative to the root folder)', + default='materials' + ) + +def register(): + bpy.utils.register_class(ExampleExtensionProperties) + bpy.types.Scene.ExampleExtensionProperties = bpy.props.PointerProperty(type=ExampleExtensionProperties) + +def register_panel(): + # Register the panel on demand, we need to be sure to only register it once + # This is necessary because the panel is a child of the extensions panel, + # which may not be registered when we try to register this extension + try: + bpy.utils.register_class(GLTF_PT_UserExtensionPanel) + except Exception: + pass + + # If the glTF exporter is disabled, we need to unregister the extension panel + # Just return a function to the exporter so it can unregister the panel + return unregister_panel - def draw_header(self, context): - layout = self.layout - name = context.object.name if context.object != None else '' - layout.label(text="Gltf auto export ") - - def draw(self, context): - layout = self.layout - object = context.object - collection = context.collection - - layout.label(text="MAKE SURE TO CLICK ON 'REMEMBER EXPORT SETTINGS' !!") - op = layout.operator("EXPORT_SCENE_OT_gltf", text='Gltf settings')#'glTF 2.0 (.glb/.gltf)') - #op.export_format = 'GLTF_SEPARATE' - op.use_selection=True - op.will_save_settings=True - op.use_visible=True # Export visible and hidden objects. See Object/Batch Export to skip. - op.use_renderable=True - op.use_active_collection = True - op.use_active_collection_with_nested=True - op.use_active_scene = True - op.filepath="dummy" - - #print("GLTF_PT_export_main", GLTF_PT_export_main.bl_parent_id) +def unregister_panel(): + # Since panel is registered on demand, it is possible it is not registered + try: + bpy.utils.unregister_class(GLTF_PT_UserExtensionPanel) + except Exception: + pass -# wrapper operator -"""class WrapperOperator(bpy.types.Operator): - bl_idname = "node.delete" - bl_label = "Delete" +def unregister(): + unregister_panel() + bpy.utils.unregister_class(ExampleExtensionProperties) + del bpy.types.Scene.ExampleExtensionProperties + +class GLTF_PT_UserExtensionPanel(bpy.types.Panel): + + bl_space_type = 'FILE_BROWSER' + bl_region_type = 'TOOL_PROPS' + bl_label = "Enabled" + bl_parent_id = "GLTF_PT_export_user_extensions" + bl_options = {'DEFAULT_CLOSED'} @classmethod - def poll(cls, context): - return True - - def execute(self, context): - # unregister wrapper replacement operator - bpy.utils.unregister_class(DeleteOperator) - # call original native operator - bpy.ops.node.delete() - # register back the wrapper - bpy.utils.register_class(DeleteOperator) - return {'FINISHED'}""" - - -class GLTF_PT_export_main2(bpy.types.Panel): - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' - bl_label = "" - bl_parent_id = "Testing_PT_MainPanel" - bl_options = {'HIDE_HEADER'} - - - '''@classmethod def poll(cls, context): sfile = context.space_data operator = sfile.active_operator + return operator.bl_idname == "EXPORT_SCENE_OT_gltf" - return operator.bl_idname == "EXPORT_SCENE_OT_gltf"''' + def draw_header(self, context): + props = bpy.context.scene.ExampleExtensionProperties + self.layout.prop(props, 'enabled') def draw(self, context): layout = self.layout layout.use_property_split = True layout.use_property_decorate = False # No animation. - """sfile = context.space_data - operator = sfile.active_operator + props = bpy.context.scene.ExampleExtensionProperties + layout.active = props.enabled - layout.prop(operator, 'export_format') - if operator.export_format == 'GLTF_SEPARATE': - layout.prop(operator, 'export_keep_originals') - if operator.export_keep_originals is False: - layout.prop(operator, 'export_texture_dir', icon='FILE_FOLDER') - if operator.export_format == 'GLTF_EMBEDDED': - layout.label(text="This is the least efficient of the available forms, and should only be used when required.", icon='ERROR') - - layout.prop(operator, 'export_copyright') - layout.prop(operator, 'will_save_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, - SceneLinks, - CUSTOM_PG_sceneName, - SCENE_UL_GLTF_auto_export, - SCENES_LIST_OT_actions, - - AutoExportGLTF, - #AutoExportGltfAddonPreferences, - - CollectionToExport, - CollectionsToExport, - - GLTF_PT_auto_export_main, - GLTF_PT_auto_export_root, - GLTF_PT_auto_export_blueprints, - GLTF_PT_auto_export_collections_list, - GLTF_PT_auto_export_gltf, - - AutoExportTracker, - - Testing_PT_MainPanel, - GLTF_PT_export_main2, - -] - -def menu_func_import(self, context): - self.layout.operator(AutoExportGLTF.bl_idname, text="glTF auto Export (.glb/gltf)") -from bpy.app.handlers import persistent - -@persistent -def post_update(scene, depsgraph): - bpy.context.window_manager.auto_export_tracker.deps_update_handler( scene, depsgraph) - -@persistent -def post_save(scene, depsgraph): - bpy.context.window_manager.auto_export_tracker.save_handler( scene, depsgraph) + props = bpy.context.scene.ExampleExtensionProperties + for bla in props.__annotations__: + layout.prop(props, bla) +class glTF2ExportUserExtension: + def __init__(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.ExampleExtensionProperties -def register(): - for cls in classes: - bpy.utils.register_class(cls) - # for some reason, adding these directly to the tracker class in register() do not work reliably - bpy.app.handlers.depsgraph_update_post.append(post_update) - bpy.app.handlers.save_post.append(post_save) + def gather_node_hook(self, gltf2_object, blender_object, export_settings): + 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 pre_export_hook(self): + print("pre export callback") - # add our addon to the toolbar - bpy.types.TOPBAR_MT_file_export.append(menu_func_import) +def glTF2_pre_export_callback(data): + print("pre_export", data) - bpy.types.WindowManager.was_good_operator = BoolProperty(default=False) - bpy.types.Scene.was_good_operator = BoolProperty(default=False) - - #GLTF_PT_export_main.bl_parent_id = "Testing_PT_MainPanel" - #GLTF_PT_export_include.bl_parent_id = "Testing_PT_MainPanel" - """try: - bpy.app.timers.unregister(watch_schema) - except Exception as error: - print("failed to unregister", error) - pass""" - - -def unregister(): - for cls in classes: - bpy.utils.unregister_class(cls) - bpy.types.TOPBAR_MT_file_export.remove(menu_func_import) - - bpy.app.handlers.depsgraph_update_post.remove(post_update) - bpy.app.handlers.save_post.remove(post_save) - - - - -if "gltf_auto_export" == "__main__": - register() +def glTF2_post_export_callback(data): + print("post_export", data) \ No newline at end of file diff --git a/tools/gltf_auto_export/__init__old.py b/tools/gltf_auto_export/__init__old.py new file mode 100644 index 0000000..af6f898 --- /dev/null +++ b/tools/gltf_auto_export/__init__old.py @@ -0,0 +1,189 @@ +bl_info = { + "name": "gltf_auto_export", + "author": "kaosigh", + "version": (0, 16, 0), + "blender": (3, 4, 0), + "location": "File > Import-Export", + "description": "glTF/glb auto-export", + "warning": "", + "wiki_url": "https://github.com/kaosat-dev/Blender_bevy_components_workflow", + "tracker_url": "https://github.com/kaosat-dev/Blender_bevy_components_workflow/issues/new", + "category": "Import-Export" +} +import bpy +from bpy.types import Context +from bpy.props import (StringProperty, BoolProperty, PointerProperty) + +from .extension import ExampleExtensionProperties, GLTF_PT_UserExtensionPanel, unregister_panel + +from .auto_export.operators import AutoExportGLTF +from .auto_export.tracker import AutoExportTracker +from .auto_export.preferences import (AutoExportGltfAddonPreferences) + +from .auto_export.internals import (SceneLink, + SceneLinks, + CollectionToExport, + CollectionsToExport, + CUSTOM_PG_sceneName + ) +from .ui.main import (GLTF_PT_auto_export_main, + GLTF_PT_auto_export_root, + GLTF_PT_auto_export_blueprints, + GLTF_PT_auto_export_collections_list, + GLTF_PT_auto_export_gltf, + SCENE_UL_GLTF_auto_export, + + + #GLTF_PT_export_data, + #GLTF_PT_export_data_scene + ) +from .ui.operators import (SCENES_LIST_OT_actions) + + +###################################################### +""" there are two places where we load settings for auto_export from: +- in ui/main AutoExportGLTF -> invoke +- in auto_export.py -> auto_export +This is a workaround needed because of the way the settings are stored , perhaps there is a better way to deal with it ? ie by calling the AutoExportGLTF operator from the auto_export function ? +""" +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" + bl_label = "" + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_category = "Gltf auto_export" + bl_context = "objectmode" + + + def draw_header(self, context): + layout = self.layout + layout.label(text="Gltf auto export ") + + def draw(self, context): + layout = self.layout + layout.label(text="MAKE SURE TO KEEP 'REMEMBER EXPORT SETTINGS' !!") + op = layout.operator("EXPORT_SCENE_OT_gltf", text='Gltf settings')#'glTF 2.0 (.glb/.gltf)') + #op.export_format = 'GLTF_SEPARATE' + op.use_selection=True + op.will_save_settings=True + op.use_visible=True # Export visible and hidden objects. See Object/Batch Export to skip. + op.use_renderable=True + op.use_active_collection = True + op.use_active_collection_with_nested=True + op.use_active_scene = True + op.filepath="dummy" + #print("GLTF_PT_export_main", GLTF_PT_export_main.bl_parent_id) + + +#see here for original gltf exporter infos https://github.com/KhronosGroup/glTF-Blender-IO/blob/main/addons/io_scene_gltf2/__init__.py +classes = [ + SceneLink, + SceneLinks, + CUSTOM_PG_sceneName, + SCENE_UL_GLTF_auto_export, + SCENES_LIST_OT_actions, + + AutoExportGLTF, + #AutoExportGltfAddonPreferences, + + CollectionToExport, + CollectionsToExport, + + GLTF_PT_auto_export_main, + GLTF_PT_auto_export_root, + 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, + + AutoExportTracker, + + #Testing_PT_MainPanel, +] + +def menu_func_import(self, context): + self.layout.operator(AutoExportGLTF.bl_idname, text="glTF auto Export (.glb/gltf)") +from bpy.app.handlers import persistent + +@persistent +def post_update(scene, depsgraph): + bpy.context.window_manager.auto_export_tracker.deps_update_handler( scene, depsgraph) + +@persistent +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) + # for some reason, adding these directly to the tracker class in register() do not work reliably + bpy.app.handlers.depsgraph_update_post.append(post_update) + bpy.app.handlers.save_post.append(post_save) + + # add our addon to the toolbar + bpy.types.TOPBAR_MT_file_export.append(menu_func_import) + + bpy.types.WindowManager.was_good_operator = BoolProperty(default=False) + bpy.types.Scene.was_good_operator = BoolProperty(default=False) + + + # ExportGLTF2.invoke = invoke_override + GLTF_PT_export_main.bl_parent_id = "GLTF_PT_auto_export_root" + + bpy.utils.register_class(ExampleExtensionProperties) + bpy.utils.register_class(GLTF_PT_UserExtensionPanel) + + bpy.types.Scene.ExampleExtensionProperties = bpy.props.PointerProperty(type=ExampleExtensionProperties) + +def unregister(): + for cls in classes: + bpy.utils.unregister_class(cls) + bpy.types.TOPBAR_MT_file_export.remove(menu_func_import) + + bpy.app.handlers.depsgraph_update_post.remove(post_update) + bpy.app.handlers.save_post.remove(post_save) + + unregister_panel() + bpy.utils.unregister_class(ExampleExtensionProperties) + del bpy.types.Scene.ExampleExtensionProperties + +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 d252bea..83ef245 100644 --- a/tools/gltf_auto_export/auto_export/operators.py +++ b/tools/gltf_auto_export/auto_export/operators.py @@ -8,7 +8,9 @@ from ..helpers.helpers_scenes import (get_scenes) from ..helpers.helpers_collections import (get_exportable_collections) from .auto_export import auto_export -class AutoExportGLTF(Operator, AutoExportGltfAddonPreferences, ExportHelper): +from io_scene_gltf2 import (ExportGLTF2, GLTF_PT_export_main,ExportGLTF2_Base, GLTF_PT_export_include) + +class AutoExportGLTF(Operator, AutoExportGltfAddonPreferences, ExportHelper, ExportGLTF2_Base): """auto export gltf""" #bl_idname = "object.xxx" bl_idname = "export_scenes.auto_gltf" diff --git a/tools/gltf_auto_export/helpers/to_remove_later.py b/tools/gltf_auto_export/helpers/to_remove_later.py index 595bc93..e012e4e 100644 --- a/tools/gltf_auto_export/helpers/to_remove_later.py +++ b/tools/gltf_auto_export/helpers/to_remove_later.py @@ -30,6 +30,7 @@ glTF_extension_name = "EXT_auto_export" # 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 +from io_scene_gltf2 import (GLTF_PT_export_main, GLTF_PT_export_include) class ExampleExtensionProperties(bpy.types.PropertyGroup): enabled: bpy.props.BoolProperty( diff --git a/tools/gltf_auto_export/ui/main.py b/tools/gltf_auto_export/ui/main.py index 0e7edb9..7406c11 100644 --- a/tools/gltf_auto_export/ui/main.py +++ b/tools/gltf_auto_export/ui/main.py @@ -223,9 +223,9 @@ class GLTF_PT_auto_export_gltf(bpy.types.Panel): #self.layout.operator("EXPORT_SCENE_OT_gltf", text='glTF 2.0 (.glb/.gltf)') #bpy.ops.export_scene.gltf - for key in addon_prefs.__annotations__.keys(): + """for key in addon_prefs.__annotations__.keys(): if key not in AutoExportGltfPreferenceNames: - layout.prop(operator, key) + layout.prop(operator, key)""" class SCENE_UL_GLTF_auto_export(bpy.types.UIList): # The draw_item function is called for each item of the collection that is visible in the list. @@ -260,3 +260,39 @@ class SCENE_UL_GLTF_auto_export(bpy.types.UIList): layout.label(text="", icon_value=icon) + + + +from io_scene_gltf2 import (ExportGLTF2, GLTF_PT_export_main,ExportGLTF2_Base, GLTF_PT_export_include) +import io_scene_gltf2 as gltf_exporter_original +#import io_scene_gltf2.GLTF_PT_export_data_scene as GLTF_PT_export_data_scene_original +""" +class GLTF_PT_export_data(gltf_exporter_original.GLTF_PT_export_data): + bl_space_type = 'FILE_BROWSER' + bl_region_type = 'TOOL_PROPS' + bl_label = "Data" + bl_parent_id = "GLTF_PT_auto_export_gltf" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + sfile = context.space_data + operator = sfile.active_operator + + return operator.bl_idname == "EXPORT_SCENES_OT_auto_gltf" + +class GLTF_PT_export_data_scene(gltf_exporter_original.GLTF_PT_export_data_scene): + bl_space_type = 'FILE_BROWSER' + bl_region_type = 'TOOL_PROPS' + bl_label = "Scene Graph" + bl_parent_id = "GLTF_PT_export_data" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + sfile = context.space_data + operator = sfile.active_operator + return operator.bl_idname == "EXPORT_SCENES_OT_auto_gltf" + + def draw(self, context): + return super().draw(context)""" \ No newline at end of file diff --git a/tools/gltf_auto_export/ui2/main.py b/tools/gltf_auto_export/ui2/main.py new file mode 100644 index 0000000..492d442 --- /dev/null +++ b/tools/gltf_auto_export/ui2/main.py @@ -0,0 +1,265 @@ +import bpy +from bpy.types import Operator +from bpy_extras.io_utils import ExportHelper +from bpy.props import (BoolProperty, + IntProperty, + StringProperty, + EnumProperty, + CollectionProperty + ) + +from ..auto_export import auto_export + +from ..auto_export.preferences import (AutoExportGltfAddonPreferences, AutoExportGltfPreferenceNames) +from ..helpers.helpers_scenes import (get_scenes) +from ..helpers.helpers_collections import (get_exportable_collections) +###################################################### +## ui logic & co + +class GLTF_PT_auto_export_main(bpy.types.Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_label = "" + #bl_options = {'HIDE_HEADER'} + bl_category = "Gltf auto_export" + bl_context = "objectmode" + + """ @classmethod + def poll(cls, context): + sfile = context.space_data + operator = sfile.active_operator + + return operator.bl_idname == "EXPORT_SCENES_OT_auto_gltf" + """ + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False # No animation. + +class GLTF_PT_auto_export_root(bpy.types.Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_label = "Auto export" + bl_parent_id = "GLTF_PT_auto_export_main" + #bl_options = {'DEFAULT_CLOSED'} + + """@classmethod + def poll(cls, context): + sfile = context.space_data + operator = sfile.active_operator + return operator.bl_idname == "EXPORT_SCENES_OT_auto_gltf" + """ + + """def draw_header(self, context): + sfile = context.space_data + operator = sfile.active_operator + self.layout.prop(operator, "auto_export", text="")""" + + def draw(self, context): + + pass + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False # No animation. + + sfile = context.space_data + operator = layout.operator("export_scenes.auto_gltf")#bpy.ops.export_scenes.auto_gltf #sfile.active_operator + + layout.active = operator.auto_export + layout.prop(operator, 'will_save_settings') + layout.prop(operator, "export_change_detection") + layout.prop(operator, "export_output_folder") + layout.prop(operator, "export_scene_settings") + layout.prop(operator, "export_legacy_mode") + + # scene selectors + row = layout.row() + col = row.column(align=True) + col.separator() + + source = operator + + rows = 2 + + # main/level scenes + layout.label(text="main scenes") + layout.prop(context.window_manager, "main_scene", text='') + + row = layout.row() + row.template_list("SCENE_UL_GLTF_auto_export", "level scenes", source, "main_scenes", source, "main_scenes_index", rows=rows) + + col = row.column(align=True) + sub_row = col.row() + add_operator = sub_row.operator("scene_list.list_action", icon='ADD', text="") + add_operator.action = 'ADD' + add_operator.scene_type = 'level' + #add_operator.source = operator + sub_row.enabled = context.window_manager.main_scene is not None + + sub_row = col.row() + remove_operator = sub_row.operator("scene_list.list_action", icon='REMOVE', text="") + remove_operator.action = 'REMOVE' + remove_operator.scene_type = 'level' + col.separator() + + #up_operator = col.operator("scene_list.list_action", icon='TRIA_UP', text="") + #up_operator.action = 'UP' + #col.operator("scene_list.list_action", icon='TRIA_DOWN', text="").action = 'DOWN' + + # library scenes + layout.label(text="library scenes") + layout.prop(context.window_manager, "library_scene", text='') + + row = layout.row() + row.template_list("SCENE_UL_GLTF_auto_export", "library scenes", source, "library_scenes", source, "library_scenes_index", rows=rows) + + col = row.column(align=True) + sub_row = col.row() + add_operator = sub_row.operator("scene_list.list_action", icon='ADD', text="") + add_operator.action = 'ADD' + add_operator.scene_type = 'library' + sub_row.enabled = context.window_manager.library_scene is not None + + + sub_row = col.row() + remove_operator = sub_row.operator("scene_list.list_action", icon='REMOVE', text="") + remove_operator.action = 'REMOVE' + remove_operator.scene_type = 'library' + col.separator() + +class GLTF_PT_auto_export_blueprints(bpy.types.Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_label = "Blueprints" + bl_parent_id = "GLTF_PT_auto_export_main" + + """@classmethod + def poll(cls, context): + sfile = context.space_data + operator = sfile.active_operator + + return operator.bl_idname == "EXPORT_SCENES_OT_auto_gltf" #"EXPORT_SCENE_OT_gltf" + """ + + + """def draw_header(self, context): + layout = self.layout + sfile = context.space_data + operator = sfile.active_operator + layout.prop(operator, "export_blueprints", text="")""" + + #self.layout.prop(operator, "auto_export", text="") + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False # No animation. + + sfile = context.space_data + operator = layout.operator("export_scenes.auto_gltf")#bpy.ops.export_scenes.auto_gltf #sfile.active_operator +#sfile.active_operator + + layout.active = operator.export_blueprints + + # collections/blueprints + layout.prop(operator, "export_blueprints_path") + layout.prop(operator, "collection_instances_combine_mode") + layout.prop(operator, "export_marked_assets") + layout.prop(operator, "export_separate_dynamic_and_static_objects") + layout.separator() + # materials + layout.prop(operator, "export_materials_library") + layout.prop(operator, "export_materials_path") + + +class GLTF_PT_auto_export_collections_list(bpy.types.Panel): + bl_space_type = 'FILE_BROWSER' + bl_region_type = 'TOOL_PROPS' + bl_label = "Blueprints: Exported Collections" + bl_parent_id = "GLTF_PT_auto_export_blueprints" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + sfile = context.space_data + operator = sfile.active_operator + + return operator.bl_idname == "EXPORT_SCENES_OT_auto_gltf" #"EXPORT_SCENE_OT_gltf" + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False # No animation. + + sfile = context.space_data + operator = sfile.active_operator + + for collection in bpy.context.window_manager.exportedCollections: + row = layout.row() + row.label(text=collection.name) + +class GLTF_PT_auto_export_gltf(bpy.types.Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_label = "Gltf" + bl_parent_id = "GLTF_PT_auto_export_main" + bl_options = {'DEFAULT_CLOSED'} + + """@classmethod + def poll(cls, context): + sfile = context.space_data + operator = sfile.active_operator + + return operator.bl_idname == "EXPORT_SCENES_OT_auto_gltf" #"EXPORT_SCENE_OT_gltf" + """ + + def draw(self, context): + preferences = context.preferences + layout = self.layout + + layout.label(text="MAKE SURE TO KEEP 'REMEMBER EXPORT SETTINGS' !!") + op = layout.operator("EXPORT_SCENE_OT_gltf", text='Gltf settings')#'glTF 2.0 (.glb/.gltf)') + #op.export_format = 'GLTF_SEPARATE' + op.use_selection=True + op.will_save_settings=True + op.use_visible=True # Export visible and hidden objects. See Object/Batch Export to skip. + op.use_renderable=True + op.use_active_collection = True + op.use_active_collection_with_nested=True + op.use_active_scene = True + op.filepath="dummy" + +class SCENE_UL_GLTF_auto_export(bpy.types.UIList): + # The draw_item function is called for each item of the collection that is visible in the list. + # data is the RNA object containing the collection, + # item is the current drawn item of the collection, + # icon is the "computed" icon for the item (as an integer, because some objects like materials or textures + # have custom icons ID, which are not available as enum items). + # active_data is the RNA object containing the active property for the collection (i.e. integer pointing to the + # active item of the collection). + # active_propname is the name of the active property (use 'getattr(active_data, active_propname)'). + # index is index of the current item in the collection. + # flt_flag is the result of the filtering process for this item. + # Note: as index and flt_flag are optional arguments, you do not have to use/declare them here if you don't + # need them. + def draw_item(self, context, layout, data, item, icon, active_data, active_propname): + ob = data + # draw_item must handle the three layout types... Usually 'DEFAULT' and 'COMPACT' can share the same code. + if self.layout_type in {'DEFAULT', 'COMPACT'}: + # You should always start your row layout by a label (icon + text), or a non-embossed text field, + # this will also make the row easily selectable in the list! The later also enables ctrl-click rename. + # We use icon_value of label, as our given icon is an integer value, not an enum ID. + # Note "data" names should never be translated! + #if ma: + # layout.prop(ma, "name", text="", emboss=False, icon_value=icon) + #else: + # layout.label(text="", translate=False, icon_value=icon) + layout.label(text=item.name, icon_value=icon) + #layout.prop(item, "name", text="", emboss=False, icon_value=icon) + # 'GRID' layout type should be as compact as possible (typically a single icon!). + elif self.layout_type == 'GRID': + layout.alignment = 'CENTER' + layout.label(text="", icon_value=icon) + + diff --git a/tools/gltf_auto_export/ui2/operators.py b/tools/gltf_auto_export/ui2/operators.py new file mode 100644 index 0000000..31ea51d --- /dev/null +++ b/tools/gltf_auto_export/ui2/operators.py @@ -0,0 +1,83 @@ + +import bpy +from bpy.types import Operator + +class SCENES_LIST_OT_actions(Operator): + """Move items up and down, add and remove""" + bl_idname = "scene_list.list_action" + bl_label = "List Actions" + bl_description = "Move items up and down, add and remove" + bl_options = {'REGISTER'} + + action: bpy.props.EnumProperty( + items=( + ('UP', "Up", ""), + ('DOWN', "Down", ""), + ('REMOVE', "Remove", ""), + ('ADD', "Add", ""))) + + + scene_type: bpy.props.StringProperty()#TODO: replace with enum + + def invoke(self, context, event): + source = context.space_data.active_operator + target_name = "library_scenes" + target_index = "library_scenes_index" + if self.scene_type == "level": + target_name = "main_scenes" + target_index = "main_scenes_index" + + target = getattr(source, target_name) + idx = getattr(source, target_index) + current_index = getattr(source, target_index) + + try: + item = target[idx] + except IndexError: + pass + else: + if self.action == 'DOWN' and idx < len(target) - 1: + target.move(idx, idx + 1) + setattr(source, target_index, current_index +1 ) + info = 'Item "%s" moved to position %d' % (item.name, current_index + 1) + self.report({'INFO'}, info) + + elif self.action == 'UP' and idx >= 1: + target.move(idx, idx - 1) + setattr(source, target_index, current_index -1 ) + info = 'Item "%s" moved to position %d' % (item.name, current_index + 1) + self.report({'INFO'}, info) + + elif self.action == 'REMOVE': + info = 'Item "%s" removed from list' % (target[idx].name) + setattr(source, target_index, current_index -1 ) + target.remove(idx) + self.report({'INFO'}, info) + + if self.action == 'ADD': + new_scene_name = None + if self.scene_type == "level": + if context.window_manager.main_scene: + new_scene_name = context.window_manager.main_scene.name + else: + if context.window_manager.library_scene: + new_scene_name = context.window_manager.library_scene.name + if new_scene_name: + item = target.add() + item.name = new_scene_name#f"Rule {idx +1}" + + if self.scene_type == "level": + context.window_manager.main_scene = None + else: + context.window_manager.library_scene = None + + #name = f"Rule {idx +1}" + #target.append({"name": name}) + setattr(source, target_index, len(target) - 1) + #source[target_index] = len(target) - 1 + info = '"%s" added to list' % (item.name) + self.report({'INFO'}, info) + + return {"FINISHED"} + +