Cleanup: trailing space
[blender-addons.git] / rigify / ui.py
blob54ae4a70cf0b49242274f2af0bbf15f3a67f42dd
1 # SPDX-License-Identifier: GPL-2.0-or-later
3 # <pep8 compliant>
5 import bpy
6 from bpy.props import (
7 BoolProperty,
8 IntProperty,
9 EnumProperty,
10 StringProperty
13 from mathutils import Color
15 from .utils.errors import MetarigError
16 from .utils.rig import write_metarig
17 from .utils.widgets import write_widget
18 from .utils.naming import unique_name
19 from .utils.rig import upgradeMetarigTypes, outdated_types
21 from .rigs.utils import get_limb_generated_names
23 from .utils.animation import get_keyed_frames_in_range, bones_in_frame, overwrite_prop_animation
24 from .utils.animation import RIGIFY_OT_get_frame_range
26 from .utils.animation import register as animation_register
27 from .utils.animation import unregister as animation_unregister
29 from . import base_rig
30 from . import rig_lists
31 from . import generate
32 from . import rot_mode
33 from . import feature_set_list
36 def build_type_list(context, rigify_types):
37 rigify_types.clear()
39 for r in sorted(rig_lists.rigs):
40 if (context.object.data.active_feature_set in ('all', rig_lists.rigs[r]['feature_set'])
41 or len(feature_set_list.get_enabled_modules_names()) == 0
43 a = rigify_types.add()
44 a.name = r
47 class DATA_PT_rigify(bpy.types.Panel):
48 bl_label = "Rigify"
49 bl_space_type = 'PROPERTIES'
50 bl_region_type = 'WINDOW'
51 bl_context = "data"
53 @classmethod
54 def poll(cls, context):
55 obj = context.object
56 if not context.object:
57 return False
58 return obj.type == 'ARMATURE' \
59 and obj.data.get("rig_id") is None
61 def draw(self, context):
62 C = context
63 layout = self.layout
64 obj = C.object
66 WARNING = "Warning: Some features may change after generation"
67 show_warning = False
68 show_update_metarig = False
69 show_not_updatable = False
70 show_upgrade_face = False
72 check_props = ['IK_follow', 'root/parent', 'FK_limb_follow', 'IK_Stretch']
74 for posebone in obj.pose.bones:
75 bone = posebone.bone
76 if not bone:
77 # If we are in edit mode and the bone was just created,
78 # a pose bone won't exist yet.
79 continue
80 if bone.layers[30] and (list(set(posebone.keys()) & set(check_props))):
81 show_warning = True
82 break
84 for b in obj.pose.bones:
85 if b.rigify_type in outdated_types.keys():
86 old_bone = b.name
87 old_rig = b.rigify_type
88 if outdated_types[b.rigify_type]:
89 show_update_metarig = True
90 else:
91 show_update_metarig = False
92 show_not_updatable = True
93 break
94 elif b.rigify_type == 'faces.super_face':
95 show_upgrade_face = True
97 if show_warning:
98 layout.label(text=WARNING, icon='ERROR')
100 enable_generate = not (show_not_updatable or show_update_metarig)
102 if show_not_updatable:
103 layout.label(text="WARNING: This metarig contains deprecated rigify rig-types and cannot be upgraded automatically.", icon='ERROR')
104 layout.label(text="("+old_rig+" on bone "+old_bone+")")
105 elif show_update_metarig:
106 layout.label(text="This metarig contains old rig-types that can be automatically upgraded to benefit of rigify's new features.", icon='ERROR')
107 layout.label(text="("+old_rig+" on bone "+old_bone+")")
108 layout.operator("pose.rigify_upgrade_types", text="Upgrade Metarig")
109 elif show_upgrade_face:
110 layout.label(text="This metarig uses the old face rig.", icon='INFO')
111 layout.operator("pose.rigify_upgrade_face")
113 row = layout.row()
114 # Rig type field
116 col = layout.column(align=True)
117 col.active = (not 'rig_id' in C.object.data)
119 col.separator()
120 row = col.row()
121 text = "Re-Generate Rig" if obj.data.rigify_target_rig else "Generate Rig"
122 row.operator("pose.rigify_generate", text=text, icon='POSE_HLT')
123 row.enabled = enable_generate
126 class DATA_PT_rigify_advanced(bpy.types.Panel):
127 bl_space_type = 'PROPERTIES'
128 bl_region_type = 'WINDOW'
129 bl_context = "data"
130 bl_label = "Advanced"
131 bl_parent_id = 'DATA_PT_rigify'
132 bl_options = {'DEFAULT_CLOSED'}
134 def draw(self, context):
135 layout = self.layout
136 layout.use_property_split = True
137 layout.use_property_decorate = False
139 armature_id_store = context.object.data
141 col = layout.column()
142 col.row().prop(armature_id_store, "rigify_target_rig", text="Target Rig")
143 col.row().prop(armature_id_store, "rigify_rig_ui", text="Rig UI Script")
144 col.separator()
145 col.row().prop(armature_id_store, "rigify_widgets_collection")
146 col.row().prop(armature_id_store, "rigify_force_widget_update")
147 col.row().prop(armature_id_store, "rigify_mirror_widgets")
148 col.separator()
149 col.row().prop(armature_id_store, "rigify_finalize_script", text="Run Script")
152 class DATA_PT_rigify_samples(bpy.types.Panel):
153 bl_label = "Samples"
154 bl_space_type = 'PROPERTIES'
155 bl_region_type = 'WINDOW'
156 bl_context = "data"
157 bl_parent_id = "DATA_PT_rigify"
158 bl_options = {'DEFAULT_CLOSED'}
160 @classmethod
161 def poll(cls, context):
162 obj = context.object
163 if not obj:
164 return False
165 return obj.type == 'ARMATURE' \
166 and obj.data.get("rig_id") is None \
167 and obj.mode == 'EDIT'
169 def draw(self, context):
170 layout = self.layout
171 layout.use_property_split = True
172 layout.use_property_decorate = False
173 obj = context.object
174 id_store = context.window_manager
176 # Build types list
177 build_type_list(context, id_store.rigify_types)
179 if id_store.rigify_active_type > len(id_store.rigify_types):
180 id_store.rigify_active_type = 0
182 # Rig type list
183 if len(feature_set_list.get_enabled_modules_names()) > 0:
184 row = layout.row()
185 row.prop(context.object.data, "active_feature_set")
186 row = layout.row()
187 row.template_list("UI_UL_list", "rigify_types", id_store, "rigify_types", id_store, 'rigify_active_type')
189 props = layout.operator("armature.metarig_sample_add", text="Add sample")
190 props.metarig_type = id_store.rigify_types[id_store.rigify_active_type].name
193 class DATA_PT_rigify_layer_names(bpy.types.Panel):
194 bl_label = "Layer Names"
195 bl_space_type = 'PROPERTIES'
196 bl_region_type = 'WINDOW'
197 bl_context = "data"
198 bl_options = {'DEFAULT_CLOSED'}
199 bl_parent_id = "DATA_PT_rigify"
201 @classmethod
202 def poll(cls, context):
203 if not context.object:
204 return False
205 return context.object.type == 'ARMATURE' and context.active_object.data.get("rig_id") is None
207 def draw(self, context):
208 layout = self.layout
209 obj = context.object
210 arm = obj.data
212 # Ensure that the layers exist
213 if 0:
214 for i in range(1 + len(arm.rigify_layers), 29):
215 arm.rigify_layers.add()
216 else:
217 # Can't add while drawing, just use button
218 if len(arm.rigify_layers) < 29:
219 layout.operator("pose.rigify_layer_init")
220 return
222 # UI
223 main_row = layout.row(align=True).split(factor=0.05)
224 col1 = main_row.column()
225 col2 = main_row.column()
226 col1.label()
227 for i in range(32):
228 if i == 16 or i == 29:
229 col1.label()
230 col1.label(text=str(i+1) + '.')
232 for i, rigify_layer in enumerate(arm.rigify_layers):
233 # note: rigify_layer == arm.rigify_layers[i]
234 if (i % 16) == 0:
235 col = col2.column()
236 if i == 0:
237 col.label(text="Top Row:")
238 else:
239 col.label(text="Bottom Row:")
240 if (i % 8) == 0:
241 col = col2.column()
242 if i != 28:
243 row = col.row(align=True)
244 icon = 'RESTRICT_VIEW_OFF' if arm.layers[i] else 'RESTRICT_VIEW_ON'
245 row.prop(arm, "layers", index=i, text="", toggle=True, icon=icon)
246 #row.prop(arm, "layers", index=i, text="Layer %d" % (i + 1), toggle=True, icon=icon)
247 row.prop(rigify_layer, "name", text="")
248 row.prop(rigify_layer, "row", text="UI Row")
249 icon = 'RADIOBUT_ON' if rigify_layer.selset else 'RADIOBUT_OFF'
250 row.prop(rigify_layer, "selset", text="", toggle=True, icon=icon)
251 row.prop(rigify_layer, "group", text="Bone Group")
252 else:
253 row = col.row(align=True)
255 icon = 'RESTRICT_VIEW_OFF' if arm.layers[i] else 'RESTRICT_VIEW_ON'
256 row.prop(arm, "layers", index=i, text="", toggle=True, icon=icon)
257 # row.prop(arm, "layers", index=i, text="Layer %d" % (i + 1), toggle=True, icon=icon)
258 row1 = row.split(align=True).row(align=True)
259 row1.prop(rigify_layer, "name", text="")
260 row1.prop(rigify_layer, "row", text="UI Row")
261 row1.enabled = False
262 icon = 'RADIOBUT_ON' if rigify_layer.selset else 'RADIOBUT_OFF'
263 row.prop(rigify_layer, "selset", text="", toggle=True, icon=icon)
264 row.prop(rigify_layer, "group", text="Bone Group")
265 if rigify_layer.group == 0:
266 row.label(text='None')
267 else:
268 row.label(text=arm.rigify_colors[rigify_layer.group-1].name)
270 col = col2.column()
271 col.label(text="Reserved:")
272 # reserved_names = {28: 'Root', 29: 'DEF', 30: 'MCH', 31: 'ORG'}
273 reserved_names = {29: 'DEF', 30: 'MCH', 31: 'ORG'}
274 # for i in range(28, 32):
275 for i in range(29, 32):
276 row = col.row(align=True)
277 icon = 'RESTRICT_VIEW_OFF' if arm.layers[i] else 'RESTRICT_VIEW_ON'
278 row.prop(arm, "layers", index=i, text="", toggle=True, icon=icon)
279 row.label(text=reserved_names[i])
282 class DATA_OT_rigify_add_bone_groups(bpy.types.Operator):
283 bl_idname = "armature.rigify_add_bone_groups"
284 bl_label = "Rigify Add Standard Bone Groups"
286 @classmethod
287 def poll(cls, context):
288 return context.object and context.object.type == 'ARMATURE'
290 def execute(self, context):
291 obj = context.object
292 armature = obj.data
293 if not hasattr(armature, 'rigify_colors'):
294 return {'FINISHED'}
296 groups = ['Root', 'IK', 'Special', 'Tweak', 'FK', 'Extra']
298 for g in groups:
299 if g in armature.rigify_colors.keys():
300 continue
302 armature.rigify_colors.add()
303 armature.rigify_colors[-1].name = g
305 armature.rigify_colors[g].select = Color((0.3140000104904175, 0.7839999794960022, 1.0))
306 armature.rigify_colors[g].active = Color((0.5490000247955322, 1.0, 1.0))
307 armature.rigify_colors[g].standard_colors_lock = True
309 if g == "Root":
310 armature.rigify_colors[g].normal = Color((0.43529415130615234, 0.18431372940540314, 0.41568630933761597))
311 if g == "IK":
312 armature.rigify_colors[g].normal = Color((0.6039215922355652, 0.0, 0.0))
313 if g== "Special":
314 armature.rigify_colors[g].normal = Color((0.9568628072738647, 0.7882353663444519, 0.0470588281750679))
315 if g== "Tweak":
316 armature.rigify_colors[g].normal = Color((0.03921568766236305, 0.21176472306251526, 0.5803921818733215))
317 if g== "FK":
318 armature.rigify_colors[g].normal = Color((0.11764706671237946, 0.5686274766921997, 0.03529411926865578))
319 if g== "Extra":
320 armature.rigify_colors[g].normal = Color((0.9686275124549866, 0.250980406999588, 0.0941176563501358))
322 return {'FINISHED'}
325 class DATA_OT_rigify_use_standard_colors(bpy.types.Operator):
326 bl_idname = "armature.rigify_use_standard_colors"
327 bl_label = "Rigify Get active/select colors from current theme"
329 @classmethod
330 def poll(cls, context):
331 return context.object and context.object.type == 'ARMATURE'
333 def execute(self, context):
334 obj = context.object
335 armature = obj.data
336 if not hasattr(armature, 'rigify_colors'):
337 return {'FINISHED'}
339 current_theme = bpy.context.preferences.themes.items()[0][0]
340 theme = bpy.context.preferences.themes[current_theme]
342 armature.rigify_selection_colors.select = theme.view_3d.bone_pose
343 armature.rigify_selection_colors.active = theme.view_3d.bone_pose_active
345 # for col in armature.rigify_colors:
346 # col.select = theme.view_3d.bone_pose
347 # col.active = theme.view_3d.bone_pose_active
349 return {'FINISHED'}
352 class DATA_OT_rigify_apply_selection_colors(bpy.types.Operator):
353 bl_idname = "armature.rigify_apply_selection_colors"
354 bl_label = "Rigify Apply user defined active/select colors"
356 @classmethod
357 def poll(cls, context):
358 return context.object and context.object.type == 'ARMATURE'
360 def execute(self, context):
361 obj = context.object
362 armature = obj.data
363 if not hasattr(armature, 'rigify_colors'):
364 return {'FINISHED'}
366 #current_theme = bpy.context.preferences.themes.items()[0][0]
367 #theme = bpy.context.preferences.themes[current_theme]
369 for col in armature.rigify_colors:
370 col.select = armature.rigify_selection_colors.select
371 col.active = armature.rigify_selection_colors.active
373 return {'FINISHED'}
376 class DATA_OT_rigify_bone_group_add(bpy.types.Operator):
377 bl_idname = "armature.rigify_bone_group_add"
378 bl_label = "Rigify Add Bone Group color set"
380 @classmethod
381 def poll(cls, context):
382 return context.object and context.object.type == 'ARMATURE'
384 def execute(self, context):
385 obj = context.object
386 armature = obj.data
388 if hasattr(armature, 'rigify_colors'):
389 armature.rigify_colors.add()
390 armature.rigify_colors[-1].name = unique_name(armature.rigify_colors, 'Group')
392 current_theme = bpy.context.preferences.themes.items()[0][0]
393 theme = bpy.context.preferences.themes[current_theme]
395 armature.rigify_colors[-1].normal = theme.view_3d.wire
396 armature.rigify_colors[-1].normal.hsv = theme.view_3d.wire.hsv
397 armature.rigify_colors[-1].select = theme.view_3d.bone_pose
398 armature.rigify_colors[-1].select.hsv = theme.view_3d.bone_pose.hsv
399 armature.rigify_colors[-1].active = theme.view_3d.bone_pose_active
400 armature.rigify_colors[-1].active.hsv = theme.view_3d.bone_pose_active.hsv
402 return {'FINISHED'}
405 class DATA_OT_rigify_bone_group_add_theme(bpy.types.Operator):
406 bl_idname = "armature.rigify_bone_group_add_theme"
407 bl_label = "Rigify Add Bone Group color set from Theme"
408 bl_options = {"REGISTER", "UNDO"}
410 theme: EnumProperty(items=(
411 ('THEME01', 'THEME01', ''),
412 ('THEME02', 'THEME02', ''),
413 ('THEME03', 'THEME03', ''),
414 ('THEME04', 'THEME04', ''),
415 ('THEME05', 'THEME05', ''),
416 ('THEME06', 'THEME06', ''),
417 ('THEME07', 'THEME07', ''),
418 ('THEME08', 'THEME08', ''),
419 ('THEME09', 'THEME09', ''),
420 ('THEME10', 'THEME10', ''),
421 ('THEME11', 'THEME11', ''),
422 ('THEME12', 'THEME12', ''),
423 ('THEME13', 'THEME13', ''),
424 ('THEME14', 'THEME14', ''),
425 ('THEME15', 'THEME15', ''),
426 ('THEME16', 'THEME16', ''),
427 ('THEME17', 'THEME17', ''),
428 ('THEME18', 'THEME18', ''),
429 ('THEME19', 'THEME19', ''),
430 ('THEME20', 'THEME20', '')
432 name='Theme')
434 @classmethod
435 def poll(cls, context):
436 return context.object and context.object.type == 'ARMATURE'
438 def execute(self, context):
439 obj = context.object
440 armature = obj.data
442 if hasattr(armature, 'rigify_colors'):
444 if self.theme in armature.rigify_colors.keys():
445 return {'FINISHED'}
446 armature.rigify_colors.add()
447 armature.rigify_colors[-1].name = self.theme
449 id = int(self.theme[-2:]) - 1
451 theme_color_set = bpy.context.preferences.themes[0].bone_color_sets[id]
453 armature.rigify_colors[-1].normal = theme_color_set.normal
454 armature.rigify_colors[-1].select = theme_color_set.select
455 armature.rigify_colors[-1].active = theme_color_set.active
457 return {'FINISHED'}
460 class DATA_OT_rigify_bone_group_remove(bpy.types.Operator):
461 bl_idname = "armature.rigify_bone_group_remove"
462 bl_label = "Rigify Remove Bone Group color set"
464 idx: IntProperty()
466 @classmethod
467 def poll(cls, context):
468 return context.object and context.object.type == 'ARMATURE'
470 def execute(self, context):
471 obj = context.object
472 obj.data.rigify_colors.remove(self.idx)
474 # set layers references to 0
475 for l in obj.data.rigify_layers:
476 if l.group == self.idx + 1:
477 l.group = 0
478 elif l.group > self.idx + 1:
479 l.group -= 1
481 return {'FINISHED'}
484 class DATA_OT_rigify_bone_group_remove_all(bpy.types.Operator):
485 bl_idname = "armature.rigify_bone_group_remove_all"
486 bl_label = "Rigify Remove All Bone Groups"
488 @classmethod
489 def poll(cls, context):
490 return context.object and context.object.type == 'ARMATURE'
492 def execute(self, context):
493 obj = context.object
495 for i, col in enumerate(obj.data.rigify_colors):
496 obj.data.rigify_colors.remove(0)
497 # set layers references to 0
498 for l in obj.data.rigify_layers:
499 if l.group == i + 1:
500 l.group = 0
502 return {'FINISHED'}
505 class DATA_UL_rigify_bone_groups(bpy.types.UIList):
506 def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
507 row = layout.row(align=True)
508 row = row.split(factor=0.1)
509 row.label(text=str(index+1))
510 row = row.split(factor=0.7)
511 row.prop(item, "name", text='', emboss=False)
512 row = row.row(align=True)
513 icon = 'LOCKED' if item.standard_colors_lock else 'UNLOCKED'
514 #row.prop(item, "standard_colors_lock", text='', icon=icon)
515 row.prop(item, "normal", text='')
516 row2 = row.row(align=True)
517 row2.prop(item, "select", text='')
518 row2.prop(item, "active", text='')
519 #row2.enabled = not item.standard_colors_lock
520 row2.enabled = not bpy.context.object.data.rigify_colors_lock
523 class DATA_MT_rigify_bone_groups_context_menu(bpy.types.Menu):
524 bl_label = 'Rigify Bone Groups Specials'
526 def draw(self, context):
527 layout = self.layout
529 layout.operator('armature.rigify_bone_group_remove_all')
532 class DATA_PT_rigify_bone_groups(bpy.types.Panel):
533 bl_label = "Bone Groups"
534 bl_space_type = 'PROPERTIES'
535 bl_region_type = 'WINDOW'
536 bl_context = "data"
537 bl_options = {'DEFAULT_CLOSED'}
538 bl_parent_id = "DATA_PT_rigify"
540 @classmethod
541 def poll(cls, context):
542 if not context.object:
543 return False
544 return context.object.type == 'ARMATURE' and context.active_object.data.get("rig_id") is None
546 def draw(self, context):
547 obj = context.object
548 armature = obj.data
549 color_sets = obj.data.rigify_colors
550 idx = obj.data.rigify_colors_index
552 layout = self.layout
553 row = layout.row()
554 row.operator("armature.rigify_use_standard_colors", icon='FILE_REFRESH', text='')
555 row = row.row(align=True)
556 row.prop(armature.rigify_selection_colors, 'select', text='')
557 row.prop(armature.rigify_selection_colors, 'active', text='')
558 row = layout.row(align=True)
559 icon = 'LOCKED' if armature.rigify_colors_lock else 'UNLOCKED'
560 row.prop(armature, 'rigify_colors_lock', text = 'Unified select/active colors', icon=icon)
561 row.operator("armature.rigify_apply_selection_colors", icon='FILE_REFRESH', text='Apply')
562 row = layout.row()
563 row.template_list("DATA_UL_rigify_bone_groups", "", obj.data, "rigify_colors", obj.data, "rigify_colors_index")
565 col = row.column(align=True)
566 col.operator("armature.rigify_bone_group_add", icon='ZOOM_IN', text="")
567 col.operator("armature.rigify_bone_group_remove", icon='ZOOM_OUT', text="").idx = obj.data.rigify_colors_index
568 col.menu("DATA_MT_rigify_bone_groups_context_menu", icon='DOWNARROW_HLT', text="")
569 row = layout.row()
570 row.prop(armature, 'rigify_theme_to_add', text = 'Theme')
571 op = row.operator("armature.rigify_bone_group_add_theme", text="Add From Theme")
572 op.theme = armature.rigify_theme_to_add
573 row = layout.row()
574 row.operator("armature.rigify_add_bone_groups", text="Add Standard")
577 class BONE_PT_rigify_buttons(bpy.types.Panel):
578 bl_label = "Rigify Type"
579 bl_space_type = 'PROPERTIES'
580 bl_region_type = 'WINDOW'
581 bl_context = "bone"
582 #bl_options = {'DEFAULT_OPEN'}
584 @classmethod
585 def poll(cls, context):
586 if not context.object:
587 return False
588 return context.object.type == 'ARMATURE' and context.active_pose_bone\
589 and context.active_object.data.get("rig_id") is None
591 def draw(self, context):
592 C = context
593 id_store = C.window_manager
594 bone = context.active_pose_bone
595 rig_name = str(context.active_pose_bone.rigify_type).replace(" ", "")
597 layout = self.layout
599 # Build types list
600 build_type_list(context, id_store.rigify_types)
602 # Rig type field
603 if len(feature_set_list.get_enabled_modules_names()) > 0:
604 row = layout.row()
605 row.prop(context.object.data, "active_feature_set")
606 row = layout.row()
607 row.prop_search(bone, "rigify_type", id_store, "rigify_types", text="Rig type")
609 # Rig type parameters / Rig type non-exist alert
610 if rig_name != "":
611 try:
612 rig = rig_lists.rigs[rig_name]['module']
613 except (ImportError, AttributeError, KeyError):
614 row = layout.row()
615 box = row.box()
616 box.label(text="ERROR: type \"%s\" does not exist!" % rig_name, icon='ERROR')
617 else:
618 if hasattr(rig.Rig, 'parameters_ui'):
619 rig = rig.Rig
621 try:
622 param_cb = rig.parameters_ui
624 # Ignore the known empty base method
625 if getattr(param_cb, '__func__', None) == base_rig.BaseRig.parameters_ui.__func__:
626 param_cb = None
627 except AttributeError:
628 param_cb = None
630 if param_cb is None:
631 col = layout.column()
632 col.label(text="No options")
633 else:
634 col = layout.column()
635 col.label(text="Options:")
636 box = layout.box()
637 param_cb(box, bone.rigify_parameters)
640 class VIEW3D_PT_tools_rigify_dev(bpy.types.Panel):
641 bl_label = "Rigify Dev Tools"
642 bl_space_type = 'VIEW_3D'
643 bl_region_type = 'UI'
644 bl_category = "Rigify"
646 @classmethod
647 def poll(cls, context):
648 return context.mode in ['EDIT_ARMATURE', 'EDIT_MESH']
650 @classmethod
651 def poll(cls, context):
652 return context.mode in ['EDIT_ARMATURE', 'EDIT_MESH']
654 def draw(self, context):
655 obj = context.active_object
656 if obj is not None:
657 if context.mode == 'EDIT_ARMATURE':
658 r = self.layout.row()
659 r.operator("armature.rigify_encode_metarig", text="Encode Metarig to Python")
660 r = self.layout.row()
661 r.operator("armature.rigify_encode_metarig_sample", text="Encode Sample to Python")
663 if context.mode == 'EDIT_MESH':
664 r = self.layout.row()
665 r.operator("mesh.rigify_encode_mesh_widget", text="Encode Mesh Widget to Python")
668 class VIEW3D_PT_rigify_animation_tools(bpy.types.Panel):
669 bl_label = "Rigify Animation Tools"
670 bl_context = "posemode"
671 bl_space_type = 'VIEW_3D'
672 bl_region_type = 'UI'
673 bl_category = "Rigify"
675 @classmethod
676 def poll(cls, context):
677 obj = context.active_object
678 if obj and obj.type == 'ARMATURE':
679 rig_id = obj.data.get("rig_id")
680 if rig_id is not None:
681 has_arm = hasattr(bpy.types, 'POSE_OT_rigify_arm_ik2fk_' + rig_id)
682 has_leg = hasattr(bpy.types, 'POSE_OT_rigify_leg_ik2fk_' + rig_id)
683 return has_arm or has_leg
685 return False
687 def draw(self, context):
688 obj = context.active_object
689 id_store = context.window_manager
690 if obj is not None:
691 row = self.layout.row()
693 if id_store.rigify_transfer_only_selected:
694 icon = 'OUTLINER_DATA_ARMATURE'
695 else:
696 icon = 'ARMATURE_DATA'
698 row.prop(id_store, 'rigify_transfer_only_selected', toggle=True, icon=icon)
700 row = self.layout.row(align=True)
701 row.operator("rigify.ik2fk", text='IK2FK Pose', icon='SNAP_ON')
702 row.operator("rigify.fk2ik", text='FK2IK Pose', icon='SNAP_ON')
704 row = self.layout.row(align=True)
705 row.operator("rigify.transfer_fk_to_ik", text='IK2FK Action', icon='ACTION_TWEAK')
706 row.operator("rigify.transfer_ik_to_fk", text='FK2IK Action', icon='ACTION_TWEAK')
708 row = self.layout.row(align=True)
709 row.operator("rigify.clear_animation", text="Clear IK Action", icon='CANCEL').anim_type = "IK"
710 row.operator("rigify.clear_animation", text="Clear FK Action", icon='CANCEL').anim_type = "FK"
712 row = self.layout.row(align=True)
713 op = row.operator("rigify.rotation_pole", icon='FORCE_HARMONIC', text='Switch to pole')
714 op.value = True
715 op.toggle = False
716 op.bake = True
717 op = row.operator("rigify.rotation_pole", icon='FORCE_MAGNETIC', text='Switch to rotation')
718 op.value = False
719 op.toggle = False
720 op.bake = True
721 RIGIFY_OT_get_frame_range.draw_range_ui(context, self.layout)
724 def rigify_report_exception(operator, exception):
725 import traceback
726 import sys
727 import os
728 # find the non-utils module name where the error happened
729 # hint, this is the metarig type!
730 exceptionType, exceptionValue, exceptionTraceback = sys.exc_info()
731 fns = [ item.filename for item in traceback.extract_tb(exceptionTraceback) ]
732 fns_rig = [ fn for fn in fns if os.path.basename(os.path.dirname(fn)) != 'utils' ]
733 fn = fns_rig[-1]
734 fn = os.path.basename(fn)
735 fn = os.path.splitext(fn)[0]
736 message = []
737 if fn.startswith("__"):
738 message.append("Incorrect armature...")
739 else:
740 message.append("Incorrect armature for type '%s'" % fn)
741 message.append(exception.message)
743 message.reverse() # XXX - stupid! menu's are upside down!
745 operator.report({'ERROR'}, '\n'.join(message))
748 class LayerInit(bpy.types.Operator):
749 """Initialize armature rigify layers"""
751 bl_idname = "pose.rigify_layer_init"
752 bl_label = "Add Rigify Layers"
753 bl_options = {'UNDO', 'INTERNAL'}
755 def execute(self, context):
756 obj = context.object
757 arm = obj.data
758 for i in range(1 + len(arm.rigify_layers), 30):
759 arm.rigify_layers.add()
760 arm.rigify_layers[28].name = 'Root'
761 arm.rigify_layers[28].row = 14
762 return {'FINISHED'}
765 def is_metarig(obj):
766 if not (obj and obj.data and obj.type == 'ARMATURE'):
767 return False
768 if 'rig_id' in obj.data:
769 return False
770 for b in obj.pose.bones:
771 if b.rigify_type != "":
772 return True
773 return False
775 class Generate(bpy.types.Operator):
776 """Generates a rig from the active metarig armature"""
778 bl_idname = "pose.rigify_generate"
779 bl_label = "Rigify Generate Rig"
780 bl_options = {'UNDO'}
781 bl_description = 'Generates a rig from the active metarig armature'
783 @classmethod
784 def poll(cls, context):
785 return is_metarig(context.object)
787 def execute(self, context):
788 metarig = context.object
789 try:
790 generate.generate_rig(context, metarig)
791 except MetarigError as rig_exception:
792 import traceback
793 traceback.print_exc()
795 rigify_report_exception(self, rig_exception)
796 except Exception as rig_exception:
797 import traceback
798 traceback.print_exc()
800 self.report({'ERROR'}, 'Generation has thrown an exception: ' + str(rig_exception))
801 else:
802 self.report({'INFO'}, 'Successfully generated: "' + metarig.data.rigify_target_rig.name + '"')
803 finally:
804 bpy.ops.object.mode_set(mode='OBJECT')
806 return {'FINISHED'}
809 class UpgradeMetarigTypes(bpy.types.Operator):
810 """Upgrades metarig bones rigify_types"""
812 bl_idname = "pose.rigify_upgrade_types"
813 bl_label = "Rigify Upgrade Metarig Types"
814 bl_description = 'Upgrades the rigify types on the active metarig armature'
815 bl_options = {'UNDO'}
817 def execute(self, context):
818 for obj in bpy.data.objects:
819 if type(obj.data) == bpy.types.Armature:
820 upgradeMetarigTypes(obj)
821 return {'FINISHED'}
822 class Sample(bpy.types.Operator):
823 """Create a sample metarig to be modified before generating the final rig"""
825 bl_idname = "armature.metarig_sample_add"
826 bl_label = "Add Metarig Sample"
827 bl_options = {'UNDO'}
829 metarig_type: StringProperty(
830 name="Type",
831 description="Name of the rig type to generate a sample of",
832 maxlen=128,
833 options={'SKIP_SAVE'}
836 @classmethod
837 def poll(cls, context):
838 return context.mode == 'EDIT_ARMATURE'
840 def draw(self, context):
841 layout = self.layout
842 layout.use_property_split = True
843 layout.use_property_decorate = False
844 col = layout.column()
845 build_type_list(context, context.window_manager.rigify_types)
846 col.prop(context.object.data, "active_feature_set")
847 col.prop_search(self, "metarig_type", context.window_manager, "rigify_types")
849 def invoke(self, context, event):
850 if self.metarig_type == "":
851 return context.window_manager.invoke_props_dialog(self)
852 return self.execute(context)
854 def execute(self, context):
855 if self.metarig_type == "":
856 self.report({'ERROR'}, "You must select a rig type to create a sample of.")
857 return {'CANCELLED'}
858 try:
859 rig = rig_lists.rigs[self.metarig_type]["module"]
860 create_sample = rig.create_sample
861 except (ImportError, AttributeError, KeyError):
862 raise Exception("rig type '" + self.metarig_type + "' has no sample.")
863 else:
864 create_sample(context.active_object)
865 finally:
866 bpy.ops.object.mode_set(mode='EDIT')
868 return {'FINISHED'}
871 class EncodeMetarig(bpy.types.Operator):
872 """Creates Python code that will generate the selected metarig"""
873 bl_idname = "armature.rigify_encode_metarig"
874 bl_label = "Rigify Encode Metarig"
875 bl_options = {'UNDO'}
877 @classmethod
878 def poll(self, context):
879 return context.mode == 'EDIT_ARMATURE' and is_metarig(context.object)
881 def execute(self, context):
882 name = "metarig.py"
884 if name in bpy.data.texts:
885 text_block = bpy.data.texts[name]
886 text_block.clear()
887 else:
888 text_block = bpy.data.texts.new(name)
890 text = write_metarig(context.active_object, layers=True, func_name="create", groups=True, widgets=True)
891 text_block.write(text)
892 bpy.ops.object.mode_set(mode='EDIT')
893 self.report({'INFO'}, f"Metarig written to text datablock: {text_block.name}")
894 return {'FINISHED'}
897 class EncodeMetarigSample(bpy.types.Operator):
898 """Creates Python code that will generate the selected metarig as a sample"""
899 bl_idname = "armature.rigify_encode_metarig_sample"
900 bl_label = "Rigify Encode Metarig Sample"
901 bl_options = {'UNDO'}
903 @classmethod
904 def poll(self, context):
905 return context.mode == 'EDIT_ARMATURE' and is_metarig(context.object)
907 def execute(self, context):
908 name = "metarig_sample.py"
910 if name in bpy.data.texts:
911 text_block = bpy.data.texts[name]
912 text_block.clear()
913 else:
914 text_block = bpy.data.texts.new(name)
916 text = write_metarig(context.active_object, layers=False, func_name="create_sample")
917 text_block.write(text)
918 bpy.ops.object.mode_set(mode='EDIT')
920 self.report({'INFO'}, f"Metarig Sample written to text datablock: {text_block.name}")
921 return {'FINISHED'}
924 class VIEW3D_MT_rigify(bpy.types.Menu):
925 bl_label = "Rigify"
926 bl_idname = "VIEW3D_MT_rigify"
928 def draw(self, context):
929 layout = self.layout
930 obj = context.object
932 text = "Re-Generate Rig" if obj.data.rigify_target_rig else "Generate Rig"
933 layout.operator(Generate.bl_idname, text=text)
935 if context.mode == 'EDIT_ARMATURE':
936 layout.separator()
937 layout.operator(Sample.bl_idname)
938 layout.separator()
939 layout.operator(EncodeMetarig.bl_idname, text="Encode Metarig")
940 layout.operator(EncodeMetarigSample.bl_idname, text="Encode Metarig Sample")
943 def draw_rigify_menu(self, context):
944 if is_metarig(context.object):
945 self.layout.menu(VIEW3D_MT_rigify.bl_idname)
947 class EncodeWidget(bpy.types.Operator):
948 """ Creates Python code that will generate the selected metarig.
950 bl_idname = "mesh.rigify_encode_mesh_widget"
951 bl_label = "Rigify Encode Widget"
952 bl_options = {'UNDO'}
954 @classmethod
955 def poll(self, context):
956 return context.mode == 'EDIT_MESH'
958 def execute(self, context):
959 name = "widget.py"
961 if name in bpy.data.texts:
962 text_block = bpy.data.texts[name]
963 text_block.clear()
964 else:
965 text_block = bpy.data.texts.new(name)
967 text = write_widget(context.active_object)
968 text_block.write(text)
969 bpy.ops.object.mode_set(mode='EDIT')
971 return {'FINISHED'}
973 def draw_mesh_edit_menu(self, context):
974 self.layout.operator(EncodeWidget.bl_idname)
975 self.layout.separator()
978 def FktoIk(rig, window='ALL'):
980 scn = bpy.context.scene
981 id_store = bpy.context.window_manager
983 rig_id = rig.data['rig_id']
984 leg_ik2fk = eval('bpy.ops.pose.rigify_leg_ik2fk_' + rig_id)
985 arm_ik2fk = eval('bpy.ops.pose.rigify_arm_ik2fk_' + rig_id)
986 limb_generated_names = get_limb_generated_names(rig)
988 if window == 'ALL':
989 frames = get_keyed_frames_in_range(bpy.context, rig)
990 elif window == 'CURRENT':
991 frames = [scn.frame_current]
992 else:
993 frames = [scn.frame_current]
995 if not id_store.rigify_transfer_only_selected:
996 pbones = rig.pose.bones
997 bpy.ops.pose.select_all(action='DESELECT')
998 else:
999 pbones = bpy.context.selected_pose_bones
1000 bpy.ops.pose.select_all(action='DESELECT')
1002 for b in pbones:
1003 for group in limb_generated_names:
1004 if b.name in limb_generated_names[group].values() or b.name in limb_generated_names[group]['controls']\
1005 or b.name in limb_generated_names[group]['ik_ctrl']:
1006 names = limb_generated_names[group]
1007 if names['limb_type'] == 'arm':
1008 func = arm_ik2fk
1009 controls = names['controls']
1010 ik_ctrl = names['ik_ctrl']
1011 fk_ctrl = names['fk_ctrl']
1012 parent = names['parent']
1013 pole = names['pole']
1014 rig.pose.bones[controls[0]].bone.select = True
1015 rig.pose.bones[controls[4]].bone.select = True
1016 rig.pose.bones[pole].bone.select = True
1017 rig.pose.bones[parent].bone.select = True
1018 kwargs = {'uarm_fk': controls[1], 'farm_fk': controls[2], 'hand_fk': controls[3],
1019 'uarm_ik': controls[0], 'farm_ik': ik_ctrl[1], 'hand_ik': controls[4],
1020 'pole': pole, 'main_parent': parent}
1021 args = (controls[0], controls[1], controls[2], controls[3],
1022 controls[4], pole, parent)
1023 else:
1024 func = leg_ik2fk
1025 controls = names['controls']
1026 ik_ctrl = names['ik_ctrl']
1027 fk_ctrl = names['fk_ctrl']
1028 parent = names['parent']
1029 pole = names['pole']
1030 rig.pose.bones[controls[0]].bone.select = True
1031 rig.pose.bones[controls[6]].bone.select = True
1032 rig.pose.bones[controls[5]].bone.select = True
1033 rig.pose.bones[pole].bone.select = True
1034 rig.pose.bones[parent].bone.select = True
1035 kwargs = {'thigh_fk': controls[1], 'shin_fk': controls[2], 'foot_fk': controls[3],
1036 'mfoot_fk': controls[7], 'thigh_ik': controls[0], 'shin_ik': ik_ctrl[1],
1037 'foot_ik': controls[6], 'pole': pole, 'footroll': controls[5], 'mfoot_ik': ik_ctrl[2],
1038 'main_parent': parent}
1039 args = (controls[0], controls[1], controls[2], controls[3],
1040 controls[6], controls[5], pole, parent)
1042 for f in frames:
1043 if not bones_in_frame(f, rig, *args):
1044 continue
1045 scn.frame_set(f)
1046 func(**kwargs)
1047 bpy.ops.anim.keyframe_insert_menu(type='BUILTIN_KSI_VisualLocRot')
1048 bpy.ops.anim.keyframe_insert_menu(type='Scaling')
1050 bpy.ops.pose.select_all(action='DESELECT')
1051 limb_generated_names.pop(group)
1052 break
1055 def IktoFk(rig, window='ALL'):
1057 scn = bpy.context.scene
1058 id_store = bpy.context.window_manager
1060 rig_id = rig.data['rig_id']
1061 leg_fk2ik = eval('bpy.ops.pose.rigify_leg_fk2ik_' + rig_id)
1062 arm_fk2ik = eval('bpy.ops.pose.rigify_arm_fk2ik_' + rig_id)
1063 limb_generated_names = get_limb_generated_names(rig)
1065 if window == 'ALL':
1066 frames = get_keyed_frames_in_range(bpy.context, rig)
1067 elif window == 'CURRENT':
1068 frames = [scn.frame_current]
1069 else:
1070 frames = [scn.frame_current]
1072 if not id_store.rigify_transfer_only_selected:
1073 bpy.ops.pose.select_all(action='DESELECT')
1074 pbones = rig.pose.bones
1075 else:
1076 pbones = bpy.context.selected_pose_bones
1077 bpy.ops.pose.select_all(action='DESELECT')
1079 for b in pbones:
1080 for group in limb_generated_names:
1081 if b.name in limb_generated_names[group].values() or b.name in limb_generated_names[group]['controls']\
1082 or b.name in limb_generated_names[group]['ik_ctrl']:
1083 names = limb_generated_names[group]
1084 if names['limb_type'] == 'arm':
1085 func = arm_fk2ik
1086 controls = names['controls']
1087 ik_ctrl = names['ik_ctrl']
1088 fk_ctrl = names['fk_ctrl']
1089 parent = names['parent']
1090 pole = names['pole']
1091 rig.pose.bones[controls[1]].bone.select = True
1092 rig.pose.bones[controls[2]].bone.select = True
1093 rig.pose.bones[controls[3]].bone.select = True
1094 kwargs = {'uarm_fk': controls[1], 'farm_fk': controls[2], 'hand_fk': controls[3],
1095 'uarm_ik': controls[0], 'farm_ik': ik_ctrl[1],
1096 'hand_ik': controls[4]}
1097 args = (controls[0], controls[1], controls[2], controls[3],
1098 controls[4], pole, parent)
1099 else:
1100 func = leg_fk2ik
1101 controls = names['controls']
1102 ik_ctrl = names['ik_ctrl']
1103 fk_ctrl = names['fk_ctrl']
1104 parent = names['parent']
1105 pole = names['pole']
1106 rig.pose.bones[controls[1]].bone.select = True
1107 rig.pose.bones[controls[2]].bone.select = True
1108 rig.pose.bones[controls[3]].bone.select = True
1109 kwargs = {'thigh_fk': controls[1], 'shin_fk': controls[2], 'foot_fk': controls[3],
1110 'mfoot_fk': controls[7], 'thigh_ik': controls[0], 'shin_ik': ik_ctrl[1],
1111 'foot_ik': ik_ctrl[2], 'mfoot_ik': ik_ctrl[2]}
1112 args = (controls[0], controls[1], controls[2], controls[3],
1113 controls[6], controls[5], pole, parent)
1115 for f in frames:
1116 if not bones_in_frame(f, rig, *args):
1117 continue
1118 scn.frame_set(f)
1119 func(**kwargs)
1120 bpy.ops.anim.keyframe_insert_menu(type='BUILTIN_KSI_VisualLocRot')
1121 bpy.ops.anim.keyframe_insert_menu(type='Scaling')
1123 bpy.ops.pose.select_all(action='DESELECT')
1124 limb_generated_names.pop(group)
1125 break
1128 def clearAnimation(act, anim_type, names):
1130 bones = []
1131 for group in names:
1132 if names[group]['limb_type'] == 'arm':
1133 if anim_type == 'IK':
1134 bones.extend([names[group]['controls'][0], names[group]['controls'][4]])
1135 elif anim_type == 'FK':
1136 bones.extend([names[group]['controls'][1], names[group]['controls'][2], names[group]['controls'][3]])
1137 else:
1138 if anim_type == 'IK':
1139 bones.extend([names[group]['controls'][0], names[group]['controls'][6], names[group]['controls'][5],
1140 names[group]['controls'][4]])
1141 elif anim_type == 'FK':
1142 bones.extend([names[group]['controls'][1], names[group]['controls'][2], names[group]['controls'][3],
1143 names[group]['controls'][4]])
1144 FCurves = []
1145 for fcu in act.fcurves:
1146 words = fcu.data_path.split('"')
1147 if (words[0] == "pose.bones[" and
1148 words[1] in bones):
1149 FCurves.append(fcu)
1151 if FCurves == []:
1152 return
1154 for fcu in FCurves:
1155 act.fcurves.remove(fcu)
1157 # Put cleared bones back to rest pose
1158 bpy.ops.pose.loc_clear()
1159 bpy.ops.pose.rot_clear()
1160 bpy.ops.pose.scale_clear()
1162 # updateView3D()
1165 def rotPoleToggle(rig, window='ALL', value=False, toggle=False, bake=False):
1167 scn = bpy.context.scene
1168 id_store = bpy.context.window_manager
1170 rig_id = rig.data['rig_id']
1171 leg_fk2ik = eval('bpy.ops.pose.rigify_leg_fk2ik_' + rig_id)
1172 arm_fk2ik = eval('bpy.ops.pose.rigify_arm_fk2ik_' + rig_id)
1173 leg_ik2fk = eval('bpy.ops.pose.rigify_leg_ik2fk_' + rig_id)
1174 arm_ik2fk = eval('bpy.ops.pose.rigify_arm_ik2fk_' + rig_id)
1175 limb_generated_names = get_limb_generated_names(rig)
1177 if window == 'ALL':
1178 frames = get_keyed_frames_in_range(bpy.context, rig)
1179 elif window == 'CURRENT':
1180 frames = [scn.frame_current]
1181 else:
1182 frames = [scn.frame_current]
1184 if not id_store.rigify_transfer_only_selected:
1185 bpy.ops.pose.select_all(action='DESELECT')
1186 pbones = rig.pose.bones
1187 else:
1188 pbones = bpy.context.selected_pose_bones
1189 bpy.ops.pose.select_all(action='DESELECT')
1191 for b in pbones:
1192 for group in limb_generated_names:
1193 names = limb_generated_names[group]
1195 if toggle:
1196 new_pole_vector_value = not rig.pose.bones[names['parent']]['pole_vector']
1197 else:
1198 new_pole_vector_value = value
1200 if b.name in names.values() or b.name in names['controls'] or b.name in names['ik_ctrl']:
1201 if names['limb_type'] == 'arm':
1202 func1 = arm_fk2ik
1203 func2 = arm_ik2fk
1204 controls = names['controls']
1205 ik_ctrl = names['ik_ctrl']
1206 fk_ctrl = names['fk_ctrl']
1207 parent = names['parent']
1208 pole = names['pole']
1209 rig.pose.bones[controls[0]].bone.select = not new_pole_vector_value
1210 rig.pose.bones[controls[4]].bone.select = not new_pole_vector_value
1211 rig.pose.bones[parent].bone.select = not new_pole_vector_value
1212 rig.pose.bones[pole].bone.select = new_pole_vector_value
1214 kwargs1 = {'uarm_fk': controls[1], 'farm_fk': controls[2], 'hand_fk': controls[3],
1215 'uarm_ik': controls[0], 'farm_ik': ik_ctrl[1],
1216 'hand_ik': controls[4]}
1217 kwargs2 = {'uarm_fk': controls[1], 'farm_fk': controls[2], 'hand_fk': controls[3],
1218 'uarm_ik': controls[0], 'farm_ik': ik_ctrl[1], 'hand_ik': controls[4],
1219 'pole': pole, 'main_parent': parent}
1220 args = (controls[0], controls[4], pole, parent)
1221 else:
1222 func1 = leg_fk2ik
1223 func2 = leg_ik2fk
1224 controls = names['controls']
1225 ik_ctrl = names['ik_ctrl']
1226 fk_ctrl = names['fk_ctrl']
1227 parent = names['parent']
1228 pole = names['pole']
1229 rig.pose.bones[controls[0]].bone.select = not new_pole_vector_value
1230 rig.pose.bones[controls[6]].bone.select = not new_pole_vector_value
1231 rig.pose.bones[controls[5]].bone.select = not new_pole_vector_value
1232 rig.pose.bones[parent].bone.select = not new_pole_vector_value
1233 rig.pose.bones[pole].bone.select = new_pole_vector_value
1235 kwargs1 = {'thigh_fk': controls[1], 'shin_fk': controls[2], 'foot_fk': controls[3],
1236 'mfoot_fk': controls[7], 'thigh_ik': controls[0], 'shin_ik': ik_ctrl[1],
1237 'foot_ik': ik_ctrl[2], 'mfoot_ik': ik_ctrl[2]}
1238 kwargs2 = {'thigh_fk': controls[1], 'shin_fk': controls[2], 'foot_fk': controls[3],
1239 'mfoot_fk': controls[7], 'thigh_ik': controls[0], 'shin_ik': ik_ctrl[1],
1240 'foot_ik': controls[6], 'pole': pole, 'footroll': controls[5], 'mfoot_ik': ik_ctrl[2],
1241 'main_parent': parent}
1242 args = (controls[0], controls[6], controls[5], pole, parent)
1244 for f in frames:
1245 if bake and not bones_in_frame(f, rig, *args):
1246 continue
1247 scn.frame_set(f)
1248 func1(**kwargs1)
1249 rig.pose.bones[names['parent']]['pole_vector'] = new_pole_vector_value
1250 func2(**kwargs2)
1251 if bake:
1252 bpy.ops.anim.keyframe_insert_menu(type='BUILTIN_KSI_VisualLocRot')
1253 bpy.ops.anim.keyframe_insert_menu(type='Scaling')
1254 overwrite_prop_animation(rig, rig.pose.bones[parent], 'pole_vector', new_pole_vector_value, [f])
1256 bpy.ops.pose.select_all(action='DESELECT')
1257 limb_generated_names.pop(group)
1258 break
1259 scn.frame_set(0)
1262 class OBJECT_OT_IK2FK(bpy.types.Operator):
1263 """ Snaps IK limb on FK limb at current frame"""
1264 bl_idname = "rigify.ik2fk"
1265 bl_label = "IK2FK"
1266 bl_description = "Snaps IK limb on FK"
1267 bl_options = {'INTERNAL'}
1269 def execute(self,context):
1270 rig = context.object
1271 id_store = context.window_manager
1273 FktoIk(rig, window='CURRENT')
1275 return {'FINISHED'}
1278 class OBJECT_OT_FK2IK(bpy.types.Operator):
1279 """ Snaps FK limb on IK limb at current frame"""
1280 bl_idname = "rigify.fk2ik"
1281 bl_label = "FK2IK"
1282 bl_description = "Snaps FK limb on IK"
1283 bl_options = {'INTERNAL'}
1285 def execute(self,context):
1286 rig = context.object
1288 IktoFk(rig, window='CURRENT')
1290 return {'FINISHED'}
1293 class OBJECT_OT_TransferFKtoIK(bpy.types.Operator):
1294 """Transfers FK animation to IK"""
1295 bl_idname = "rigify.transfer_fk_to_ik"
1296 bl_label = "Transfer FK anim to IK"
1297 bl_description = "Transfer FK animation to IK bones"
1298 bl_options = {'INTERNAL'}
1300 def execute(self, context):
1301 rig = context.object
1302 id_store = context.window_manager
1304 FktoIk(rig)
1306 return {'FINISHED'}
1309 class OBJECT_OT_TransferIKtoFK(bpy.types.Operator):
1310 """Transfers FK animation to IK"""
1311 bl_idname = "rigify.transfer_ik_to_fk"
1312 bl_label = "Transfer IK anim to FK"
1313 bl_description = "Transfer IK animation to FK bones"
1314 bl_options = {'INTERNAL'}
1316 def execute(self, context):
1317 rig = context.object
1319 IktoFk(rig)
1321 return {'FINISHED'}
1324 class OBJECT_OT_ClearAnimation(bpy.types.Operator):
1325 bl_idname = "rigify.clear_animation"
1326 bl_label = "Clear Animation"
1327 bl_description = "Clear Animation For FK or IK Bones"
1328 bl_options = {'INTERNAL'}
1330 anim_type: StringProperty()
1332 def execute(self, context):
1333 rig = context.object
1334 scn = context.scene
1335 if not rig.animation_data:
1336 return {'FINISHED'}
1337 act = rig.animation_data.action
1338 if not act:
1339 return {'FINISHED'}
1341 clearAnimation(act, self.anim_type, names=get_limb_generated_names(rig))
1342 return {'FINISHED'}
1345 class OBJECT_OT_Rot2Pole(bpy.types.Operator):
1346 bl_idname = "rigify.rotation_pole"
1347 bl_label = "Rotation - Pole toggle"
1348 bl_description = "Toggles IK chain between rotation and pole target"
1349 bl_options = {'INTERNAL'}
1351 bone_name: StringProperty(default='')
1352 window: StringProperty(default='ALL')
1353 toggle: BoolProperty(default=True)
1354 value: BoolProperty(default=True)
1355 bake: BoolProperty(default=True)
1357 def execute(self, context):
1358 rig = context.object
1360 if self.bone_name:
1361 bpy.ops.pose.select_all(action='DESELECT')
1362 rig.pose.bones[self.bone_name].bone.select = True
1364 rotPoleToggle(rig, window=self.window, toggle=self.toggle, value=self.value, bake=self.bake)
1365 return {'FINISHED'}
1368 ### Registering ###
1371 classes = (
1372 DATA_OT_rigify_add_bone_groups,
1373 DATA_OT_rigify_use_standard_colors,
1374 DATA_OT_rigify_apply_selection_colors,
1375 DATA_OT_rigify_bone_group_add,
1376 DATA_OT_rigify_bone_group_add_theme,
1377 DATA_OT_rigify_bone_group_remove,
1378 DATA_OT_rigify_bone_group_remove_all,
1379 DATA_UL_rigify_bone_groups,
1380 DATA_MT_rigify_bone_groups_context_menu,
1381 DATA_PT_rigify,
1382 DATA_PT_rigify_advanced,
1383 DATA_PT_rigify_bone_groups,
1384 DATA_PT_rigify_layer_names,
1385 DATA_PT_rigify_samples,
1386 BONE_PT_rigify_buttons,
1387 VIEW3D_PT_rigify_animation_tools,
1388 VIEW3D_PT_tools_rigify_dev,
1389 LayerInit,
1390 Generate,
1391 UpgradeMetarigTypes,
1392 Sample,
1393 VIEW3D_MT_rigify,
1394 EncodeMetarig,
1395 EncodeMetarigSample,
1396 EncodeWidget,
1397 OBJECT_OT_FK2IK,
1398 OBJECT_OT_IK2FK,
1399 OBJECT_OT_TransferFKtoIK,
1400 OBJECT_OT_TransferIKtoFK,
1401 OBJECT_OT_ClearAnimation,
1402 OBJECT_OT_Rot2Pole,
1406 def register():
1407 from bpy.utils import register_class
1409 animation_register()
1411 # Classes.
1412 for cls in classes:
1413 register_class(cls)
1415 bpy.types.VIEW3D_MT_editor_menus.append(draw_rigify_menu)
1416 bpy.types.VIEW3D_MT_edit_mesh.prepend(draw_mesh_edit_menu)
1418 # Sub-modules.
1419 rot_mode.register()
1422 def unregister():
1423 from bpy.utils import unregister_class
1425 # Sub-modules.
1426 rot_mode.unregister()
1428 # Classes.
1429 for cls in classes:
1430 unregister_class(cls)
1432 bpy.types.VIEW3D_MT_editor_menus.remove(draw_rigify_menu)
1433 bpy.types.VIEW3D_MT_edit_mesh.remove(draw_mesh_edit_menu)
1435 animation_unregister()