Alexander Gavrilov
2fe1c5a693
A combination of fixing naming, and adding words to local dictionary. Also, BlIdLowercase should be disabled in the editor.
1535 lines
54 KiB
Python
1535 lines
54 KiB
Python
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
import bpy
|
|
from bpy.props import (
|
|
BoolProperty,
|
|
IntProperty,
|
|
EnumProperty,
|
|
StringProperty
|
|
)
|
|
|
|
from typing import TYPE_CHECKING, Callable
|
|
from mathutils import Color
|
|
|
|
from .utils.errors import MetarigError
|
|
from .utils.rig import write_metarig, get_rigify_type, get_rigify_target_rig, get_rigify_layers, \
|
|
get_rigify_colors, get_rigify_params
|
|
from .utils.widgets import write_widget
|
|
from .utils.naming import unique_name
|
|
from .utils.rig import upgrade_metarig_types, outdated_types
|
|
from .utils.misc import verify_armature_obj, ArmatureObject, IdPropSequence
|
|
|
|
from .rigs.utils import get_limb_generated_names
|
|
|
|
from .utils.animation import get_keyed_frames_in_range, bones_in_frame, overwrite_prop_animation
|
|
from .utils.animation import RIGIFY_OT_get_frame_range
|
|
|
|
from .utils.animation import register as animation_register
|
|
from .utils.animation import unregister as animation_unregister
|
|
|
|
from . import base_rig
|
|
from . import rig_lists
|
|
from . import generate
|
|
from . import rot_mode
|
|
from . import feature_set_list
|
|
|
|
if TYPE_CHECKING:
|
|
from . import RigifyName, RigifySelectionColors
|
|
|
|
|
|
def get_rigify_types(id_store: bpy.types.WindowManager) -> IdPropSequence['RigifyName']:
|
|
return id_store.rigify_types # noqa
|
|
|
|
|
|
def get_transfer_only_selected(id_store: bpy.types.WindowManager) -> bool:
|
|
return id_store.rigify_transfer_only_selected # noqa
|
|
|
|
|
|
def get_selection_colors(armature: bpy.types.Armature) -> 'RigifySelectionColors':
|
|
return armature.rigify_selection_colors # noqa
|
|
|
|
|
|
def get_colors_lock(armature: bpy.types.Armature) -> bool:
|
|
return armature.rigify_colors_lock # noqa
|
|
|
|
|
|
def get_colors_index(armature: bpy.types.Armature) -> int:
|
|
return armature.rigify_colors_index # noqa
|
|
|
|
|
|
def get_theme_to_add(armature: bpy.types.Armature) -> str:
|
|
return armature.rigify_theme_to_add # noqa
|
|
|
|
|
|
def build_type_list(context, rigify_types: IdPropSequence['RigifyName']):
|
|
rigify_types.clear()
|
|
|
|
for r in sorted(rig_lists.rigs):
|
|
if (context.object.data.active_feature_set in ('all', rig_lists.rigs[r]['feature_set'])
|
|
or len(feature_set_list.get_enabled_modules_names()) == 0):
|
|
a = rigify_types.add()
|
|
a.name = r
|
|
|
|
|
|
# noinspection PyPep8Naming
|
|
class DATA_PT_rigify(bpy.types.Panel):
|
|
bl_label = "Rigify"
|
|
bl_space_type = 'PROPERTIES'
|
|
bl_region_type = 'WINDOW'
|
|
bl_context = "data"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
obj = context.object
|
|
if not context.object:
|
|
return False
|
|
return obj.type == 'ARMATURE' \
|
|
and obj.data.get("rig_id") is None
|
|
|
|
def draw(self, context):
|
|
C = context
|
|
layout = self.layout
|
|
obj = verify_armature_obj(C.object)
|
|
|
|
WARNING = "Warning: Some features may change after generation"
|
|
show_warning = False
|
|
show_update_metarig = False
|
|
show_not_updatable = False
|
|
show_upgrade_face = False
|
|
|
|
check_props = ['IK_follow', 'root/parent', 'FK_limb_follow', 'IK_Stretch']
|
|
|
|
for pose_bone in obj.pose.bones:
|
|
bone = pose_bone.bone
|
|
if not bone:
|
|
# If we are in edit mode and the bone was just created,
|
|
# a pose bone won't exist yet.
|
|
continue
|
|
if bone.layers[30] and (list(set(pose_bone.keys()) & set(check_props))):
|
|
show_warning = True
|
|
break
|
|
|
|
old_rig = ''
|
|
old_bone = ''
|
|
|
|
for b in obj.pose.bones:
|
|
old_rig = get_rigify_type(b)
|
|
if old_rig in outdated_types:
|
|
old_bone = b.name
|
|
if outdated_types[old_rig]:
|
|
show_update_metarig = True
|
|
else:
|
|
show_update_metarig = False
|
|
show_not_updatable = True
|
|
break
|
|
elif old_rig == 'faces.super_face':
|
|
show_upgrade_face = True
|
|
|
|
if show_warning:
|
|
layout.label(text=WARNING, icon='ERROR')
|
|
|
|
enable_generate = not (show_not_updatable or show_update_metarig)
|
|
|
|
if show_not_updatable:
|
|
layout.label(text="WARNING: This metarig contains deprecated rigify rig-types and "
|
|
"cannot be upgraded automatically.", icon='ERROR')
|
|
layout.label(text="(" + old_rig + " on bone " + old_bone + ")")
|
|
elif show_update_metarig:
|
|
layout.label(text="This metarig contains old rig-types that can be automatically "
|
|
"upgraded to benefit from new rigify features.", icon='ERROR')
|
|
layout.label(text="(" + old_rig + " on bone " + old_bone + ")")
|
|
layout.operator("pose.rigify_upgrade_types", text="Upgrade Metarig")
|
|
elif show_upgrade_face:
|
|
layout.label(text="This metarig uses the old face rig.", icon='INFO')
|
|
layout.operator("pose.rigify_upgrade_face")
|
|
|
|
# Rig type field
|
|
|
|
col = layout.column(align=True)
|
|
col.active = ('rig_id' not in C.object.data)
|
|
|
|
col.separator()
|
|
row = col.row()
|
|
text = "Re-Generate Rig" if get_rigify_target_rig(obj.data) else "Generate Rig"
|
|
row.operator("pose.rigify_generate", text=text, icon='POSE_HLT')
|
|
row.enabled = enable_generate
|
|
|
|
|
|
# noinspection PyPep8Naming
|
|
class DATA_PT_rigify_advanced(bpy.types.Panel):
|
|
bl_space_type = 'PROPERTIES'
|
|
bl_region_type = 'WINDOW'
|
|
bl_context = "data"
|
|
bl_label = "Advanced"
|
|
bl_parent_id = 'DATA_PT_rigify'
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
layout.use_property_decorate = False
|
|
|
|
armature_id_store = verify_armature_obj(context.object).data
|
|
|
|
col = layout.column()
|
|
|
|
row = col.row()
|
|
row.active = not get_rigify_target_rig(armature_id_store)
|
|
row.prop(armature_id_store, "rigify_rig_basename", text="Rig Name")
|
|
|
|
col.separator()
|
|
|
|
col2 = col.box().column()
|
|
col2.label(text="Overwrite Existing:")
|
|
col2.row().prop(armature_id_store, "rigify_target_rig", text="Target Rig")
|
|
col2.row().prop(armature_id_store, "rigify_rig_ui", text="Rig UI Script")
|
|
col2.row().prop(armature_id_store, "rigify_widgets_collection")
|
|
|
|
col.separator()
|
|
col.row().prop(armature_id_store, "rigify_force_widget_update")
|
|
col.row().prop(armature_id_store, "rigify_mirror_widgets")
|
|
col.separator()
|
|
col.row().prop(armature_id_store, "rigify_finalize_script", text="Run Script")
|
|
|
|
|
|
# noinspection PyPep8Naming
|
|
class DATA_PT_rigify_samples(bpy.types.Panel):
|
|
bl_label = "Samples"
|
|
bl_space_type = 'PROPERTIES'
|
|
bl_region_type = 'WINDOW'
|
|
bl_context = "data"
|
|
bl_parent_id = "DATA_PT_rigify"
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
obj = context.object
|
|
if not obj:
|
|
return False
|
|
return obj.type == 'ARMATURE' \
|
|
and obj.data.get("rig_id") is None \
|
|
and obj.mode == 'EDIT'
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
layout.use_property_decorate = False
|
|
id_store = context.window_manager
|
|
|
|
# Build types list
|
|
rigify_types = get_rigify_types(id_store)
|
|
build_type_list(context, rigify_types)
|
|
|
|
if id_store.rigify_active_type > len(rigify_types):
|
|
id_store.rigify_active_type = 0
|
|
|
|
# Rig type list
|
|
if len(feature_set_list.get_enabled_modules_names()) > 0:
|
|
row = layout.row()
|
|
row.prop(context.object.data, "active_feature_set")
|
|
row = layout.row()
|
|
row.template_list("UI_UL_list", "rigify_types", id_store, "rigify_types", id_store, 'rigify_active_type')
|
|
|
|
props = layout.operator("armature.metarig_sample_add", text="Add sample")
|
|
props.metarig_type = rigify_types[id_store.rigify_active_type].name
|
|
|
|
|
|
# noinspection PyPep8Naming
|
|
class DATA_PT_rigify_layer_names(bpy.types.Panel):
|
|
bl_label = "Layer Names"
|
|
bl_space_type = 'PROPERTIES'
|
|
bl_region_type = 'WINDOW'
|
|
bl_context = "data"
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
bl_parent_id = "DATA_PT_rigify"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
if not context.object:
|
|
return False
|
|
return context.object.type == 'ARMATURE' and context.active_object.data.get("rig_id") is None
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
obj = verify_armature_obj(context.object)
|
|
arm = obj.data
|
|
|
|
rigify_layers = get_rigify_layers(arm)
|
|
rigify_colors = get_rigify_colors(arm)
|
|
|
|
# Ensure that the layers exist
|
|
if len(rigify_layers) < 29:
|
|
layout.operator("pose.rigify_layer_init")
|
|
return
|
|
|
|
# UI
|
|
main_row = layout.row(align=True).split(factor=0.05)
|
|
col1 = main_row.column()
|
|
col2 = main_row.column()
|
|
col1.label()
|
|
for i in range(32):
|
|
if i == 16 or i == 29:
|
|
col1.label()
|
|
col1.label(text=str(i))
|
|
|
|
col: bpy.types.UILayout = col2
|
|
|
|
for i, rigify_layer in enumerate(rigify_layers):
|
|
# note: rigify_layer == arm.rigify_layers[i]
|
|
if (i % 16) == 0:
|
|
col = col2.column()
|
|
if i == 0:
|
|
col.label(text="Top Row:")
|
|
else:
|
|
col.label(text="Bottom Row:")
|
|
if (i % 8) == 0:
|
|
col = col2.column()
|
|
if i != 28:
|
|
row = col.row(align=True)
|
|
icon = 'RESTRICT_VIEW_OFF' if arm.layers[i] else 'RESTRICT_VIEW_ON'
|
|
row.prop(arm, "layers", index=i, text="", toggle=True, icon=icon)
|
|
# row.prop(arm, "layers", index=i, text="Layer %d" % (i + 1), toggle=True, icon=icon)
|
|
row.prop(rigify_layer, "name", text="")
|
|
row.prop(rigify_layer, "row", text="UI Row")
|
|
icon = 'RADIOBUT_ON' if rigify_layer.selset else 'RADIOBUT_OFF'
|
|
row.prop(rigify_layer, "selset", text="", toggle=True, icon=icon)
|
|
row.prop(rigify_layer, "group", text="Bone Group")
|
|
else:
|
|
row = col.row(align=True)
|
|
|
|
icon = 'RESTRICT_VIEW_OFF' if arm.layers[i] else 'RESTRICT_VIEW_ON'
|
|
row.prop(arm, "layers", index=i, text="", toggle=True, icon=icon)
|
|
# row.prop(arm, "layers", index=i, text="Layer %d" % (i + 1), toggle=True, icon=icon)
|
|
row1 = row.split(align=True).row(align=True)
|
|
row1.prop(rigify_layer, "name", text="")
|
|
row1.prop(rigify_layer, "row", text="UI Row")
|
|
row1.enabled = False
|
|
icon = 'RADIOBUT_ON' if rigify_layer.selset else 'RADIOBUT_OFF'
|
|
row.prop(rigify_layer, "selset", text="", toggle=True, icon=icon)
|
|
row.prop(rigify_layer, "group", text="Bone Group")
|
|
if rigify_layer.group == 0:
|
|
row.label(text='None')
|
|
else:
|
|
row.label(text=rigify_colors[rigify_layer.group-1].name)
|
|
|
|
col = col2.column()
|
|
col.label(text="Reserved:")
|
|
# reserved_names = {28: 'Root', 29: 'DEF', 30: 'MCH', 31: 'ORG'}
|
|
reserved_names = {29: 'DEF', 30: 'MCH', 31: 'ORG'}
|
|
# for i in range(28, 32):
|
|
for i in range(29, 32):
|
|
row = col.row(align=True)
|
|
icon = 'RESTRICT_VIEW_OFF' if arm.layers[i] else 'RESTRICT_VIEW_ON'
|
|
row.prop(arm, "layers", index=i, text="", toggle=True, icon=icon)
|
|
row.label(text=reserved_names[i])
|
|
|
|
|
|
# noinspection PyPep8Naming
|
|
class DATA_OT_rigify_add_bone_groups(bpy.types.Operator):
|
|
bl_idname = "armature.rigify_add_bone_groups"
|
|
bl_label = "Rigify Add Standard Bone Groups"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return context.object and context.object.type == 'ARMATURE'
|
|
|
|
def execute(self, context):
|
|
obj = verify_armature_obj(context.object)
|
|
armature = obj.data
|
|
|
|
if not hasattr(armature, 'rigify_colors'):
|
|
return {'FINISHED'}
|
|
|
|
rigify_colors = get_rigify_colors(armature)
|
|
groups = ['Root', 'IK', 'Special', 'Tweak', 'FK', 'Extra']
|
|
|
|
for g in groups:
|
|
if g in rigify_colors:
|
|
continue
|
|
|
|
color = rigify_colors.add()
|
|
color.name = g
|
|
|
|
color.select = Color((0.3140000104904175, 0.7839999794960022, 1.0))
|
|
color.active = Color((0.5490000247955322, 1.0, 1.0))
|
|
color.standard_colors_lock = True
|
|
|
|
if g == "Root":
|
|
color.normal = Color((0.43529415130615234, 0.18431372940540314, 0.41568630933761597))
|
|
if g == "IK":
|
|
color.normal = Color((0.6039215922355652, 0.0, 0.0))
|
|
if g == "Special":
|
|
color.normal = Color((0.9568628072738647, 0.7882353663444519, 0.0470588281750679))
|
|
if g == "Tweak":
|
|
color.normal = Color((0.03921568766236305, 0.21176472306251526, 0.5803921818733215))
|
|
if g == "FK":
|
|
color.normal = Color((0.11764706671237946, 0.5686274766921997, 0.03529411926865578))
|
|
if g == "Extra":
|
|
color.normal = Color((0.9686275124549866, 0.250980406999588, 0.0941176563501358))
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
# noinspection PyPep8Naming
|
|
class DATA_OT_rigify_use_standard_colors(bpy.types.Operator):
|
|
bl_idname = "armature.rigify_use_standard_colors"
|
|
bl_label = "Rigify Get active/select colors from current theme"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return context.object and context.object.type == 'ARMATURE'
|
|
|
|
def execute(self, context):
|
|
obj = verify_armature_obj(context.object)
|
|
armature = obj.data
|
|
if not hasattr(armature, 'rigify_colors'):
|
|
return {'FINISHED'}
|
|
|
|
current_theme = bpy.context.preferences.themes.items()[0][0]
|
|
theme = bpy.context.preferences.themes[current_theme]
|
|
|
|
selection_colors = get_selection_colors(armature)
|
|
selection_colors.select = theme.view_3d.bone_pose
|
|
selection_colors.active = theme.view_3d.bone_pose_active
|
|
|
|
# for col in armature.rigify_colors:
|
|
# col.select = theme.view_3d.bone_pose
|
|
# col.active = theme.view_3d.bone_pose_active
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
# noinspection PyPep8Naming
|
|
class DATA_OT_rigify_apply_selection_colors(bpy.types.Operator):
|
|
bl_idname = "armature.rigify_apply_selection_colors"
|
|
bl_label = "Rigify Apply user defined active/select colors"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return context.object and context.object.type == 'ARMATURE'
|
|
|
|
def execute(self, context):
|
|
obj = verify_armature_obj(context.object)
|
|
armature = obj.data
|
|
|
|
if not hasattr(armature, 'rigify_colors'):
|
|
return {'FINISHED'}
|
|
|
|
# current_theme = bpy.context.preferences.themes.items()[0][0]
|
|
# theme = bpy.context.preferences.themes[current_theme]
|
|
|
|
rigify_colors = get_rigify_colors(armature)
|
|
selection_colors = get_selection_colors(armature)
|
|
|
|
for col in rigify_colors:
|
|
col.select = selection_colors.select
|
|
col.active = selection_colors.active
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
# noinspection PyPep8Naming
|
|
class DATA_OT_rigify_bone_group_add(bpy.types.Operator):
|
|
bl_idname = "armature.rigify_bone_group_add"
|
|
bl_label = "Rigify Add Bone Group color set"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return context.object and context.object.type == 'ARMATURE'
|
|
|
|
def execute(self, context):
|
|
obj = context.object
|
|
armature = obj.data
|
|
|
|
if hasattr(armature, 'rigify_colors'):
|
|
armature.rigify_colors.add()
|
|
armature.rigify_colors[-1].name = unique_name(armature.rigify_colors, 'Group')
|
|
|
|
current_theme = bpy.context.preferences.themes.items()[0][0]
|
|
theme = bpy.context.preferences.themes[current_theme]
|
|
|
|
armature.rigify_colors[-1].normal = theme.view_3d.wire
|
|
armature.rigify_colors[-1].normal.hsv = theme.view_3d.wire.hsv
|
|
armature.rigify_colors[-1].select = theme.view_3d.bone_pose
|
|
armature.rigify_colors[-1].select.hsv = theme.view_3d.bone_pose.hsv
|
|
armature.rigify_colors[-1].active = theme.view_3d.bone_pose_active
|
|
armature.rigify_colors[-1].active.hsv = theme.view_3d.bone_pose_active.hsv
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
# noinspection PyPep8Naming
|
|
class DATA_OT_rigify_bone_group_add_theme(bpy.types.Operator):
|
|
bl_idname = "armature.rigify_bone_group_add_theme"
|
|
bl_label = "Rigify Add Bone Group color set from Theme"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
theme: EnumProperty(items=(
|
|
('THEME01', 'THEME01', ''),
|
|
('THEME02', 'THEME02', ''),
|
|
('THEME03', 'THEME03', ''),
|
|
('THEME04', 'THEME04', ''),
|
|
('THEME05', 'THEME05', ''),
|
|
('THEME06', 'THEME06', ''),
|
|
('THEME07', 'THEME07', ''),
|
|
('THEME08', 'THEME08', ''),
|
|
('THEME09', 'THEME09', ''),
|
|
('THEME10', 'THEME10', ''),
|
|
('THEME11', 'THEME11', ''),
|
|
('THEME12', 'THEME12', ''),
|
|
('THEME13', 'THEME13', ''),
|
|
('THEME14', 'THEME14', ''),
|
|
('THEME15', 'THEME15', ''),
|
|
('THEME16', 'THEME16', ''),
|
|
('THEME17', 'THEME17', ''),
|
|
('THEME18', 'THEME18', ''),
|
|
('THEME19', 'THEME19', ''),
|
|
('THEME20', 'THEME20', '')
|
|
),
|
|
name='Theme')
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return context.object and context.object.type == 'ARMATURE'
|
|
|
|
def execute(self, context):
|
|
obj = verify_armature_obj(context.object)
|
|
armature = obj.data
|
|
|
|
if hasattr(armature, 'rigify_colors'):
|
|
rigify_colors = get_rigify_colors(armature)
|
|
|
|
if self.theme in rigify_colors.keys():
|
|
return {'FINISHED'}
|
|
|
|
rigify_colors.add()
|
|
rigify_colors[-1].name = self.theme
|
|
|
|
color_id = int(self.theme[-2:]) - 1
|
|
|
|
theme_color_set = bpy.context.preferences.themes[0].bone_color_sets[color_id]
|
|
|
|
rigify_colors[-1].normal = theme_color_set.normal
|
|
rigify_colors[-1].select = theme_color_set.select
|
|
rigify_colors[-1].active = theme_color_set.active
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
# noinspection PyPep8Naming
|
|
class DATA_OT_rigify_bone_group_remove(bpy.types.Operator):
|
|
bl_idname = "armature.rigify_bone_group_remove"
|
|
bl_label = "Rigify Remove Bone Group color set"
|
|
|
|
idx: IntProperty()
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return context.object and context.object.type == 'ARMATURE'
|
|
|
|
def execute(self, context):
|
|
obj = verify_armature_obj(context.object)
|
|
|
|
rigify_colors = get_rigify_colors(obj.data)
|
|
rigify_colors.remove(self.idx)
|
|
|
|
# set layers references to 0
|
|
rigify_layers = get_rigify_layers(obj.data)
|
|
|
|
for layer in rigify_layers:
|
|
if layer.group == self.idx + 1:
|
|
layer.group = 0
|
|
elif layer.group > self.idx + 1:
|
|
layer.group -= 1
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
# noinspection PyPep8Naming
|
|
class DATA_OT_rigify_bone_group_remove_all(bpy.types.Operator):
|
|
bl_idname = "armature.rigify_bone_group_remove_all"
|
|
bl_label = "Rigify Remove All Bone Groups"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return context.object and context.object.type == 'ARMATURE'
|
|
|
|
def execute(self, context):
|
|
obj = verify_armature_obj(context.object)
|
|
|
|
rigify_colors = get_rigify_colors(obj.data)
|
|
while len(rigify_colors) > 0:
|
|
rigify_colors.remove(0)
|
|
|
|
# set layers references to 0
|
|
for layer in get_rigify_layers(obj.data):
|
|
layer.group = 0
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
# noinspection PyPep8Naming
|
|
class DATA_UL_rigify_bone_groups(bpy.types.UIList):
|
|
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index=0, flt_flag=0):
|
|
row = layout.row(align=True)
|
|
row = row.split(factor=0.1)
|
|
row.label(text=str(index+1))
|
|
row = row.split(factor=0.7)
|
|
row.prop(item, "name", text='', emboss=False)
|
|
row = row.row(align=True)
|
|
# icon = 'LOCKED' if item.standard_colors_lock else 'UNLOCKED'
|
|
# row.prop(item, "standard_colors_lock", text='', icon=icon)
|
|
row.prop(item, "normal", text='')
|
|
row2 = row.row(align=True)
|
|
row2.prop(item, "select", text='')
|
|
row2.prop(item, "active", text='')
|
|
# row2.enabled = not item.standard_colors_lock
|
|
arm = verify_armature_obj(context.object).data
|
|
row2.enabled = not get_colors_lock(arm)
|
|
|
|
|
|
# noinspection PyPep8Naming
|
|
class DATA_MT_rigify_bone_groups_context_menu(bpy.types.Menu):
|
|
bl_label = 'Rigify Bone Groups Specials'
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
layout.operator('armature.rigify_bone_group_remove_all')
|
|
|
|
|
|
# noinspection PyPep8Naming
|
|
class DATA_PT_rigify_bone_groups(bpy.types.Panel):
|
|
bl_label = "Bone Groups"
|
|
bl_space_type = 'PROPERTIES'
|
|
bl_region_type = 'WINDOW'
|
|
bl_context = "data"
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
bl_parent_id = "DATA_PT_rigify"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
if not context.object:
|
|
return False
|
|
return context.object.type == 'ARMATURE' and context.active_object.data.get("rig_id") is None
|
|
|
|
def draw(self, context):
|
|
obj = verify_armature_obj(context.object)
|
|
armature = obj.data
|
|
idx = get_colors_index(armature)
|
|
selection_colors = get_selection_colors(armature)
|
|
is_locked = get_colors_lock(armature)
|
|
theme = get_theme_to_add(armature)
|
|
|
|
layout = self.layout
|
|
row = layout.row()
|
|
row.operator("armature.rigify_use_standard_colors", icon='FILE_REFRESH', text='')
|
|
row = row.row(align=True)
|
|
row.prop(selection_colors, 'select', text='')
|
|
row.prop(selection_colors, 'active', text='')
|
|
row = layout.row(align=True)
|
|
icon = 'LOCKED' if is_locked else 'UNLOCKED'
|
|
row.prop(armature, 'rigify_colors_lock', text='Unified select/active colors', icon=icon)
|
|
row.operator("armature.rigify_apply_selection_colors", icon='FILE_REFRESH', text='Apply')
|
|
row = layout.row()
|
|
row.template_list("DATA_UL_rigify_bone_groups", "", obj.data, "rigify_colors", obj.data, "rigify_colors_index")
|
|
|
|
col = row.column(align=True)
|
|
col.operator("armature.rigify_bone_group_add", icon='ADD', text="")
|
|
col.operator("armature.rigify_bone_group_remove", icon='REMOVE', text="").idx = idx
|
|
col.menu("DATA_MT_rigify_bone_groups_context_menu", icon='DOWNARROW_HLT', text="")
|
|
row = layout.row()
|
|
row.prop(armature, 'rigify_theme_to_add', text='Theme')
|
|
op = row.operator("armature.rigify_bone_group_add_theme", text="Add From Theme")
|
|
op.theme = theme
|
|
row = layout.row()
|
|
row.operator("armature.rigify_add_bone_groups", text="Add Standard")
|
|
|
|
|
|
# noinspection PyPep8Naming
|
|
class BONE_PT_rigify_buttons(bpy.types.Panel):
|
|
bl_label = "Rigify Type"
|
|
bl_space_type = 'PROPERTIES'
|
|
bl_region_type = 'WINDOW'
|
|
bl_context = "bone"
|
|
# bl_options = {'DEFAULT_OPEN'}
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
if not context.object:
|
|
return False
|
|
return (context.object.type == 'ARMATURE' and context.active_pose_bone and
|
|
context.active_object.data.get("rig_id") is None)
|
|
|
|
def draw(self, context):
|
|
C = context
|
|
id_store = C.window_manager
|
|
bone = context.active_pose_bone
|
|
rig_name = get_rigify_type(bone)
|
|
|
|
layout = self.layout
|
|
rig_types = get_rigify_types(id_store)
|
|
|
|
# Build types list
|
|
build_type_list(context, rig_types)
|
|
|
|
# Rig type field
|
|
if len(feature_set_list.get_enabled_modules_names()) > 0:
|
|
row = layout.row()
|
|
row.prop(context.object.data, "active_feature_set")
|
|
row = layout.row()
|
|
row.prop_search(bone, "rigify_type", id_store, "rigify_types", text="Rig type")
|
|
|
|
# Rig type parameters / Rig type non-exist alert
|
|
if rig_name != "":
|
|
try:
|
|
rig = rig_lists.rigs[rig_name]['module']
|
|
except (ImportError, AttributeError, KeyError):
|
|
row = layout.row()
|
|
box = row.box()
|
|
box.label(text="ERROR: type \"%s\" does not exist!" % rig_name, icon='ERROR')
|
|
else:
|
|
if hasattr(rig.Rig, 'parameters_ui'):
|
|
rig = rig.Rig
|
|
|
|
try:
|
|
param_cb = rig.parameters_ui
|
|
|
|
# Ignore the known empty base method
|
|
if getattr(param_cb, '__func__', None) == \
|
|
getattr(base_rig.BaseRig.parameters_ui, '__func__'):
|
|
param_cb = None
|
|
except AttributeError:
|
|
param_cb = None
|
|
|
|
if param_cb is None:
|
|
col = layout.column()
|
|
col.label(text="No options")
|
|
else:
|
|
col = layout.column()
|
|
col.label(text="Options:")
|
|
box = layout.box()
|
|
param_cb(box, get_rigify_params(bone))
|
|
|
|
|
|
# noinspection PyPep8Naming
|
|
class VIEW3D_PT_tools_rigify_dev(bpy.types.Panel):
|
|
bl_label = "Rigify Dev Tools"
|
|
bl_space_type = 'VIEW_3D'
|
|
bl_region_type = 'UI'
|
|
bl_category = "Rigify"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return context.mode in ['EDIT_ARMATURE', 'EDIT_MESH']
|
|
|
|
def draw(self, context):
|
|
obj = context.active_object
|
|
if obj is not None:
|
|
if context.mode == 'EDIT_ARMATURE':
|
|
r = self.layout.row()
|
|
r.operator("armature.rigify_encode_metarig", text="Encode Metarig to Python")
|
|
r = self.layout.row()
|
|
r.operator("armature.rigify_encode_metarig_sample", text="Encode Sample to Python")
|
|
|
|
if context.mode == 'EDIT_MESH':
|
|
r = self.layout.row()
|
|
r.operator("mesh.rigify_encode_mesh_widget", text="Encode Mesh Widget to Python")
|
|
|
|
|
|
# noinspection PyPep8Naming
|
|
class VIEW3D_PT_rigify_animation_tools(bpy.types.Panel):
|
|
bl_label = "Rigify Animation Tools"
|
|
bl_context = "posemode" # noqa
|
|
bl_space_type = 'VIEW_3D'
|
|
bl_region_type = 'UI'
|
|
bl_category = "Rigify"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
obj = context.active_object
|
|
if obj and obj.type == 'ARMATURE':
|
|
rig_id = obj.data.get("rig_id")
|
|
if rig_id is not None:
|
|
has_arm = hasattr(bpy.types, 'POSE_OT_rigify_arm_ik2fk_' + rig_id)
|
|
has_leg = hasattr(bpy.types, 'POSE_OT_rigify_leg_ik2fk_' + rig_id)
|
|
return has_arm or has_leg
|
|
|
|
return False
|
|
|
|
def draw(self, context):
|
|
obj = context.active_object
|
|
id_store = context.window_manager
|
|
if obj is not None:
|
|
row = self.layout.row()
|
|
|
|
only_selected = get_transfer_only_selected(id_store)
|
|
|
|
if only_selected:
|
|
icon = 'OUTLINER_DATA_ARMATURE'
|
|
else:
|
|
icon = 'ARMATURE_DATA'
|
|
|
|
row.prop(id_store, 'rigify_transfer_only_selected', toggle=True, icon=icon)
|
|
|
|
row = self.layout.row(align=True)
|
|
row.operator("rigify.ik2fk", text='IK2FK Pose', icon='SNAP_ON')
|
|
row.operator("rigify.fk2ik", text='FK2IK Pose', icon='SNAP_ON')
|
|
|
|
row = self.layout.row(align=True)
|
|
row.operator("rigify.transfer_fk_to_ik", text='IK2FK Action', icon='ACTION_TWEAK')
|
|
row.operator("rigify.transfer_ik_to_fk", text='FK2IK Action', icon='ACTION_TWEAK')
|
|
|
|
row = self.layout.row(align=True)
|
|
row.operator("rigify.clear_animation", text="Clear IK Action", icon='CANCEL').anim_type = "IK"
|
|
row.operator("rigify.clear_animation", text="Clear FK Action", icon='CANCEL').anim_type = "FK"
|
|
|
|
row = self.layout.row(align=True)
|
|
op = row.operator("rigify.rotation_pole", icon='FORCE_HARMONIC', text='Switch to pole')
|
|
op.value = True
|
|
op.toggle = False
|
|
op.bake = True
|
|
op = row.operator("rigify.rotation_pole", icon='FORCE_MAGNETIC', text='Switch to rotation')
|
|
op.value = False
|
|
op.toggle = False
|
|
op.bake = True
|
|
RIGIFY_OT_get_frame_range.draw_range_ui(context, self.layout)
|
|
|
|
|
|
def rigify_report_exception(operator, exception):
|
|
import traceback
|
|
import sys
|
|
import os
|
|
# find the non-utils module name where the error happened
|
|
# hint, this is the metarig type!
|
|
_exception_type, _exception_value, exception_traceback = sys.exc_info()
|
|
fns = [item.filename for item in traceback.extract_tb(exception_traceback)]
|
|
fns_rig = [fn for fn in fns if os.path.basename(os.path.dirname(fn)) != 'utils']
|
|
fn = fns_rig[-1]
|
|
fn = os.path.basename(fn)
|
|
fn = os.path.splitext(fn)[0]
|
|
message = []
|
|
if fn.startswith("__"):
|
|
message.append("Incorrect armature...")
|
|
else:
|
|
message.append("Incorrect armature for type '%s'" % fn)
|
|
message.append(exception.message)
|
|
|
|
message.reverse() # XXX - stupid! menu's are upside down!
|
|
|
|
operator.report({'ERROR'}, '\n'.join(message))
|
|
|
|
|
|
class LayerInit(bpy.types.Operator):
|
|
"""Initialize armature rigify layers"""
|
|
|
|
bl_idname = "pose.rigify_layer_init"
|
|
bl_label = "Add Rigify Layers"
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
|
|
|
def execute(self, context):
|
|
obj = verify_armature_obj(context.object)
|
|
arm = obj.data
|
|
rigify_layers = get_rigify_layers(arm)
|
|
for i in range(1 + len(rigify_layers), 30):
|
|
rigify_layers.add()
|
|
rigify_layers[28].name = 'Root'
|
|
rigify_layers[28].row = 14
|
|
return {'FINISHED'}
|
|
|
|
|
|
def is_metarig(obj):
|
|
if not (obj and obj.data and obj.type == 'ARMATURE'):
|
|
return False
|
|
if 'rig_id' in obj.data:
|
|
return False
|
|
for b in obj.pose.bones:
|
|
if b.rigify_type != "":
|
|
return True
|
|
return False
|
|
|
|
|
|
class Generate(bpy.types.Operator):
|
|
"""Generates a rig from the active metarig armature"""
|
|
|
|
bl_idname = "pose.rigify_generate"
|
|
bl_label = "Rigify Generate Rig"
|
|
bl_options = {'UNDO'}
|
|
bl_description = 'Generates a rig from the active metarig armature'
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return is_metarig(context.object)
|
|
|
|
def execute(self, context):
|
|
metarig = verify_armature_obj(context.object)
|
|
try:
|
|
generate.generate_rig(context, metarig)
|
|
except MetarigError as rig_exception:
|
|
import traceback
|
|
traceback.print_exc()
|
|
|
|
rigify_report_exception(self, rig_exception)
|
|
except Exception as rig_exception:
|
|
import traceback
|
|
traceback.print_exc()
|
|
|
|
self.report({'ERROR'}, 'Generation has thrown an exception: ' + str(rig_exception))
|
|
else:
|
|
target_rig = get_rigify_target_rig(metarig.data)
|
|
self.report({'INFO'}, f'Successfully generated: "{target_rig.name}"')
|
|
finally:
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
class UpgradeMetarigTypes(bpy.types.Operator):
|
|
"""Upgrades metarig bones rigify_types"""
|
|
|
|
bl_idname = "pose.rigify_upgrade_types"
|
|
bl_label = "Rigify Upgrade Metarig Types"
|
|
bl_description = 'Upgrades the rigify types on the active metarig armature'
|
|
bl_options = {'UNDO'}
|
|
|
|
def execute(self, context):
|
|
for obj in bpy.data.objects:
|
|
if type(obj.data) == bpy.types.Armature:
|
|
upgrade_metarig_types(obj)
|
|
return {'FINISHED'}
|
|
|
|
|
|
class Sample(bpy.types.Operator):
|
|
"""Create a sample metarig to be modified before generating the final rig"""
|
|
|
|
bl_idname = "armature.metarig_sample_add"
|
|
bl_label = "Add Metarig Sample"
|
|
bl_options = {'UNDO'}
|
|
|
|
metarig_type: StringProperty(
|
|
name="Type",
|
|
description="Name of the rig type to generate a sample of",
|
|
maxlen=128,
|
|
options={'SKIP_SAVE'}
|
|
)
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return context.mode == 'EDIT_ARMATURE'
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
layout.use_property_decorate = False
|
|
col = layout.column()
|
|
build_type_list(context, get_rigify_types(context.window_manager))
|
|
col.prop(context.object.data, "active_feature_set")
|
|
col.prop_search(self, "metarig_type", context.window_manager, "rigify_types")
|
|
|
|
def invoke(self, context, event):
|
|
if self.metarig_type == "":
|
|
return context.window_manager.invoke_props_dialog(self)
|
|
return self.execute(context)
|
|
|
|
def execute(self, context):
|
|
if self.metarig_type == "":
|
|
self.report({'ERROR'}, "You must select a rig type to create a sample of.")
|
|
return {'CANCELLED'}
|
|
try:
|
|
rig = rig_lists.rigs[self.metarig_type]["module"]
|
|
create_sample = rig.create_sample
|
|
except (ImportError, AttributeError, KeyError):
|
|
raise Exception("rig type '" + self.metarig_type + "' has no sample.")
|
|
else:
|
|
create_sample(context.active_object)
|
|
finally:
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
class EncodeMetarig(bpy.types.Operator):
|
|
"""Creates Python code that will generate the selected metarig"""
|
|
bl_idname = "armature.rigify_encode_metarig"
|
|
bl_label = "Rigify Encode Metarig"
|
|
bl_options = {'UNDO'}
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return context.mode == 'EDIT_ARMATURE' and is_metarig(context.object)
|
|
|
|
def execute(self, context):
|
|
name = "metarig.py"
|
|
|
|
if name in bpy.data.texts:
|
|
text_block = bpy.data.texts[name]
|
|
text_block.clear()
|
|
else:
|
|
text_block = bpy.data.texts.new(name)
|
|
|
|
obj = verify_armature_obj(context.active_object)
|
|
text = write_metarig(obj, layers=True, func_name="create", groups=True, widgets=True)
|
|
text_block.write(text)
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
self.report({'INFO'}, f"Metarig written to text datablock: {text_block.name}")
|
|
return {'FINISHED'}
|
|
|
|
|
|
class EncodeMetarigSample(bpy.types.Operator):
|
|
"""Creates Python code that will generate the selected metarig as a sample"""
|
|
bl_idname = "armature.rigify_encode_metarig_sample"
|
|
bl_label = "Rigify Encode Metarig Sample"
|
|
bl_options = {'UNDO'}
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return context.mode == 'EDIT_ARMATURE' and is_metarig(context.object)
|
|
|
|
def execute(self, context):
|
|
name = "metarig_sample.py"
|
|
|
|
if name in bpy.data.texts:
|
|
text_block = bpy.data.texts[name]
|
|
text_block.clear()
|
|
else:
|
|
text_block = bpy.data.texts.new(name)
|
|
|
|
obj = verify_armature_obj(context.active_object)
|
|
text = write_metarig(obj, layers=False, func_name="create_sample")
|
|
text_block.write(text)
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
|
|
self.report({'INFO'}, f"Metarig Sample written to text datablock: {text_block.name}")
|
|
return {'FINISHED'}
|
|
|
|
|
|
# noinspection PyPep8Naming
|
|
class VIEW3D_MT_rigify(bpy.types.Menu):
|
|
bl_label = "Rigify"
|
|
bl_idname = "VIEW3D_MT_rigify"
|
|
|
|
append: Callable
|
|
remove: Callable
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
obj = verify_armature_obj(context.object)
|
|
target_rig = get_rigify_target_rig(obj.data)
|
|
|
|
text = "Re-Generate Rig" if target_rig else "Generate Rig"
|
|
layout.operator(Generate.bl_idname, text=text)
|
|
|
|
if context.mode == 'EDIT_ARMATURE':
|
|
layout.separator()
|
|
layout.operator(Sample.bl_idname)
|
|
layout.separator()
|
|
layout.operator(EncodeMetarig.bl_idname, text="Encode Metarig")
|
|
layout.operator(EncodeMetarigSample.bl_idname, text="Encode Metarig Sample")
|
|
|
|
|
|
def draw_rigify_menu(self, context):
|
|
if is_metarig(context.object):
|
|
self.layout.menu(VIEW3D_MT_rigify.bl_idname)
|
|
|
|
|
|
class EncodeWidget(bpy.types.Operator):
|
|
""" Creates Python code that will generate the selected metarig.
|
|
"""
|
|
bl_idname = "mesh.rigify_encode_mesh_widget"
|
|
bl_label = "Rigify Encode Widget"
|
|
bl_options = {'UNDO'}
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return context.mode == 'EDIT_MESH'
|
|
|
|
def execute(self, context):
|
|
name = "widget.py"
|
|
|
|
if name in bpy.data.texts:
|
|
text_block = bpy.data.texts[name]
|
|
text_block.clear()
|
|
else:
|
|
text_block = bpy.data.texts.new(name)
|
|
|
|
text = write_widget(context.active_object)
|
|
text_block.write(text)
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
def draw_mesh_edit_menu(self, _context: bpy.types.Context):
|
|
self.layout.operator(EncodeWidget.bl_idname)
|
|
self.layout.separator()
|
|
|
|
|
|
def fk_to_ik(rig: ArmatureObject, window='ALL'):
|
|
scn = bpy.context.scene
|
|
id_store = bpy.context.window_manager
|
|
|
|
rig_id = rig.data['rig_id']
|
|
leg_ik2fk = eval('bpy.ops.pose.rigify_leg_ik2fk_' + rig_id)
|
|
arm_ik2fk = eval('bpy.ops.pose.rigify_arm_ik2fk_' + rig_id)
|
|
limb_generated_names = get_limb_generated_names(rig)
|
|
|
|
if window == 'ALL':
|
|
frames = get_keyed_frames_in_range(bpy.context, rig)
|
|
elif window == 'CURRENT':
|
|
frames = [scn.frame_current]
|
|
else:
|
|
frames = [scn.frame_current]
|
|
|
|
only_selected = get_transfer_only_selected(id_store)
|
|
|
|
if not only_selected:
|
|
pose_bones = rig.pose.bones
|
|
bpy.ops.pose.select_all(action='DESELECT')
|
|
else:
|
|
pose_bones = bpy.context.selected_pose_bones
|
|
bpy.ops.pose.select_all(action='DESELECT')
|
|
|
|
for b in pose_bones:
|
|
for group in limb_generated_names:
|
|
if b.name in limb_generated_names[group].values() or b.name in limb_generated_names[group]['controls']\
|
|
or b.name in limb_generated_names[group]['ik_ctrl']:
|
|
names = limb_generated_names[group]
|
|
if names['limb_type'] == 'arm':
|
|
func = arm_ik2fk
|
|
controls = names['controls']
|
|
ik_ctrl = names['ik_ctrl']
|
|
# fk_ctrl = names['fk_ctrl']
|
|
parent = names['parent']
|
|
pole = names['pole']
|
|
rig.pose.bones[controls[0]].bone.select = True
|
|
rig.pose.bones[controls[4]].bone.select = True
|
|
rig.pose.bones[pole].bone.select = True
|
|
rig.pose.bones[parent].bone.select = True
|
|
kwargs = {'uarm_fk': controls[1], 'farm_fk': controls[2], 'hand_fk': controls[3],
|
|
'uarm_ik': controls[0], 'farm_ik': ik_ctrl[1], 'hand_ik': controls[4],
|
|
'pole': pole, 'main_parent': parent}
|
|
args = (controls[0], controls[1], controls[2], controls[3],
|
|
controls[4], pole, parent)
|
|
else:
|
|
func = leg_ik2fk
|
|
controls = names['controls']
|
|
ik_ctrl = names['ik_ctrl']
|
|
# fk_ctrl = names['fk_ctrl']
|
|
parent = names['parent']
|
|
pole = names['pole']
|
|
rig.pose.bones[controls[0]].bone.select = True
|
|
rig.pose.bones[controls[6]].bone.select = True
|
|
rig.pose.bones[controls[5]].bone.select = True
|
|
rig.pose.bones[pole].bone.select = True
|
|
rig.pose.bones[parent].bone.select = True
|
|
# noinspection SpellCheckingInspection
|
|
kwargs = {'thigh_fk': controls[1], 'shin_fk': controls[2], 'foot_fk': controls[3],
|
|
'mfoot_fk': controls[7], 'thigh_ik': controls[0], 'shin_ik': ik_ctrl[1],
|
|
'foot_ik': controls[6], 'pole': pole, 'footroll': controls[5], 'mfoot_ik': ik_ctrl[2],
|
|
'main_parent': parent}
|
|
args = (controls[0], controls[1], controls[2], controls[3],
|
|
controls[6], controls[5], pole, parent)
|
|
|
|
for f in frames:
|
|
if not bones_in_frame(f, rig, *args):
|
|
continue
|
|
scn.frame_set(f)
|
|
func(**kwargs)
|
|
bpy.ops.anim.keyframe_insert_menu(type='BUILTIN_KSI_VisualLocRot')
|
|
bpy.ops.anim.keyframe_insert_menu(type='Scaling')
|
|
|
|
bpy.ops.pose.select_all(action='DESELECT')
|
|
limb_generated_names.pop(group)
|
|
break
|
|
|
|
|
|
def ik_to_fk(rig: ArmatureObject, window='ALL'):
|
|
scn = bpy.context.scene
|
|
id_store = bpy.context.window_manager
|
|
|
|
rig_id = rig.data['rig_id']
|
|
leg_fk2ik = eval('bpy.ops.pose.rigify_leg_fk2ik_' + rig_id)
|
|
arm_fk2ik = eval('bpy.ops.pose.rigify_arm_fk2ik_' + rig_id)
|
|
limb_generated_names = get_limb_generated_names(rig)
|
|
|
|
if window == 'ALL':
|
|
frames = get_keyed_frames_in_range(bpy.context, rig)
|
|
elif window == 'CURRENT':
|
|
frames = [scn.frame_current]
|
|
else:
|
|
frames = [scn.frame_current]
|
|
|
|
only_selected = get_transfer_only_selected(id_store)
|
|
|
|
if not only_selected:
|
|
bpy.ops.pose.select_all(action='DESELECT')
|
|
pose_bones = rig.pose.bones
|
|
else:
|
|
pose_bones = bpy.context.selected_pose_bones
|
|
bpy.ops.pose.select_all(action='DESELECT')
|
|
|
|
for b in pose_bones:
|
|
for group in limb_generated_names:
|
|
if b.name in limb_generated_names[group].values() or b.name in limb_generated_names[group]['controls']\
|
|
or b.name in limb_generated_names[group]['ik_ctrl']:
|
|
names = limb_generated_names[group]
|
|
if names['limb_type'] == 'arm':
|
|
func = arm_fk2ik
|
|
controls = names['controls']
|
|
ik_ctrl = names['ik_ctrl']
|
|
# fk_ctrl = names['fk_ctrl']
|
|
parent = names['parent']
|
|
pole = names['pole']
|
|
rig.pose.bones[controls[1]].bone.select = True
|
|
rig.pose.bones[controls[2]].bone.select = True
|
|
rig.pose.bones[controls[3]].bone.select = True
|
|
kwargs = {'uarm_fk': controls[1], 'farm_fk': controls[2], 'hand_fk': controls[3],
|
|
'uarm_ik': controls[0], 'farm_ik': ik_ctrl[1],
|
|
'hand_ik': controls[4]}
|
|
args = (controls[0], controls[1], controls[2], controls[3],
|
|
controls[4], pole, parent)
|
|
else:
|
|
func = leg_fk2ik
|
|
controls = names['controls']
|
|
ik_ctrl = names['ik_ctrl']
|
|
# fk_ctrl = names['fk_ctrl']
|
|
parent = names['parent']
|
|
pole = names['pole']
|
|
rig.pose.bones[controls[1]].bone.select = True
|
|
rig.pose.bones[controls[2]].bone.select = True
|
|
rig.pose.bones[controls[3]].bone.select = True
|
|
# noinspection SpellCheckingInspection
|
|
kwargs = {'thigh_fk': controls[1], 'shin_fk': controls[2], 'foot_fk': controls[3],
|
|
'mfoot_fk': controls[7], 'thigh_ik': controls[0], 'shin_ik': ik_ctrl[1],
|
|
'foot_ik': ik_ctrl[2], 'mfoot_ik': ik_ctrl[2]}
|
|
args = (controls[0], controls[1], controls[2], controls[3],
|
|
controls[6], controls[5], pole, parent)
|
|
|
|
for f in frames:
|
|
if not bones_in_frame(f, rig, *args):
|
|
continue
|
|
scn.frame_set(f)
|
|
func(**kwargs)
|
|
bpy.ops.anim.keyframe_insert_menu(type='BUILTIN_KSI_VisualLocRot')
|
|
bpy.ops.anim.keyframe_insert_menu(type='Scaling')
|
|
|
|
bpy.ops.pose.select_all(action='DESELECT')
|
|
limb_generated_names.pop(group)
|
|
break
|
|
|
|
|
|
def clear_animation(act, anim_type, names):
|
|
bones = []
|
|
for group in names:
|
|
if names[group]['limb_type'] == 'arm':
|
|
if anim_type == 'IK':
|
|
bones.extend([names[group]['controls'][0], names[group]['controls'][4]])
|
|
elif anim_type == 'FK':
|
|
bones.extend([names[group]['controls'][1], names[group]['controls'][2], names[group]['controls'][3]])
|
|
else:
|
|
if anim_type == 'IK':
|
|
bones.extend([names[group]['controls'][0], names[group]['controls'][6], names[group]['controls'][5],
|
|
names[group]['controls'][4]])
|
|
elif anim_type == 'FK':
|
|
bones.extend([names[group]['controls'][1], names[group]['controls'][2], names[group]['controls'][3],
|
|
names[group]['controls'][4]])
|
|
f_curves = []
|
|
for fcu in act.fcurves:
|
|
words = fcu.data_path.split('"')
|
|
if words[0] == "pose.bones[" and words[1] in bones:
|
|
f_curves.append(fcu)
|
|
|
|
if not f_curves:
|
|
return
|
|
|
|
for fcu in f_curves:
|
|
act.fcurves.remove(fcu)
|
|
|
|
# Put cleared bones back to rest pose
|
|
bpy.ops.pose.loc_clear()
|
|
bpy.ops.pose.rot_clear()
|
|
bpy.ops.pose.scale_clear()
|
|
|
|
# updateView3D()
|
|
|
|
|
|
def rot_pole_toggle(rig: ArmatureObject, window='ALL', value=False, toggle=False, bake=False):
|
|
scn = bpy.context.scene
|
|
id_store = bpy.context.window_manager
|
|
|
|
rig_id = rig.data['rig_id']
|
|
leg_fk2ik = eval('bpy.ops.pose.rigify_leg_fk2ik_' + rig_id)
|
|
arm_fk2ik = eval('bpy.ops.pose.rigify_arm_fk2ik_' + rig_id)
|
|
leg_ik2fk = eval('bpy.ops.pose.rigify_leg_ik2fk_' + rig_id)
|
|
arm_ik2fk = eval('bpy.ops.pose.rigify_arm_ik2fk_' + rig_id)
|
|
limb_generated_names = get_limb_generated_names(rig)
|
|
|
|
if window == 'ALL':
|
|
frames = get_keyed_frames_in_range(bpy.context, rig)
|
|
elif window == 'CURRENT':
|
|
frames = [scn.frame_current]
|
|
else:
|
|
frames = [scn.frame_current]
|
|
|
|
only_selected = get_transfer_only_selected(id_store)
|
|
|
|
if not only_selected:
|
|
bpy.ops.pose.select_all(action='DESELECT')
|
|
pose_bones = rig.pose.bones
|
|
else:
|
|
pose_bones = bpy.context.selected_pose_bones
|
|
bpy.ops.pose.select_all(action='DESELECT')
|
|
|
|
for b in pose_bones:
|
|
for group in limb_generated_names:
|
|
names = limb_generated_names[group]
|
|
|
|
if toggle:
|
|
new_pole_vector_value = not rig.pose.bones[names['parent']]['pole_vector']
|
|
else:
|
|
new_pole_vector_value = value
|
|
|
|
if b.name in names.values() or b.name in names['controls'] or b.name in names['ik_ctrl']:
|
|
if names['limb_type'] == 'arm':
|
|
func1 = arm_fk2ik
|
|
func2 = arm_ik2fk
|
|
controls = names['controls']
|
|
ik_ctrl = names['ik_ctrl']
|
|
# fk_ctrl = names['fk_ctrl']
|
|
parent = names['parent']
|
|
pole = names['pole']
|
|
rig.pose.bones[controls[0]].bone.select = not new_pole_vector_value
|
|
rig.pose.bones[controls[4]].bone.select = not new_pole_vector_value
|
|
rig.pose.bones[parent].bone.select = not new_pole_vector_value
|
|
rig.pose.bones[pole].bone.select = new_pole_vector_value
|
|
|
|
kwargs1 = {'uarm_fk': controls[1], 'farm_fk': controls[2], 'hand_fk': controls[3],
|
|
'uarm_ik': controls[0], 'farm_ik': ik_ctrl[1],
|
|
'hand_ik': controls[4]}
|
|
kwargs2 = {'uarm_fk': controls[1], 'farm_fk': controls[2], 'hand_fk': controls[3],
|
|
'uarm_ik': controls[0], 'farm_ik': ik_ctrl[1], 'hand_ik': controls[4],
|
|
'pole': pole, 'main_parent': parent}
|
|
args = (controls[0], controls[4], pole, parent)
|
|
else:
|
|
func1 = leg_fk2ik
|
|
func2 = leg_ik2fk
|
|
controls = names['controls']
|
|
ik_ctrl = names['ik_ctrl']
|
|
# fk_ctrl = names['fk_ctrl']
|
|
parent = names['parent']
|
|
pole = names['pole']
|
|
rig.pose.bones[controls[0]].bone.select = not new_pole_vector_value
|
|
rig.pose.bones[controls[6]].bone.select = not new_pole_vector_value
|
|
rig.pose.bones[controls[5]].bone.select = not new_pole_vector_value
|
|
rig.pose.bones[parent].bone.select = not new_pole_vector_value
|
|
rig.pose.bones[pole].bone.select = new_pole_vector_value
|
|
|
|
# noinspection SpellCheckingInspection
|
|
kwargs1 = {'thigh_fk': controls[1], 'shin_fk': controls[2], 'foot_fk': controls[3],
|
|
'mfoot_fk': controls[7], 'thigh_ik': controls[0], 'shin_ik': ik_ctrl[1],
|
|
'foot_ik': ik_ctrl[2], 'mfoot_ik': ik_ctrl[2]}
|
|
# noinspection SpellCheckingInspection
|
|
kwargs2 = {'thigh_fk': controls[1], 'shin_fk': controls[2], 'foot_fk': controls[3],
|
|
'mfoot_fk': controls[7], 'thigh_ik': controls[0], 'shin_ik': ik_ctrl[1],
|
|
'foot_ik': controls[6], 'pole': pole, 'footroll': controls[5], 'mfoot_ik': ik_ctrl[2],
|
|
'main_parent': parent}
|
|
args = (controls[0], controls[6], controls[5], pole, parent)
|
|
|
|
for f in frames:
|
|
if bake and not bones_in_frame(f, rig, *args):
|
|
continue
|
|
scn.frame_set(f)
|
|
func1(**kwargs1)
|
|
rig.pose.bones[names['parent']]['pole_vector'] = new_pole_vector_value
|
|
func2(**kwargs2)
|
|
if bake:
|
|
bpy.ops.anim.keyframe_insert_menu(type='BUILTIN_KSI_VisualLocRot')
|
|
bpy.ops.anim.keyframe_insert_menu(type='Scaling')
|
|
overwrite_prop_animation(rig, rig.pose.bones[parent], 'pole_vector', new_pole_vector_value, [f])
|
|
|
|
bpy.ops.pose.select_all(action='DESELECT')
|
|
limb_generated_names.pop(group)
|
|
break
|
|
scn.frame_set(0)
|
|
|
|
|
|
# noinspection PyPep8Naming
|
|
class OBJECT_OT_IK2FK(bpy.types.Operator):
|
|
""" Snaps IK limb on FK limb at current frame"""
|
|
bl_idname = "rigify.ik2fk"
|
|
bl_label = "IK2FK"
|
|
bl_description = "Snaps IK limb on FK"
|
|
bl_options = {'INTERNAL'}
|
|
|
|
def execute(self, context):
|
|
rig = verify_armature_obj(context.object)
|
|
|
|
fk_to_ik(rig, window='CURRENT')
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
# noinspection PyPep8Naming
|
|
class OBJECT_OT_FK2IK(bpy.types.Operator):
|
|
""" Snaps FK limb on IK limb at current frame"""
|
|
bl_idname = "rigify.fk2ik"
|
|
bl_label = "FK2IK"
|
|
bl_description = "Snaps FK limb on IK"
|
|
bl_options = {'INTERNAL'}
|
|
|
|
def execute(self, context):
|
|
rig = verify_armature_obj(context.object)
|
|
|
|
ik_to_fk(rig, window='CURRENT')
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
# noinspection PyPep8Naming
|
|
class OBJECT_OT_TransferFKtoIK(bpy.types.Operator):
|
|
"""Transfers FK animation to IK"""
|
|
bl_idname = "rigify.transfer_fk_to_ik"
|
|
bl_label = "Transfer FK anim to IK"
|
|
bl_description = "Transfer FK animation to IK bones"
|
|
bl_options = {'INTERNAL'}
|
|
|
|
def execute(self, context):
|
|
rig = verify_armature_obj(context.object)
|
|
|
|
fk_to_ik(rig)
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
# noinspection PyPep8Naming
|
|
class OBJECT_OT_TransferIKtoFK(bpy.types.Operator):
|
|
"""Transfers FK animation to IK"""
|
|
bl_idname = "rigify.transfer_ik_to_fk"
|
|
bl_label = "Transfer IK anim to FK"
|
|
bl_description = "Transfer IK animation to FK bones"
|
|
bl_options = {'INTERNAL'}
|
|
|
|
def execute(self, context):
|
|
rig = verify_armature_obj(context.object)
|
|
|
|
ik_to_fk(rig)
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
# noinspection PyPep8Naming
|
|
class OBJECT_OT_ClearAnimation(bpy.types.Operator):
|
|
bl_idname = "rigify.clear_animation"
|
|
bl_label = "Clear Animation"
|
|
bl_description = "Clear Animation For FK or IK Bones"
|
|
bl_options = {'INTERNAL'}
|
|
|
|
anim_type: StringProperty()
|
|
|
|
def execute(self, context):
|
|
rig = verify_armature_obj(context.object)
|
|
|
|
if not rig.animation_data:
|
|
return {'FINISHED'}
|
|
|
|
act = rig.animation_data.action
|
|
if not act:
|
|
return {'FINISHED'}
|
|
|
|
clear_animation(act, self.anim_type, names=get_limb_generated_names(rig))
|
|
return {'FINISHED'}
|
|
|
|
|
|
# noinspection PyPep8Naming
|
|
class OBJECT_OT_Rot2Pole(bpy.types.Operator):
|
|
bl_idname = "rigify.rotation_pole"
|
|
bl_label = "Rotation - Pole toggle"
|
|
bl_description = "Toggles IK chain between rotation and pole target"
|
|
bl_options = {'INTERNAL'}
|
|
|
|
bone_name: StringProperty(default='')
|
|
window: StringProperty(default='ALL')
|
|
toggle: BoolProperty(default=True)
|
|
value: BoolProperty(default=True)
|
|
bake: BoolProperty(default=True)
|
|
|
|
def execute(self, context):
|
|
rig = verify_armature_obj(context.object)
|
|
|
|
if self.bone_name:
|
|
bpy.ops.pose.select_all(action='DESELECT')
|
|
rig.pose.bones[self.bone_name].bone.select = True
|
|
|
|
rot_pole_toggle(rig, window=self.window, toggle=self.toggle, value=self.value, bake=self.bake)
|
|
return {'FINISHED'}
|
|
|
|
|
|
###############
|
|
# Registering
|
|
|
|
classes = (
|
|
DATA_OT_rigify_add_bone_groups,
|
|
DATA_OT_rigify_use_standard_colors,
|
|
DATA_OT_rigify_apply_selection_colors,
|
|
DATA_OT_rigify_bone_group_add,
|
|
DATA_OT_rigify_bone_group_add_theme,
|
|
DATA_OT_rigify_bone_group_remove,
|
|
DATA_OT_rigify_bone_group_remove_all,
|
|
DATA_UL_rigify_bone_groups,
|
|
DATA_MT_rigify_bone_groups_context_menu,
|
|
DATA_PT_rigify,
|
|
DATA_PT_rigify_advanced,
|
|
DATA_PT_rigify_bone_groups,
|
|
DATA_PT_rigify_layer_names,
|
|
DATA_PT_rigify_samples,
|
|
BONE_PT_rigify_buttons,
|
|
VIEW3D_PT_rigify_animation_tools,
|
|
VIEW3D_PT_tools_rigify_dev,
|
|
LayerInit,
|
|
Generate,
|
|
UpgradeMetarigTypes,
|
|
Sample,
|
|
VIEW3D_MT_rigify,
|
|
EncodeMetarig,
|
|
EncodeMetarigSample,
|
|
EncodeWidget,
|
|
OBJECT_OT_FK2IK,
|
|
OBJECT_OT_IK2FK,
|
|
OBJECT_OT_TransferFKtoIK,
|
|
OBJECT_OT_TransferIKtoFK,
|
|
OBJECT_OT_ClearAnimation,
|
|
OBJECT_OT_Rot2Pole,
|
|
)
|
|
|
|
|
|
def register():
|
|
from bpy.utils import register_class
|
|
|
|
animation_register()
|
|
|
|
# Classes.
|
|
for cls in classes:
|
|
register_class(cls)
|
|
|
|
bpy.types.VIEW3D_MT_editor_menus.append(draw_rigify_menu)
|
|
bpy.types.VIEW3D_MT_edit_mesh.prepend(draw_mesh_edit_menu)
|
|
|
|
# Sub-modules.
|
|
rot_mode.register()
|
|
|
|
|
|
def unregister():
|
|
from bpy.utils import unregister_class
|
|
|
|
# Sub-modules.
|
|
rot_mode.unregister()
|
|
|
|
# Classes.
|
|
for cls in classes:
|
|
unregister_class(cls)
|
|
|
|
bpy.types.VIEW3D_MT_editor_menus.remove(draw_rigify_menu)
|
|
bpy.types.VIEW3D_MT_edit_mesh.remove(draw_mesh_edit_menu)
|
|
|
|
animation_unregister()
|