Anim: add operator to remove unused bone collections
Add an operator to remove all unused bone collections. This is added to the bone collections "specials" menu and the tree view context menu. A bone collection is considered "unused" when it has no bones assigned, and also no child collection that has bones assigned.
This commit is contained in:
parent
784ff4abc0
commit
a7354fb2af
|
@ -588,6 +588,90 @@ class ARMATURE_OT_collection_show_all(Operator):
|
|||
return {'FINISHED'}
|
||||
|
||||
|
||||
class ARMATURE_OT_collection_remove_unused(Operator):
|
||||
"""Remove all bone collections that have neither bones nor children."""
|
||||
bl_idname = "armature.collection_remove_unused"
|
||||
bl_label = "Remove Unused Bone Collections"
|
||||
bl_description = ("Remove all bone collections that have neither bones nor children.\n"
|
||||
"This is done recursively, so bone collections that only have unused children are also removed")
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not context.object or context.object.type != 'ARMATURE':
|
||||
return False
|
||||
arm = context.object.data
|
||||
return len(arm.collections) > 0
|
||||
|
||||
def execute(self, context):
|
||||
if context.object.mode == 'EDIT':
|
||||
return self.execute_edit_mode(context)
|
||||
|
||||
armature = context.object.data
|
||||
|
||||
# Build a set of bone collections that don't contain any bones, and
|
||||
# whose children also don't contain any bones.
|
||||
bcolls_to_remove = {
|
||||
bcoll
|
||||
for bcoll in armature.collections_all
|
||||
if len(bcoll.bones_recursive) == 0}
|
||||
|
||||
if not bcolls_to_remove:
|
||||
self.report({'INFO'}, "All bone collections are in use")
|
||||
return {'CANCELLED'}
|
||||
|
||||
self.remove_bcolls(armature, bcolls_to_remove)
|
||||
return {'FINISHED'}
|
||||
|
||||
def execute_edit_mode(self, context):
|
||||
# BoneCollection.bones_recursive or .bones are not available in armature
|
||||
# edit mode, because that has a completely separate list of edit bones.
|
||||
# This is why edit mode needs separate handling.
|
||||
|
||||
armature = context.object.data
|
||||
bcolls_with_bones = {
|
||||
bcoll
|
||||
for ebone in armature.edit_bones
|
||||
for bcoll in ebone.collections
|
||||
}
|
||||
|
||||
bcolls_to_remove = []
|
||||
for root in armature.collections:
|
||||
self.visit(root, bcolls_with_bones, bcolls_to_remove)
|
||||
|
||||
if not bcolls_to_remove:
|
||||
self.report({'INFO'}, "All bone collections are in use")
|
||||
return {'CANCELLED'}
|
||||
|
||||
self.remove_bcolls(armature, bcolls_to_remove)
|
||||
return {'FINISHED'}
|
||||
|
||||
def visit(self, bcoll, bcolls_with_bones, bcolls_to_remove):
|
||||
has_bones = bcoll in bcolls_with_bones
|
||||
|
||||
for child in bcoll.children:
|
||||
child_has_bones = self.visit(child, bcolls_with_bones, bcolls_to_remove)
|
||||
has_bones = has_bones or child_has_bones
|
||||
|
||||
if not has_bones:
|
||||
bcolls_to_remove.append(bcoll)
|
||||
|
||||
return has_bones
|
||||
|
||||
def remove_bcolls(self, armature, bcolls_to_remove):
|
||||
# Count things before they get removed.
|
||||
num_bcolls_before_removal = len(armature.collections_all)
|
||||
num_bcolls_to_remove = len(bcolls_to_remove)
|
||||
|
||||
# Create a copy of bcolls_to_remove so that it doesn't change when we
|
||||
# remove bone collections.
|
||||
for bcoll in reversed(list(bcolls_to_remove)):
|
||||
armature.collections.remove(bcoll)
|
||||
|
||||
self.report({'INFO'}, 'Removed %d of %d bone collections' %
|
||||
(num_bcolls_to_remove, num_bcolls_before_removal))
|
||||
|
||||
|
||||
classes = (
|
||||
ANIM_OT_keying_set_export,
|
||||
NLA_OT_bake,
|
||||
|
@ -596,4 +680,5 @@ classes = (
|
|||
ARMATURE_OT_copy_bone_color_to_selected,
|
||||
ARMATURE_OT_collection_solo_visibility,
|
||||
ARMATURE_OT_collection_show_all,
|
||||
ARMATURE_OT_collection_remove_unused,
|
||||
)
|
||||
|
|
|
@ -150,6 +150,10 @@ class ARMATURE_MT_collection_context_menu(Menu):
|
|||
props.name = active_bcoll.name if active_bcoll else ""
|
||||
layout.operator("armature.collection_show_all")
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("armature.collection_remove_unused", text="Remove Unused")
|
||||
|
||||
|
||||
class ARMATURE_MT_collection_tree_context_menu(Menu):
|
||||
bl_label = "Bone Collections"
|
||||
|
@ -167,6 +171,7 @@ class ARMATURE_MT_collection_tree_context_menu(Menu):
|
|||
sub.enabled = not active_bcoll_is_locked
|
||||
sub.operator("armature.collection_add", text="Add Child Collection")
|
||||
sub.operator("armature.collection_remove")
|
||||
sub.operator("armature.collection_remove_unused", text="Remove Unused Collections")
|
||||
|
||||
layout.separator()
|
||||
|
||||
|
|
Loading…
Reference in New Issue