Cleanup: quiet float argument to in type warning
[blender-addons.git] / rigify / ui.py
blob030096a233a6d9b621c0e900d5c49e6dd9e3b37b
1 # SPDX-License-Identifier: GPL-2.0-or-later
3 import bpy
4 from bpy.props import (
5 BoolProperty,
6 IntProperty,
7 EnumProperty,
8 StringProperty
11 from typing import TYPE_CHECKING, Callable
12 from mathutils import Color
14 from .utils.errors import MetarigError
15 from .utils.rig import write_metarig, get_rigify_type, get_rigify_target_rig, get_rigify_layers, \
16 get_rigify_colors, get_rigify_params
17 from .utils.widgets import write_widget
18 from .utils.naming import unique_name
19 from .utils.rig import upgrade_metarig_types, outdated_types
20 from .utils.misc import verify_armature_obj, ArmatureObject, IdPropSequence
22 from .rigs.utils import get_limb_generated_names
24 from .utils.animation import get_keyed_frames_in_range, bones_in_frame, overwrite_prop_animation
25 from .utils.animation import RIGIFY_OT_get_frame_range
27 from .utils.animation import register as animation_register
28 from .utils.animation import unregister as animation_unregister
30 from . import base_rig
31 from . import rig_lists
32 from . import generate
33 from . import rot_mode
34 from . import feature_set_list
36 if TYPE_CHECKING:
37 from . import RigifyName, RigifySelectionColors
40 def get_rigify_types(id_store: bpy.types.WindowManager) -> IdPropSequence['RigifyName']:
41 return id_store.rigify_types # noqa
44 def get_transfer_only_selected(id_store: bpy.types.WindowManager) -> bool:
45 return id_store.rigify_transfer_only_selected # noqa
48 def get_selection_colors(armature: bpy.types.Armature) -> 'RigifySelectionColors':
49 return armature.rigify_selection_colors # noqa
52 def get_colors_lock(armature: bpy.types.Armature) -> bool:
53 return armature.rigify_colors_lock # noqa
56 def get_colors_index(armature: bpy.types.Armature) -> int:
57 return armature.rigify_colors_index # noqa
60 def get_theme_to_add(armature: bpy.types.Armature) -> str:
61 return armature.rigify_theme_to_add # noqa
64 def build_type_list(context, rigify_types: IdPropSequence['RigifyName']):
65 rigify_types.clear()
67 for r in sorted(rig_lists.rigs):
68 if (context.object.data.active_feature_set in ('all', rig_lists.rigs[r]['feature_set'])
69 or len(feature_set_list.get_enabled_modules_names()) == 0):
70 a = rigify_types.add()
71 a.name = r
74 # noinspection PyPep8Naming
75 class DATA_PT_rigify(bpy.types.Panel):
76 bl_label = "Rigify"
77 bl_space_type = 'PROPERTIES'
78 bl_region_type = 'WINDOW'
79 bl_context = "data"
81 @classmethod
82 def poll(cls, context):
83 obj = context.object
84 if not context.object:
85 return False
86 return obj.type == 'ARMATURE' \
87 and obj.data.get("rig_id") is None
89 def draw(self, context):
90 C = context
91 layout = self.layout
92 obj = verify_armature_obj(C.object)
94 WARNING = "Warning: Some features may change after generation"
95 show_warning = False
96 show_update_metarig = False
97 show_not_updatable = False
98 show_upgrade_face = False
100 check_props = ['IK_follow', 'root/parent', 'FK_limb_follow', 'IK_Stretch']
102 for pose_bone in obj.pose.bones:
103 bone = pose_bone.bone
104 if not bone:
105 # If we are in edit mode and the bone was just created,
106 # a pose bone won't exist yet.
107 continue
108 if bone.layers[30] and (list(set(pose_bone.keys()) & set(check_props))):
109 show_warning = True
110 break
112 old_rig = ''
113 old_bone = ''
115 for b in obj.pose.bones:
116 old_rig = get_rigify_type(b)
117 if old_rig in outdated_types:
118 old_bone = b.name
119 if outdated_types[old_rig]:
120 show_update_metarig = True
121 else:
122 show_update_metarig = False
123 show_not_updatable = True
124 break
125 elif old_rig == 'faces.super_face':
126 show_upgrade_face = True
128 if show_warning:
129 layout.label(text=WARNING, icon='ERROR')
131 enable_generate = not (show_not_updatable or show_update_metarig)
133 if show_not_updatable:
134 layout.label(text="WARNING: This metarig contains deprecated rigify rig-types and "
135 "cannot be upgraded automatically.", icon='ERROR')
136 layout.label(text="(" + old_rig + " on bone " + old_bone + ")")
137 elif show_update_metarig:
138 layout.label(text="This metarig contains old rig-types that can be automatically "
139 "upgraded to benefit from new rigify features.", icon='ERROR')
140 layout.label(text="(" + old_rig + " on bone " + old_bone + ")")
141 layout.operator("pose.rigify_upgrade_types", text="Upgrade Metarig")
142 elif show_upgrade_face:
143 layout.label(text="This metarig uses the old face rig.", icon='INFO')
144 layout.operator("pose.rigify_upgrade_face")
146 # Rig type field
148 col = layout.column(align=True)
149 col.active = ('rig_id' not in C.object.data)
151 col.separator()
152 row = col.row()
153 text = "Re-Generate Rig" if get_rigify_target_rig(obj.data) else "Generate Rig"
154 row.operator("pose.rigify_generate", text=text, icon='POSE_HLT')
155 row.enabled = enable_generate
158 # noinspection PyPep8Naming
159 class DATA_PT_rigify_advanced(bpy.types.Panel):
160 bl_space_type = 'PROPERTIES'
161 bl_region_type = 'WINDOW'
162 bl_context = "data"
163 bl_label = "Advanced"
164 bl_parent_id = 'DATA_PT_rigify'
165 bl_options = {'DEFAULT_CLOSED'}
167 def draw(self, context):
168 layout = self.layout
169 layout.use_property_split = True
170 layout.use_property_decorate = False
172 armature_id_store = verify_armature_obj(context.object).data
174 col = layout.column()
176 row = col.row()
177 row.active = not get_rigify_target_rig(armature_id_store)
178 row.prop(armature_id_store, "rigify_rig_basename", text="Rig Name")
180 col.separator()
182 col2 = col.box().column()
183 col2.label(text="Overwrite Existing:")
184 col2.row().prop(armature_id_store, "rigify_target_rig", text="Target Rig")
185 col2.row().prop(armature_id_store, "rigify_rig_ui", text="Rig UI Script")
186 col2.row().prop(armature_id_store, "rigify_widgets_collection")
188 col.separator()
189 col.row().prop(armature_id_store, "rigify_force_widget_update")
190 col.row().prop(armature_id_store, "rigify_mirror_widgets")
191 col.separator()
192 col.row().prop(armature_id_store, "rigify_finalize_script", text="Run Script")
195 # noinspection PyPep8Naming
196 class DATA_PT_rigify_samples(bpy.types.Panel):
197 bl_label = "Samples"
198 bl_space_type = 'PROPERTIES'
199 bl_region_type = 'WINDOW'
200 bl_context = "data"
201 bl_parent_id = "DATA_PT_rigify"
202 bl_options = {'DEFAULT_CLOSED'}
204 @classmethod
205 def poll(cls, context):
206 obj = context.object
207 if not obj:
208 return False
209 return obj.type == 'ARMATURE' \
210 and obj.data.get("rig_id") is None \
211 and obj.mode == 'EDIT'
213 def draw(self, context):
214 layout = self.layout
215 layout.use_property_split = True
216 layout.use_property_decorate = False
217 id_store = context.window_manager
219 # Build types list
220 rigify_types = get_rigify_types(id_store)
221 build_type_list(context, rigify_types)
223 if id_store.rigify_active_type > len(rigify_types):
224 id_store.rigify_active_type = 0
226 # Rig type list
227 if len(feature_set_list.get_enabled_modules_names()) > 0:
228 row = layout.row()
229 row.prop(context.object.data, "active_feature_set")
230 row = layout.row()
231 row.template_list("UI_UL_list", "rigify_types", id_store, "rigify_types", id_store, 'rigify_active_type')
233 props = layout.operator("armature.metarig_sample_add", text="Add sample")
234 props.metarig_type = rigify_types[id_store.rigify_active_type].name
237 # noinspection PyPep8Naming
238 class DATA_PT_rigify_layer_names(bpy.types.Panel):
239 bl_label = "Layer Names"
240 bl_space_type = 'PROPERTIES'
241 bl_region_type = 'WINDOW'
242 bl_context = "data"
243 bl_options = {'DEFAULT_CLOSED'}
244 bl_parent_id = "DATA_PT_rigify"
246 @classmethod
247 def poll(cls, context):
248 if not context.object:
249 return False
250 return context.object.type == 'ARMATURE' and context.active_object.data.get("rig_id") is None
252 def draw(self, context):
253 layout = self.layout
254 obj = verify_armature_obj(context.object)
255 arm = obj.data
257 rigify_layers = get_rigify_layers(arm)
258 rigify_colors = get_rigify_colors(arm)
260 # Ensure that the layers exist
261 if len(rigify_layers) < 29:
262 layout.operator("pose.rigify_layer_init")
263 return
265 # UI
266 main_row = layout.row(align=True).split(factor=0.05)
267 col1 = main_row.column()
268 col2 = main_row.column()
269 col1.label()
270 for i in range(32):
271 if i == 16 or i == 29:
272 col1.label()
273 col1.label(text=str(i))
275 col: bpy.types.UILayout = col2
277 for i, rigify_layer in enumerate(rigify_layers):
278 # note: rigify_layer == arm.rigify_layers[i]
279 if (i % 16) == 0:
280 col = col2.column()
281 if i == 0:
282 col.label(text="Top Row:")
283 else:
284 col.label(text="Bottom Row:")
285 if (i % 8) == 0:
286 col = col2.column()
287 if i != 28:
288 row = col.row(align=True)
289 icon = 'RESTRICT_VIEW_OFF' if arm.layers[i] else 'RESTRICT_VIEW_ON'
290 row.prop(arm, "layers", index=i, text="", toggle=True, icon=icon)
291 # row.prop(arm, "layers", index=i, text="Layer %d" % (i + 1), toggle=True, icon=icon)
292 row.prop(rigify_layer, "name", text="")
293 row.prop(rigify_layer, "row", text="UI Row")
294 icon = 'RADIOBUT_ON' if rigify_layer.selset else 'RADIOBUT_OFF'
295 row.prop(rigify_layer, "selset", text="", toggle=True, icon=icon)
296 row.prop(rigify_layer, "group", text="Bone Group")
297 else:
298 row = col.row(align=True)
300 icon = 'RESTRICT_VIEW_OFF' if arm.layers[i] else 'RESTRICT_VIEW_ON'
301 row.prop(arm, "layers", index=i, text="", toggle=True, icon=icon)
302 # row.prop(arm, "layers", index=i, text="Layer %d" % (i + 1), toggle=True, icon=icon)
303 row1 = row.split(align=True).row(align=True)
304 row1.prop(rigify_layer, "name", text="")
305 row1.prop(rigify_layer, "row", text="UI Row")
306 row1.enabled = False
307 icon = 'RADIOBUT_ON' if rigify_layer.selset else 'RADIOBUT_OFF'
308 row.prop(rigify_layer, "selset", text="", toggle=True, icon=icon)
309 row.prop(rigify_layer, "group", text="Bone Group")
310 if rigify_layer.group == 0:
311 row.label(text='None')
312 else:
313 row.label(text=rigify_colors[rigify_layer.group-1].name)
315 col = col2.column()
316 col.label(text="Reserved:")
317 # reserved_names = {28: 'Root', 29: 'DEF', 30: 'MCH', 31: 'ORG'}
318 reserved_names = {29: 'DEF', 30: 'MCH', 31: 'ORG'}
319 # for i in range(28, 32):
320 for i in range(29, 32):
321 row = col.row(align=True)
322 icon = 'RESTRICT_VIEW_OFF' if arm.layers[i] else 'RESTRICT_VIEW_ON'
323 row.prop(arm, "layers", index=i, text="", toggle=True, icon=icon)
324 row.label(text=reserved_names[i])
327 # noinspection PyPep8Naming
328 class DATA_OT_rigify_add_bone_groups(bpy.types.Operator):
329 bl_idname = "armature.rigify_add_bone_groups"
330 bl_label = "Rigify Add Standard Bone Groups"
332 @classmethod
333 def poll(cls, context):
334 return context.object and context.object.type == 'ARMATURE'
336 def execute(self, context):
337 obj = verify_armature_obj(context.object)
338 armature = obj.data
340 if not hasattr(armature, 'rigify_colors'):
341 return {'FINISHED'}
343 rigify_colors = get_rigify_colors(armature)
344 groups = ['Root', 'IK', 'Special', 'Tweak', 'FK', 'Extra']
346 for g in groups:
347 if g in rigify_colors:
348 continue
350 color = rigify_colors.add()
351 color.name = g
353 color.select = Color((0.3140000104904175, 0.7839999794960022, 1.0))
354 color.active = Color((0.5490000247955322, 1.0, 1.0))
355 color.standard_colors_lock = True
357 if g == "Root":
358 color.normal = Color((0.43529415130615234, 0.18431372940540314, 0.41568630933761597))
359 if g == "IK":
360 color.normal = Color((0.6039215922355652, 0.0, 0.0))
361 if g == "Special":
362 color.normal = Color((0.9568628072738647, 0.7882353663444519, 0.0470588281750679))
363 if g == "Tweak":
364 color.normal = Color((0.03921568766236305, 0.21176472306251526, 0.5803921818733215))
365 if g == "FK":
366 color.normal = Color((0.11764706671237946, 0.5686274766921997, 0.03529411926865578))
367 if g == "Extra":
368 color.normal = Color((0.9686275124549866, 0.250980406999588, 0.0941176563501358))
370 return {'FINISHED'}
373 # noinspection PyPep8Naming
374 class DATA_OT_rigify_use_standard_colors(bpy.types.Operator):
375 bl_idname = "armature.rigify_use_standard_colors"
376 bl_label = "Rigify Get active/select colors from current theme"
378 @classmethod
379 def poll(cls, context):
380 return context.object and context.object.type == 'ARMATURE'
382 def execute(self, context):
383 obj = verify_armature_obj(context.object)
384 armature = obj.data
385 if not hasattr(armature, 'rigify_colors'):
386 return {'FINISHED'}
388 current_theme = bpy.context.preferences.themes.items()[0][0]
389 theme = bpy.context.preferences.themes[current_theme]
391 selection_colors = get_selection_colors(armature)
392 selection_colors.select = theme.view_3d.bone_pose
393 selection_colors.active = theme.view_3d.bone_pose_active
395 # for col in armature.rigify_colors:
396 # col.select = theme.view_3d.bone_pose
397 # col.active = theme.view_3d.bone_pose_active
399 return {'FINISHED'}
402 # noinspection PyPep8Naming
403 class DATA_OT_rigify_apply_selection_colors(bpy.types.Operator):
404 bl_idname = "armature.rigify_apply_selection_colors"
405 bl_label = "Rigify Apply user defined active/select colors"
407 @classmethod
408 def poll(cls, context):
409 return context.object and context.object.type == 'ARMATURE'
411 def execute(self, context):
412 obj = verify_armature_obj(context.object)
413 armature = obj.data
415 if not hasattr(armature, 'rigify_colors'):
416 return {'FINISHED'}
418 # current_theme = bpy.context.preferences.themes.items()[0][0]
419 # theme = bpy.context.preferences.themes[current_theme]
421 rigify_colors = get_rigify_colors(armature)
422 selection_colors = get_selection_colors(armature)
424 for col in rigify_colors:
425 col.select = selection_colors.select
426 col.active = selection_colors.active
428 return {'FINISHED'}
431 # noinspection PyPep8Naming
432 class DATA_OT_rigify_bone_group_add(bpy.types.Operator):
433 bl_idname = "armature.rigify_bone_group_add"
434 bl_label = "Rigify Add Bone Group color set"
436 @classmethod
437 def poll(cls, context):
438 return context.object and context.object.type == 'ARMATURE'
440 def execute(self, context):
441 obj = context.object
442 armature = obj.data
444 if hasattr(armature, 'rigify_colors'):
445 armature.rigify_colors.add()
446 armature.rigify_colors[-1].name = unique_name(armature.rigify_colors, 'Group')
448 current_theme = bpy.context.preferences.themes.items()[0][0]
449 theme = bpy.context.preferences.themes[current_theme]
451 armature.rigify_colors[-1].normal = theme.view_3d.wire
452 armature.rigify_colors[-1].normal.hsv = theme.view_3d.wire.hsv
453 armature.rigify_colors[-1].select = theme.view_3d.bone_pose
454 armature.rigify_colors[-1].select.hsv = theme.view_3d.bone_pose.hsv
455 armature.rigify_colors[-1].active = theme.view_3d.bone_pose_active
456 armature.rigify_colors[-1].active.hsv = theme.view_3d.bone_pose_active.hsv
458 return {'FINISHED'}
461 # noinspection PyPep8Naming
462 class DATA_OT_rigify_bone_group_add_theme(bpy.types.Operator):
463 bl_idname = "armature.rigify_bone_group_add_theme"
464 bl_label = "Rigify Add Bone Group color set from Theme"
465 bl_options = {"REGISTER", "UNDO"}
467 theme: EnumProperty(items=(
468 ('THEME01', 'THEME01', ''),
469 ('THEME02', 'THEME02', ''),
470 ('THEME03', 'THEME03', ''),
471 ('THEME04', 'THEME04', ''),
472 ('THEME05', 'THEME05', ''),
473 ('THEME06', 'THEME06', ''),
474 ('THEME07', 'THEME07', ''),
475 ('THEME08', 'THEME08', ''),
476 ('THEME09', 'THEME09', ''),
477 ('THEME10', 'THEME10', ''),
478 ('THEME11', 'THEME11', ''),
479 ('THEME12', 'THEME12', ''),
480 ('THEME13', 'THEME13', ''),
481 ('THEME14', 'THEME14', ''),
482 ('THEME15', 'THEME15', ''),
483 ('THEME16', 'THEME16', ''),
484 ('THEME17', 'THEME17', ''),
485 ('THEME18', 'THEME18', ''),
486 ('THEME19', 'THEME19', ''),
487 ('THEME20', 'THEME20', '')
489 name='Theme')
491 @classmethod
492 def poll(cls, context):
493 return context.object and context.object.type == 'ARMATURE'
495 def execute(self, context):
496 obj = verify_armature_obj(context.object)
497 armature = obj.data
499 if hasattr(armature, 'rigify_colors'):
500 rigify_colors = get_rigify_colors(armature)
502 if self.theme in rigify_colors.keys():
503 return {'FINISHED'}
505 rigify_colors.add()
506 rigify_colors[-1].name = self.theme
508 color_id = int(self.theme[-2:]) - 1
510 theme_color_set = bpy.context.preferences.themes[0].bone_color_sets[color_id]
512 rigify_colors[-1].normal = theme_color_set.normal
513 rigify_colors[-1].select = theme_color_set.select
514 rigify_colors[-1].active = theme_color_set.active
516 return {'FINISHED'}
519 # noinspection PyPep8Naming
520 class DATA_OT_rigify_bone_group_remove(bpy.types.Operator):
521 bl_idname = "armature.rigify_bone_group_remove"
522 bl_label = "Rigify Remove Bone Group color set"
524 idx: IntProperty()
526 @classmethod
527 def poll(cls, context):
528 return context.object and context.object.type == 'ARMATURE'
530 def execute(self, context):
531 obj = verify_armature_obj(context.object)
533 rigify_colors = get_rigify_colors(obj.data)
534 rigify_colors.remove(self.idx)
536 # set layers references to 0
537 rigify_layers = get_rigify_layers(obj.data)
539 for layer in rigify_layers:
540 if layer.group == self.idx + 1:
541 layer.group = 0
542 elif layer.group > self.idx + 1:
543 layer.group -= 1
545 return {'FINISHED'}
548 # noinspection PyPep8Naming
549 class DATA_OT_rigify_bone_group_remove_all(bpy.types.Operator):
550 bl_idname = "armature.rigify_bone_group_remove_all"
551 bl_label = "Rigify Remove All Bone Groups"
553 @classmethod
554 def poll(cls, context):
555 return context.object and context.object.type == 'ARMATURE'
557 def execute(self, context):
558 obj = verify_armature_obj(context.object)
560 rigify_colors = get_rigify_colors(obj.data)
561 while len(rigify_colors) > 0:
562 rigify_colors.remove(0)
564 # set layers references to 0
565 for layer in get_rigify_layers(obj.data):
566 layer.group = 0
568 return {'FINISHED'}
571 # noinspection PyPep8Naming
572 class DATA_UL_rigify_bone_groups(bpy.types.UIList):
573 def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index=0, flt_flag=0):
574 row = layout.row(align=True)
575 row = row.split(factor=0.1)
576 row.label(text=str(index+1))
577 row = row.split(factor=0.7)
578 row.prop(item, "name", text='', emboss=False)
579 row = row.row(align=True)
580 # icon = 'LOCKED' if item.standard_colors_lock else 'UNLOCKED'
581 # row.prop(item, "standard_colors_lock", text='', icon=icon)
582 row.prop(item, "normal", text='')
583 row2 = row.row(align=True)
584 row2.prop(item, "select", text='')
585 row2.prop(item, "active", text='')
586 # row2.enabled = not item.standard_colors_lock
587 arm = verify_armature_obj(context.object).data
588 row2.enabled = not get_colors_lock(arm)
591 # noinspection PyPep8Naming
592 class DATA_MT_rigify_bone_groups_context_menu(bpy.types.Menu):
593 bl_label = 'Rigify Bone Groups Specials'
595 def draw(self, context):
596 layout = self.layout
598 layout.operator('armature.rigify_bone_group_remove_all')
601 # noinspection PyPep8Naming
602 class DATA_PT_rigify_bone_groups(bpy.types.Panel):
603 bl_label = "Bone Groups"
604 bl_space_type = 'PROPERTIES'
605 bl_region_type = 'WINDOW'
606 bl_context = "data"
607 bl_options = {'DEFAULT_CLOSED'}
608 bl_parent_id = "DATA_PT_rigify"
610 @classmethod
611 def poll(cls, context):
612 if not context.object:
613 return False
614 return context.object.type == 'ARMATURE' and context.active_object.data.get("rig_id") is None
616 def draw(self, context):
617 obj = verify_armature_obj(context.object)
618 armature = obj.data
619 idx = get_colors_index(armature)
620 selection_colors = get_selection_colors(armature)
621 is_locked = get_colors_lock(armature)
622 theme = get_theme_to_add(armature)
624 layout = self.layout
625 row = layout.row()
626 row.operator("armature.rigify_use_standard_colors", icon='FILE_REFRESH', text='')
627 row = row.row(align=True)
628 row.prop(selection_colors, 'select', text='')
629 row.prop(selection_colors, 'active', text='')
630 row = layout.row(align=True)
631 icon = 'LOCKED' if is_locked else 'UNLOCKED'
632 row.prop(armature, 'rigify_colors_lock', text='Unified select/active colors', icon=icon)
633 row.operator("armature.rigify_apply_selection_colors", icon='FILE_REFRESH', text='Apply')
634 row = layout.row()
635 row.template_list("DATA_UL_rigify_bone_groups", "", obj.data, "rigify_colors", obj.data, "rigify_colors_index")
637 col = row.column(align=True)
638 col.operator("armature.rigify_bone_group_add", icon='ADD', text="")
639 col.operator("armature.rigify_bone_group_remove", icon='REMOVE', text="").idx = idx
640 col.menu("DATA_MT_rigify_bone_groups_context_menu", icon='DOWNARROW_HLT', text="")
641 row = layout.row()
642 row.prop(armature, 'rigify_theme_to_add', text='Theme')
643 op = row.operator("armature.rigify_bone_group_add_theme", text="Add From Theme")
644 op.theme = theme
645 row = layout.row()
646 row.operator("armature.rigify_add_bone_groups", text="Add Standard")
649 # noinspection PyPep8Naming
650 class BONE_PT_rigify_buttons(bpy.types.Panel):
651 bl_label = "Rigify Type"
652 bl_space_type = 'PROPERTIES'
653 bl_region_type = 'WINDOW'
654 bl_context = "bone"
655 # bl_options = {'DEFAULT_OPEN'}
657 @classmethod
658 def poll(cls, context):
659 if not context.object:
660 return False
661 return (context.object.type == 'ARMATURE' and context.active_pose_bone and
662 context.active_object.data.get("rig_id") is None)
664 def draw(self, context):
665 C = context
666 id_store = C.window_manager
667 bone = context.active_pose_bone
668 rig_name = get_rigify_type(bone)
670 layout = self.layout
671 rig_types = get_rigify_types(id_store)
673 # Build types list
674 build_type_list(context, rig_types)
676 # Rig type field
677 if len(feature_set_list.get_enabled_modules_names()) > 0:
678 row = layout.row()
679 row.prop(context.object.data, "active_feature_set")
680 row = layout.row()
681 row.prop_search(bone, "rigify_type", id_store, "rigify_types", text="Rig type")
683 # Rig type parameters / Rig type non-exist alert
684 if rig_name != "":
685 try:
686 rig = rig_lists.rigs[rig_name]['module']
687 except (ImportError, AttributeError, KeyError):
688 row = layout.row()
689 box = row.box()
690 box.label(text="ERROR: type \"%s\" does not exist!" % rig_name, icon='ERROR')
691 else:
692 if hasattr(rig.Rig, 'parameters_ui'):
693 rig = rig.Rig
695 try:
696 param_cb = rig.parameters_ui
698 # Ignore the known empty base method
699 if getattr(param_cb, '__func__', None) == \
700 getattr(base_rig.BaseRig.parameters_ui, '__func__'):
701 param_cb = None
702 except AttributeError:
703 param_cb = None
705 if param_cb is None:
706 col = layout.column()
707 col.label(text="No options")
708 else:
709 col = layout.column()
710 col.label(text="Options:")
711 box = layout.box()
712 param_cb(box, get_rigify_params(bone))
715 # noinspection PyPep8Naming
716 class VIEW3D_PT_tools_rigify_dev(bpy.types.Panel):
717 bl_label = "Rigify Dev Tools"
718 bl_space_type = 'VIEW_3D'
719 bl_region_type = 'UI'
720 bl_category = "Rigify"
722 @classmethod
723 def poll(cls, context):
724 return context.mode in ['EDIT_ARMATURE', 'EDIT_MESH']
726 def draw(self, context):
727 obj = context.active_object
728 if obj is not None:
729 if context.mode == 'EDIT_ARMATURE':
730 r = self.layout.row()
731 r.operator("armature.rigify_encode_metarig", text="Encode Metarig to Python")
732 r = self.layout.row()
733 r.operator("armature.rigify_encode_metarig_sample", text="Encode Sample to Python")
735 if context.mode == 'EDIT_MESH':
736 r = self.layout.row()
737 r.operator("mesh.rigify_encode_mesh_widget", text="Encode Mesh Widget to Python")
740 # noinspection PyPep8Naming
741 class VIEW3D_PT_rigify_animation_tools(bpy.types.Panel):
742 bl_label = "Rigify Animation Tools"
743 bl_context = "posemode" # noqa
744 bl_space_type = 'VIEW_3D'
745 bl_region_type = 'UI'
746 bl_category = "Rigify"
748 @classmethod
749 def poll(cls, context):
750 obj = context.active_object
751 if obj and obj.type == 'ARMATURE':
752 rig_id = obj.data.get("rig_id")
753 if rig_id is not None:
754 has_arm = hasattr(bpy.types, 'POSE_OT_rigify_arm_ik2fk_' + rig_id)
755 has_leg = hasattr(bpy.types, 'POSE_OT_rigify_leg_ik2fk_' + rig_id)
756 return has_arm or has_leg
758 return False
760 def draw(self, context):
761 obj = context.active_object
762 id_store = context.window_manager
763 if obj is not None:
764 row = self.layout.row()
766 only_selected = get_transfer_only_selected(id_store)
768 if only_selected:
769 icon = 'OUTLINER_DATA_ARMATURE'
770 else:
771 icon = 'ARMATURE_DATA'
773 row.prop(id_store, 'rigify_transfer_only_selected', toggle=True, icon=icon)
775 row = self.layout.row(align=True)
776 row.operator("rigify.ik2fk", text='IK2FK Pose', icon='SNAP_ON')
777 row.operator("rigify.fk2ik", text='FK2IK Pose', icon='SNAP_ON')
779 row = self.layout.row(align=True)
780 row.operator("rigify.transfer_fk_to_ik", text='IK2FK Action', icon='ACTION_TWEAK')
781 row.operator("rigify.transfer_ik_to_fk", text='FK2IK Action', icon='ACTION_TWEAK')
783 row = self.layout.row(align=True)
784 row.operator("rigify.clear_animation", text="Clear IK Action", icon='CANCEL').anim_type = "IK"
785 row.operator("rigify.clear_animation", text="Clear FK Action", icon='CANCEL').anim_type = "FK"
787 row = self.layout.row(align=True)
788 op = row.operator("rigify.rotation_pole", icon='FORCE_HARMONIC', text='Switch to pole')
789 op.value = True
790 op.toggle = False
791 op.bake = True
792 op = row.operator("rigify.rotation_pole", icon='FORCE_MAGNETIC', text='Switch to rotation')
793 op.value = False
794 op.toggle = False
795 op.bake = True
796 RIGIFY_OT_get_frame_range.draw_range_ui(context, self.layout)
799 def rigify_report_exception(operator, exception):
800 import traceback
801 import sys
802 import os
803 # find the non-utils module name where the error happened
804 # hint, this is the metarig type!
805 _exception_type, _exception_value, exception_traceback = sys.exc_info()
806 fns = [item.filename for item in traceback.extract_tb(exception_traceback)]
807 fns_rig = [fn for fn in fns if os.path.basename(os.path.dirname(fn)) != 'utils']
808 fn = fns_rig[-1]
809 fn = os.path.basename(fn)
810 fn = os.path.splitext(fn)[0]
811 message = []
812 if fn.startswith("__"):
813 message.append("Incorrect armature...")
814 else:
815 message.append("Incorrect armature for type '%s'" % fn)
816 message.append(exception.message)
818 message.reverse() # XXX - stupid! menu's are upside down!
820 operator.report({'ERROR'}, '\n'.join(message))
823 class LayerInit(bpy.types.Operator):
824 """Initialize armature rigify layers"""
826 bl_idname = "pose.rigify_layer_init"
827 bl_label = "Add Rigify Layers"
828 bl_options = {'UNDO', 'INTERNAL'}
830 def execute(self, context):
831 obj = verify_armature_obj(context.object)
832 arm = obj.data
833 rigify_layers = get_rigify_layers(arm)
834 for i in range(1 + len(rigify_layers), 30):
835 rigify_layers.add()
836 rigify_layers[28].name = 'Root'
837 rigify_layers[28].row = 14
838 return {'FINISHED'}
841 def is_metarig(obj):
842 if not (obj and obj.data and obj.type == 'ARMATURE'):
843 return False
844 if 'rig_id' in obj.data:
845 return False
846 for b in obj.pose.bones:
847 if b.rigify_type != "":
848 return True
849 return False
852 class Generate(bpy.types.Operator):
853 """Generates a rig from the active metarig armature"""
855 bl_idname = "pose.rigify_generate"
856 bl_label = "Rigify Generate Rig"
857 bl_options = {'UNDO'}
858 bl_description = 'Generates a rig from the active metarig armature'
860 @classmethod
861 def poll(cls, context):
862 return is_metarig(context.object)
864 def execute(self, context):
865 metarig = verify_armature_obj(context.object)
866 try:
867 generate.generate_rig(context, metarig)
868 except MetarigError as rig_exception:
869 import traceback
870 traceback.print_exc()
872 rigify_report_exception(self, rig_exception)
873 except Exception as rig_exception:
874 import traceback
875 traceback.print_exc()
877 self.report({'ERROR'}, 'Generation has thrown an exception: ' + str(rig_exception))
878 else:
879 target_rig = get_rigify_target_rig(metarig.data)
880 self.report({'INFO'}, f'Successfully generated: "{target_rig.name}"')
881 finally:
882 bpy.ops.object.mode_set(mode='OBJECT')
884 return {'FINISHED'}
887 class UpgradeMetarigTypes(bpy.types.Operator):
888 """Upgrades metarig bones rigify_types"""
890 bl_idname = "pose.rigify_upgrade_types"
891 bl_label = "Rigify Upgrade Metarig Types"
892 bl_description = 'Upgrades the rigify types on the active metarig armature'
893 bl_options = {'UNDO'}
895 def execute(self, context):
896 for obj in bpy.data.objects:
897 if type(obj.data) == bpy.types.Armature:
898 upgrade_metarig_types(obj)
899 return {'FINISHED'}
902 class Sample(bpy.types.Operator):
903 """Create a sample metarig to be modified before generating the final rig"""
905 bl_idname = "armature.metarig_sample_add"
906 bl_label = "Add Metarig Sample"
907 bl_options = {'UNDO'}
909 metarig_type: StringProperty(
910 name="Type",
911 description="Name of the rig type to generate a sample of",
912 maxlen=128,
913 options={'SKIP_SAVE'}
916 @classmethod
917 def poll(cls, context):
918 return context.mode == 'EDIT_ARMATURE'
920 def draw(self, context):
921 layout = self.layout
922 layout.use_property_split = True
923 layout.use_property_decorate = False
924 col = layout.column()
925 build_type_list(context, get_rigify_types(context.window_manager))
926 col.prop(context.object.data, "active_feature_set")
927 col.prop_search(self, "metarig_type", context.window_manager, "rigify_types")
929 def invoke(self, context, event):
930 if self.metarig_type == "":
931 return context.window_manager.invoke_props_dialog(self)
932 return self.execute(context)
934 def execute(self, context):
935 if self.metarig_type == "":
936 self.report({'ERROR'}, "You must select a rig type to create a sample of.")
937 return {'CANCELLED'}
938 try:
939 rig = rig_lists.rigs[self.metarig_type]["module"]
940 create_sample = rig.create_sample
941 except (ImportError, AttributeError, KeyError):
942 raise Exception("rig type '" + self.metarig_type + "' has no sample.")
943 else:
944 create_sample(context.active_object)
945 finally:
946 bpy.ops.object.mode_set(mode='EDIT')
948 return {'FINISHED'}
951 class EncodeMetarig(bpy.types.Operator):
952 """Creates Python code that will generate the selected metarig"""
953 bl_idname = "armature.rigify_encode_metarig"
954 bl_label = "Rigify Encode Metarig"
955 bl_options = {'UNDO'}
957 @classmethod
958 def poll(cls, context):
959 return context.mode == 'EDIT_ARMATURE' and is_metarig(context.object)
961 def execute(self, context):
962 name = "metarig.py"
964 if name in bpy.data.texts:
965 text_block = bpy.data.texts[name]
966 text_block.clear()
967 else:
968 text_block = bpy.data.texts.new(name)
970 obj = verify_armature_obj(context.active_object)
971 text = write_metarig(obj, layers=True, func_name="create", groups=True, widgets=True)
972 text_block.write(text)
973 bpy.ops.object.mode_set(mode='EDIT')
974 self.report({'INFO'}, f"Metarig written to text datablock: {text_block.name}")
975 return {'FINISHED'}
978 class EncodeMetarigSample(bpy.types.Operator):
979 """Creates Python code that will generate the selected metarig as a sample"""
980 bl_idname = "armature.rigify_encode_metarig_sample"
981 bl_label = "Rigify Encode Metarig Sample"
982 bl_options = {'UNDO'}
984 @classmethod
985 def poll(cls, context):
986 return context.mode == 'EDIT_ARMATURE' and is_metarig(context.object)
988 def execute(self, context):
989 name = "metarig_sample.py"
991 if name in bpy.data.texts:
992 text_block = bpy.data.texts[name]
993 text_block.clear()
994 else:
995 text_block = bpy.data.texts.new(name)
997 obj = verify_armature_obj(context.active_object)
998 text = write_metarig(obj, layers=False, func_name="create_sample")
999 text_block.write(text)
1000 bpy.ops.object.mode_set(mode='EDIT')
1002 self.report({'INFO'}, f"Metarig Sample written to text datablock: {text_block.name}")
1003 return {'FINISHED'}
1006 # noinspection PyPep8Naming
1007 class VIEW3D_MT_rigify(bpy.types.Menu):
1008 bl_label = "Rigify"
1009 bl_idname = "VIEW3D_MT_rigify"
1011 append: Callable
1012 remove: Callable
1014 def draw(self, context):
1015 layout = self.layout
1016 obj = verify_armature_obj(context.object)
1017 target_rig = get_rigify_target_rig(obj.data)
1019 text = "Re-Generate Rig" if target_rig else "Generate Rig"
1020 layout.operator(Generate.bl_idname, text=text)
1022 if context.mode == 'EDIT_ARMATURE':
1023 layout.separator()
1024 layout.operator(Sample.bl_idname)
1025 layout.separator()
1026 layout.operator(EncodeMetarig.bl_idname, text="Encode Metarig")
1027 layout.operator(EncodeMetarigSample.bl_idname, text="Encode Metarig Sample")
1030 def draw_rigify_menu(self, context):
1031 if is_metarig(context.object):
1032 self.layout.menu(VIEW3D_MT_rigify.bl_idname)
1035 class EncodeWidget(bpy.types.Operator):
1036 """ Creates Python code that will generate the selected metarig.
1038 bl_idname = "mesh.rigify_encode_mesh_widget"
1039 bl_label = "Rigify Encode Widget"
1040 bl_options = {'UNDO'}
1042 @classmethod
1043 def poll(cls, context):
1044 return context.mode == 'EDIT_MESH'
1046 def execute(self, context):
1047 name = "widget.py"
1049 if name in bpy.data.texts:
1050 text_block = bpy.data.texts[name]
1051 text_block.clear()
1052 else:
1053 text_block = bpy.data.texts.new(name)
1055 text = write_widget(context.active_object)
1056 text_block.write(text)
1057 bpy.ops.object.mode_set(mode='EDIT')
1059 return {'FINISHED'}
1062 def draw_mesh_edit_menu(self, _context: bpy.types.Context):
1063 self.layout.operator(EncodeWidget.bl_idname)
1064 self.layout.separator()
1067 def fk_to_ik(rig: ArmatureObject, window='ALL'):
1068 scn = bpy.context.scene
1069 id_store = bpy.context.window_manager
1071 rig_id = rig.data['rig_id']
1072 leg_ik2fk = eval('bpy.ops.pose.rigify_leg_ik2fk_' + rig_id)
1073 arm_ik2fk = eval('bpy.ops.pose.rigify_arm_ik2fk_' + rig_id)
1074 limb_generated_names = get_limb_generated_names(rig)
1076 if window == 'ALL':
1077 frames = get_keyed_frames_in_range(bpy.context, rig)
1078 elif window == 'CURRENT':
1079 frames = [scn.frame_current]
1080 else:
1081 frames = [scn.frame_current]
1083 only_selected = get_transfer_only_selected(id_store)
1085 if not only_selected:
1086 pose_bones = rig.pose.bones
1087 bpy.ops.pose.select_all(action='DESELECT')
1088 else:
1089 pose_bones = bpy.context.selected_pose_bones
1090 bpy.ops.pose.select_all(action='DESELECT')
1092 for b in pose_bones:
1093 for group in limb_generated_names:
1094 if b.name in limb_generated_names[group].values() or b.name in limb_generated_names[group]['controls']\
1095 or b.name in limb_generated_names[group]['ik_ctrl']:
1096 names = limb_generated_names[group]
1097 if names['limb_type'] == 'arm':
1098 func = arm_ik2fk
1099 controls = names['controls']
1100 ik_ctrl = names['ik_ctrl']
1101 # fk_ctrl = names['fk_ctrl']
1102 parent = names['parent']
1103 pole = names['pole']
1104 rig.pose.bones[controls[0]].bone.select = True
1105 rig.pose.bones[controls[4]].bone.select = True
1106 rig.pose.bones[pole].bone.select = True
1107 rig.pose.bones[parent].bone.select = True
1108 kwargs = {'uarm_fk': controls[1], 'farm_fk': controls[2], 'hand_fk': controls[3],
1109 'uarm_ik': controls[0], 'farm_ik': ik_ctrl[1], 'hand_ik': controls[4],
1110 'pole': pole, 'main_parent': parent}
1111 args = (controls[0], controls[1], controls[2], controls[3],
1112 controls[4], pole, parent)
1113 else:
1114 func = leg_ik2fk
1115 controls = names['controls']
1116 ik_ctrl = names['ik_ctrl']
1117 # fk_ctrl = names['fk_ctrl']
1118 parent = names['parent']
1119 pole = names['pole']
1120 rig.pose.bones[controls[0]].bone.select = True
1121 rig.pose.bones[controls[6]].bone.select = True
1122 rig.pose.bones[controls[5]].bone.select = True
1123 rig.pose.bones[pole].bone.select = True
1124 rig.pose.bones[parent].bone.select = True
1125 # noinspection SpellCheckingInspection
1126 kwargs = {'thigh_fk': controls[1], 'shin_fk': controls[2], 'foot_fk': controls[3],
1127 'mfoot_fk': controls[7], 'thigh_ik': controls[0], 'shin_ik': ik_ctrl[1],
1128 'foot_ik': controls[6], 'pole': pole, 'footroll': controls[5], 'mfoot_ik': ik_ctrl[2],
1129 'main_parent': parent}
1130 args = (controls[0], controls[1], controls[2], controls[3],
1131 controls[6], controls[5], pole, parent)
1133 for f in frames:
1134 if not bones_in_frame(f, rig, *args):
1135 continue
1136 scn.frame_set(f)
1137 func(**kwargs)
1138 bpy.ops.anim.keyframe_insert_menu(type='BUILTIN_KSI_VisualLocRot')
1139 bpy.ops.anim.keyframe_insert_menu(type='Scaling')
1141 bpy.ops.pose.select_all(action='DESELECT')
1142 limb_generated_names.pop(group)
1143 break
1146 def ik_to_fk(rig: ArmatureObject, window='ALL'):
1147 scn = bpy.context.scene
1148 id_store = bpy.context.window_manager
1150 rig_id = rig.data['rig_id']
1151 leg_fk2ik = eval('bpy.ops.pose.rigify_leg_fk2ik_' + rig_id)
1152 arm_fk2ik = eval('bpy.ops.pose.rigify_arm_fk2ik_' + rig_id)
1153 limb_generated_names = get_limb_generated_names(rig)
1155 if window == 'ALL':
1156 frames = get_keyed_frames_in_range(bpy.context, rig)
1157 elif window == 'CURRENT':
1158 frames = [scn.frame_current]
1159 else:
1160 frames = [scn.frame_current]
1162 only_selected = get_transfer_only_selected(id_store)
1164 if not only_selected:
1165 bpy.ops.pose.select_all(action='DESELECT')
1166 pose_bones = rig.pose.bones
1167 else:
1168 pose_bones = bpy.context.selected_pose_bones
1169 bpy.ops.pose.select_all(action='DESELECT')
1171 for b in pose_bones:
1172 for group in limb_generated_names:
1173 if b.name in limb_generated_names[group].values() or b.name in limb_generated_names[group]['controls']\
1174 or b.name in limb_generated_names[group]['ik_ctrl']:
1175 names = limb_generated_names[group]
1176 if names['limb_type'] == 'arm':
1177 func = arm_fk2ik
1178 controls = names['controls']
1179 ik_ctrl = names['ik_ctrl']
1180 # fk_ctrl = names['fk_ctrl']
1181 parent = names['parent']
1182 pole = names['pole']
1183 rig.pose.bones[controls[1]].bone.select = True
1184 rig.pose.bones[controls[2]].bone.select = True
1185 rig.pose.bones[controls[3]].bone.select = True
1186 kwargs = {'uarm_fk': controls[1], 'farm_fk': controls[2], 'hand_fk': controls[3],
1187 'uarm_ik': controls[0], 'farm_ik': ik_ctrl[1],
1188 'hand_ik': controls[4]}
1189 args = (controls[0], controls[1], controls[2], controls[3],
1190 controls[4], pole, parent)
1191 else:
1192 func = leg_fk2ik
1193 controls = names['controls']
1194 ik_ctrl = names['ik_ctrl']
1195 # fk_ctrl = names['fk_ctrl']
1196 parent = names['parent']
1197 pole = names['pole']
1198 rig.pose.bones[controls[1]].bone.select = True
1199 rig.pose.bones[controls[2]].bone.select = True
1200 rig.pose.bones[controls[3]].bone.select = True
1201 # noinspection SpellCheckingInspection
1202 kwargs = {'thigh_fk': controls[1], 'shin_fk': controls[2], 'foot_fk': controls[3],
1203 'mfoot_fk': controls[7], 'thigh_ik': controls[0], 'shin_ik': ik_ctrl[1],
1204 'foot_ik': ik_ctrl[2], 'mfoot_ik': ik_ctrl[2]}
1205 args = (controls[0], controls[1], controls[2], controls[3],
1206 controls[6], controls[5], pole, parent)
1208 for f in frames:
1209 if not bones_in_frame(f, rig, *args):
1210 continue
1211 scn.frame_set(f)
1212 func(**kwargs)
1213 bpy.ops.anim.keyframe_insert_menu(type='BUILTIN_KSI_VisualLocRot')
1214 bpy.ops.anim.keyframe_insert_menu(type='Scaling')
1216 bpy.ops.pose.select_all(action='DESELECT')
1217 limb_generated_names.pop(group)
1218 break
1221 def clear_animation(act, anim_type, names):
1222 bones = []
1223 for group in names:
1224 if names[group]['limb_type'] == 'arm':
1225 if anim_type == 'IK':
1226 bones.extend([names[group]['controls'][0], names[group]['controls'][4]])
1227 elif anim_type == 'FK':
1228 bones.extend([names[group]['controls'][1], names[group]['controls'][2], names[group]['controls'][3]])
1229 else:
1230 if anim_type == 'IK':
1231 bones.extend([names[group]['controls'][0], names[group]['controls'][6], names[group]['controls'][5],
1232 names[group]['controls'][4]])
1233 elif anim_type == 'FK':
1234 bones.extend([names[group]['controls'][1], names[group]['controls'][2], names[group]['controls'][3],
1235 names[group]['controls'][4]])
1236 f_curves = []
1237 for fcu in act.fcurves:
1238 words = fcu.data_path.split('"')
1239 if words[0] == "pose.bones[" and words[1] in bones:
1240 f_curves.append(fcu)
1242 if not f_curves:
1243 return
1245 for fcu in f_curves:
1246 act.fcurves.remove(fcu)
1248 # Put cleared bones back to rest pose
1249 bpy.ops.pose.loc_clear()
1250 bpy.ops.pose.rot_clear()
1251 bpy.ops.pose.scale_clear()
1253 # updateView3D()
1256 def rot_pole_toggle(rig: ArmatureObject, window='ALL', value=False, toggle=False, bake=False):
1257 scn = bpy.context.scene
1258 id_store = bpy.context.window_manager
1260 rig_id = rig.data['rig_id']
1261 leg_fk2ik = eval('bpy.ops.pose.rigify_leg_fk2ik_' + rig_id)
1262 arm_fk2ik = eval('bpy.ops.pose.rigify_arm_fk2ik_' + rig_id)
1263 leg_ik2fk = eval('bpy.ops.pose.rigify_leg_ik2fk_' + rig_id)
1264 arm_ik2fk = eval('bpy.ops.pose.rigify_arm_ik2fk_' + rig_id)
1265 limb_generated_names = get_limb_generated_names(rig)
1267 if window == 'ALL':
1268 frames = get_keyed_frames_in_range(bpy.context, rig)
1269 elif window == 'CURRENT':
1270 frames = [scn.frame_current]
1271 else:
1272 frames = [scn.frame_current]
1274 only_selected = get_transfer_only_selected(id_store)
1276 if not only_selected:
1277 bpy.ops.pose.select_all(action='DESELECT')
1278 pose_bones = rig.pose.bones
1279 else:
1280 pose_bones = bpy.context.selected_pose_bones
1281 bpy.ops.pose.select_all(action='DESELECT')
1283 for b in pose_bones:
1284 for group in limb_generated_names:
1285 names = limb_generated_names[group]
1287 if toggle:
1288 new_pole_vector_value = not rig.pose.bones[names['parent']]['pole_vector']
1289 else:
1290 new_pole_vector_value = value
1292 if b.name in names.values() or b.name in names['controls'] or b.name in names['ik_ctrl']:
1293 if names['limb_type'] == 'arm':
1294 func1 = arm_fk2ik
1295 func2 = arm_ik2fk
1296 controls = names['controls']
1297 ik_ctrl = names['ik_ctrl']
1298 # fk_ctrl = names['fk_ctrl']
1299 parent = names['parent']
1300 pole = names['pole']
1301 rig.pose.bones[controls[0]].bone.select = not new_pole_vector_value
1302 rig.pose.bones[controls[4]].bone.select = not new_pole_vector_value
1303 rig.pose.bones[parent].bone.select = not new_pole_vector_value
1304 rig.pose.bones[pole].bone.select = new_pole_vector_value
1306 kwargs1 = {'uarm_fk': controls[1], 'farm_fk': controls[2], 'hand_fk': controls[3],
1307 'uarm_ik': controls[0], 'farm_ik': ik_ctrl[1],
1308 'hand_ik': controls[4]}
1309 kwargs2 = {'uarm_fk': controls[1], 'farm_fk': controls[2], 'hand_fk': controls[3],
1310 'uarm_ik': controls[0], 'farm_ik': ik_ctrl[1], 'hand_ik': controls[4],
1311 'pole': pole, 'main_parent': parent}
1312 args = (controls[0], controls[4], pole, parent)
1313 else:
1314 func1 = leg_fk2ik
1315 func2 = leg_ik2fk
1316 controls = names['controls']
1317 ik_ctrl = names['ik_ctrl']
1318 # fk_ctrl = names['fk_ctrl']
1319 parent = names['parent']
1320 pole = names['pole']
1321 rig.pose.bones[controls[0]].bone.select = not new_pole_vector_value
1322 rig.pose.bones[controls[6]].bone.select = not new_pole_vector_value
1323 rig.pose.bones[controls[5]].bone.select = not new_pole_vector_value
1324 rig.pose.bones[parent].bone.select = not new_pole_vector_value
1325 rig.pose.bones[pole].bone.select = new_pole_vector_value
1327 # noinspection SpellCheckingInspection
1328 kwargs1 = {'thigh_fk': controls[1], 'shin_fk': controls[2], 'foot_fk': controls[3],
1329 'mfoot_fk': controls[7], 'thigh_ik': controls[0], 'shin_ik': ik_ctrl[1],
1330 'foot_ik': ik_ctrl[2], 'mfoot_ik': ik_ctrl[2]}
1331 # noinspection SpellCheckingInspection
1332 kwargs2 = {'thigh_fk': controls[1], 'shin_fk': controls[2], 'foot_fk': controls[3],
1333 'mfoot_fk': controls[7], 'thigh_ik': controls[0], 'shin_ik': ik_ctrl[1],
1334 'foot_ik': controls[6], 'pole': pole, 'footroll': controls[5], 'mfoot_ik': ik_ctrl[2],
1335 'main_parent': parent}
1336 args = (controls[0], controls[6], controls[5], pole, parent)
1338 for f in frames:
1339 if bake and not bones_in_frame(f, rig, *args):
1340 continue
1341 scn.frame_set(f)
1342 func1(**kwargs1)
1343 rig.pose.bones[names['parent']]['pole_vector'] = new_pole_vector_value
1344 func2(**kwargs2)
1345 if bake:
1346 bpy.ops.anim.keyframe_insert_menu(type='BUILTIN_KSI_VisualLocRot')
1347 bpy.ops.anim.keyframe_insert_menu(type='Scaling')
1348 overwrite_prop_animation(rig, rig.pose.bones[parent], 'pole_vector', new_pole_vector_value, [f])
1350 bpy.ops.pose.select_all(action='DESELECT')
1351 limb_generated_names.pop(group)
1352 break
1353 scn.frame_set(0)
1356 # noinspection PyPep8Naming
1357 class OBJECT_OT_IK2FK(bpy.types.Operator):
1358 """ Snaps IK limb on FK limb at current frame"""
1359 bl_idname = "rigify.ik2fk"
1360 bl_label = "IK2FK"
1361 bl_description = "Snaps IK limb on FK"
1362 bl_options = {'INTERNAL'}
1364 def execute(self, context):
1365 rig = verify_armature_obj(context.object)
1367 fk_to_ik(rig, window='CURRENT')
1369 return {'FINISHED'}
1372 # noinspection PyPep8Naming
1373 class OBJECT_OT_FK2IK(bpy.types.Operator):
1374 """ Snaps FK limb on IK limb at current frame"""
1375 bl_idname = "rigify.fk2ik"
1376 bl_label = "FK2IK"
1377 bl_description = "Snaps FK limb on IK"
1378 bl_options = {'INTERNAL'}
1380 def execute(self, context):
1381 rig = verify_armature_obj(context.object)
1383 ik_to_fk(rig, window='CURRENT')
1385 return {'FINISHED'}
1388 # noinspection PyPep8Naming
1389 class OBJECT_OT_TransferFKtoIK(bpy.types.Operator):
1390 """Transfers FK animation to IK"""
1391 bl_idname = "rigify.transfer_fk_to_ik"
1392 bl_label = "Transfer FK anim to IK"
1393 bl_description = "Transfer FK animation to IK bones"
1394 bl_options = {'INTERNAL'}
1396 def execute(self, context):
1397 rig = verify_armature_obj(context.object)
1399 fk_to_ik(rig)
1401 return {'FINISHED'}
1404 # noinspection PyPep8Naming
1405 class OBJECT_OT_TransferIKtoFK(bpy.types.Operator):
1406 """Transfers FK animation to IK"""
1407 bl_idname = "rigify.transfer_ik_to_fk"
1408 bl_label = "Transfer IK anim to FK"
1409 bl_description = "Transfer IK animation to FK bones"
1410 bl_options = {'INTERNAL'}
1412 def execute(self, context):
1413 rig = verify_armature_obj(context.object)
1415 ik_to_fk(rig)
1417 return {'FINISHED'}
1420 # noinspection PyPep8Naming
1421 class OBJECT_OT_ClearAnimation(bpy.types.Operator):
1422 bl_idname = "rigify.clear_animation"
1423 bl_label = "Clear Animation"
1424 bl_description = "Clear Animation For FK or IK Bones"
1425 bl_options = {'INTERNAL'}
1427 anim_type: StringProperty()
1429 def execute(self, context):
1430 rig = verify_armature_obj(context.object)
1432 if not rig.animation_data:
1433 return {'FINISHED'}
1435 act = rig.animation_data.action
1436 if not act:
1437 return {'FINISHED'}
1439 clear_animation(act, self.anim_type, names=get_limb_generated_names(rig))
1440 return {'FINISHED'}
1443 # noinspection PyPep8Naming
1444 class OBJECT_OT_Rot2Pole(bpy.types.Operator):
1445 bl_idname = "rigify.rotation_pole"
1446 bl_label = "Rotation - Pole toggle"
1447 bl_description = "Toggles IK chain between rotation and pole target"
1448 bl_options = {'INTERNAL'}
1450 bone_name: StringProperty(default='')
1451 window: StringProperty(default='ALL')
1452 toggle: BoolProperty(default=True)
1453 value: BoolProperty(default=True)
1454 bake: BoolProperty(default=True)
1456 def execute(self, context):
1457 rig = verify_armature_obj(context.object)
1459 if self.bone_name:
1460 bpy.ops.pose.select_all(action='DESELECT')
1461 rig.pose.bones[self.bone_name].bone.select = True
1463 rot_pole_toggle(rig, window=self.window, toggle=self.toggle, value=self.value, bake=self.bake)
1464 return {'FINISHED'}
1467 ###############
1468 # Registering
1470 classes = (
1471 DATA_OT_rigify_add_bone_groups,
1472 DATA_OT_rigify_use_standard_colors,
1473 DATA_OT_rigify_apply_selection_colors,
1474 DATA_OT_rigify_bone_group_add,
1475 DATA_OT_rigify_bone_group_add_theme,
1476 DATA_OT_rigify_bone_group_remove,
1477 DATA_OT_rigify_bone_group_remove_all,
1478 DATA_UL_rigify_bone_groups,
1479 DATA_MT_rigify_bone_groups_context_menu,
1480 DATA_PT_rigify,
1481 DATA_PT_rigify_advanced,
1482 DATA_PT_rigify_bone_groups,
1483 DATA_PT_rigify_layer_names,
1484 DATA_PT_rigify_samples,
1485 BONE_PT_rigify_buttons,
1486 VIEW3D_PT_rigify_animation_tools,
1487 VIEW3D_PT_tools_rigify_dev,
1488 LayerInit,
1489 Generate,
1490 UpgradeMetarigTypes,
1491 Sample,
1492 VIEW3D_MT_rigify,
1493 EncodeMetarig,
1494 EncodeMetarigSample,
1495 EncodeWidget,
1496 OBJECT_OT_FK2IK,
1497 OBJECT_OT_IK2FK,
1498 OBJECT_OT_TransferFKtoIK,
1499 OBJECT_OT_TransferIKtoFK,
1500 OBJECT_OT_ClearAnimation,
1501 OBJECT_OT_Rot2Pole,
1505 def register():
1506 from bpy.utils import register_class
1508 animation_register()
1510 # Classes.
1511 for cls in classes:
1512 register_class(cls)
1514 bpy.types.VIEW3D_MT_editor_menus.append(draw_rigify_menu)
1515 bpy.types.VIEW3D_MT_edit_mesh.prepend(draw_mesh_edit_menu)
1517 # Sub-modules.
1518 rot_mode.register()
1521 def unregister():
1522 from bpy.utils import unregister_class
1524 # Sub-modules.
1525 rot_mode.unregister()
1527 # Classes.
1528 for cls in classes:
1529 unregister_class(cls)
1531 bpy.types.VIEW3D_MT_editor_menus.remove(draw_rigify_menu)
1532 bpy.types.VIEW3D_MT_edit_mesh.remove(draw_mesh_edit_menu)
1534 animation_unregister()