Add zero weight bone removal

This commit is contained in:
989onan
2024-08-19 13:59:31 -04:00
parent ae5c72d5d3
commit daba045364
3 changed files with 72 additions and 1 deletions
+64
View File
@@ -0,0 +1,64 @@
import bpy
from ..core import common
from bpy.types import Operator, Context, Mesh, Armature, EditBone, PoseBone
from ..core.register import register_wrap
from .translations import t
@register_wrap
class AvatarToolkit_RemoveZeroWeightBones(Operator):
bl_idname = "avatar_toolkit.remove_zero_weight_bones"
bl_label = t("Tools.remove_zero_weight_bones.label")
bl_description = t("Tools.remove_zero_weight_bones.desc")
bl_options = {'REGISTER', 'UNDO'}
threshold: bpy.props.FloatProperty(
default=0.01,
name=t("Tools.remove_zero_weight_bones.threshold.label"),
description=t("Tools.remove_zero_weight_bones.threshold.desc"),
min=0.0000001,
max=0.9999999)
@classmethod
def poll(cls, context: Context) -> bool:
return common.get_selected_armature(context) is not None
def execute(self, context: Context) -> set[str]:
armature = common.get_selected_armature(context)
if not common.is_valid_armature(armature):
self.report({'ERROR'}, t("Tools.apply_transforms.invalid_armature"))
return {'CANCELLED'}
weighted_bones: list[str] = []
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
armature.select_set(True)
context.view_layer.objects.active = armature
meshes = common.get_all_meshes(context)
for mesh in meshes:
mesh_data: Mesh = mesh.data
for vertex in mesh_data.vertices:
for group in vertex.groups:
if group.weight > self.threshold:
weighted_bones.append(mesh.vertex_groups[group.group].name) #add bone name to list of bones that are greater than the weight threshold
bpy.ops.object.mode_set(mode='EDIT')
amature_data: Armature = armature.data
unweighted_bones: list[str] = []
#doing 2 loops to prevent modification of array during iteration
for bone in amature_data.edit_bones:
if bone.name not in weighted_bones:
unweighted_bones.append(bone.name) #add bones that arent in the list of bones that have weight into the list of bones that don't
for bone_name in unweighted_bones:
for edit_bone in amature_data.edit_bones[bone_name].children:
edit_bone.use_connect = False #to fix randomly moving bones
edit_bone.parent = amature_data.edit_bones[bone_name].parent #to fix unparented bones.
amature_data.edit_bones.remove(amature_data.edit_bones[bone_name]) #delete list of unweighted bones from the armature
self.report({'INFO'}, t("Tools.remove_zero_weight_bones.success"))
return {'FINISHED'}
+5
View File
@@ -130,6 +130,11 @@
"Tools.apply_transforms.desc": "Apply position, rotation, and scale to the armature and its meshes", "Tools.apply_transforms.desc": "Apply position, rotation, and scale to the armature and its meshes",
"Tools.apply_transforms.invalid_armature": "Invalid armature selected", "Tools.apply_transforms.invalid_armature": "Invalid armature selected",
"Tools.apply_transforms.success": "Transforms applied successfully to armature and meshes", "Tools.apply_transforms.success": "Transforms applied successfully to armature and meshes",
"Tools.remove_zero_weight_bones.success": "Zero weight bones removed successfully",
"Tools.remove_zero_weight_bones.label": "Remove Zero Weight Bones",
"Tools.remove_zero_weight_bones.desc": "Remove bones from the armature that have weights less than threshold.",
"Tools.remove_zero_weight_bones.threshold.label": "Weight Threshold",
"Tools.remove_zero_weight_bones.threshold.desc": "If a bone is not weighted to any part of any mesh under the armature with a threshold greater than this, it is removed",
"VisemePanel.create_visemes": "Create Visemes", "VisemePanel.create_visemes": "Create Visemes",
"VisemePanel.creating_viseme": "Creating viseme: {viseme_name}", "VisemePanel.creating_viseme": "Creating viseme: {viseme_name}",
"VisemePanel.creating_viseme_detail": "Creating viseme: {viseme_name}", "VisemePanel.creating_viseme_detail": "Creating viseme: {viseme_name}",
+3 -1
View File
@@ -7,6 +7,7 @@ from ..functions.translations import t
from ..core.common import get_selected_armature from ..core.common import get_selected_armature
from ..functions.seperate_by import SeparateByMaterials, SeparateByLooseParts from ..functions.seperate_by import SeparateByMaterials, SeparateByLooseParts
from ..functions.additional_tools import ApplyTransforms from ..functions.additional_tools import ApplyTransforms
from ..functions.armature_modifying import AvatarToolkit_RemoveZeroWeightBones
@register_wrap @register_wrap
class AvatarToolkitToolsPanel(bpy.types.Panel): class AvatarToolkitToolsPanel(bpy.types.Panel):
@@ -33,10 +34,11 @@ class AvatarToolkitToolsPanel(bpy.types.Panel):
row.operator(CreateDigitigradeLegs.bl_idname, text=t("Tools.create_digitigrade_legs.label"), icon='BONE_DATA') row.operator(CreateDigitigradeLegs.bl_idname, text=t("Tools.create_digitigrade_legs.label"), icon='BONE_DATA')
layout.separator() layout.separator()
row = layout.row(align=True) row = layout.row(align=True)
layout.label(text=t("Tools.separate_by.label"), icon='MESH') layout.label(text=t("Tools.separate_by.label"), icon='MESH_DATA')
row.operator(SeparateByMaterials.bl_idname, text=t("Tools.separate_by_materials.label"), icon='MATERIAL') row.operator(SeparateByMaterials.bl_idname, text=t("Tools.separate_by_materials.label"), icon='MATERIAL')
row.operator(SeparateByLooseParts.bl_idname, text=t("Tools.separate_by_loose_parts.label"), icon='OUTLINER_OB_MESH') row.operator(SeparateByLooseParts.bl_idname, text=t("Tools.separate_by_loose_parts.label"), icon='OUTLINER_OB_MESH')
row = layout.row(align=True) row = layout.row(align=True)
row.operator(ApplyTransforms.bl_idname, text=t("Tools.apply_transforms.label"), icon='OBJECT_ORIGIN') row.operator(ApplyTransforms.bl_idname, text=t("Tools.apply_transforms.label"), icon='OBJECT_ORIGIN')
row.operator(AvatarToolkit_RemoveZeroWeightBones.bl_idname, text=t("Tools.remove_zero_weight_bones.label"), icon='BONE_DATA')
else: else:
layout.label(text=t("Tools.select_armature"), icon='ERROR') layout.label(text=t("Tools.select_armature"), icon='ERROR')