mirror of
https://github.com/kaosat-dev/Blender_bevy_components_workflow.git
synced 2024-11-22 03:50:52 +00:00
feat(tools): add ability to mark unused collections for export (#17)
* closes #11 * bumped Blender tool version to 0.2 * changed storage of internal data to WindowManager instead of Scenes: this fixes a few issues & logical flaws * updated docs accordingly
This commit is contained in:
parent
4866ce1620
commit
27a061fc06
Binary file not shown.
BIN
assets/advanced/models/library/Unused_in_level_test.glb
Normal file
BIN
assets/advanced/models/library/Unused_in_level_test.glb
Normal file
Binary file not shown.
BIN
docs/exported_collections.png
Normal file
BIN
docs/exported_collections.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 30 KiB |
BIN
docs/force_export.jpg
Normal file
BIN
docs/force_export.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 50 KiB |
@ -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 .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
|
#### Process
|
||||||
|
|
||||||
This is the internal logic of the export process with blueprints
|
This is the internal logic of the export process with blueprints
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
bl_info = {
|
bl_info = {
|
||||||
"name": "gltf_auto_export",
|
"name": "gltf_auto_export",
|
||||||
"author": "kaosigh",
|
"author": "kaosigh",
|
||||||
"version": (0, 1),
|
"version": (0, 2),
|
||||||
"blender": (3, 4, 0),
|
"blender": (3, 4, 0),
|
||||||
"location": "File > Import-Export",
|
"location": "File > Import-Export",
|
||||||
"description": "glTF/glb auto-export",
|
"description": "glTF/glb auto-export",
|
||||||
@ -31,18 +31,26 @@ from bpy.props import (BoolProperty,
|
|||||||
@persistent
|
@persistent
|
||||||
def deps_update_handler(scene, depsgraph):
|
def deps_update_handler(scene, depsgraph):
|
||||||
|
|
||||||
#print("depsgraph_update_post", scene.name)
|
print("depsgraph_update_post", scene.name)
|
||||||
"""
|
print("toto")
|
||||||
print("-------------")
|
print("-------------")
|
||||||
changed_objects = []
|
changed_objects = []
|
||||||
for obj in depsgraph.updates:
|
for obj in depsgraph.updates:
|
||||||
if isinstance(obj.id, bpy.types.Object):
|
if isinstance(obj.id, bpy.types.Object):
|
||||||
print("object changed, amazing", obj.id, obj.id.name)
|
print("object changed, amazing", obj.id, obj.id.name)
|
||||||
changed_objects.append(obj)
|
changed_objects.append(obj.id.name)
|
||||||
"""
|
|
||||||
changed = scene.name or ""
|
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
|
@persistent
|
||||||
def save_handler(dummy):
|
def save_handler(dummy):
|
||||||
@ -58,12 +66,6 @@ def set_changedScene(self, value):
|
|||||||
self["changedScene"] = 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
|
#https://docs.blender.org/api/current/bpy.ops.export_scene.html#bpy.ops.export_scene.gltf
|
||||||
def export_gltf (path, export_settings):
|
def export_gltf (path, export_settings):
|
||||||
settings = {**export_settings, "filepath": path}
|
settings = {**export_settings, "filepath": path}
|
||||||
@ -232,6 +234,24 @@ def get_used_collections(scene):
|
|||||||
return (collection_names, used_collections)
|
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):
|
def generate_gltf_export_preferences(addon_prefs):
|
||||||
# default values
|
# default values
|
||||||
gltf_export_preferences = dict(
|
gltf_export_preferences = dict(
|
||||||
@ -276,19 +296,31 @@ def generate_gltf_export_preferences(addon_prefs):
|
|||||||
######################################################
|
######################################################
|
||||||
#### Export logic #####
|
#### 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)
|
(collection_names, used_collections) = get_used_collections(scene)
|
||||||
library_scene = getattr(addon_prefs, "export_library_scene_name")
|
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("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)
|
# set active scene to be the library scene (hack for now)
|
||||||
bpy.context.window.scene = bpy.data.scenes[library_scene]
|
bpy.context.window.scene = bpy.data.scenes[library_scene]
|
||||||
# save current active collection
|
# save current active collection
|
||||||
active_collection = bpy.context.view_layer.active_layer_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)
|
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
|
layer_collection = bpy.context.view_layer.layer_collection
|
||||||
layerColl = recurLayerCollection(layer_collection, collection_name)
|
layerColl = recurLayerCollection(layer_collection, collection_name)
|
||||||
# set active collection to the collection
|
# set active collection to the collection
|
||||||
@ -322,7 +354,7 @@ def export_main(scene, folder_path, addon_prefs):
|
|||||||
print("LIBRARY EXPORT", export_blueprints_path )
|
print("LIBRARY EXPORT", export_blueprints_path )
|
||||||
|
|
||||||
try:
|
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:
|
except Exception as error:
|
||||||
print("failed to export collections to gltf: ", error)
|
print("failed to export collections to gltf: ", error)
|
||||||
|
|
||||||
@ -363,10 +395,10 @@ def auto_export():
|
|||||||
# get the preferences for our addon
|
# get the preferences for our addon
|
||||||
addon_prefs = bpy.context.preferences.addons[__name__].preferences
|
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
|
# 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_main_scene_name = getattr(addon_prefs,"export_main_scene_name")
|
||||||
export_on_library_changes = getattr(addon_prefs,"export_on_library_changes")
|
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")
|
||||||
layout.prop(addon_prefs, "export_blueprints_path")
|
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):
|
class GLTF_PT_auto_export_gltf(bpy.types.Panel):
|
||||||
bl_space_type = 'FILE_BROWSER'
|
bl_space_type = 'FILE_BROWSER'
|
||||||
bl_region_type = 'TOOL_PROPS'
|
bl_region_type = 'TOOL_PROPS'
|
||||||
@ -871,15 +931,44 @@ class GLTF_PT_auto_export_gltf(bpy.types.Panel):
|
|||||||
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)")
|
||||||
|
|
||||||
|
######################################################
|
||||||
|
# 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 = [
|
classes = [
|
||||||
AutoExportGLTF,
|
AutoExportGLTF,
|
||||||
AutoExportGltfAddonPreferences,
|
AutoExportGltfAddonPreferences,
|
||||||
|
|
||||||
|
ChangedObject,
|
||||||
|
ChangedObjects,
|
||||||
|
|
||||||
|
CollectionToExport,
|
||||||
|
CollectionsToExport,
|
||||||
|
|
||||||
GLTF_PT_auto_export_main,
|
GLTF_PT_auto_export_main,
|
||||||
GLTF_PT_auto_export_root,
|
GLTF_PT_auto_export_root,
|
||||||
GLTF_PT_auto_export_blueprints,
|
GLTF_PT_auto_export_blueprints,
|
||||||
|
GLTF_PT_auto_export_collections_list,
|
||||||
GLTF_PT_auto_export_gltf
|
GLTF_PT_auto_export_gltf
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
for cls in classes:
|
for cls in classes:
|
||||||
bpy.utils.register_class(cls)
|
bpy.utils.register_class(cls)
|
||||||
@ -887,14 +976,15 @@ def register():
|
|||||||
# setup handlers for updates & saving
|
# setup handlers for updates & saving
|
||||||
bpy.app.handlers.depsgraph_update_post.append(deps_update_handler)
|
bpy.app.handlers.depsgraph_update_post.append(deps_update_handler)
|
||||||
bpy.app.handlers.save_post.append(save_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
|
# add our addon to the toolbar
|
||||||
bpy.types.TOPBAR_MT_file_export.append(menu_func_import)
|
bpy.types.TOPBAR_MT_file_export.append(menu_func_import)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
for cls in classes:
|
for cls in classes:
|
||||||
bpy.utils.unregister_class(cls)
|
bpy.utils.unregister_class(cls)
|
||||||
@ -904,8 +994,10 @@ def unregister():
|
|||||||
# remove handlers & co
|
# remove handlers & co
|
||||||
bpy.app.handlers.depsgraph_update_post.remove(deps_update_handler)
|
bpy.app.handlers.depsgraph_update_post.remove(deps_update_handler)
|
||||||
bpy.app.handlers.save_post.remove(save_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__":
|
if __name__ == "__main__":
|
||||||
register()
|
register()
|
Loading…
Reference in New Issue
Block a user