WIP: Rigify - modifier keys on Rig Layers buttons #104998
@ -202,6 +202,11 @@ def should_skip_bone(bone, pose_bones):
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class BoneMultiSelect(bpy.types.PropertyGroup):
|
||||||
|
bone_name: bpy.props.StringProperty(name="")
|
||||||
|
do_select: bpy.props.BoolProperty(name="select")
|
||||||
|
|
||||||
# =============================================
|
# =============================================
|
||||||
# Operators
|
# Operators
|
||||||
|
|
||||||
@ -288,94 +293,118 @@ class RIGIFY_OT_display_select_group(bpy.types.Operator):
|
|||||||
bone.select = False
|
bone.select = False
|
||||||
elif self.action == 'MENU':
|
elif self.action == 'MENU':
|
||||||
context.object.data.collections.active = coll
|
context.object.data.collections.active = coll
|
||||||
bpy.ops.wm.call_menu(name=RIGIFY_MT_select_from_group.bl_idname)
|
bpy.ops.wm.call_panel(name=RIGIFY_PT_select_active_group_bones.bl_idname)
|
||||||
else:
|
else:
|
||||||
coll.is_visible = not coll.is_visible
|
coll.is_visible = not coll.is_visible
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyPep8Naming
|
class RIGIFY_OT_select_prefix_bones(bpy.types.Operator):
|
||||||
class RIGIFY_OT_select_bone(bpy.types.Operator):
|
|
||||||
"""Select armature bone"""
|
"""Select armature bone"""
|
||||||
|
|
||||||
bl_idname = "object.rigify_select_bone"
|
bl_idname = "object.rigify_select_prefix_bones"
|
||||||
bl_label = "Select bone"
|
bl_label = "Select collection bone from menu"
|
||||||
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
bone_name: StringProperty(default="", options={'SKIP_SAVE'})
|
prefix: bpy.props.StringProperty(name="")
|
||||||
|
bone_multi: bpy.props.CollectionProperty(type=BoneMultiSelect, options={'SKIP_SAVE'})
|
||||||
|
|
||||||
@classmethod
|
def draw(self, context):
|
||||||
def poll(cls, context):
|
layout = self.layout
|
||||||
return context.mode == 'POSE'
|
layout.operator_context = 'EXEC_REGION_WIN'
|
||||||
|
|
||||||
|
for bone_select in self.bone_multi:
|
||||||
|
layout.prop(bone_select, 'do_select', text=bone_select.bone_name, toggle=True)
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
self.bone_multi.clear()
|
||||||
|
bones = context.object.data.collections.active.bones
|
||||||
|
|
||||||
|
for bone in bones:
|
||||||
|
if not bone.name.startswith(self.prefix):
|
||||||
|
continue
|
||||||
|
|
||||||
|
select_bone = self.bone_multi.add()
|
||||||
|
select_bone.bone_name = bone.name
|
||||||
|
select_bone.do_select = bone.select
|
||||||
|
|
||||||
|
return context.window_manager.invoke_props_popup(self, event)
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
context.object.data.bones[self.bone_name].select = True
|
for bone_select in self.bone_multi:
|
||||||
|
context.object.data.bones[bone_select.bone_name].select = bone_select.do_select
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
# =============================================
|
# =============================================
|
||||||
# UI Panel
|
# UI Panel
|
||||||
|
|
||||||
class RIGIFY_MT_select_from_group(bpy.types.Menu):
|
class RIGIFY_PT_select_active_group_bones(bpy.types.Panel):
|
||||||
"""Display bones from the active collection in columns"""
|
"""Display Panel"""
|
||||||
bl_idname = "object.rigify_select_bone_from_group"
|
bl_idname = "object.rigify_active_group_select_bones"
|
||||||
bl_label = "Select Bone"
|
bl_label = "Select Bone"
|
||||||
|
|
||||||
|
bl_options = {'INSTANCED'}
|
||||||
|
bl_space_type = 'VIEW_3D'
|
||||||
|
bl_region_type = 'WINDOW'
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return context.mode == 'POSE'
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def collect_bone_sides(collection, pose_bones):
|
def collect_bone_sides(collection, pose_bones):
|
||||||
left_bones = []
|
left_bones = []
|
||||||
mid_bones = []
|
mid_bones = []
|
||||||
right_bones = []
|
right_bones = []
|
||||||
|
prefix_bones = set()
|
||||||
|
|
||||||
digits = ".0123456789" # used for stripping dot and number from bone names
|
digits = ".0123456789" # used for stripping dot and number from bone names
|
||||||
for bone in collection.bones:
|
for bone in collection.bones:
|
||||||
if should_skip_bone(bone, pose_bones):
|
if should_skip_bone(bone, pose_bones):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if bone.name.rstrip(digits).endswith('.L'): # e.g. "*.L", "*.L.015", "*.L.023"
|
if bone.name[-1].isdigit():
|
||||||
|
base_name = bone.name.rstrip(digits)
|
||||||
|
if base_name in pose_bones: # e.g. "*.L", "*.L.015", "*.R.023"
|
||||||
|
prefix_bones.add(base_name)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if bone.name.endswith('.L'):
|
||||||
left_bones.append(bone)
|
left_bones.append(bone)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if bone.name.rstrip(digits).endswith('.R'): # e.g. "*.R", "*.R.015", "*.R.023"
|
if bone.name.endswith('.R'):
|
||||||
right_bones.append(bone)
|
right_bones.append(bone)
|
||||||
else:
|
else:
|
||||||
mid_bones.append(bone)
|
mid_bones.append(bone)
|
||||||
|
|
||||||
return left_bones, mid_bones, right_bones
|
return (left_bones, right_bones, mid_bones), prefix_bones
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
collection = context.object.data.collections.active
|
collection = context.object.data.collections.active
|
||||||
|
bone_lists, prefix_bones = self.collect_bone_sides(collection, context.object.pose.bones)
|
||||||
|
|
||||||
# we need to know the side of each bone in advance
|
# display Left and Right bones
|
||||||
left_bones, mid_bones, right_bones = self.collect_bone_sides(collection, context.object.pose.bones)
|
row = self.layout.row()
|
||||||
|
columns = row.column(), row.column()
|
||||||
|
for col, bone_list in zip(columns, bone_lists[:2]):
|
||||||
|
for bone in bone_list:
|
||||||
|
if bone.name in prefix_bones:
|
||||||
|
col.operator(RIGIFY_OT_select_prefix_bones.bl_idname, text=bone.name, icon='TRIA_RIGHT').prefix = bone.name
|
||||||
|
else:
|
||||||
|
col.prop(bone, 'select', text=bone.name, toggle=True, expand=True)
|
||||||
|
|
||||||
layout = self.layout
|
col = self.layout.column()
|
||||||
# layout.label(text=collection.name)
|
|
||||||
|
|
||||||
# in case all the bones belong to one side, take all three columns and then bail out.
|
# display Mid bones
|
||||||
# XOR (^) is True if only one or all three lists contain values
|
for bone in bone_lists[2]:
|
||||||
if not all((left_bones, mid_bones, right_bones)) and bool(left_bones) ^ bool(mid_bones) ^ bool(right_bones):
|
if bone.name in prefix_bones:
|
||||||
grid = layout.grid_flow(columns=3)
|
col.operator(RIGIFY_OT_select_prefix_bones.bl_idname, text=bone.name, icon='TRIA_RIGHT').prefix = bone.name
|
||||||
for bone in left_bones + mid_bones + right_bones:
|
else:
|
||||||
grid.operator(RIGIFY_OT_select_bone.bl_idname, text=bone.name).bone_name = bone.name
|
col.prop(bone, 'select', text=bone.name, toggle=True, expand=True)
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
row = layout.row()
|
|
||||||
if left_bones:
|
|
||||||
column = row.column()
|
|
||||||
for bone in left_bones:
|
|
||||||
column.operator(RIGIFY_OT_select_bone.bl_idname, text=bone.name).bone_name = bone.name
|
|
||||||
|
|
||||||
if mid_bones:
|
|
||||||
column = row.column()
|
|
||||||
for bone in mid_bones:
|
|
||||||
column.operator(RIGIFY_OT_select_bone.bl_idname, text=bone.name).bone_name = bone.name
|
|
||||||
|
|
||||||
if right_bones:
|
|
||||||
column = row.column()
|
|
||||||
for bone in right_bones:
|
|
||||||
column.operator(RIGIFY_OT_select_bone.bl_idname, text=bone.name).bone_name = bone.name
|
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyPep8Naming
|
# noinspection PyPep8Naming
|
||||||
@ -657,11 +686,12 @@ class DATA_PT_rigify_actions(Panel):
|
|||||||
|
|
||||||
classes = (
|
classes = (
|
||||||
ActionSlot,
|
ActionSlot,
|
||||||
|
BoneMultiSelect,
|
||||||
RIGIFY_OT_action_create,
|
RIGIFY_OT_action_create,
|
||||||
RIGIFY_OT_jump_to_action_slot,
|
RIGIFY_OT_jump_to_action_slot,
|
||||||
RIGIFY_OT_display_select_group,
|
RIGIFY_OT_display_select_group,
|
||||||
RIGIFY_OT_select_bone,
|
RIGIFY_OT_select_prefix_bones,
|
||||||
RIGIFY_MT_select_from_group,
|
RIGIFY_PT_select_active_group_bones,
|
||||||
RIGIFY_UL_action_slots,
|
RIGIFY_UL_action_slots,
|
||||||
DATA_PT_rigify_actions,
|
DATA_PT_rigify_actions,
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user