Fix T71100: Node Wrangler creates nodes on linked node trees
[blender-addons.git] / rigify / ui.py
blob8b719a3f128df405807670538d2e91cccd20b18f
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()
141 row = col.row()
142 row.active = not armature_id_store.rigify_target_rig
143 row.prop(armature_id_store, "rigify_rig_basename", text="Rig Name")
145 col.separator()
147 col2 = col.box().column()
148 col2.label(text="Overwrite Existing:")
149 col2.row().prop(armature_id_store, "rigify_target_rig", text="Target Rig")
150 col2.row().prop(armature_id_store, "rigify_rig_ui", text="Rig UI Script")
151 col2.row().prop(armature_id_store, "rigify_widgets_collection")
153 col.separator()
154 col.row().prop(armature_id_store, "rigify_force_widget_update")
155 col.row().prop(armature_id_store, "rigify_mirror_widgets")
156 col.separator()
157 col.row().prop(armature_id_store, "rigify_finalize_script", text="Run Script")
160 class DATA_PT_rigify_samples(bpy.types.Panel):
161 bl_label = "Samples"
162 bl_space_type = 'PROPERTIES'
163 bl_region_type = 'WINDOW'
164 bl_context = "data"
165 bl_parent_id = "DATA_PT_rigify"
166 bl_options = {'DEFAULT_CLOSED'}
168 @classmethod
169 def poll(cls, context):
170 obj = context.object
171 if not obj:
172 return False
173 return obj.type == 'ARMATURE' \
174 and obj.data.get("rig_id") is None \
175 and obj.mode == 'EDIT'
177 def draw(self, context):
178 layout = self.layout
179 layout.use_property_split = True
180 layout.use_property_decorate = False
181 obj = context.object
182 id_store = context.window_manager
184 # Build types list
185 build_type_list(context, id_store.rigify_types)
187 if id_store.rigify_active_type > len(id_store.rigify_types):
188 id_store.rigify_active_type = 0
190 # Rig type list
191 if len(feature_set_list.get_enabled_modules_names()) > 0:
192 row = layout.row()
193 row.prop(context.object.data, "active_feature_set")
194 row = layout.row()
195 row.template_list("UI_UL_list", "rigify_types", id_store, "rigify_types", id_store, 'rigify_active_type')
197 props = layout.operator("armature.metarig_sample_add", text="Add sample")
198 props.metarig_type = id_store.rigify_types[id_store.rigify_active_type].name
201 class DATA_PT_rigify_layer_names(bpy.types.Panel):
202 bl_label = "Layer Names"
203 bl_space_type = 'PROPERTIES'
204 bl_region_type = 'WINDOW'
205 bl_context = "data"
206 bl_options = {'DEFAULT_CLOSED'}
207 bl_parent_id = "DATA_PT_rigify"
209 @classmethod
210 def poll(cls, context):
211 if not context.object:
212 return False
213 return context.object.type == 'ARMATURE' and context.active_object.data.get("rig_id") is None
215 def draw(self, context):
216 layout = self.layout
217 obj = context.object
218 arm = obj.data
220 # Ensure that the layers exist
221 if 0:
222 for i in range(1 + len(arm.rigify_layers), 29):
223 arm.rigify_layers.add()
224 else:
225 # Can't add while drawing, just use button
226 if len(arm.rigify_layers) < 29:
227 layout.operator("pose.rigify_layer_init")
228 return
230 # UI
231 main_row = layout.row(align=True).split(factor=0.05)
232 col1 = main_row.column()
233 col2 = main_row.column()
234 col1.label()
235 for i in range(32):
236 if i == 16 or i == 29:
237 col1.label()
238 col1.label(text=str(i))
240 for i, rigify_layer in enumerate(arm.rigify_layers):
241 # note: rigify_layer == arm.rigify_layers[i]
242 if (i % 16) == 0:
243 col = col2.column()
244 if i == 0:
245 col.label(text="Top Row:")
246 else:
247 col.label(text="Bottom Row:")
248 if (i % 8) == 0:
249 col = col2.column()
250 if i != 28:
251 row = col.row(align=True)
252 icon = 'RESTRICT_VIEW_OFF' if arm.layers[i] else 'RESTRICT_VIEW_ON'
253 row.prop(arm, "layers", index=i, text="", toggle=True, icon=icon)
254 #row.prop(arm, "layers", index=i, text="Layer %d" % (i + 1), toggle=True, icon=icon)
255 row.prop(rigify_layer, "name", text="")
256 row.prop(rigify_layer, "row", text="UI Row")
257 icon = 'RADIOBUT_ON' if rigify_layer.selset else 'RADIOBUT_OFF'
258 row.prop(rigify_layer, "selset", text="", toggle=True, icon=icon)
259 row.prop(rigify_layer, "group", text="Bone Group")
260 else:
261 row = col.row(align=True)
263 icon = 'RESTRICT_VIEW_OFF' if arm.layers[i] else 'RESTRICT_VIEW_ON'
264 row.prop(arm, "layers", index=i, text="", toggle=True, icon=icon)
265 # row.prop(arm, "layers", index=i, text="Layer %d" % (i + 1), toggle=True, icon=icon)
266 row1 = row.split(align=True).row(align=True)
267 row1.prop(rigify_layer, "name", text="")
268 row1.prop(rigify_layer, "row", text="UI Row")
269 row1.enabled = False
270 icon = 'RADIOBUT_ON' if rigify_layer.selset else 'RADIOBUT_OFF'
271 row.prop(rigify_layer, "selset", text="", toggle=True, icon=icon)
272 row.prop(rigify_layer, "group", text="Bone Group")
273 if rigify_layer.group == 0:
274 row.label(text='None')
275 else:
276 row.label(text=arm.rigify_colors[rigify_layer.group-1].name)
278 col = col2.column()
279 col.label(text="Reserved:")
280 # reserved_names = {28: 'Root', 29: 'DEF', 30: 'MCH', 31: 'ORG'}
281 reserved_names = {29: 'DEF', 30: 'MCH', 31: 'ORG'}
282 # for i in range(28, 32):
283 for i in range(29, 32):
284 row = col.row(align=True)
285 icon = 'RESTRICT_VIEW_OFF' if arm.layers[i] else 'RESTRICT_VIEW_ON'
286 row.prop(arm, "layers", index=i, text="", toggle=True, icon=icon)
287 row.label(text=reserved_names[i])
290 class DATA_OT_rigify_add_bone_groups(bpy.types.Operator):
291 bl_idname = "armature.rigify_add_bone_groups"
292 bl_label = "Rigify Add Standard Bone Groups"
294 @classmethod
295 def poll(cls, context):
296 return context.object and context.object.type == 'ARMATURE'
298 def execute(self, context):
299 obj = context.object
300 armature = obj.data
301 if not hasattr(armature, 'rigify_colors'):
302 return {'FINISHED'}
304 groups = ['Root', 'IK', 'Special', 'Tweak', 'FK', 'Extra']
306 for g in groups:
307 if g in armature.rigify_colors.keys():
308 continue
310 armature.rigify_colors.add()
311 armature.rigify_colors[-1].name = g
313 armature.rigify_colors[g].select = Color((0.3140000104904175, 0.7839999794960022, 1.0))
314 armature.rigify_colors[g].active = Color((0.5490000247955322, 1.0, 1.0))
315 armature.rigify_colors[g].standard_colors_lock = True
317 if g == "Root":
318 armature.rigify_colors[g].normal = Color((0.43529415130615234, 0.18431372940540314, 0.41568630933761597))
319 if g == "IK":
320 armature.rigify_colors[g].normal = Color((0.6039215922355652, 0.0, 0.0))
321 if g== "Special":
322 armature.rigify_colors[g].normal = Color((0.9568628072738647, 0.7882353663444519, 0.0470588281750679))
323 if g== "Tweak":
324 armature.rigify_colors[g].normal = Color((0.03921568766236305, 0.21176472306251526, 0.5803921818733215))
325 if g== "FK":
326 armature.rigify_colors[g].normal = Color((0.11764706671237946, 0.5686274766921997, 0.03529411926865578))
327 if g== "Extra":
328 armature.rigify_colors[g].normal = Color((0.9686275124549866, 0.250980406999588, 0.0941176563501358))
330 return {'FINISHED'}
333 class DATA_OT_rigify_use_standard_colors(bpy.types.Operator):
334 bl_idname = "armature.rigify_use_standard_colors"
335 bl_label = "Rigify Get active/select colors from current theme"
337 @classmethod
338 def poll(cls, context):
339 return context.object and context.object.type == 'ARMATURE'
341 def execute(self, context):
342 obj = context.object
343 armature = obj.data
344 if not hasattr(armature, 'rigify_colors'):
345 return {'FINISHED'}
347 current_theme = bpy.context.preferences.themes.items()[0][0]
348 theme = bpy.context.preferences.themes[current_theme]
350 armature.rigify_selection_colors.select = theme.view_3d.bone_pose
351 armature.rigify_selection_colors.active = theme.view_3d.bone_pose_active
353 # for col in armature.rigify_colors:
354 # col.select = theme.view_3d.bone_pose
355 # col.active = theme.view_3d.bone_pose_active
357 return {'FINISHED'}
360 class DATA_OT_rigify_apply_selection_colors(bpy.types.Operator):
361 bl_idname = "armature.rigify_apply_selection_colors"
362 bl_label = "Rigify Apply user defined active/select colors"
364 @classmethod
365 def poll(cls, context):
366 return context.object and context.object.type == 'ARMATURE'
368 def execute(self, context):
369 obj = context.object
370 armature = obj.data
371 if not hasattr(armature, 'rigify_colors'):
372 return {'FINISHED'}
374 #current_theme = bpy.context.preferences.themes.items()[0][0]
375 #theme = bpy.context.preferences.themes[current_theme]
377 for col in armature.rigify_colors:
378 col.select = armature.rigify_selection_colors.select
379 col.active = armature.rigify_selection_colors.active
381 return {'FINISHED'}
384 class DATA_OT_rigify_bone_group_add(bpy.types.Operator):
385 bl_idname = "armature.rigify_bone_group_add"
386 bl_label = "Rigify Add Bone Group color set"
388 @classmethod
389 def poll(cls, context):
390 return context.object and context.object.type == 'ARMATURE'
392 def execute(self, context):
393 obj = context.object
394 armature = obj.data
396 if hasattr(armature, 'rigify_colors'):
397 armature.rigify_colors.add()
398 armature.rigify_colors[-1].name = unique_name(armature.rigify_colors, 'Group')
400 current_theme = bpy.context.preferences.themes.items()[0][0]
401 theme = bpy.context.preferences.themes[current_theme]
403 armature.rigify_colors[-1].normal = theme.view_3d.wire
404 armature.rigify_colors[-1].normal.hsv = theme.view_3d.wire.hsv
405 armature.rigify_colors[-1].select = theme.view_3d.bone_pose
406 armature.rigify_colors[-1].select.hsv = theme.view_3d.bone_pose.hsv
407 armature.rigify_colors[-1].active = theme.view_3d.bone_pose_active
408 armature.rigify_colors[-1].active.hsv = theme.view_3d.bone_pose_active.hsv
410 return {'FINISHED'}
413 class DATA_OT_rigify_bone_group_add_theme(bpy.types.Operator):
414 bl_idname = "armature.rigify_bone_group_add_theme"
415 bl_label = "Rigify Add Bone Group color set from Theme"
416 bl_options = {"REGISTER", "UNDO"}
418 theme: EnumProperty(items=(
419 ('THEME01', 'THEME01', ''),
420 ('THEME02', 'THEME02', ''),
421 ('THEME03', 'THEME03', ''),
422 ('THEME04', 'THEME04', ''),
423 ('THEME05', 'THEME05', ''),
424 ('THEME06', 'THEME06', ''),
425 ('THEME07', 'THEME07', ''),
426 ('THEME08', 'THEME08', ''),
427 ('THEME09', 'THEME09', ''),
428 ('THEME10', 'THEME10', ''),
429 ('THEME11', 'THEME11', ''),
430 ('THEME12', 'THEME12', ''),
431 ('THEME13', 'THEME13', ''),
432 ('THEME14', 'THEME14', ''),
433 ('THEME15', 'THEME15', ''),
434 ('THEME16', 'THEME16', ''),
435 ('THEME17', 'THEME17', ''),
436 ('THEME18', 'THEME18', ''),
437 ('THEME19', 'THEME19', ''),
438 ('THEME20', 'THEME20', '')
440 name='Theme')
442 @classmethod
443 def poll(cls, context):
444 return context.object and context.object.type == 'ARMATURE'
446 def execute(self, context):
447 obj = context.object
448 armature = obj.data
450 if hasattr(armature, 'rigify_colors'):
452 if self.theme in armature.rigify_colors.keys():
453 return {'FINISHED'}
454 armature.rigify_colors.add()
455 armature.rigify_colors[-1].name = self.theme
457 id = int(self.theme[-2:]) - 1
459 theme_color_set = bpy.context.preferences.themes[0].bone_color_sets[id]
461 armature.rigify_colors[-1].normal = theme_color_set.normal
462 armature.rigify_colors[-1].select = theme_color_set.select
463 armature.rigify_colors[-1].active = theme_color_set.active
465 return {'FINISHED'}
468 class DATA_OT_rigify_bone_group_remove(bpy.types.Operator):
469 bl_idname = "armature.rigify_bone_group_remove"
470 bl_label = "Rigify Remove Bone Group color set"
472 idx: IntProperty()
474 @classmethod
475 def poll(cls, context):
476 return context.object and context.object.type == 'ARMATURE'
478 def execute(self, context):
479 obj = context.object
480 obj.data.rigify_colors.remove(self.idx)
482 # set layers references to 0
483 for l in obj.data.rigify_layers:
484 if l.group == self.idx + 1:
485 l.group = 0
486 elif l.group > self.idx + 1:
487 l.group -= 1
489 return {'FINISHED'}
492 class DATA_OT_rigify_bone_group_remove_all(bpy.types.Operator):
493 bl_idname = "armature.rigify_bone_group_remove_all"
494 bl_label = "Rigify Remove All Bone Groups"
496 @classmethod
497 def poll(cls, context):
498 return context.object and context.object.type == 'ARMATURE'
500 def execute(self, context):
501 obj = context.object
503 for i, col in enumerate(obj.data.rigify_colors):
504 obj.data.rigify_colors.remove(0)
505 # set layers references to 0
506 for l in obj.data.rigify_layers:
507 if l.group == i + 1:
508 l.group = 0
510 return {'FINISHED'}
513 class DATA_UL_rigify_bone_groups(bpy.types.UIList):
514 def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
515 row = layout.row(align=True)
516 row = row.split(factor=0.1)
517 row.label(text=str(index+1))
518 row = row.split(factor=0.7)
519 row.prop(item, "name", text='', emboss=False)
520 row = row.row(align=True)
521 icon = 'LOCKED' if item.standard_colors_lock else 'UNLOCKED'
522 #row.prop(item, "standard_colors_lock", text='', icon=icon)
523 row.prop(item, "normal", text='')
524 row2 = row.row(align=True)
525 row2.prop(item, "select", text='')
526 row2.prop(item, "active", text='')
527 #row2.enabled = not item.standard_colors_lock
528 row2.enabled = not bpy.context.object.data.rigify_colors_lock
531 class DATA_MT_rigify_bone_groups_context_menu(bpy.types.Menu):
532 bl_label = 'Rigify Bone Groups Specials'
534 def draw(self, context):
535 layout = self.layout
537 layout.operator('armature.rigify_bone_group_remove_all')
540 class DATA_PT_rigify_bone_groups(bpy.types.Panel):
541 bl_label = "Bone Groups"
542 bl_space_type = 'PROPERTIES'
543 bl_region_type = 'WINDOW'
544 bl_context = "data"
545 bl_options = {'DEFAULT_CLOSED'}
546 bl_parent_id = "DATA_PT_rigify"
548 @classmethod
549 def poll(cls, context):
550 if not context.object:
551 return False
552 return context.object.type == 'ARMATURE' and context.active_object.data.get("rig_id") is None
554 def draw(self, context):
555 obj = context.object
556 armature = obj.data
557 color_sets = obj.data.rigify_colors
558 idx = obj.data.rigify_colors_index
560 layout = self.layout
561 row = layout.row()
562 row.operator("armature.rigify_use_standard_colors", icon='FILE_REFRESH', text='')
563 row = row.row(align=True)
564 row.prop(armature.rigify_selection_colors, 'select', text='')
565 row.prop(armature.rigify_selection_colors, 'active', text='')
566 row = layout.row(align=True)
567 icon = 'LOCKED' if armature.rigify_colors_lock else 'UNLOCKED'
568 row.prop(armature, 'rigify_colors_lock', text = 'Unified select/active colors', icon=icon)
569 row.operator("armature.rigify_apply_selection_colors", icon='FILE_REFRESH', text='Apply')
570 row = layout.row()
571 row.template_list("DATA_UL_rigify_bone_groups", "", obj.data, "rigify_colors", obj.data, "rigify_colors_index")
573 col = row.column(align=True)
574 col.operator("armature.rigify_bone_group_add", icon='ADD', text="")
575 col.operator("armature.rigify_bone_group_remove", icon='REMOVE', text="").idx = obj.data.rigify_colors_index
576 col.menu("DATA_MT_rigify_bone_groups_context_menu", icon='DOWNARROW_HLT', text="")
577 row = layout.row()
578 row.prop(armature, 'rigify_theme_to_add', text = 'Theme')
579 op = row.operator("armature.rigify_bone_group_add_theme", text="Add From Theme")
580 op.theme = armature.rigify_theme_to_add
581 row = layout.row()
582 row.operator("armature.rigify_add_bone_groups", text="Add Standard")
585 class BONE_PT_rigify_buttons(bpy.types.Panel):
586 bl_label = "Rigify Type"
587 bl_space_type = 'PROPERTIES'
588 bl_region_type = 'WINDOW'
589 bl_context = "bone"
590 #bl_options = {'DEFAULT_OPEN'}
592 @classmethod
593 def poll(cls, context):
594 if not context.object:
595 return False
596 return context.object.type == 'ARMATURE' and context.active_pose_bone\
597 and context.active_object.data.get("rig_id") is None
599 def draw(self, context):
600 C = context
601 id_store = C.window_manager
602 bone = context.active_pose_bone
603 rig_name = str(context.active_pose_bone.rigify_type).replace(" ", "")
605 layout = self.layout
607 # Build types list
608 build_type_list(context, id_store.rigify_types)
610 # Rig type field
611 if len(feature_set_list.get_enabled_modules_names()) > 0:
612 row = layout.row()
613 row.prop(context.object.data, "active_feature_set")
614 row = layout.row()
615 row.prop_search(bone, "rigify_type", id_store, "rigify_types", text="Rig type")
617 # Rig type parameters / Rig type non-exist alert
618 if rig_name != "":
619 try:
620 rig = rig_lists.rigs[rig_name]['module']
621 except (ImportError, AttributeError, KeyError):
622 row = layout.row()
623 box = row.box()
624 box.label(text="ERROR: type \"%s\" does not exist!" % rig_name, icon='ERROR')
625 else:
626 if hasattr(rig.Rig, 'parameters_ui'):
627 rig = rig.Rig
629 try:
630 param_cb = rig.parameters_ui
632 # Ignore the known empty base method
633 if getattr(param_cb, '__func__', None) == base_rig.BaseRig.parameters_ui.__func__:
634 param_cb = None
635 except AttributeError:
636 param_cb = None
638 if param_cb is None:
639 col = layout.column()
640 col.label(text="No options")
641 else:
642 col = layout.column()
643 col.label(text="Options:")
644 box = layout.box()
645 param_cb(box, bone.rigify_parameters)
648 class VIEW3D_PT_tools_rigify_dev(bpy.types.Panel):
649 bl_label = "Rigify Dev Tools"
650 bl_space_type = 'VIEW_3D'
651 bl_region_type = 'UI'
652 bl_category = "Rigify"
654 @classmethod
655 def poll(cls, context):
656 return context.mode in ['EDIT_ARMATURE', 'EDIT_MESH']
658 @classmethod
659 def poll(cls, context):
660 return context.mode in ['EDIT_ARMATURE', 'EDIT_MESH']
662 def draw(self, context):
663 obj = context.active_object
664 if obj is not None:
665 if context.mode == 'EDIT_ARMATURE':
666 r = self.layout.row()
667 r.operator("armature.rigify_encode_metarig", text="Encode Metarig to Python")
668 r = self.layout.row()
669 r.operator("armature.rigify_encode_metarig_sample", text="Encode Sample to Python")
671 if context.mode == 'EDIT_MESH':
672 r = self.layout.row()
673 r.operator("mesh.rigify_encode_mesh_widget", text="Encode Mesh Widget to Python")
676 class VIEW3D_PT_rigify_animation_tools(bpy.types.Panel):
677 bl_label = "Rigify Animation Tools"
678 bl_context = "posemode"
679 bl_space_type = 'VIEW_3D'
680 bl_region_type = 'UI'
681 bl_category = "Rigify"
683 @classmethod
684 def poll(cls, context):
685 obj = context.active_object
686 if obj and obj.type == 'ARMATURE':
687 rig_id = obj.data.get("rig_id")
688 if rig_id is not None:
689 has_arm = hasattr(bpy.types, 'POSE_OT_rigify_arm_ik2fk_' + rig_id)
690 has_leg = hasattr(bpy.types, 'POSE_OT_rigify_leg_ik2fk_' + rig_id)
691 return has_arm or has_leg
693 return False
695 def draw(self, context):
696 obj = context.active_object
697 id_store = context.window_manager
698 if obj is not None:
699 row = self.layout.row()
701 if id_store.rigify_transfer_only_selected:
702 icon = 'OUTLINER_DATA_ARMATURE'
703 else:
704 icon = 'ARMATURE_DATA'
706 row.prop(id_store, 'rigify_transfer_only_selected', toggle=True, icon=icon)
708 row = self.layout.row(align=True)
709 row.operator("rigify.ik2fk", text='IK2FK Pose', icon='SNAP_ON')
710 row.operator("rigify.fk2ik", text='FK2IK Pose', icon='SNAP_ON')
712 row = self.layout.row(align=True)
713 row.operator("rigify.transfer_fk_to_ik", text='IK2FK Action', icon='ACTION_TWEAK')
714 row.operator("rigify.transfer_ik_to_fk", text='FK2IK Action', icon='ACTION_TWEAK')
716 row = self.layout.row(align=True)
717 row.operator("rigify.clear_animation", text="Clear IK Action", icon='CANCEL').anim_type = "IK"
718 row.operator("rigify.clear_animation", text="Clear FK Action", icon='CANCEL').anim_type = "FK"
720 row = self.layout.row(align=True)
721 op = row.operator("rigify.rotation_pole", icon='FORCE_HARMONIC', text='Switch to pole')
722 op.value = True
723 op.toggle = False
724 op.bake = True
725 op = row.operator("rigify.rotation_pole", icon='FORCE_MAGNETIC', text='Switch to rotation')
726 op.value = False
727 op.toggle = False
728 op.bake = True
729 RIGIFY_OT_get_frame_range.draw_range_ui(context, self.layout)
732 def rigify_report_exception(operator, exception):
733 import traceback
734 import sys
735 import os
736 # find the non-utils module name where the error happened
737 # hint, this is the metarig type!
738 exceptionType, exceptionValue, exceptionTraceback = sys.exc_info()
739 fns = [ item.filename for item in traceback.extract_tb(exceptionTraceback) ]
740 fns_rig = [ fn for fn in fns if os.path.basename(os.path.dirname(fn)) != 'utils' ]
741 fn = fns_rig[-1]
742 fn = os.path.basename(fn)
743 fn = os.path.splitext(fn)[0]
744 message = []
745 if fn.startswith("__"):
746 message.append("Incorrect armature...")
747 else:
748 message.append("Incorrect armature for type '%s'" % fn)
749 message.append(exception.message)
751 message.reverse() # XXX - stupid! menu's are upside down!
753 operator.report({'ERROR'}, '\n'.join(message))
756 class LayerInit(bpy.types.Operator):
757 """Initialize armature rigify layers"""
759 bl_idname = "pose.rigify_layer_init"
760 bl_label = "Add Rigify Layers"
761 bl_options = {'UNDO', 'INTERNAL'}
763 def execute(self, context):
764 obj = context.object
765 arm = obj.data
766 for i in range(1 + len(arm.rigify_layers), 30):
767 arm.rigify_layers.add()
768 arm.rigify_layers[28].name = 'Root'
769 arm.rigify_layers[28].row = 14
770 return {'FINISHED'}
773 def is_metarig(obj):
774 if not (obj and obj.data and obj.type == 'ARMATURE'):
775 return False
776 if 'rig_id' in obj.data:
777 return False
778 for b in obj.pose.bones:
779 if b.rigify_type != "":
780 return True
781 return False
783 class Generate(bpy.types.Operator):
784 """Generates a rig from the active metarig armature"""
786 bl_idname = "pose.rigify_generate"
787 bl_label = "Rigify Generate Rig"
788 bl_options = {'UNDO'}
789 bl_description = 'Generates a rig from the active metarig armature'
791 @classmethod
792 def poll(cls, context):
793 return is_metarig(context.object)
795 def execute(self, context):
796 metarig = context.object
797 try:
798 generate.generate_rig(context, metarig)
799 except MetarigError as rig_exception:
800 import traceback
801 traceback.print_exc()
803 rigify_report_exception(self, rig_exception)
804 except Exception as rig_exception:
805 import traceback
806 traceback.print_exc()
808 self.report({'ERROR'}, 'Generation has thrown an exception: ' + str(rig_exception))
809 else:
810 self.report({'INFO'}, 'Successfully generated: "' + metarig.data.rigify_target_rig.name + '"')
811 finally:
812 bpy.ops.object.mode_set(mode='OBJECT')
814 return {'FINISHED'}
817 class UpgradeMetarigTypes(bpy.types.Operator):
818 """Upgrades metarig bones rigify_types"""
820 bl_idname = "pose.rigify_upgrade_types"
821 bl_label = "Rigify Upgrade Metarig Types"
822 bl_description = 'Upgrades the rigify types on the active metarig armature'
823 bl_options = {'UNDO'}
825 def execute(self, context):
826 for obj in bpy.data.objects:
827 if type(obj.data) == bpy.types.Armature:
828 upgradeMetarigTypes(obj)
829 return {'FINISHED'}
830 class Sample(bpy.types.Operator):
831 """Create a sample metarig to be modified before generating the final rig"""
833 bl_idname = "armature.metarig_sample_add"
834 bl_label = "Add Metarig Sample"
835 bl_options = {'UNDO'}
837 metarig_type: StringProperty(
838 name="Type",
839 description="Name of the rig type to generate a sample of",
840 maxlen=128,
841 options={'SKIP_SAVE'}
844 @classmethod
845 def poll(cls, context):
846 return context.mode == 'EDIT_ARMATURE'
848 def draw(self, context):
849 layout = self.layout
850 layout.use_property_split = True
851 layout.use_property_decorate = False
852 col = layout.column()
853 build_type_list(context, context.window_manager.rigify_types)
854 col.prop(context.object.data, "active_feature_set")
855 col.prop_search(self, "metarig_type", context.window_manager, "rigify_types")
857 def invoke(self, context, event):
858 if self.metarig_type == "":
859 return context.window_manager.invoke_props_dialog(self)
860 return self.execute(context)
862 def execute(self, context):
863 if self.metarig_type == "":
864 self.report({'ERROR'}, "You must select a rig type to create a sample of.")
865 return {'CANCELLED'}
866 try:
867 rig = rig_lists.rigs[self.metarig_type]["module"]
868 create_sample = rig.create_sample
869 except (ImportError, AttributeError, KeyError):
870 raise Exception("rig type '" + self.metarig_type + "' has no sample.")
871 else:
872 create_sample(context.active_object)
873 finally:
874 bpy.ops.object.mode_set(mode='EDIT')
876 return {'FINISHED'}
879 class EncodeMetarig(bpy.types.Operator):
880 """Creates Python code that will generate the selected metarig"""
881 bl_idname = "armature.rigify_encode_metarig"
882 bl_label = "Rigify Encode Metarig"
883 bl_options = {'UNDO'}
885 @classmethod
886 def poll(self, context):
887 return context.mode == 'EDIT_ARMATURE' and is_metarig(context.object)
889 def execute(self, context):
890 name = "metarig.py"
892 if name in bpy.data.texts:
893 text_block = bpy.data.texts[name]
894 text_block.clear()
895 else:
896 text_block = bpy.data.texts.new(name)
898 text = write_metarig(context.active_object, layers=True, func_name="create", groups=True, widgets=True)
899 text_block.write(text)
900 bpy.ops.object.mode_set(mode='EDIT')
901 self.report({'INFO'}, f"Metarig written to text datablock: {text_block.name}")
902 return {'FINISHED'}
905 class EncodeMetarigSample(bpy.types.Operator):
906 """Creates Python code that will generate the selected metarig as a sample"""
907 bl_idname = "armature.rigify_encode_metarig_sample"
908 bl_label = "Rigify Encode Metarig Sample"
909 bl_options = {'UNDO'}
911 @classmethod
912 def poll(self, context):
913 return context.mode == 'EDIT_ARMATURE' and is_metarig(context.object)
915 def execute(self, context):
916 name = "metarig_sample.py"
918 if name in bpy.data.texts:
919 text_block = bpy.data.texts[name]
920 text_block.clear()
921 else:
922 text_block = bpy.data.texts.new(name)
924 text = write_metarig(context.active_object, layers=False, func_name="create_sample")
925 text_block.write(text)
926 bpy.ops.object.mode_set(mode='EDIT')
928 self.report({'INFO'}, f"Metarig Sample written to text datablock: {text_block.name}")
929 return {'FINISHED'}
932 class VIEW3D_MT_rigify(bpy.types.Menu):
933 bl_label = "Rigify"
934 bl_idname = "VIEW3D_MT_rigify"
936 def draw(self, context):
937 layout = self.layout
938 obj = context.object
940 text = "Re-Generate Rig" if obj.data.rigify_target_rig else "Generate Rig"
941 layout.operator(Generate.bl_idname, text=text)
943 if context.mode == 'EDIT_ARMATURE':
944 layout.separator()
945 layout.operator(Sample.bl_idname)
946 layout.separator()
947 layout.operator(EncodeMetarig.bl_idname, text="Encode Metarig")
948 layout.operator(EncodeMetarigSample.bl_idname, text="Encode Metarig Sample")
951 def draw_rigify_menu(self, context):
952 if is_metarig(context.object):
953 self.layout.menu(VIEW3D_MT_rigify.bl_idname)
955 class EncodeWidget(bpy.types.Operator):
956 """ Creates Python code that will generate the selected metarig.
958 bl_idname = "mesh.rigify_encode_mesh_widget"
959 bl_label = "Rigify Encode Widget"
960 bl_options = {'UNDO'}
962 @classmethod
963 def poll(self, context):
964 return context.mode == 'EDIT_MESH'
966 def execute(self, context):
967 name = "widget.py"
969 if name in bpy.data.texts:
970 text_block = bpy.data.texts[name]
971 text_block.clear()
972 else:
973 text_block = bpy.data.texts.new(name)
975 text = write_widget(context.active_object)
976 text_block.write(text)
977 bpy.ops.object.mode_set(mode='EDIT')
979 return {'FINISHED'}
981 def draw_mesh_edit_menu(self, context):
982 self.layout.operator(EncodeWidget.bl_idname)
983 self.layout.separator()
986 def FktoIk(rig, window='ALL'):
988 scn = bpy.context.scene
989 id_store = bpy.context.window_manager
991 rig_id = rig.data['rig_id']
992 leg_ik2fk = eval('bpy.ops.pose.rigify_leg_ik2fk_' + rig_id)
993 arm_ik2fk = eval('bpy.ops.pose.rigify_arm_ik2fk_' + rig_id)
994 limb_generated_names = get_limb_generated_names(rig)
996 if window == 'ALL':
997 frames = get_keyed_frames_in_range(bpy.context, rig)
998 elif window == 'CURRENT':
999 frames = [scn.frame_current]
1000 else:
1001 frames = [scn.frame_current]
1003 if not id_store.rigify_transfer_only_selected:
1004 pbones = rig.pose.bones
1005 bpy.ops.pose.select_all(action='DESELECT')
1006 else:
1007 pbones = bpy.context.selected_pose_bones
1008 bpy.ops.pose.select_all(action='DESELECT')
1010 for b in pbones:
1011 for group in limb_generated_names:
1012 if b.name in limb_generated_names[group].values() or b.name in limb_generated_names[group]['controls']\
1013 or b.name in limb_generated_names[group]['ik_ctrl']:
1014 names = limb_generated_names[group]
1015 if names['limb_type'] == 'arm':
1016 func = arm_ik2fk
1017 controls = names['controls']
1018 ik_ctrl = names['ik_ctrl']
1019 fk_ctrl = names['fk_ctrl']
1020 parent = names['parent']
1021 pole = names['pole']
1022 rig.pose.bones[controls[0]].bone.select = True
1023 rig.pose.bones[controls[4]].bone.select = True
1024 rig.pose.bones[pole].bone.select = True
1025 rig.pose.bones[parent].bone.select = True
1026 kwargs = {'uarm_fk': controls[1], 'farm_fk': controls[2], 'hand_fk': controls[3],
1027 'uarm_ik': controls[0], 'farm_ik': ik_ctrl[1], 'hand_ik': controls[4],
1028 'pole': pole, 'main_parent': parent}
1029 args = (controls[0], controls[1], controls[2], controls[3],
1030 controls[4], pole, parent)
1031 else:
1032 func = leg_ik2fk
1033 controls = names['controls']
1034 ik_ctrl = names['ik_ctrl']
1035 fk_ctrl = names['fk_ctrl']
1036 parent = names['parent']
1037 pole = names['pole']
1038 rig.pose.bones[controls[0]].bone.select = True
1039 rig.pose.bones[controls[6]].bone.select = True
1040 rig.pose.bones[controls[5]].bone.select = True
1041 rig.pose.bones[pole].bone.select = True
1042 rig.pose.bones[parent].bone.select = True
1043 kwargs = {'thigh_fk': controls[1], 'shin_fk': controls[2], 'foot_fk': controls[3],
1044 'mfoot_fk': controls[7], 'thigh_ik': controls[0], 'shin_ik': ik_ctrl[1],
1045 'foot_ik': controls[6], 'pole': pole, 'footroll': controls[5], 'mfoot_ik': ik_ctrl[2],
1046 'main_parent': parent}
1047 args = (controls[0], controls[1], controls[2], controls[3],
1048 controls[6], controls[5], pole, parent)
1050 for f in frames:
1051 if not bones_in_frame(f, rig, *args):
1052 continue
1053 scn.frame_set(f)
1054 func(**kwargs)
1055 bpy.ops.anim.keyframe_insert_menu(type='BUILTIN_KSI_VisualLocRot')
1056 bpy.ops.anim.keyframe_insert_menu(type='Scaling')
1058 bpy.ops.pose.select_all(action='DESELECT')
1059 limb_generated_names.pop(group)
1060 break
1063 def IktoFk(rig, window='ALL'):
1065 scn = bpy.context.scene
1066 id_store = bpy.context.window_manager
1068 rig_id = rig.data['rig_id']
1069 leg_fk2ik = eval('bpy.ops.pose.rigify_leg_fk2ik_' + rig_id)
1070 arm_fk2ik = eval('bpy.ops.pose.rigify_arm_fk2ik_' + rig_id)
1071 limb_generated_names = get_limb_generated_names(rig)
1073 if window == 'ALL':
1074 frames = get_keyed_frames_in_range(bpy.context, rig)
1075 elif window == 'CURRENT':
1076 frames = [scn.frame_current]
1077 else:
1078 frames = [scn.frame_current]
1080 if not id_store.rigify_transfer_only_selected:
1081 bpy.ops.pose.select_all(action='DESELECT')
1082 pbones = rig.pose.bones
1083 else:
1084 pbones = bpy.context.selected_pose_bones
1085 bpy.ops.pose.select_all(action='DESELECT')
1087 for b in pbones:
1088 for group in limb_generated_names:
1089 if b.name in limb_generated_names[group].values() or b.name in limb_generated_names[group]['controls']\
1090 or b.name in limb_generated_names[group]['ik_ctrl']:
1091 names = limb_generated_names[group]
1092 if names['limb_type'] == 'arm':
1093 func = arm_fk2ik
1094 controls = names['controls']
1095 ik_ctrl = names['ik_ctrl']
1096 fk_ctrl = names['fk_ctrl']
1097 parent = names['parent']
1098 pole = names['pole']
1099 rig.pose.bones[controls[1]].bone.select = True
1100 rig.pose.bones[controls[2]].bone.select = True
1101 rig.pose.bones[controls[3]].bone.select = True
1102 kwargs = {'uarm_fk': controls[1], 'farm_fk': controls[2], 'hand_fk': controls[3],
1103 'uarm_ik': controls[0], 'farm_ik': ik_ctrl[1],
1104 'hand_ik': controls[4]}
1105 args = (controls[0], controls[1], controls[2], controls[3],
1106 controls[4], pole, parent)
1107 else:
1108 func = leg_fk2ik
1109 controls = names['controls']
1110 ik_ctrl = names['ik_ctrl']
1111 fk_ctrl = names['fk_ctrl']
1112 parent = names['parent']
1113 pole = names['pole']
1114 rig.pose.bones[controls[1]].bone.select = True
1115 rig.pose.bones[controls[2]].bone.select = True
1116 rig.pose.bones[controls[3]].bone.select = True
1117 kwargs = {'thigh_fk': controls[1], 'shin_fk': controls[2], 'foot_fk': controls[3],
1118 'mfoot_fk': controls[7], 'thigh_ik': controls[0], 'shin_ik': ik_ctrl[1],
1119 'foot_ik': ik_ctrl[2], 'mfoot_ik': ik_ctrl[2]}
1120 args = (controls[0], controls[1], controls[2], controls[3],
1121 controls[6], controls[5], pole, parent)
1123 for f in frames:
1124 if not bones_in_frame(f, rig, *args):
1125 continue
1126 scn.frame_set(f)
1127 func(**kwargs)
1128 bpy.ops.anim.keyframe_insert_menu(type='BUILTIN_KSI_VisualLocRot')
1129 bpy.ops.anim.keyframe_insert_menu(type='Scaling')
1131 bpy.ops.pose.select_all(action='DESELECT')
1132 limb_generated_names.pop(group)
1133 break
1136 def clearAnimation(act, anim_type, names):
1138 bones = []
1139 for group in names:
1140 if names[group]['limb_type'] == 'arm':
1141 if anim_type == 'IK':
1142 bones.extend([names[group]['controls'][0], names[group]['controls'][4]])
1143 elif anim_type == 'FK':
1144 bones.extend([names[group]['controls'][1], names[group]['controls'][2], names[group]['controls'][3]])
1145 else:
1146 if anim_type == 'IK':
1147 bones.extend([names[group]['controls'][0], names[group]['controls'][6], names[group]['controls'][5],
1148 names[group]['controls'][4]])
1149 elif anim_type == 'FK':
1150 bones.extend([names[group]['controls'][1], names[group]['controls'][2], names[group]['controls'][3],
1151 names[group]['controls'][4]])
1152 FCurves = []
1153 for fcu in act.fcurves:
1154 words = fcu.data_path.split('"')
1155 if (words[0] == "pose.bones[" and
1156 words[1] in bones):
1157 FCurves.append(fcu)
1159 if FCurves == []:
1160 return
1162 for fcu in FCurves:
1163 act.fcurves.remove(fcu)
1165 # Put cleared bones back to rest pose
1166 bpy.ops.pose.loc_clear()
1167 bpy.ops.pose.rot_clear()
1168 bpy.ops.pose.scale_clear()
1170 # updateView3D()
1173 def rotPoleToggle(rig, window='ALL', value=False, toggle=False, bake=False):
1175 scn = bpy.context.scene
1176 id_store = bpy.context.window_manager
1178 rig_id = rig.data['rig_id']
1179 leg_fk2ik = eval('bpy.ops.pose.rigify_leg_fk2ik_' + rig_id)
1180 arm_fk2ik = eval('bpy.ops.pose.rigify_arm_fk2ik_' + rig_id)
1181 leg_ik2fk = eval('bpy.ops.pose.rigify_leg_ik2fk_' + rig_id)
1182 arm_ik2fk = eval('bpy.ops.pose.rigify_arm_ik2fk_' + rig_id)
1183 limb_generated_names = get_limb_generated_names(rig)
1185 if window == 'ALL':
1186 frames = get_keyed_frames_in_range(bpy.context, rig)
1187 elif window == 'CURRENT':
1188 frames = [scn.frame_current]
1189 else:
1190 frames = [scn.frame_current]
1192 if not id_store.rigify_transfer_only_selected:
1193 bpy.ops.pose.select_all(action='DESELECT')
1194 pbones = rig.pose.bones
1195 else:
1196 pbones = bpy.context.selected_pose_bones
1197 bpy.ops.pose.select_all(action='DESELECT')
1199 for b in pbones:
1200 for group in limb_generated_names:
1201 names = limb_generated_names[group]
1203 if toggle:
1204 new_pole_vector_value = not rig.pose.bones[names['parent']]['pole_vector']
1205 else:
1206 new_pole_vector_value = value
1208 if b.name in names.values() or b.name in names['controls'] or b.name in names['ik_ctrl']:
1209 if names['limb_type'] == 'arm':
1210 func1 = arm_fk2ik
1211 func2 = arm_ik2fk
1212 controls = names['controls']
1213 ik_ctrl = names['ik_ctrl']
1214 fk_ctrl = names['fk_ctrl']
1215 parent = names['parent']
1216 pole = names['pole']
1217 rig.pose.bones[controls[0]].bone.select = not new_pole_vector_value
1218 rig.pose.bones[controls[4]].bone.select = not new_pole_vector_value
1219 rig.pose.bones[parent].bone.select = not new_pole_vector_value
1220 rig.pose.bones[pole].bone.select = new_pole_vector_value
1222 kwargs1 = {'uarm_fk': controls[1], 'farm_fk': controls[2], 'hand_fk': controls[3],
1223 'uarm_ik': controls[0], 'farm_ik': ik_ctrl[1],
1224 'hand_ik': controls[4]}
1225 kwargs2 = {'uarm_fk': controls[1], 'farm_fk': controls[2], 'hand_fk': controls[3],
1226 'uarm_ik': controls[0], 'farm_ik': ik_ctrl[1], 'hand_ik': controls[4],
1227 'pole': pole, 'main_parent': parent}
1228 args = (controls[0], controls[4], pole, parent)
1229 else:
1230 func1 = leg_fk2ik
1231 func2 = leg_ik2fk
1232 controls = names['controls']
1233 ik_ctrl = names['ik_ctrl']
1234 fk_ctrl = names['fk_ctrl']
1235 parent = names['parent']
1236 pole = names['pole']
1237 rig.pose.bones[controls[0]].bone.select = not new_pole_vector_value
1238 rig.pose.bones[controls[6]].bone.select = not new_pole_vector_value
1239 rig.pose.bones[controls[5]].bone.select = not new_pole_vector_value
1240 rig.pose.bones[parent].bone.select = not new_pole_vector_value
1241 rig.pose.bones[pole].bone.select = new_pole_vector_value
1243 kwargs1 = {'thigh_fk': controls[1], 'shin_fk': controls[2], 'foot_fk': controls[3],
1244 'mfoot_fk': controls[7], 'thigh_ik': controls[0], 'shin_ik': ik_ctrl[1],
1245 'foot_ik': ik_ctrl[2], 'mfoot_ik': ik_ctrl[2]}
1246 kwargs2 = {'thigh_fk': controls[1], 'shin_fk': controls[2], 'foot_fk': controls[3],
1247 'mfoot_fk': controls[7], 'thigh_ik': controls[0], 'shin_ik': ik_ctrl[1],
1248 'foot_ik': controls[6], 'pole': pole, 'footroll': controls[5], 'mfoot_ik': ik_ctrl[2],
1249 'main_parent': parent}
1250 args = (controls[0], controls[6], controls[5], pole, parent)
1252 for f in frames:
1253 if bake and not bones_in_frame(f, rig, *args):
1254 continue
1255 scn.frame_set(f)
1256 func1(**kwargs1)
1257 rig.pose.bones[names['parent']]['pole_vector'] = new_pole_vector_value
1258 func2(**kwargs2)
1259 if bake:
1260 bpy.ops.anim.keyframe_insert_menu(type='BUILTIN_KSI_VisualLocRot')
1261 bpy.ops.anim.keyframe_insert_menu(type='Scaling')
1262 overwrite_prop_animation(rig, rig.pose.bones[parent], 'pole_vector', new_pole_vector_value, [f])
1264 bpy.ops.pose.select_all(action='DESELECT')
1265 limb_generated_names.pop(group)
1266 break
1267 scn.frame_set(0)
1270 class OBJECT_OT_IK2FK(bpy.types.Operator):
1271 """ Snaps IK limb on FK limb at current frame"""
1272 bl_idname = "rigify.ik2fk"
1273 bl_label = "IK2FK"
1274 bl_description = "Snaps IK limb on FK"
1275 bl_options = {'INTERNAL'}
1277 def execute(self,context):
1278 rig = context.object
1279 id_store = context.window_manager
1281 FktoIk(rig, window='CURRENT')
1283 return {'FINISHED'}
1286 class OBJECT_OT_FK2IK(bpy.types.Operator):
1287 """ Snaps FK limb on IK limb at current frame"""
1288 bl_idname = "rigify.fk2ik"
1289 bl_label = "FK2IK"
1290 bl_description = "Snaps FK limb on IK"
1291 bl_options = {'INTERNAL'}
1293 def execute(self,context):
1294 rig = context.object
1296 IktoFk(rig, window='CURRENT')
1298 return {'FINISHED'}
1301 class OBJECT_OT_TransferFKtoIK(bpy.types.Operator):
1302 """Transfers FK animation to IK"""
1303 bl_idname = "rigify.transfer_fk_to_ik"
1304 bl_label = "Transfer FK anim to IK"
1305 bl_description = "Transfer FK animation to IK bones"
1306 bl_options = {'INTERNAL'}
1308 def execute(self, context):
1309 rig = context.object
1310 id_store = context.window_manager
1312 FktoIk(rig)
1314 return {'FINISHED'}
1317 class OBJECT_OT_TransferIKtoFK(bpy.types.Operator):
1318 """Transfers FK animation to IK"""
1319 bl_idname = "rigify.transfer_ik_to_fk"
1320 bl_label = "Transfer IK anim to FK"
1321 bl_description = "Transfer IK animation to FK bones"
1322 bl_options = {'INTERNAL'}
1324 def execute(self, context):
1325 rig = context.object
1327 IktoFk(rig)
1329 return {'FINISHED'}
1332 class OBJECT_OT_ClearAnimation(bpy.types.Operator):
1333 bl_idname = "rigify.clear_animation"
1334 bl_label = "Clear Animation"
1335 bl_description = "Clear Animation For FK or IK Bones"
1336 bl_options = {'INTERNAL'}
1338 anim_type: StringProperty()
1340 def execute(self, context):
1341 rig = context.object
1342 scn = context.scene
1343 if not rig.animation_data:
1344 return {'FINISHED'}
1345 act = rig.animation_data.action
1346 if not act:
1347 return {'FINISHED'}
1349 clearAnimation(act, self.anim_type, names=get_limb_generated_names(rig))
1350 return {'FINISHED'}
1353 class OBJECT_OT_Rot2Pole(bpy.types.Operator):
1354 bl_idname = "rigify.rotation_pole"
1355 bl_label = "Rotation - Pole toggle"
1356 bl_description = "Toggles IK chain between rotation and pole target"
1357 bl_options = {'INTERNAL'}
1359 bone_name: StringProperty(default='')
1360 window: StringProperty(default='ALL')
1361 toggle: BoolProperty(default=True)
1362 value: BoolProperty(default=True)
1363 bake: BoolProperty(default=True)
1365 def execute(self, context):
1366 rig = context.object
1368 if self.bone_name:
1369 bpy.ops.pose.select_all(action='DESELECT')
1370 rig.pose.bones[self.bone_name].bone.select = True
1372 rotPoleToggle(rig, window=self.window, toggle=self.toggle, value=self.value, bake=self.bake)
1373 return {'FINISHED'}
1376 ### Registering ###
1379 classes = (
1380 DATA_OT_rigify_add_bone_groups,
1381 DATA_OT_rigify_use_standard_colors,
1382 DATA_OT_rigify_apply_selection_colors,
1383 DATA_OT_rigify_bone_group_add,
1384 DATA_OT_rigify_bone_group_add_theme,
1385 DATA_OT_rigify_bone_group_remove,
1386 DATA_OT_rigify_bone_group_remove_all,
1387 DATA_UL_rigify_bone_groups,
1388 DATA_MT_rigify_bone_groups_context_menu,
1389 DATA_PT_rigify,
1390 DATA_PT_rigify_advanced,
1391 DATA_PT_rigify_bone_groups,
1392 DATA_PT_rigify_layer_names,
1393 DATA_PT_rigify_samples,
1394 BONE_PT_rigify_buttons,
1395 VIEW3D_PT_rigify_animation_tools,
1396 VIEW3D_PT_tools_rigify_dev,
1397 LayerInit,
1398 Generate,
1399 UpgradeMetarigTypes,
1400 Sample,
1401 VIEW3D_MT_rigify,
1402 EncodeMetarig,
1403 EncodeMetarigSample,
1404 EncodeWidget,
1405 OBJECT_OT_FK2IK,
1406 OBJECT_OT_IK2FK,
1407 OBJECT_OT_TransferFKtoIK,
1408 OBJECT_OT_TransferIKtoFK,
1409 OBJECT_OT_ClearAnimation,
1410 OBJECT_OT_Rot2Pole,
1414 def register():
1415 from bpy.utils import register_class
1417 animation_register()
1419 # Classes.
1420 for cls in classes:
1421 register_class(cls)
1423 bpy.types.VIEW3D_MT_editor_menus.append(draw_rigify_menu)
1424 bpy.types.VIEW3D_MT_edit_mesh.prepend(draw_mesh_edit_menu)
1426 # Sub-modules.
1427 rot_mode.register()
1430 def unregister():
1431 from bpy.utils import unregister_class
1433 # Sub-modules.
1434 rot_mode.unregister()
1436 # Classes.
1437 for cls in classes:
1438 unregister_class(cls)
1440 bpy.types.VIEW3D_MT_editor_menus.remove(draw_rigify_menu)
1441 bpy.types.VIEW3D_MT_edit_mesh.remove(draw_mesh_edit_menu)
1443 animation_unregister()