AnimAll: Rename UVMap and Vertex Group settings again
[blender-addons.git] / rigify / ui.py
blob68cfd330ff4561887eee9b4183fd6027779ef347
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 mathutils import Color
13 from .utils.errors import MetarigError
14 from .utils.rig import write_metarig
15 from .utils.widgets import write_widget
16 from .utils.naming import unique_name
17 from .utils.rig import upgradeMetarigTypes, outdated_types
19 from .rigs.utils import get_limb_generated_names
21 from .utils.animation import get_keyed_frames_in_range, bones_in_frame, overwrite_prop_animation
22 from .utils.animation import RIGIFY_OT_get_frame_range
24 from .utils.animation import register as animation_register
25 from .utils.animation import unregister as animation_unregister
27 from . import base_rig
28 from . import rig_lists
29 from . import generate
30 from . import rot_mode
31 from . import feature_set_list
34 def build_type_list(context, rigify_types):
35 rigify_types.clear()
37 for r in sorted(rig_lists.rigs):
38 if (context.object.data.active_feature_set in ('all', rig_lists.rigs[r]['feature_set'])
39 or len(feature_set_list.get_enabled_modules_names()) == 0
41 a = rigify_types.add()
42 a.name = r
45 class DATA_PT_rigify(bpy.types.Panel):
46 bl_label = "Rigify"
47 bl_space_type = 'PROPERTIES'
48 bl_region_type = 'WINDOW'
49 bl_context = "data"
51 @classmethod
52 def poll(cls, context):
53 obj = context.object
54 if not context.object:
55 return False
56 return obj.type == 'ARMATURE' \
57 and obj.data.get("rig_id") is None
59 def draw(self, context):
60 C = context
61 layout = self.layout
62 obj = C.object
64 WARNING = "Warning: Some features may change after generation"
65 show_warning = False
66 show_update_metarig = False
67 show_not_updatable = False
68 show_upgrade_face = False
70 check_props = ['IK_follow', 'root/parent', 'FK_limb_follow', 'IK_Stretch']
72 for posebone in obj.pose.bones:
73 bone = posebone.bone
74 if not bone:
75 # If we are in edit mode and the bone was just created,
76 # a pose bone won't exist yet.
77 continue
78 if bone.layers[30] and (list(set(posebone.keys()) & set(check_props))):
79 show_warning = True
80 break
82 for b in obj.pose.bones:
83 if b.rigify_type in outdated_types.keys():
84 old_bone = b.name
85 old_rig = b.rigify_type
86 if outdated_types[b.rigify_type]:
87 show_update_metarig = True
88 else:
89 show_update_metarig = False
90 show_not_updatable = True
91 break
92 elif b.rigify_type == 'faces.super_face':
93 show_upgrade_face = True
95 if show_warning:
96 layout.label(text=WARNING, icon='ERROR')
98 enable_generate = not (show_not_updatable or show_update_metarig)
100 if show_not_updatable:
101 layout.label(text="WARNING: This metarig contains deprecated rigify rig-types and cannot be upgraded automatically.", icon='ERROR')
102 layout.label(text="("+old_rig+" on bone "+old_bone+")")
103 elif show_update_metarig:
104 layout.label(text="This metarig contains old rig-types that can be automatically upgraded to benefit of rigify's new features.", icon='ERROR')
105 layout.label(text="("+old_rig+" on bone "+old_bone+")")
106 layout.operator("pose.rigify_upgrade_types", text="Upgrade Metarig")
107 elif show_upgrade_face:
108 layout.label(text="This metarig uses the old face rig.", icon='INFO')
109 layout.operator("pose.rigify_upgrade_face")
111 row = layout.row()
112 # Rig type field
114 col = layout.column(align=True)
115 col.active = (not 'rig_id' in C.object.data)
117 col.separator()
118 row = col.row()
119 text = "Re-Generate Rig" if obj.data.rigify_target_rig else "Generate Rig"
120 row.operator("pose.rigify_generate", text=text, icon='POSE_HLT')
121 row.enabled = enable_generate
124 class DATA_PT_rigify_advanced(bpy.types.Panel):
125 bl_space_type = 'PROPERTIES'
126 bl_region_type = 'WINDOW'
127 bl_context = "data"
128 bl_label = "Advanced"
129 bl_parent_id = 'DATA_PT_rigify'
130 bl_options = {'DEFAULT_CLOSED'}
132 def draw(self, context):
133 layout = self.layout
134 layout.use_property_split = True
135 layout.use_property_decorate = False
137 armature_id_store = context.object.data
139 col = layout.column()
140 col.row().prop(armature_id_store, "rigify_target_rig", text="Target Rig")
141 col.row().prop(armature_id_store, "rigify_rig_ui", text="Rig UI Script")
142 col.separator()
143 col.row().prop(armature_id_store, "rigify_widgets_collection")
144 col.row().prop(armature_id_store, "rigify_force_widget_update")
145 col.row().prop(armature_id_store, "rigify_mirror_widgets")
146 col.separator()
147 col.row().prop(armature_id_store, "rigify_finalize_script", text="Run Script")
150 class DATA_PT_rigify_samples(bpy.types.Panel):
151 bl_label = "Samples"
152 bl_space_type = 'PROPERTIES'
153 bl_region_type = 'WINDOW'
154 bl_context = "data"
155 bl_parent_id = "DATA_PT_rigify"
156 bl_options = {'DEFAULT_CLOSED'}
158 @classmethod
159 def poll(cls, context):
160 obj = context.object
161 if not obj:
162 return False
163 return obj.type == 'ARMATURE' \
164 and obj.data.get("rig_id") is None \
165 and obj.mode == 'EDIT'
167 def draw(self, context):
168 layout = self.layout
169 layout.use_property_split = True
170 layout.use_property_decorate = False
171 obj = context.object
172 id_store = context.window_manager
174 # Build types list
175 build_type_list(context, id_store.rigify_types)
177 if id_store.rigify_active_type > len(id_store.rigify_types):
178 id_store.rigify_active_type = 0
180 # Rig type list
181 if len(feature_set_list.get_enabled_modules_names()) > 0:
182 row = layout.row()
183 row.prop(context.object.data, "active_feature_set")
184 row = layout.row()
185 row.template_list("UI_UL_list", "rigify_types", id_store, "rigify_types", id_store, 'rigify_active_type')
187 props = layout.operator("armature.metarig_sample_add", text="Add sample")
188 props.metarig_type = id_store.rigify_types[id_store.rigify_active_type].name
191 class DATA_PT_rigify_layer_names(bpy.types.Panel):
192 bl_label = "Layer Names"
193 bl_space_type = 'PROPERTIES'
194 bl_region_type = 'WINDOW'
195 bl_context = "data"
196 bl_options = {'DEFAULT_CLOSED'}
197 bl_parent_id = "DATA_PT_rigify"
199 @classmethod
200 def poll(cls, context):
201 if not context.object:
202 return False
203 return context.object.type == 'ARMATURE' and context.active_object.data.get("rig_id") is None
205 def draw(self, context):
206 layout = self.layout
207 obj = context.object
208 arm = obj.data
210 # Ensure that the layers exist
211 if 0:
212 for i in range(1 + len(arm.rigify_layers), 29):
213 arm.rigify_layers.add()
214 else:
215 # Can't add while drawing, just use button
216 if len(arm.rigify_layers) < 29:
217 layout.operator("pose.rigify_layer_init")
218 return
220 # UI
221 main_row = layout.row(align=True).split(factor=0.05)
222 col1 = main_row.column()
223 col2 = main_row.column()
224 col1.label()
225 for i in range(32):
226 if i == 16 or i == 29:
227 col1.label()
228 col1.label(text=str(i))
230 for i, rigify_layer in enumerate(arm.rigify_layers):
231 # note: rigify_layer == arm.rigify_layers[i]
232 if (i % 16) == 0:
233 col = col2.column()
234 if i == 0:
235 col.label(text="Top Row:")
236 else:
237 col.label(text="Bottom Row:")
238 if (i % 8) == 0:
239 col = col2.column()
240 if i != 28:
241 row = col.row(align=True)
242 icon = 'RESTRICT_VIEW_OFF' if arm.layers[i] else 'RESTRICT_VIEW_ON'
243 row.prop(arm, "layers", index=i, text="", toggle=True, icon=icon)
244 #row.prop(arm, "layers", index=i, text="Layer %d" % (i + 1), toggle=True, icon=icon)
245 row.prop(rigify_layer, "name", text="")
246 row.prop(rigify_layer, "row", text="UI Row")
247 icon = 'RADIOBUT_ON' if rigify_layer.selset else 'RADIOBUT_OFF'
248 row.prop(rigify_layer, "selset", text="", toggle=True, icon=icon)
249 row.prop(rigify_layer, "group", text="Bone Group")
250 else:
251 row = col.row(align=True)
253 icon = 'RESTRICT_VIEW_OFF' if arm.layers[i] else 'RESTRICT_VIEW_ON'
254 row.prop(arm, "layers", index=i, text="", toggle=True, icon=icon)
255 # row.prop(arm, "layers", index=i, text="Layer %d" % (i + 1), toggle=True, icon=icon)
256 row1 = row.split(align=True).row(align=True)
257 row1.prop(rigify_layer, "name", text="")
258 row1.prop(rigify_layer, "row", text="UI Row")
259 row1.enabled = False
260 icon = 'RADIOBUT_ON' if rigify_layer.selset else 'RADIOBUT_OFF'
261 row.prop(rigify_layer, "selset", text="", toggle=True, icon=icon)
262 row.prop(rigify_layer, "group", text="Bone Group")
263 if rigify_layer.group == 0:
264 row.label(text='None')
265 else:
266 row.label(text=arm.rigify_colors[rigify_layer.group-1].name)
268 col = col2.column()
269 col.label(text="Reserved:")
270 # reserved_names = {28: 'Root', 29: 'DEF', 30: 'MCH', 31: 'ORG'}
271 reserved_names = {29: 'DEF', 30: 'MCH', 31: 'ORG'}
272 # for i in range(28, 32):
273 for i in range(29, 32):
274 row = col.row(align=True)
275 icon = 'RESTRICT_VIEW_OFF' if arm.layers[i] else 'RESTRICT_VIEW_ON'
276 row.prop(arm, "layers", index=i, text="", toggle=True, icon=icon)
277 row.label(text=reserved_names[i])
280 class DATA_OT_rigify_add_bone_groups(bpy.types.Operator):
281 bl_idname = "armature.rigify_add_bone_groups"
282 bl_label = "Rigify Add Standard Bone Groups"
284 @classmethod
285 def poll(cls, context):
286 return context.object and context.object.type == 'ARMATURE'
288 def execute(self, context):
289 obj = context.object
290 armature = obj.data
291 if not hasattr(armature, 'rigify_colors'):
292 return {'FINISHED'}
294 groups = ['Root', 'IK', 'Special', 'Tweak', 'FK', 'Extra']
296 for g in groups:
297 if g in armature.rigify_colors.keys():
298 continue
300 armature.rigify_colors.add()
301 armature.rigify_colors[-1].name = g
303 armature.rigify_colors[g].select = Color((0.3140000104904175, 0.7839999794960022, 1.0))
304 armature.rigify_colors[g].active = Color((0.5490000247955322, 1.0, 1.0))
305 armature.rigify_colors[g].standard_colors_lock = True
307 if g == "Root":
308 armature.rigify_colors[g].normal = Color((0.43529415130615234, 0.18431372940540314, 0.41568630933761597))
309 if g == "IK":
310 armature.rigify_colors[g].normal = Color((0.6039215922355652, 0.0, 0.0))
311 if g== "Special":
312 armature.rigify_colors[g].normal = Color((0.9568628072738647, 0.7882353663444519, 0.0470588281750679))
313 if g== "Tweak":
314 armature.rigify_colors[g].normal = Color((0.03921568766236305, 0.21176472306251526, 0.5803921818733215))
315 if g== "FK":
316 armature.rigify_colors[g].normal = Color((0.11764706671237946, 0.5686274766921997, 0.03529411926865578))
317 if g== "Extra":
318 armature.rigify_colors[g].normal = Color((0.9686275124549866, 0.250980406999588, 0.0941176563501358))
320 return {'FINISHED'}
323 class DATA_OT_rigify_use_standard_colors(bpy.types.Operator):
324 bl_idname = "armature.rigify_use_standard_colors"
325 bl_label = "Rigify Get active/select colors from current theme"
327 @classmethod
328 def poll(cls, context):
329 return context.object and context.object.type == 'ARMATURE'
331 def execute(self, context):
332 obj = context.object
333 armature = obj.data
334 if not hasattr(armature, 'rigify_colors'):
335 return {'FINISHED'}
337 current_theme = bpy.context.preferences.themes.items()[0][0]
338 theme = bpy.context.preferences.themes[current_theme]
340 armature.rigify_selection_colors.select = theme.view_3d.bone_pose
341 armature.rigify_selection_colors.active = theme.view_3d.bone_pose_active
343 # for col in armature.rigify_colors:
344 # col.select = theme.view_3d.bone_pose
345 # col.active = theme.view_3d.bone_pose_active
347 return {'FINISHED'}
350 class DATA_OT_rigify_apply_selection_colors(bpy.types.Operator):
351 bl_idname = "armature.rigify_apply_selection_colors"
352 bl_label = "Rigify Apply user defined active/select colors"
354 @classmethod
355 def poll(cls, context):
356 return context.object and context.object.type == 'ARMATURE'
358 def execute(self, context):
359 obj = context.object
360 armature = obj.data
361 if not hasattr(armature, 'rigify_colors'):
362 return {'FINISHED'}
364 #current_theme = bpy.context.preferences.themes.items()[0][0]
365 #theme = bpy.context.preferences.themes[current_theme]
367 for col in armature.rigify_colors:
368 col.select = armature.rigify_selection_colors.select
369 col.active = armature.rigify_selection_colors.active
371 return {'FINISHED'}
374 class DATA_OT_rigify_bone_group_add(bpy.types.Operator):
375 bl_idname = "armature.rigify_bone_group_add"
376 bl_label = "Rigify Add Bone Group color set"
378 @classmethod
379 def poll(cls, context):
380 return context.object and context.object.type == 'ARMATURE'
382 def execute(self, context):
383 obj = context.object
384 armature = obj.data
386 if hasattr(armature, 'rigify_colors'):
387 armature.rigify_colors.add()
388 armature.rigify_colors[-1].name = unique_name(armature.rigify_colors, 'Group')
390 current_theme = bpy.context.preferences.themes.items()[0][0]
391 theme = bpy.context.preferences.themes[current_theme]
393 armature.rigify_colors[-1].normal = theme.view_3d.wire
394 armature.rigify_colors[-1].normal.hsv = theme.view_3d.wire.hsv
395 armature.rigify_colors[-1].select = theme.view_3d.bone_pose
396 armature.rigify_colors[-1].select.hsv = theme.view_3d.bone_pose.hsv
397 armature.rigify_colors[-1].active = theme.view_3d.bone_pose_active
398 armature.rigify_colors[-1].active.hsv = theme.view_3d.bone_pose_active.hsv
400 return {'FINISHED'}
403 class DATA_OT_rigify_bone_group_add_theme(bpy.types.Operator):
404 bl_idname = "armature.rigify_bone_group_add_theme"
405 bl_label = "Rigify Add Bone Group color set from Theme"
406 bl_options = {"REGISTER", "UNDO"}
408 theme: EnumProperty(items=(
409 ('THEME01', 'THEME01', ''),
410 ('THEME02', 'THEME02', ''),
411 ('THEME03', 'THEME03', ''),
412 ('THEME04', 'THEME04', ''),
413 ('THEME05', 'THEME05', ''),
414 ('THEME06', 'THEME06', ''),
415 ('THEME07', 'THEME07', ''),
416 ('THEME08', 'THEME08', ''),
417 ('THEME09', 'THEME09', ''),
418 ('THEME10', 'THEME10', ''),
419 ('THEME11', 'THEME11', ''),
420 ('THEME12', 'THEME12', ''),
421 ('THEME13', 'THEME13', ''),
422 ('THEME14', 'THEME14', ''),
423 ('THEME15', 'THEME15', ''),
424 ('THEME16', 'THEME16', ''),
425 ('THEME17', 'THEME17', ''),
426 ('THEME18', 'THEME18', ''),
427 ('THEME19', 'THEME19', ''),
428 ('THEME20', 'THEME20', '')
430 name='Theme')
432 @classmethod
433 def poll(cls, context):
434 return context.object and context.object.type == 'ARMATURE'
436 def execute(self, context):
437 obj = context.object
438 armature = obj.data
440 if hasattr(armature, 'rigify_colors'):
442 if self.theme in armature.rigify_colors.keys():
443 return {'FINISHED'}
444 armature.rigify_colors.add()
445 armature.rigify_colors[-1].name = self.theme
447 id = int(self.theme[-2:]) - 1
449 theme_color_set = bpy.context.preferences.themes[0].bone_color_sets[id]
451 armature.rigify_colors[-1].normal = theme_color_set.normal
452 armature.rigify_colors[-1].select = theme_color_set.select
453 armature.rigify_colors[-1].active = theme_color_set.active
455 return {'FINISHED'}
458 class DATA_OT_rigify_bone_group_remove(bpy.types.Operator):
459 bl_idname = "armature.rigify_bone_group_remove"
460 bl_label = "Rigify Remove Bone Group color set"
462 idx: IntProperty()
464 @classmethod
465 def poll(cls, context):
466 return context.object and context.object.type == 'ARMATURE'
468 def execute(self, context):
469 obj = context.object
470 obj.data.rigify_colors.remove(self.idx)
472 # set layers references to 0
473 for l in obj.data.rigify_layers:
474 if l.group == self.idx + 1:
475 l.group = 0
476 elif l.group > self.idx + 1:
477 l.group -= 1
479 return {'FINISHED'}
482 class DATA_OT_rigify_bone_group_remove_all(bpy.types.Operator):
483 bl_idname = "armature.rigify_bone_group_remove_all"
484 bl_label = "Rigify Remove All Bone Groups"
486 @classmethod
487 def poll(cls, context):
488 return context.object and context.object.type == 'ARMATURE'
490 def execute(self, context):
491 obj = context.object
493 for i, col in enumerate(obj.data.rigify_colors):
494 obj.data.rigify_colors.remove(0)
495 # set layers references to 0
496 for l in obj.data.rigify_layers:
497 if l.group == i + 1:
498 l.group = 0
500 return {'FINISHED'}
503 class DATA_UL_rigify_bone_groups(bpy.types.UIList):
504 def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
505 row = layout.row(align=True)
506 row = row.split(factor=0.1)
507 row.label(text=str(index+1))
508 row = row.split(factor=0.7)
509 row.prop(item, "name", text='', emboss=False)
510 row = row.row(align=True)
511 icon = 'LOCKED' if item.standard_colors_lock else 'UNLOCKED'
512 #row.prop(item, "standard_colors_lock", text='', icon=icon)
513 row.prop(item, "normal", text='')
514 row2 = row.row(align=True)
515 row2.prop(item, "select", text='')
516 row2.prop(item, "active", text='')
517 #row2.enabled = not item.standard_colors_lock
518 row2.enabled = not bpy.context.object.data.rigify_colors_lock
521 class DATA_MT_rigify_bone_groups_context_menu(bpy.types.Menu):
522 bl_label = 'Rigify Bone Groups Specials'
524 def draw(self, context):
525 layout = self.layout
527 layout.operator('armature.rigify_bone_group_remove_all')
530 class DATA_PT_rigify_bone_groups(bpy.types.Panel):
531 bl_label = "Bone Groups"
532 bl_space_type = 'PROPERTIES'
533 bl_region_type = 'WINDOW'
534 bl_context = "data"
535 bl_options = {'DEFAULT_CLOSED'}
536 bl_parent_id = "DATA_PT_rigify"
538 @classmethod
539 def poll(cls, context):
540 if not context.object:
541 return False
542 return context.object.type == 'ARMATURE' and context.active_object.data.get("rig_id") is None
544 def draw(self, context):
545 obj = context.object
546 armature = obj.data
547 color_sets = obj.data.rigify_colors
548 idx = obj.data.rigify_colors_index
550 layout = self.layout
551 row = layout.row()
552 row.operator("armature.rigify_use_standard_colors", icon='FILE_REFRESH', text='')
553 row = row.row(align=True)
554 row.prop(armature.rigify_selection_colors, 'select', text='')
555 row.prop(armature.rigify_selection_colors, 'active', text='')
556 row = layout.row(align=True)
557 icon = 'LOCKED' if armature.rigify_colors_lock else 'UNLOCKED'
558 row.prop(armature, 'rigify_colors_lock', text = 'Unified select/active colors', icon=icon)
559 row.operator("armature.rigify_apply_selection_colors", icon='FILE_REFRESH', text='Apply')
560 row = layout.row()
561 row.template_list("DATA_UL_rigify_bone_groups", "", obj.data, "rigify_colors", obj.data, "rigify_colors_index")
563 col = row.column(align=True)
564 col.operator("armature.rigify_bone_group_add", icon='ADD', text="")
565 col.operator("armature.rigify_bone_group_remove", icon='REMOVE', text="").idx = obj.data.rigify_colors_index
566 col.menu("DATA_MT_rigify_bone_groups_context_menu", icon='DOWNARROW_HLT', text="")
567 row = layout.row()
568 row.prop(armature, 'rigify_theme_to_add', text = 'Theme')
569 op = row.operator("armature.rigify_bone_group_add_theme", text="Add From Theme")
570 op.theme = armature.rigify_theme_to_add
571 row = layout.row()
572 row.operator("armature.rigify_add_bone_groups", text="Add Standard")
575 class BONE_PT_rigify_buttons(bpy.types.Panel):
576 bl_label = "Rigify Type"
577 bl_space_type = 'PROPERTIES'
578 bl_region_type = 'WINDOW'
579 bl_context = "bone"
580 #bl_options = {'DEFAULT_OPEN'}
582 @classmethod
583 def poll(cls, context):
584 if not context.object:
585 return False
586 return context.object.type == 'ARMATURE' and context.active_pose_bone\
587 and context.active_object.data.get("rig_id") is None
589 def draw(self, context):
590 C = context
591 id_store = C.window_manager
592 bone = context.active_pose_bone
593 rig_name = str(context.active_pose_bone.rigify_type).replace(" ", "")
595 layout = self.layout
597 # Build types list
598 build_type_list(context, id_store.rigify_types)
600 # Rig type field
601 if len(feature_set_list.get_enabled_modules_names()) > 0:
602 row = layout.row()
603 row.prop(context.object.data, "active_feature_set")
604 row = layout.row()
605 row.prop_search(bone, "rigify_type", id_store, "rigify_types", text="Rig type")
607 # Rig type parameters / Rig type non-exist alert
608 if rig_name != "":
609 try:
610 rig = rig_lists.rigs[rig_name]['module']
611 except (ImportError, AttributeError, KeyError):
612 row = layout.row()
613 box = row.box()
614 box.label(text="ERROR: type \"%s\" does not exist!" % rig_name, icon='ERROR')
615 else:
616 if hasattr(rig.Rig, 'parameters_ui'):
617 rig = rig.Rig
619 try:
620 param_cb = rig.parameters_ui
622 # Ignore the known empty base method
623 if getattr(param_cb, '__func__', None) == base_rig.BaseRig.parameters_ui.__func__:
624 param_cb = None
625 except AttributeError:
626 param_cb = None
628 if param_cb is None:
629 col = layout.column()
630 col.label(text="No options")
631 else:
632 col = layout.column()
633 col.label(text="Options:")
634 box = layout.box()
635 param_cb(box, bone.rigify_parameters)
638 class VIEW3D_PT_tools_rigify_dev(bpy.types.Panel):
639 bl_label = "Rigify Dev Tools"
640 bl_space_type = 'VIEW_3D'
641 bl_region_type = 'UI'
642 bl_category = "Rigify"
644 @classmethod
645 def poll(cls, context):
646 return context.mode in ['EDIT_ARMATURE', 'EDIT_MESH']
648 @classmethod
649 def poll(cls, context):
650 return context.mode in ['EDIT_ARMATURE', 'EDIT_MESH']
652 def draw(self, context):
653 obj = context.active_object
654 if obj is not None:
655 if context.mode == 'EDIT_ARMATURE':
656 r = self.layout.row()
657 r.operator("armature.rigify_encode_metarig", text="Encode Metarig to Python")
658 r = self.layout.row()
659 r.operator("armature.rigify_encode_metarig_sample", text="Encode Sample to Python")
661 if context.mode == 'EDIT_MESH':
662 r = self.layout.row()
663 r.operator("mesh.rigify_encode_mesh_widget", text="Encode Mesh Widget to Python")
666 class VIEW3D_PT_rigify_animation_tools(bpy.types.Panel):
667 bl_label = "Rigify Animation Tools"
668 bl_context = "posemode"
669 bl_space_type = 'VIEW_3D'
670 bl_region_type = 'UI'
671 bl_category = "Rigify"
673 @classmethod
674 def poll(cls, context):
675 obj = context.active_object
676 if obj and obj.type == 'ARMATURE':
677 rig_id = obj.data.get("rig_id")
678 if rig_id is not None:
679 has_arm = hasattr(bpy.types, 'POSE_OT_rigify_arm_ik2fk_' + rig_id)
680 has_leg = hasattr(bpy.types, 'POSE_OT_rigify_leg_ik2fk_' + rig_id)
681 return has_arm or has_leg
683 return False
685 def draw(self, context):
686 obj = context.active_object
687 id_store = context.window_manager
688 if obj is not None:
689 row = self.layout.row()
691 if id_store.rigify_transfer_only_selected:
692 icon = 'OUTLINER_DATA_ARMATURE'
693 else:
694 icon = 'ARMATURE_DATA'
696 row.prop(id_store, 'rigify_transfer_only_selected', toggle=True, icon=icon)
698 row = self.layout.row(align=True)
699 row.operator("rigify.ik2fk", text='IK2FK Pose', icon='SNAP_ON')
700 row.operator("rigify.fk2ik", text='FK2IK Pose', icon='SNAP_ON')
702 row = self.layout.row(align=True)
703 row.operator("rigify.transfer_fk_to_ik", text='IK2FK Action', icon='ACTION_TWEAK')
704 row.operator("rigify.transfer_ik_to_fk", text='FK2IK Action', icon='ACTION_TWEAK')
706 row = self.layout.row(align=True)
707 row.operator("rigify.clear_animation", text="Clear IK Action", icon='CANCEL').anim_type = "IK"
708 row.operator("rigify.clear_animation", text="Clear FK Action", icon='CANCEL').anim_type = "FK"
710 row = self.layout.row(align=True)
711 op = row.operator("rigify.rotation_pole", icon='FORCE_HARMONIC', text='Switch to pole')
712 op.value = True
713 op.toggle = False
714 op.bake = True
715 op = row.operator("rigify.rotation_pole", icon='FORCE_MAGNETIC', text='Switch to rotation')
716 op.value = False
717 op.toggle = False
718 op.bake = True
719 RIGIFY_OT_get_frame_range.draw_range_ui(context, self.layout)
722 def rigify_report_exception(operator, exception):
723 import traceback
724 import sys
725 import os
726 # find the non-utils module name where the error happened
727 # hint, this is the metarig type!
728 exceptionType, exceptionValue, exceptionTraceback = sys.exc_info()
729 fns = [ item.filename for item in traceback.extract_tb(exceptionTraceback) ]
730 fns_rig = [ fn for fn in fns if os.path.basename(os.path.dirname(fn)) != 'utils' ]
731 fn = fns_rig[-1]
732 fn = os.path.basename(fn)
733 fn = os.path.splitext(fn)[0]
734 message = []
735 if fn.startswith("__"):
736 message.append("Incorrect armature...")
737 else:
738 message.append("Incorrect armature for type '%s'" % fn)
739 message.append(exception.message)
741 message.reverse() # XXX - stupid! menu's are upside down!
743 operator.report({'ERROR'}, '\n'.join(message))
746 class LayerInit(bpy.types.Operator):
747 """Initialize armature rigify layers"""
749 bl_idname = "pose.rigify_layer_init"
750 bl_label = "Add Rigify Layers"
751 bl_options = {'UNDO', 'INTERNAL'}
753 def execute(self, context):
754 obj = context.object
755 arm = obj.data
756 for i in range(1 + len(arm.rigify_layers), 30):
757 arm.rigify_layers.add()
758 arm.rigify_layers[28].name = 'Root'
759 arm.rigify_layers[28].row = 14
760 return {'FINISHED'}
763 def is_metarig(obj):
764 if not (obj and obj.data and obj.type == 'ARMATURE'):
765 return False
766 if 'rig_id' in obj.data:
767 return False
768 for b in obj.pose.bones:
769 if b.rigify_type != "":
770 return True
771 return False
773 class Generate(bpy.types.Operator):
774 """Generates a rig from the active metarig armature"""
776 bl_idname = "pose.rigify_generate"
777 bl_label = "Rigify Generate Rig"
778 bl_options = {'UNDO'}
779 bl_description = 'Generates a rig from the active metarig armature'
781 @classmethod
782 def poll(cls, context):
783 return is_metarig(context.object)
785 def execute(self, context):
786 metarig = context.object
787 try:
788 generate.generate_rig(context, metarig)
789 except MetarigError as rig_exception:
790 import traceback
791 traceback.print_exc()
793 rigify_report_exception(self, rig_exception)
794 except Exception as rig_exception:
795 import traceback
796 traceback.print_exc()
798 self.report({'ERROR'}, 'Generation has thrown an exception: ' + str(rig_exception))
799 else:
800 self.report({'INFO'}, 'Successfully generated: "' + metarig.data.rigify_target_rig.name + '"')
801 finally:
802 bpy.ops.object.mode_set(mode='OBJECT')
804 return {'FINISHED'}
807 class UpgradeMetarigTypes(bpy.types.Operator):
808 """Upgrades metarig bones rigify_types"""
810 bl_idname = "pose.rigify_upgrade_types"
811 bl_label = "Rigify Upgrade Metarig Types"
812 bl_description = 'Upgrades the rigify types on the active metarig armature'
813 bl_options = {'UNDO'}
815 def execute(self, context):
816 for obj in bpy.data.objects:
817 if type(obj.data) == bpy.types.Armature:
818 upgradeMetarigTypes(obj)
819 return {'FINISHED'}
820 class Sample(bpy.types.Operator):
821 """Create a sample metarig to be modified before generating the final rig"""
823 bl_idname = "armature.metarig_sample_add"
824 bl_label = "Add Metarig Sample"
825 bl_options = {'UNDO'}
827 metarig_type: StringProperty(
828 name="Type",
829 description="Name of the rig type to generate a sample of",
830 maxlen=128,
831 options={'SKIP_SAVE'}
834 @classmethod
835 def poll(cls, context):
836 return context.mode == 'EDIT_ARMATURE'
838 def draw(self, context):
839 layout = self.layout
840 layout.use_property_split = True
841 layout.use_property_decorate = False
842 col = layout.column()
843 build_type_list(context, context.window_manager.rigify_types)
844 col.prop(context.object.data, "active_feature_set")
845 col.prop_search(self, "metarig_type", context.window_manager, "rigify_types")
847 def invoke(self, context, event):
848 if self.metarig_type == "":
849 return context.window_manager.invoke_props_dialog(self)
850 return self.execute(context)
852 def execute(self, context):
853 if self.metarig_type == "":
854 self.report({'ERROR'}, "You must select a rig type to create a sample of.")
855 return {'CANCELLED'}
856 try:
857 rig = rig_lists.rigs[self.metarig_type]["module"]
858 create_sample = rig.create_sample
859 except (ImportError, AttributeError, KeyError):
860 raise Exception("rig type '" + self.metarig_type + "' has no sample.")
861 else:
862 create_sample(context.active_object)
863 finally:
864 bpy.ops.object.mode_set(mode='EDIT')
866 return {'FINISHED'}
869 class EncodeMetarig(bpy.types.Operator):
870 """Creates Python code that will generate the selected metarig"""
871 bl_idname = "armature.rigify_encode_metarig"
872 bl_label = "Rigify Encode Metarig"
873 bl_options = {'UNDO'}
875 @classmethod
876 def poll(self, context):
877 return context.mode == 'EDIT_ARMATURE' and is_metarig(context.object)
879 def execute(self, context):
880 name = "metarig.py"
882 if name in bpy.data.texts:
883 text_block = bpy.data.texts[name]
884 text_block.clear()
885 else:
886 text_block = bpy.data.texts.new(name)
888 text = write_metarig(context.active_object, layers=True, func_name="create", groups=True, widgets=True)
889 text_block.write(text)
890 bpy.ops.object.mode_set(mode='EDIT')
891 self.report({'INFO'}, f"Metarig written to text datablock: {text_block.name}")
892 return {'FINISHED'}
895 class EncodeMetarigSample(bpy.types.Operator):
896 """Creates Python code that will generate the selected metarig as a sample"""
897 bl_idname = "armature.rigify_encode_metarig_sample"
898 bl_label = "Rigify Encode Metarig Sample"
899 bl_options = {'UNDO'}
901 @classmethod
902 def poll(self, context):
903 return context.mode == 'EDIT_ARMATURE' and is_metarig(context.object)
905 def execute(self, context):
906 name = "metarig_sample.py"
908 if name in bpy.data.texts:
909 text_block = bpy.data.texts[name]
910 text_block.clear()
911 else:
912 text_block = bpy.data.texts.new(name)
914 text = write_metarig(context.active_object, layers=False, func_name="create_sample")
915 text_block.write(text)
916 bpy.ops.object.mode_set(mode='EDIT')
918 self.report({'INFO'}, f"Metarig Sample written to text datablock: {text_block.name}")
919 return {'FINISHED'}
922 class VIEW3D_MT_rigify(bpy.types.Menu):
923 bl_label = "Rigify"
924 bl_idname = "VIEW3D_MT_rigify"
926 def draw(self, context):
927 layout = self.layout
928 obj = context.object
930 text = "Re-Generate Rig" if obj.data.rigify_target_rig else "Generate Rig"
931 layout.operator(Generate.bl_idname, text=text)
933 if context.mode == 'EDIT_ARMATURE':
934 layout.separator()
935 layout.operator(Sample.bl_idname)
936 layout.separator()
937 layout.operator(EncodeMetarig.bl_idname, text="Encode Metarig")
938 layout.operator(EncodeMetarigSample.bl_idname, text="Encode Metarig Sample")
941 def draw_rigify_menu(self, context):
942 if is_metarig(context.object):
943 self.layout.menu(VIEW3D_MT_rigify.bl_idname)
945 class EncodeWidget(bpy.types.Operator):
946 """ Creates Python code that will generate the selected metarig.
948 bl_idname = "mesh.rigify_encode_mesh_widget"
949 bl_label = "Rigify Encode Widget"
950 bl_options = {'UNDO'}
952 @classmethod
953 def poll(self, context):
954 return context.mode == 'EDIT_MESH'
956 def execute(self, context):
957 name = "widget.py"
959 if name in bpy.data.texts:
960 text_block = bpy.data.texts[name]
961 text_block.clear()
962 else:
963 text_block = bpy.data.texts.new(name)
965 text = write_widget(context.active_object)
966 text_block.write(text)
967 bpy.ops.object.mode_set(mode='EDIT')
969 return {'FINISHED'}
971 def draw_mesh_edit_menu(self, context):
972 self.layout.operator(EncodeWidget.bl_idname)
973 self.layout.separator()
976 def FktoIk(rig, window='ALL'):
978 scn = bpy.context.scene
979 id_store = bpy.context.window_manager
981 rig_id = rig.data['rig_id']
982 leg_ik2fk = eval('bpy.ops.pose.rigify_leg_ik2fk_' + rig_id)
983 arm_ik2fk = eval('bpy.ops.pose.rigify_arm_ik2fk_' + rig_id)
984 limb_generated_names = get_limb_generated_names(rig)
986 if window == 'ALL':
987 frames = get_keyed_frames_in_range(bpy.context, rig)
988 elif window == 'CURRENT':
989 frames = [scn.frame_current]
990 else:
991 frames = [scn.frame_current]
993 if not id_store.rigify_transfer_only_selected:
994 pbones = rig.pose.bones
995 bpy.ops.pose.select_all(action='DESELECT')
996 else:
997 pbones = bpy.context.selected_pose_bones
998 bpy.ops.pose.select_all(action='DESELECT')
1000 for b in pbones:
1001 for group in limb_generated_names:
1002 if b.name in limb_generated_names[group].values() or b.name in limb_generated_names[group]['controls']\
1003 or b.name in limb_generated_names[group]['ik_ctrl']:
1004 names = limb_generated_names[group]
1005 if names['limb_type'] == 'arm':
1006 func = arm_ik2fk
1007 controls = names['controls']
1008 ik_ctrl = names['ik_ctrl']
1009 fk_ctrl = names['fk_ctrl']
1010 parent = names['parent']
1011 pole = names['pole']
1012 rig.pose.bones[controls[0]].bone.select = True
1013 rig.pose.bones[controls[4]].bone.select = True
1014 rig.pose.bones[pole].bone.select = True
1015 rig.pose.bones[parent].bone.select = True
1016 kwargs = {'uarm_fk': controls[1], 'farm_fk': controls[2], 'hand_fk': controls[3],
1017 'uarm_ik': controls[0], 'farm_ik': ik_ctrl[1], 'hand_ik': controls[4],
1018 'pole': pole, 'main_parent': parent}
1019 args = (controls[0], controls[1], controls[2], controls[3],
1020 controls[4], pole, parent)
1021 else:
1022 func = leg_ik2fk
1023 controls = names['controls']
1024 ik_ctrl = names['ik_ctrl']
1025 fk_ctrl = names['fk_ctrl']
1026 parent = names['parent']
1027 pole = names['pole']
1028 rig.pose.bones[controls[0]].bone.select = True
1029 rig.pose.bones[controls[6]].bone.select = True
1030 rig.pose.bones[controls[5]].bone.select = True
1031 rig.pose.bones[pole].bone.select = True
1032 rig.pose.bones[parent].bone.select = True
1033 kwargs = {'thigh_fk': controls[1], 'shin_fk': controls[2], 'foot_fk': controls[3],
1034 'mfoot_fk': controls[7], 'thigh_ik': controls[0], 'shin_ik': ik_ctrl[1],
1035 'foot_ik': controls[6], 'pole': pole, 'footroll': controls[5], 'mfoot_ik': ik_ctrl[2],
1036 'main_parent': parent}
1037 args = (controls[0], controls[1], controls[2], controls[3],
1038 controls[6], controls[5], pole, parent)
1040 for f in frames:
1041 if not bones_in_frame(f, rig, *args):
1042 continue
1043 scn.frame_set(f)
1044 func(**kwargs)
1045 bpy.ops.anim.keyframe_insert_menu(type='BUILTIN_KSI_VisualLocRot')
1046 bpy.ops.anim.keyframe_insert_menu(type='Scaling')
1048 bpy.ops.pose.select_all(action='DESELECT')
1049 limb_generated_names.pop(group)
1050 break
1053 def IktoFk(rig, window='ALL'):
1055 scn = bpy.context.scene
1056 id_store = bpy.context.window_manager
1058 rig_id = rig.data['rig_id']
1059 leg_fk2ik = eval('bpy.ops.pose.rigify_leg_fk2ik_' + rig_id)
1060 arm_fk2ik = eval('bpy.ops.pose.rigify_arm_fk2ik_' + rig_id)
1061 limb_generated_names = get_limb_generated_names(rig)
1063 if window == 'ALL':
1064 frames = get_keyed_frames_in_range(bpy.context, rig)
1065 elif window == 'CURRENT':
1066 frames = [scn.frame_current]
1067 else:
1068 frames = [scn.frame_current]
1070 if not id_store.rigify_transfer_only_selected:
1071 bpy.ops.pose.select_all(action='DESELECT')
1072 pbones = rig.pose.bones
1073 else:
1074 pbones = bpy.context.selected_pose_bones
1075 bpy.ops.pose.select_all(action='DESELECT')
1077 for b in pbones:
1078 for group in limb_generated_names:
1079 if b.name in limb_generated_names[group].values() or b.name in limb_generated_names[group]['controls']\
1080 or b.name in limb_generated_names[group]['ik_ctrl']:
1081 names = limb_generated_names[group]
1082 if names['limb_type'] == 'arm':
1083 func = arm_fk2ik
1084 controls = names['controls']
1085 ik_ctrl = names['ik_ctrl']
1086 fk_ctrl = names['fk_ctrl']
1087 parent = names['parent']
1088 pole = names['pole']
1089 rig.pose.bones[controls[1]].bone.select = True
1090 rig.pose.bones[controls[2]].bone.select = True
1091 rig.pose.bones[controls[3]].bone.select = True
1092 kwargs = {'uarm_fk': controls[1], 'farm_fk': controls[2], 'hand_fk': controls[3],
1093 'uarm_ik': controls[0], 'farm_ik': ik_ctrl[1],
1094 'hand_ik': controls[4]}
1095 args = (controls[0], controls[1], controls[2], controls[3],
1096 controls[4], pole, parent)
1097 else:
1098 func = leg_fk2ik
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[1]].bone.select = True
1105 rig.pose.bones[controls[2]].bone.select = True
1106 rig.pose.bones[controls[3]].bone.select = True
1107 kwargs = {'thigh_fk': controls[1], 'shin_fk': controls[2], 'foot_fk': controls[3],
1108 'mfoot_fk': controls[7], 'thigh_ik': controls[0], 'shin_ik': ik_ctrl[1],
1109 'foot_ik': ik_ctrl[2], 'mfoot_ik': ik_ctrl[2]}
1110 args = (controls[0], controls[1], controls[2], controls[3],
1111 controls[6], controls[5], pole, parent)
1113 for f in frames:
1114 if not bones_in_frame(f, rig, *args):
1115 continue
1116 scn.frame_set(f)
1117 func(**kwargs)
1118 bpy.ops.anim.keyframe_insert_menu(type='BUILTIN_KSI_VisualLocRot')
1119 bpy.ops.anim.keyframe_insert_menu(type='Scaling')
1121 bpy.ops.pose.select_all(action='DESELECT')
1122 limb_generated_names.pop(group)
1123 break
1126 def clearAnimation(act, anim_type, names):
1128 bones = []
1129 for group in names:
1130 if names[group]['limb_type'] == 'arm':
1131 if anim_type == 'IK':
1132 bones.extend([names[group]['controls'][0], names[group]['controls'][4]])
1133 elif anim_type == 'FK':
1134 bones.extend([names[group]['controls'][1], names[group]['controls'][2], names[group]['controls'][3]])
1135 else:
1136 if anim_type == 'IK':
1137 bones.extend([names[group]['controls'][0], names[group]['controls'][6], names[group]['controls'][5],
1138 names[group]['controls'][4]])
1139 elif anim_type == 'FK':
1140 bones.extend([names[group]['controls'][1], names[group]['controls'][2], names[group]['controls'][3],
1141 names[group]['controls'][4]])
1142 FCurves = []
1143 for fcu in act.fcurves:
1144 words = fcu.data_path.split('"')
1145 if (words[0] == "pose.bones[" and
1146 words[1] in bones):
1147 FCurves.append(fcu)
1149 if FCurves == []:
1150 return
1152 for fcu in FCurves:
1153 act.fcurves.remove(fcu)
1155 # Put cleared bones back to rest pose
1156 bpy.ops.pose.loc_clear()
1157 bpy.ops.pose.rot_clear()
1158 bpy.ops.pose.scale_clear()
1160 # updateView3D()
1163 def rotPoleToggle(rig, window='ALL', value=False, toggle=False, bake=False):
1165 scn = bpy.context.scene
1166 id_store = bpy.context.window_manager
1168 rig_id = rig.data['rig_id']
1169 leg_fk2ik = eval('bpy.ops.pose.rigify_leg_fk2ik_' + rig_id)
1170 arm_fk2ik = eval('bpy.ops.pose.rigify_arm_fk2ik_' + rig_id)
1171 leg_ik2fk = eval('bpy.ops.pose.rigify_leg_ik2fk_' + rig_id)
1172 arm_ik2fk = eval('bpy.ops.pose.rigify_arm_ik2fk_' + rig_id)
1173 limb_generated_names = get_limb_generated_names(rig)
1175 if window == 'ALL':
1176 frames = get_keyed_frames_in_range(bpy.context, rig)
1177 elif window == 'CURRENT':
1178 frames = [scn.frame_current]
1179 else:
1180 frames = [scn.frame_current]
1182 if not id_store.rigify_transfer_only_selected:
1183 bpy.ops.pose.select_all(action='DESELECT')
1184 pbones = rig.pose.bones
1185 else:
1186 pbones = bpy.context.selected_pose_bones
1187 bpy.ops.pose.select_all(action='DESELECT')
1189 for b in pbones:
1190 for group in limb_generated_names:
1191 names = limb_generated_names[group]
1193 if toggle:
1194 new_pole_vector_value = not rig.pose.bones[names['parent']]['pole_vector']
1195 else:
1196 new_pole_vector_value = value
1198 if b.name in names.values() or b.name in names['controls'] or b.name in names['ik_ctrl']:
1199 if names['limb_type'] == 'arm':
1200 func1 = arm_fk2ik
1201 func2 = arm_ik2fk
1202 controls = names['controls']
1203 ik_ctrl = names['ik_ctrl']
1204 fk_ctrl = names['fk_ctrl']
1205 parent = names['parent']
1206 pole = names['pole']
1207 rig.pose.bones[controls[0]].bone.select = not new_pole_vector_value
1208 rig.pose.bones[controls[4]].bone.select = not new_pole_vector_value
1209 rig.pose.bones[parent].bone.select = not new_pole_vector_value
1210 rig.pose.bones[pole].bone.select = new_pole_vector_value
1212 kwargs1 = {'uarm_fk': controls[1], 'farm_fk': controls[2], 'hand_fk': controls[3],
1213 'uarm_ik': controls[0], 'farm_ik': ik_ctrl[1],
1214 'hand_ik': controls[4]}
1215 kwargs2 = {'uarm_fk': controls[1], 'farm_fk': controls[2], 'hand_fk': controls[3],
1216 'uarm_ik': controls[0], 'farm_ik': ik_ctrl[1], 'hand_ik': controls[4],
1217 'pole': pole, 'main_parent': parent}
1218 args = (controls[0], controls[4], pole, parent)
1219 else:
1220 func1 = leg_fk2ik
1221 func2 = leg_ik2fk
1222 controls = names['controls']
1223 ik_ctrl = names['ik_ctrl']
1224 fk_ctrl = names['fk_ctrl']
1225 parent = names['parent']
1226 pole = names['pole']
1227 rig.pose.bones[controls[0]].bone.select = not new_pole_vector_value
1228 rig.pose.bones[controls[6]].bone.select = not new_pole_vector_value
1229 rig.pose.bones[controls[5]].bone.select = not new_pole_vector_value
1230 rig.pose.bones[parent].bone.select = not new_pole_vector_value
1231 rig.pose.bones[pole].bone.select = new_pole_vector_value
1233 kwargs1 = {'thigh_fk': controls[1], 'shin_fk': controls[2], 'foot_fk': controls[3],
1234 'mfoot_fk': controls[7], 'thigh_ik': controls[0], 'shin_ik': ik_ctrl[1],
1235 'foot_ik': ik_ctrl[2], 'mfoot_ik': ik_ctrl[2]}
1236 kwargs2 = {'thigh_fk': controls[1], 'shin_fk': controls[2], 'foot_fk': controls[3],
1237 'mfoot_fk': controls[7], 'thigh_ik': controls[0], 'shin_ik': ik_ctrl[1],
1238 'foot_ik': controls[6], 'pole': pole, 'footroll': controls[5], 'mfoot_ik': ik_ctrl[2],
1239 'main_parent': parent}
1240 args = (controls[0], controls[6], controls[5], pole, parent)
1242 for f in frames:
1243 if bake and not bones_in_frame(f, rig, *args):
1244 continue
1245 scn.frame_set(f)
1246 func1(**kwargs1)
1247 rig.pose.bones[names['parent']]['pole_vector'] = new_pole_vector_value
1248 func2(**kwargs2)
1249 if bake:
1250 bpy.ops.anim.keyframe_insert_menu(type='BUILTIN_KSI_VisualLocRot')
1251 bpy.ops.anim.keyframe_insert_menu(type='Scaling')
1252 overwrite_prop_animation(rig, rig.pose.bones[parent], 'pole_vector', new_pole_vector_value, [f])
1254 bpy.ops.pose.select_all(action='DESELECT')
1255 limb_generated_names.pop(group)
1256 break
1257 scn.frame_set(0)
1260 class OBJECT_OT_IK2FK(bpy.types.Operator):
1261 """ Snaps IK limb on FK limb at current frame"""
1262 bl_idname = "rigify.ik2fk"
1263 bl_label = "IK2FK"
1264 bl_description = "Snaps IK limb on FK"
1265 bl_options = {'INTERNAL'}
1267 def execute(self,context):
1268 rig = context.object
1269 id_store = context.window_manager
1271 FktoIk(rig, window='CURRENT')
1273 return {'FINISHED'}
1276 class OBJECT_OT_FK2IK(bpy.types.Operator):
1277 """ Snaps FK limb on IK limb at current frame"""
1278 bl_idname = "rigify.fk2ik"
1279 bl_label = "FK2IK"
1280 bl_description = "Snaps FK limb on IK"
1281 bl_options = {'INTERNAL'}
1283 def execute(self,context):
1284 rig = context.object
1286 IktoFk(rig, window='CURRENT')
1288 return {'FINISHED'}
1291 class OBJECT_OT_TransferFKtoIK(bpy.types.Operator):
1292 """Transfers FK animation to IK"""
1293 bl_idname = "rigify.transfer_fk_to_ik"
1294 bl_label = "Transfer FK anim to IK"
1295 bl_description = "Transfer FK animation to IK bones"
1296 bl_options = {'INTERNAL'}
1298 def execute(self, context):
1299 rig = context.object
1300 id_store = context.window_manager
1302 FktoIk(rig)
1304 return {'FINISHED'}
1307 class OBJECT_OT_TransferIKtoFK(bpy.types.Operator):
1308 """Transfers FK animation to IK"""
1309 bl_idname = "rigify.transfer_ik_to_fk"
1310 bl_label = "Transfer IK anim to FK"
1311 bl_description = "Transfer IK animation to FK bones"
1312 bl_options = {'INTERNAL'}
1314 def execute(self, context):
1315 rig = context.object
1317 IktoFk(rig)
1319 return {'FINISHED'}
1322 class OBJECT_OT_ClearAnimation(bpy.types.Operator):
1323 bl_idname = "rigify.clear_animation"
1324 bl_label = "Clear Animation"
1325 bl_description = "Clear Animation For FK or IK Bones"
1326 bl_options = {'INTERNAL'}
1328 anim_type: StringProperty()
1330 def execute(self, context):
1331 rig = context.object
1332 scn = context.scene
1333 if not rig.animation_data:
1334 return {'FINISHED'}
1335 act = rig.animation_data.action
1336 if not act:
1337 return {'FINISHED'}
1339 clearAnimation(act, self.anim_type, names=get_limb_generated_names(rig))
1340 return {'FINISHED'}
1343 class OBJECT_OT_Rot2Pole(bpy.types.Operator):
1344 bl_idname = "rigify.rotation_pole"
1345 bl_label = "Rotation - Pole toggle"
1346 bl_description = "Toggles IK chain between rotation and pole target"
1347 bl_options = {'INTERNAL'}
1349 bone_name: StringProperty(default='')
1350 window: StringProperty(default='ALL')
1351 toggle: BoolProperty(default=True)
1352 value: BoolProperty(default=True)
1353 bake: BoolProperty(default=True)
1355 def execute(self, context):
1356 rig = context.object
1358 if self.bone_name:
1359 bpy.ops.pose.select_all(action='DESELECT')
1360 rig.pose.bones[self.bone_name].bone.select = True
1362 rotPoleToggle(rig, window=self.window, toggle=self.toggle, value=self.value, bake=self.bake)
1363 return {'FINISHED'}
1366 ### Registering ###
1369 classes = (
1370 DATA_OT_rigify_add_bone_groups,
1371 DATA_OT_rigify_use_standard_colors,
1372 DATA_OT_rigify_apply_selection_colors,
1373 DATA_OT_rigify_bone_group_add,
1374 DATA_OT_rigify_bone_group_add_theme,
1375 DATA_OT_rigify_bone_group_remove,
1376 DATA_OT_rigify_bone_group_remove_all,
1377 DATA_UL_rigify_bone_groups,
1378 DATA_MT_rigify_bone_groups_context_menu,
1379 DATA_PT_rigify,
1380 DATA_PT_rigify_advanced,
1381 DATA_PT_rigify_bone_groups,
1382 DATA_PT_rigify_layer_names,
1383 DATA_PT_rigify_samples,
1384 BONE_PT_rigify_buttons,
1385 VIEW3D_PT_rigify_animation_tools,
1386 VIEW3D_PT_tools_rigify_dev,
1387 LayerInit,
1388 Generate,
1389 UpgradeMetarigTypes,
1390 Sample,
1391 VIEW3D_MT_rigify,
1392 EncodeMetarig,
1393 EncodeMetarigSample,
1394 EncodeWidget,
1395 OBJECT_OT_FK2IK,
1396 OBJECT_OT_IK2FK,
1397 OBJECT_OT_TransferFKtoIK,
1398 OBJECT_OT_TransferIKtoFK,
1399 OBJECT_OT_ClearAnimation,
1400 OBJECT_OT_Rot2Pole,
1404 def register():
1405 from bpy.utils import register_class
1407 animation_register()
1409 # Classes.
1410 for cls in classes:
1411 register_class(cls)
1413 bpy.types.VIEW3D_MT_editor_menus.append(draw_rigify_menu)
1414 bpy.types.VIEW3D_MT_edit_mesh.prepend(draw_mesh_edit_menu)
1416 # Sub-modules.
1417 rot_mode.register()
1420 def unregister():
1421 from bpy.utils import unregister_class
1423 # Sub-modules.
1424 rot_mode.unregister()
1426 # Classes.
1427 for cls in classes:
1428 unregister_class(cls)
1430 bpy.types.VIEW3D_MT_editor_menus.remove(draw_rigify_menu)
1431 bpy.types.VIEW3D_MT_edit_mesh.remove(draw_mesh_edit_menu)
1433 animation_unregister()