diff --git a/assets/advanced/advanced.blend b/assets/advanced/advanced.blend index f906237..ddb845a 100644 Binary files a/assets/advanced/advanced.blend and b/assets/advanced/advanced.blend differ diff --git a/assets/advanced/models/library/Unused_in_level_test.glb b/assets/advanced/models/library/Unused_in_level_test.glb new file mode 100644 index 0000000..dff3f87 Binary files /dev/null and b/assets/advanced/models/library/Unused_in_level_test.glb differ diff --git a/docs/exported_collections.png b/docs/exported_collections.png new file mode 100644 index 0000000..9c85db0 Binary files /dev/null and b/docs/exported_collections.png differ diff --git a/docs/force_export.jpg b/docs/force_export.jpg new file mode 100644 index 0000000..6cc7d17 Binary files /dev/null and b/docs/force_export.jpg differ diff --git a/tools/gltf_auto_export/README.md b/tools/gltf_auto_export/README.md index 37894dc..ede7351 100644 --- a/tools/gltf_auto_export/README.md +++ b/tools/gltf_auto_export/README.md @@ -55,6 +55,19 @@ You can enable this option to automatically replace all the **collection instanc the .blend file that they are generated from can be found [here](../../assets/advanced/advanced.blend) +- the above only applies to collections that have **instances** in your main scene! + if you want a specific collection in your library to always get exported regardless of its use, you need to add + a **COLLECTION** (boolean) custom property called ```AutoExport``` set to true + > not at the object level ! the collection level ! + + ![force-export](../../docs/force_export.jpg) + + It will get automatically exported like any of the "in-use" collections. + +- you can also get an overview of all the exported collections in the export menu + + ![exported collections](../../docs/exported_collections.png) + #### Process This is the internal logic of the export process with blueprints diff --git a/tools/gltf_auto_export/gltf_auto_export.py b/tools/gltf_auto_export/gltf_auto_export.py index 20174bf..229fee8 100644 --- a/tools/gltf_auto_export/gltf_auto_export.py +++ b/tools/gltf_auto_export/gltf_auto_export.py @@ -1,7 +1,7 @@ bl_info = { "name": "gltf_auto_export", "author": "kaosigh", - "version": (0, 1), + "version": (0, 2), "blender": (3, 4, 0), "location": "File > Import-Export", "description": "glTF/glb auto-export", @@ -31,18 +31,26 @@ from bpy.props import (BoolProperty, @persistent def deps_update_handler(scene, depsgraph): - #print("depsgraph_update_post", scene.name) - """ + print("depsgraph_update_post", scene.name) + print("toto") print("-------------") changed_objects = [] for obj in depsgraph.updates: if isinstance(obj.id, bpy.types.Object): print("object changed, amazing", obj.id, obj.id.name) - changed_objects.append(obj) - """ + changed_objects.append(obj.id.name) + changed = scene.name or "" - # bpy.context.scene.changedObjects = changed_objects - bpy.context.scene.changedScene = changed + + bpy.context.window_manager.changedObjects.clear() + for obj in changed_objects: + new_entry = bpy.context.window_manager.changedObjects.add() + new_entry.name = obj + #bpy.context.window_manager.changedObjects.add(obj) + + print("changed objects", bpy.context.window_manager.changedObjects) + + bpy.context.window_manager.changedScene = changed @persistent def save_handler(dummy): @@ -58,12 +66,6 @@ def set_changedScene(self, value): self["changedScene"] = value -#def get_changedObjects(self): -# return self["changedObjects"] - -#def set_changedObjects(self, value): -# self["changedObjects"] = value - #https://docs.blender.org/api/current/bpy.ops.export_scene.html#bpy.ops.export_scene.gltf def export_gltf (path, export_settings): settings = {**export_settings, "filepath": path} @@ -232,6 +234,24 @@ def get_used_collections(scene): return (collection_names, used_collections) +def traverse_tree(t): + yield t + for child in t.children: + yield from traverse_tree(child) + +# gets all collections that should ALWAYS be exported to their respective gltf files, even if they are not used in the main scene/level +def get_marked_collections(scene): + print("checking library for marked collections") + root_collection = scene.collection + marked_collections = [] + collection_names = [] + for collection in traverse_tree(root_collection): + if 'AutoExport' in collection and collection['AutoExport'] == True: + marked_collections.append(collection) + collection_names.append(collection.name) + return (collection_names, marked_collections) + + def generate_gltf_export_preferences(addon_prefs): # default values gltf_export_preferences = dict( @@ -276,19 +296,31 @@ def generate_gltf_export_preferences(addon_prefs): ###################################################### #### Export logic ##### -def export_used_collections(scene, folder_path, addon_prefs, gltf_export_preferences): +# export collections: all the collections that have an instance in the main scene AND any marked collections, even if they do not have instances +def export_collections(scene, folder_path, addon_prefs, gltf_export_preferences): (collection_names, used_collections) = get_used_collections(scene) library_scene = getattr(addon_prefs, "export_library_scene_name") + marked_collections = get_marked_collections(bpy.data.scenes[library_scene]) print("used collection names", collection_names, used_collections) + print("marked collection names", marked_collections[0]) # set active scene to be the library scene (hack for now) bpy.context.window.scene = bpy.data.scenes[library_scene] # save current active collection active_collection = bpy.context.view_layer.active_layer_collection - for collection_name in list(collection_names): + all_collections = list(collection_names) + marked_collections[0] + # we save this list of collections in the context + bpy.context.window_manager.exportedCollections.clear() + + for collection_name in all_collections: print("exporting collection", collection_name) + #TODO: add error handling for this + ui_info = bpy.context.window_manager.exportedCollections.add() + ui_info.name = collection_name + + layer_collection = bpy.context.view_layer.layer_collection layerColl = recurLayerCollection(layer_collection, collection_name) # set active collection to the collection @@ -322,7 +354,7 @@ def export_main(scene, folder_path, addon_prefs): print("LIBRARY EXPORT", export_blueprints_path ) try: - export_used_collections(scene, export_blueprints_path, addon_prefs, gltf_export_preferences) + export_collections(scene, export_blueprints_path, addon_prefs, gltf_export_preferences) except Exception as error: print("failed to export collections to gltf: ", error) @@ -363,10 +395,10 @@ def auto_export(): # get the preferences for our addon addon_prefs = bpy.context.preferences.addons[__name__].preferences - print("last changed", bpy.context.scene.changedScene) + print("last changed", bpy.context.window_manager.changedScene) # optimised variation - last_changed = bpy.context.scene.changedScene + last_changed = bpy.context.window_manager.changedScene export_main_scene_name = getattr(addon_prefs,"export_main_scene_name") export_on_library_changes = getattr(addon_prefs,"export_on_library_changes") @@ -832,6 +864,34 @@ class GLTF_PT_auto_export_blueprints(bpy.types.Panel): layout.prop(addon_prefs, "export_blueprints") layout.prop(addon_prefs, "export_blueprints_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 + addon_prefs = bpy.context.preferences.addons[__name__].preferences + + 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 = 'FILE_BROWSER' bl_region_type = 'TOOL_PROPS' @@ -871,15 +931,44 @@ class GLTF_PT_auto_export_gltf(bpy.types.Panel): def menu_func_import(self, context): self.layout.operator(AutoExportGLTF.bl_idname, text="glTF auto Export (.glb/gltf)") +###################################################### +# internals +class ChangedObject(bpy.types.PropertyGroup): + name: bpy.props.StringProperty(name="") + +class ChangedObjects(bpy.types.PropertyGroup): + name = bpy.props.StringProperty(name="List of changed objects", default="Unknown") + items: bpy.props.CollectionProperty(type = ChangedObject) + + +class CollectionToExport(bpy.types.PropertyGroup): + name: bpy.props.StringProperty(name="") + +class CollectionsToExport(bpy.types.PropertyGroup): + name = bpy.props.StringProperty(name="List of collections to export", default="Unknown") + items: bpy.props.CollectionProperty(type = CollectionToExport) + + +###################################################### + classes = [ AutoExportGLTF, AutoExportGltfAddonPreferences, + + ChangedObject, + ChangedObjects, + + 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 ] + def register(): for cls in classes: bpy.utils.register_class(cls) @@ -887,14 +976,15 @@ def register(): # setup handlers for updates & saving bpy.app.handlers.depsgraph_update_post.append(deps_update_handler) bpy.app.handlers.save_post.append(save_handler) - bpy.types.Scene.changedScene = bpy.props.StringProperty(get=get_changedScene, set=set_changedScene) - #bpy.types.Scene.changedObjects = bpy.props.CollectionProperty(get=get_changedObjects, set=set_changedObjects) + + bpy.types.WindowManager.changedScene = bpy.props.StringProperty(get=get_changedScene, set=set_changedScene) + bpy.types.WindowManager.changedObjects = bpy.props.CollectionProperty(type=ChangedObjects) + bpy.types.WindowManager.exportedCollections = bpy.props.CollectionProperty(type=CollectionsToExport) # add our addon to the toolbar bpy.types.TOPBAR_MT_file_export.append(menu_func_import) - def unregister(): for cls in classes: bpy.utils.unregister_class(cls) @@ -904,8 +994,10 @@ def unregister(): # remove handlers & co bpy.app.handlers.depsgraph_update_post.remove(deps_update_handler) bpy.app.handlers.save_post.remove(save_handler) - del bpy.types.Scene.changedScene - #del bpy.types.Scene.changedObjects + + del bpy.types.WindowManager.changedScene + del bpy.types.WindowManager.changedObjects + del bpy.types.WindowManager.exportedCollections if __name__ == "__main__": register() \ No newline at end of file