1 # SPDX-FileCopyrightText: 2010-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
6 from bpy
.types
import UIList
, UILayout
, Armature
7 from bpy
.props
import (
14 from collections
import defaultdict
15 from typing
import TYPE_CHECKING
, Callable
, Any
16 from mathutils
import Color
18 from .utils
.errors
import MetarigError
19 from .utils
.layers
import ROOT_COLLECTION
, SPECIAL_COLLECTIONS
, validate_collection_references
20 from .utils
.rig
import write_metarig
, get_rigify_type
, get_rigify_target_rig
, \
21 get_rigify_colors
, get_rigify_params
22 from .utils
.widgets
import write_widget
23 from .utils
.naming
import unique_name
24 from .utils
.rig
import upgrade_metarig_types
, outdated_types
, upgrade_metarig_layers
, \
25 is_valid_metarig
, metarig_needs_upgrade
26 from .utils
.misc
import verify_armature_obj
, ArmatureObject
, IdPropSequence
, flatten_children
28 from .rigs
.utils
import get_limb_generated_names
30 from .utils
.animation
import get_keyed_frames_in_range
, bones_in_frame
, overwrite_prop_animation
31 from .utils
.animation
import RIGIFY_OT_get_frame_range
33 from .utils
.animation
import register
as animation_register
34 from .utils
.animation
import unregister
as animation_unregister
36 from . import base_rig
37 from . import rig_lists
38 from . import generate
39 from . import rot_mode
40 from . import feature_set_list
43 from . import RigifyName
, RigifySelectionColors
46 def get_rigify_types(id_store
: bpy
.types
.WindowManager
) -> IdPropSequence
['RigifyName']:
47 return id_store
.rigify_types
# noqa
50 def get_transfer_only_selected(id_store
: bpy
.types
.WindowManager
) -> bool:
51 return id_store
.rigify_transfer_only_selected
# noqa
54 def get_selection_colors(armature
: bpy
.types
.Armature
) -> 'RigifySelectionColors':
55 return armature
.rigify_selection_colors
# noqa
58 def get_colors_lock(armature
: bpy
.types
.Armature
) -> bool:
59 return armature
.rigify_colors_lock
# noqa
62 def get_colors_index(armature
: bpy
.types
.Armature
) -> int:
63 return armature
.rigify_colors_index
# noqa
66 def get_theme_to_add(armature
: bpy
.types
.Armature
) -> str:
67 return armature
.rigify_theme_to_add
# noqa
70 def build_type_list(context
, rigify_types
: IdPropSequence
['RigifyName']):
73 for r
in sorted(rig_lists
.rigs
):
74 if (context
.object.data
.active_feature_set
in ('all', rig_lists
.rigs
[r
]['feature_set'])
75 or len(feature_set_list
.get_enabled_modules_names()) == 0):
76 a
= rigify_types
.add()
80 # noinspection PyPep8Naming
81 class DATA_PT_rigify(bpy
.types
.Panel
):
83 bl_space_type
= 'PROPERTIES'
84 bl_region_type
= 'WINDOW'
88 def poll(cls
, context
):
89 return is_valid_metarig(context
, allow_needs_upgrade
=True)
91 def draw(self
, context
):
94 obj
= verify_armature_obj(C
.object)
96 if metarig_needs_upgrade(obj
):
97 layout
.label(text
="This metarig requires upgrading to Bone Collections", icon
='ERROR')
98 layout
.operator("armature.rigify_upgrade_layers", text
="Upgrade Metarig")
101 WARNING
= "Warning: Some features may change after generation"
103 show_update_metarig
= False
104 show_not_updatable
= False
105 show_upgrade_face
= False
107 check_props
= ['IK_follow', 'root/parent', 'FK_limb_follow', 'IK_Stretch']
109 for pose_bone
in obj
.pose
.bones
:
110 bone
= pose_bone
.bone
112 # If we are in edit mode and the bone was just created,
113 # a pose bone won't exist yet.
115 if list(set(pose_bone
.keys()) & set(check_props
)): # bone.layers[30] and
122 for b
in obj
.pose
.bones
:
123 old_rig
= get_rigify_type(b
)
124 if old_rig
in outdated_types
:
126 if outdated_types
[old_rig
]:
127 show_update_metarig
= True
129 show_update_metarig
= False
130 show_not_updatable
= True
132 elif old_rig
== 'faces.super_face':
133 show_upgrade_face
= True
136 layout
.label(text
=WARNING
, icon
='ERROR')
138 enable_generate
= not (show_not_updatable
or show_update_metarig
)
140 if show_not_updatable
:
141 layout
.label(text
="WARNING: This metarig contains deprecated rigify rig-types and "
142 "cannot be upgraded automatically.", icon
='ERROR')
143 layout
.label(text
="(" + old_rig
+ " on bone " + old_bone
+ ")")
144 elif show_update_metarig
:
145 layout
.label(text
="This metarig contains old rig-types that can be automatically "
146 "upgraded to benefit from new rigify features.", icon
='ERROR')
147 layout
.label(text
="(" + old_rig
+ " on bone " + old_bone
+ ")")
148 layout
.operator("pose.rigify_upgrade_types", text
="Upgrade Metarig")
149 elif show_upgrade_face
:
150 layout
.label(text
="This metarig uses the old face rig.", icon
='INFO')
151 layout
.operator("pose.rigify_upgrade_face")
155 col
= layout
.column(align
=True)
156 col
.active
= ('rig_id' not in C
.object.data
)
160 text
= "Re-Generate Rig" if get_rigify_target_rig(obj
.data
) else "Generate Rig"
161 row
.operator("pose.rigify_generate", text
=text
, icon
='POSE_HLT')
162 row
.enabled
= enable_generate
165 # noinspection PyPep8Naming
166 class DATA_PT_rigify_advanced(bpy
.types
.Panel
):
167 bl_space_type
= 'PROPERTIES'
168 bl_region_type
= 'WINDOW'
170 bl_label
= "Advanced"
171 bl_parent_id
= 'DATA_PT_rigify'
172 bl_options
= {'DEFAULT_CLOSED'}
175 def poll(cls
, context
):
176 return is_valid_metarig(context
)
178 def draw(self
, context
):
180 layout
.use_property_split
= True
181 layout
.use_property_decorate
= False
183 armature_id_store
= verify_armature_obj(context
.object).data
185 col
= layout
.column()
188 row
.active
= not get_rigify_target_rig(armature_id_store
)
189 row
.prop(armature_id_store
, "rigify_rig_basename", text
="Rig Name")
193 col2
= col
.box().column()
194 col2
.label(text
="Overwrite Existing:")
195 col2
.row().prop(armature_id_store
, "rigify_target_rig", text
="Target Rig")
196 col2
.row().prop(armature_id_store
, "rigify_rig_ui", text
="Rig UI Script")
197 col2
.row().prop(armature_id_store
, "rigify_widgets_collection")
200 col
.row().prop(armature_id_store
, "rigify_force_widget_update")
201 col
.row().prop(armature_id_store
, "rigify_mirror_widgets")
203 col
.row().prop(armature_id_store
, "rigify_finalize_script", text
="Run Script")
206 # noinspection PyPep8Naming
207 class DATA_PT_rigify_samples(bpy
.types
.Panel
):
209 bl_space_type
= 'PROPERTIES'
210 bl_region_type
= 'WINDOW'
212 bl_parent_id
= "DATA_PT_rigify"
213 bl_options
= {'DEFAULT_CLOSED'}
216 def poll(cls
, context
):
217 return is_valid_metarig(context
) and context
.object.mode
== 'EDIT'
219 def draw(self
, context
):
221 layout
.use_property_split
= True
222 layout
.use_property_decorate
= False
223 id_store
= context
.window_manager
226 rigify_types
= get_rigify_types(id_store
)
227 build_type_list(context
, rigify_types
)
229 if id_store
.rigify_active_type
> len(rigify_types
):
230 id_store
.rigify_active_type
= 0
233 if len(feature_set_list
.get_enabled_modules_names()) > 0:
235 row
.prop(context
.object.data
, "active_feature_set")
237 row
.template_list("UI_UL_list", "rigify_types", id_store
, "rigify_types", id_store
, 'rigify_active_type')
239 props
= layout
.operator("armature.metarig_sample_add", text
="Add sample")
240 props
.metarig_type
= rigify_types
[id_store
.rigify_active_type
].name
243 # noinspection SpellCheckingInspection
244 # noinspection PyPep8Naming
245 class DATA_UL_rigify_bone_collections(UIList
):
246 def filter_items(self
, _context
, data
, propname
):
247 assert propname
== 'collections_all'
248 collections
= data
.collections_all
253 print(self
.filter_name
, self
.use_filter_invert
)
254 flags
= bpy
.types
.UI_UL_list
.filter_items_by_name(
255 self
.filter_name
, self
.bitflag_filter_item
, collections
, "name")
257 flags
= [self
.bitflag_filter_item
] * len(collections
)
260 if self
.use_filter_sort_alpha
:
261 indices
= bpy
.types
.UI_UL_list
.sort_items_by_name(collections
, "name")
264 index_map
= {c
.name
: i
for i
, c
in enumerate(flatten_children(data
.collections
))}
265 indices
= [index_map
[c
.name
] for c
in collections
]
267 return flags
, indices
269 def draw_item(self
, _context
, layout
, armature
, bcoll
, _icon
, _active_data
,
270 _active_prop_name
, _index
=0, _flt_flag
=0):
271 active_bone
= armature
.edit_bones
.active
or armature
.bones
.active
272 has_active_bone
= active_bone
and bcoll
.name
in active_bone
.collections
274 split
= layout
.split(factor
=0.7)
276 split
.prop(bcoll
, "name", text
="", emboss
=False,
277 icon
='DOT' if has_active_bone
else 'BLANK1')
279 if cset
:= bcoll
.rigify_color_set_name
:
280 split
.label(text
=cset
, icon
="COLOR", translate
=False)
282 icons
= layout
.row(align
=True)
284 icons
.prop(bcoll
, "rigify_sel_set", text
="", toggle
=True, emboss
=False,
285 icon
='RADIOBUT_ON' if bcoll
.rigify_sel_set
else 'RADIOBUT_OFF')
286 icons
.label(text
="", icon
='RESTRICT_SELECT_OFF' if bcoll
.rigify_ui_row
> 0 else 'RESTRICT_SELECT_ON')
287 icons
.prop(bcoll
, "is_visible", text
="", emboss
=False,
288 icon
='HIDE_OFF' if bcoll
.is_visible
else 'HIDE_ON')
291 # noinspection PyPep8Naming
292 class DATA_PT_rigify_collection_list(bpy
.types
.Panel
):
293 bl_label
= "Bone Collection UI"
294 bl_space_type
= 'PROPERTIES'
295 bl_region_type
= 'WINDOW'
297 bl_options
= {'DEFAULT_CLOSED'}
298 bl_parent_id
= "DATA_PT_rigify"
301 def poll(cls
, context
):
302 return is_valid_metarig(context
)
304 def draw(self
, context
):
306 obj
= verify_armature_obj(context
.object)
309 # Copy the bone collection list
310 active_coll
= arm
.collections
.active
315 "DATA_UL_rigify_bone_collections",
321 rows
=(4 if active_coll
else 1),
324 col
= row
.column(align
=True)
325 col
.operator("armature.collection_add", icon
='ADD', text
="")
326 col
.operator("armature.collection_remove", icon
='REMOVE', text
="")
329 col
.operator("armature.collection_move", icon
='TRIA_UP', text
="").direction
= 'UP'
330 col
.operator("armature.collection_move", icon
='TRIA_DOWN', text
="").direction
= 'DOWN'
332 layout
.operator(operator
='armature.rigify_validate_layers')
335 col
= layout
.column()
336 col
.use_property_split
= True
337 col
.use_property_decorate
= False
339 col
.prop(active_coll
, "rigify_color_set_name", icon
="COLOR")
340 col
.prop(active_coll
, "rigify_sel_set")
343 col
.prop(active_coll
, "rigify_ui_row", )
345 row
.active
= active_coll
.rigify_ui_row
> 0 # noqa
346 row
.prop(active_coll
, "rigify_ui_title")
348 if ROOT_COLLECTION
not in arm
.collections_all
:
349 layout
.label(text
=f
"The '{ROOT_COLLECTION}' collection will be added upon generation", icon
='INFO')
352 # noinspection PyPep8Naming
353 class DATA_PT_rigify_collection_ui(bpy
.types
.Panel
):
354 bl_label
= "UI Layout"
355 bl_space_type
= 'PROPERTIES'
356 bl_region_type
= 'WINDOW'
359 bl_parent_id
= "DATA_PT_rigify_collection_list"
362 def poll(cls
, context
):
363 return is_valid_metarig(context
) and len(verify_armature_obj(context
.object).data
.collections_all
)
366 def draw_btn_block(arm
: Armature
, parent
: UILayout
, bcoll_id
: int, loose
=False):
367 bcoll
= arm
.collections_all
[bcoll_id
]
368 block
= parent
.row(align
=True)
370 if bcoll
== arm
.collections
.active
:
371 block
.prop(bcoll
, "rigify_ui_title_name", text
="", emboss
=True)
374 props
= block
.operator(text
="", icon
="X", operator
="armature.rigify_collection_set_ui_row")
375 props
.index
= bcoll_id
378 props
= block
.operator(text
=bcoll
.rigify_ui_title_name
, operator
="armature.rigify_collection_select")
379 props
.index
= bcoll_id
381 def draw(self
, context
):
383 obj
= verify_armature_obj(context
.object)
386 # Sort into button rows
387 row_table
= defaultdict(list)
390 index_map
= {c
.name
: i
for i
, c
in enumerate(arm
.collections_all
)}
392 for bcoll
in flatten_children(arm
.collections
):
393 row_table
[bcoll
.rigify_ui_row
].append(index_map
[bcoll
.name
])
395 if bcoll
.rigify_ui_row
> 0:
398 active_bcoll_idx
= arm
.collections
.active_index
400 if active_bcoll_idx
< 0:
401 layout
.label(text
="Click a button to select a collection:", icon
="INFO")
404 last_row
= max(row_table
.keys())
406 for row_id
in range(1, last_row
+ 2):
408 row_items
= row_table
[row_id
]
410 if row_id
== 1 and not has_buttons
:
411 row
.label(text
="Click to assign the button here:", icon
="INFO")
413 grid
= row
.grid_flow(row_major
=True, columns
=len(row_items
), even_columns
=True)
414 for bcoll_id
in row_items
:
415 self
.draw_btn_block(arm
, grid
, bcoll_id
)
417 btn_row
= row
.row(align
=True)
419 if active_bcoll_idx
>= 0:
420 props
= btn_row
.operator(text
="", icon
="TRIA_LEFT", operator
="armature.rigify_collection_set_ui_row")
421 props
.index
= active_bcoll_idx
424 if row_id
< last_row
+ 1:
425 props
= btn_row
.operator(text
="", icon
="ADD", operator
="armature.rigify_collection_add_ui_row")
429 btn_row
.label(text
="", icon
="BLANK1")
431 if row_id
< last_row
:
432 props
= btn_row
.operator(text
="", icon
="REMOVE", operator
="armature.rigify_collection_add_ui_row")
433 props
.row
= row_id
+ 1
436 btn_row
.label(text
="", icon
="BLANK1")
440 box
.label(text
="Permanently hidden collections:")
442 grid
= box
.grid_flow(row_major
=True, columns
=2, even_columns
=True)
444 for i
, bcoll_id
in enumerate(row_table
[0]):
445 self
.draw_btn_block(arm
, grid
, bcoll_id
, loose
=True)
448 # noinspection PyPep8Naming
449 class DATA_OT_rigify_collection_select(bpy
.types
.Operator
):
450 bl_idname
= "armature.rigify_collection_select"
451 bl_label
= "Make Collection Active"
452 bl_description
= "Make this collection active"
453 bl_options
= {'UNDO_GROUPED'}
455 index
: IntProperty(name
="Index")
458 def button(layout
, *, index
, **kwargs
):
459 props
= layout
.operator(**kwargs
)
463 def poll(cls
, context
):
464 return context
.object and context
.object.type == 'ARMATURE'
466 def execute(self
, context
):
467 obj
= verify_armature_obj(context
.object)
468 obj
.data
.collections
.active_index
= self
.index
472 # noinspection PyPep8Naming
473 class DATA_OT_rigify_collection_set_ui_row(bpy
.types
.Operator
):
474 bl_idname
= "armature.rigify_collection_set_ui_row"
475 bl_label
= "Move Between UI Rows"
476 bl_options
= {'UNDO'}
478 index
: IntProperty(name
="Index")
479 row
: IntProperty(name
="Row")
480 select
: BoolProperty(name
="Select")
483 def poll(cls
, context
):
484 return context
.object and context
.object.type == 'ARMATURE'
487 def description(cls
, context
, properties
: Any
):
488 if properties
.row
== 0:
489 return "Remove this button from the UI panel"
491 return "Move the active button to this UI panel row"
493 def execute(self
, context
):
494 obj
= verify_armature_obj(context
.object)
496 obj
.data
.collections
.active_index
= self
.index
497 obj
.data
.collections_all
[self
.index
].rigify_ui_row
= self
.row
501 # noinspection PyPep8Naming
502 class DATA_OT_rigify_collection_add_ui_row(bpy
.types
.Operator
):
503 bl_idname
= "armature.rigify_collection_add_ui_row"
504 bl_label
= "Add/Remove UI Rows"
505 bl_options
= {'UNDO'}
507 row
: IntProperty(name
="Row")
508 add
: BoolProperty(name
="Add")
511 def poll(cls
, context
):
512 return context
.object and context
.object.type == 'ARMATURE'
515 def description(cls
, context
, properties
: Any
):
517 return "Insert a new row before this one, shifting buttons down"
519 return "Remove this row, shifting buttons up"
521 def execute(self
, context
):
522 obj
= verify_armature_obj(context
.object)
523 for coll
in obj
.data
.collections_all
:
524 if coll
.rigify_ui_row
>= self
.row
:
525 coll
.rigify_ui_row
+= (1 if self
.add
else -1)
529 # noinspection PyPep8Naming
530 class DATA_OT_rigify_add_color_sets(bpy
.types
.Operator
):
531 bl_idname
= "armature.rigify_add_color_sets"
532 bl_label
= "Rigify Add Standard Color Sets"
533 bl_options
= {'UNDO'}
536 def poll(cls
, context
):
537 return context
.object and context
.object.type == 'ARMATURE'
539 def execute(self
, context
):
540 obj
= verify_armature_obj(context
.object)
543 if not hasattr(armature
, 'rigify_colors'):
546 rigify_colors
= get_rigify_colors(armature
)
547 groups
= ['Root', 'IK', 'Special', 'Tweak', 'FK', 'Extra']
550 if g
in rigify_colors
:
553 color
= rigify_colors
.add()
556 color
.select
= Color((0.3140000104904175, 0.7839999794960022, 1.0))
557 color
.active
= Color((0.5490000247955322, 1.0, 1.0))
558 color
.standard_colors_lock
= True
561 color
.normal
= Color((0.43529415130615234, 0.18431372940540314, 0.41568630933761597))
563 color
.normal
= Color((0.6039215922355652, 0.0, 0.0))
565 color
.normal
= Color((0.9568628072738647, 0.7882353663444519, 0.0470588281750679))
567 color
.normal
= Color((0.03921568766236305, 0.21176472306251526, 0.5803921818733215))
569 color
.normal
= Color((0.11764706671237946, 0.5686274766921997, 0.03529411926865578))
571 color
.normal
= Color((0.9686275124549866, 0.250980406999588, 0.0941176563501358))
576 # noinspection PyPep8Naming
577 class DATA_OT_rigify_use_standard_colors(bpy
.types
.Operator
):
578 bl_idname
= "armature.rigify_use_standard_colors"
579 bl_label
= "Rigify Get active/select colors from current theme"
580 bl_options
= {'UNDO'}
583 def poll(cls
, context
):
584 return context
.object and context
.object.type == 'ARMATURE'
586 def execute(self
, context
):
587 obj
= verify_armature_obj(context
.object)
589 if not hasattr(armature
, 'rigify_colors'):
592 current_theme
= bpy
.context
.preferences
.themes
.items()[0][0]
593 theme
= bpy
.context
.preferences
.themes
[current_theme
]
595 selection_colors
= get_selection_colors(armature
)
596 selection_colors
.select
= theme
.view_3d
.bone_pose
597 selection_colors
.active
= theme
.view_3d
.bone_pose_active
599 # for col in armature.rigify_colors:
600 # col.select = theme.view_3d.bone_pose
601 # col.active = theme.view_3d.bone_pose_active
606 # noinspection PyPep8Naming
607 class DATA_OT_rigify_apply_selection_colors(bpy
.types
.Operator
):
608 bl_idname
= "armature.rigify_apply_selection_colors"
609 bl_label
= "Rigify Apply user defined active/select colors"
610 bl_options
= {'UNDO'}
613 def poll(cls
, context
):
614 return context
.object and context
.object.type == 'ARMATURE'
616 def execute(self
, context
):
617 obj
= verify_armature_obj(context
.object)
620 if not hasattr(armature
, 'rigify_colors'):
623 # current_theme = bpy.context.preferences.themes.items()[0][0]
624 # theme = bpy.context.preferences.themes[current_theme]
626 rigify_colors
= get_rigify_colors(armature
)
627 selection_colors
= get_selection_colors(armature
)
629 for col
in rigify_colors
:
630 col
.select
= selection_colors
.select
631 col
.active
= selection_colors
.active
636 # noinspection PyPep8Naming
637 class DATA_OT_rigify_color_set_add(bpy
.types
.Operator
):
638 bl_idname
= "armature.rigify_color_set_add"
639 bl_label
= "Rigify Add Color Set"
640 bl_options
= {'UNDO'}
643 def poll(cls
, context
):
644 return context
.object and context
.object.type == 'ARMATURE'
646 def execute(self
, context
):
650 if hasattr(armature
, 'rigify_colors'):
651 armature
.rigify_colors
.add()
652 armature
.rigify_colors
[-1].name
= unique_name(armature
.rigify_colors
, 'Group')
654 current_theme
= bpy
.context
.preferences
.themes
.items()[0][0]
655 theme
= bpy
.context
.preferences
.themes
[current_theme
]
657 armature
.rigify_colors
[-1].normal
= theme
.view_3d
.wire
658 armature
.rigify_colors
[-1].normal
.hsv
= theme
.view_3d
.wire
.hsv
659 armature
.rigify_colors
[-1].select
= theme
.view_3d
.bone_pose
660 armature
.rigify_colors
[-1].select
.hsv
= theme
.view_3d
.bone_pose
.hsv
661 armature
.rigify_colors
[-1].active
= theme
.view_3d
.bone_pose_active
662 armature
.rigify_colors
[-1].active
.hsv
= theme
.view_3d
.bone_pose_active
.hsv
667 # noinspection PyPep8Naming
668 class DATA_OT_rigify_color_set_add_theme(bpy
.types
.Operator
):
669 bl_idname
= "armature.rigify_color_set_add_theme"
670 bl_label
= "Rigify Add Color Set from Theme"
671 bl_options
= {"REGISTER", "UNDO"}
673 theme
: EnumProperty(items
=(
674 ('THEME01', 'THEME01', ''),
675 ('THEME02', 'THEME02', ''),
676 ('THEME03', 'THEME03', ''),
677 ('THEME04', 'THEME04', ''),
678 ('THEME05', 'THEME05', ''),
679 ('THEME06', 'THEME06', ''),
680 ('THEME07', 'THEME07', ''),
681 ('THEME08', 'THEME08', ''),
682 ('THEME09', 'THEME09', ''),
683 ('THEME10', 'THEME10', ''),
684 ('THEME11', 'THEME11', ''),
685 ('THEME12', 'THEME12', ''),
686 ('THEME13', 'THEME13', ''),
687 ('THEME14', 'THEME14', ''),
688 ('THEME15', 'THEME15', ''),
689 ('THEME16', 'THEME16', ''),
690 ('THEME17', 'THEME17', ''),
691 ('THEME18', 'THEME18', ''),
692 ('THEME19', 'THEME19', ''),
693 ('THEME20', 'THEME20', '')
698 def poll(cls
, context
):
699 return context
.object and context
.object.type == 'ARMATURE'
701 def execute(self
, context
):
702 obj
= verify_armature_obj(context
.object)
705 if hasattr(armature
, 'rigify_colors'):
706 rigify_colors
= get_rigify_colors(armature
)
708 if self
.theme
in rigify_colors
.keys():
712 rigify_colors
[-1].name
= self
.theme
714 color_id
= int(self
.theme
[-2:]) - 1
716 theme_color_set
= bpy
.context
.preferences
.themes
[0].bone_color_sets
[color_id
]
718 rigify_colors
[-1].normal
= theme_color_set
.normal
719 rigify_colors
[-1].select
= theme_color_set
.select
720 rigify_colors
[-1].active
= theme_color_set
.active
725 # noinspection PyPep8Naming
726 class DATA_OT_rigify_color_set_remove(bpy
.types
.Operator
):
727 bl_idname
= "armature.rigify_color_set_remove"
728 bl_label
= "Rigify Remove Color Set"
729 bl_options
= {'UNDO'}
734 def poll(cls
, context
):
735 return context
.object and context
.object.type == 'ARMATURE'
737 def execute(self
, context
):
738 obj
= verify_armature_obj(context
.object)
740 rigify_colors
= get_rigify_colors(obj
.data
)
741 rigify_colors
.remove(self
.idx
)
743 # set layers references to 0
744 for coll
in obj
.data
.collections_all
:
745 idx
= coll
.rigify_color_set_id
747 if idx
== self
.idx
+ 1:
748 coll
.rigify_color_set_id
= 0
749 elif idx
> self
.idx
+ 1:
750 coll
.rigify_color_set_id
= idx
- 1
755 # noinspection PyPep8Naming
756 class DATA_OT_rigify_color_set_remove_all(bpy
.types
.Operator
):
757 bl_idname
= "armature.rigify_color_set_remove_all"
758 bl_label
= "Rigify Remove All Color Sets"
759 bl_options
= {'UNDO'}
762 def poll(cls
, context
):
763 return context
.object and context
.object.type == 'ARMATURE'
765 def execute(self
, context
):
766 obj
= verify_armature_obj(context
.object)
768 rigify_colors
= get_rigify_colors(obj
.data
)
769 while len(rigify_colors
) > 0:
770 rigify_colors
.remove(0)
772 # set layers references to 0
773 for coll
in obj
.data
.collections_all
:
774 coll
.rigify_color_set_id
= 0
779 # noinspection PyPep8Naming
780 class DATA_UL_rigify_color_sets(bpy
.types
.UIList
):
781 def draw_item(self
, context
, layout
, data
, item
, icon
, active_data
, active_prop_name
, index
=0, flt_flag
=0):
782 row
= layout
.row(align
=True)
783 row
= row
.split(factor
=0.1)
784 row
.label(text
=str(index
+1))
785 row
= row
.split(factor
=0.7)
786 row
.prop(item
, "name", text
='', emboss
=False)
787 row
= row
.row(align
=True)
788 # icon = 'LOCKED' if item.standard_colors_lock else 'UNLOCKED'
789 # row.prop(item, "standard_colors_lock", text='', icon=icon)
790 row
.prop(item
, "normal", text
='')
791 row2
= row
.row(align
=True)
792 row2
.prop(item
, "select", text
='')
793 row2
.prop(item
, "active", text
='')
794 # row2.enabled = not item.standard_colors_lock
795 arm
= verify_armature_obj(context
.object).data
796 row2
.enabled
= not get_colors_lock(arm
)
799 # noinspection PyPep8Naming
800 class DATA_MT_rigify_color_sets_context_menu(bpy
.types
.Menu
):
801 bl_label
= 'Rigify Color Sets Specials'
803 def draw(self
, context
):
806 layout
.operator('armature.rigify_color_set_remove_all')
809 # noinspection SpellCheckingInspection
810 # noinspection PyPep8Naming
811 class DATA_PT_rigify_color_sets(bpy
.types
.Panel
):
812 bl_label
= "Color Sets"
813 bl_space_type
= 'PROPERTIES'
814 bl_region_type
= 'WINDOW'
816 bl_options
= {'DEFAULT_CLOSED'}
817 bl_parent_id
= "DATA_PT_rigify"
820 def poll(cls
, context
):
821 return is_valid_metarig(context
)
823 def draw(self
, context
):
824 obj
= verify_armature_obj(context
.object)
826 idx
= get_colors_index(armature
)
827 selection_colors
= get_selection_colors(armature
)
828 is_locked
= get_colors_lock(armature
)
829 theme
= get_theme_to_add(armature
)
833 row
.operator("armature.rigify_use_standard_colors", icon
='FILE_REFRESH', text
='')
834 row
= row
.row(align
=True)
835 row
.prop(selection_colors
, 'select', text
='')
836 row
.prop(selection_colors
, 'active', text
='')
837 row
= layout
.row(align
=True)
838 icon
= 'LOCKED' if is_locked
else 'UNLOCKED'
839 row
.prop(armature
, 'rigify_colors_lock', text
='Unified select/active colors', icon
=icon
)
840 row
.operator("armature.rigify_apply_selection_colors", icon
='FILE_REFRESH', text
='Apply')
842 row
.template_list("DATA_UL_rigify_color_sets", "", obj
.data
, "rigify_colors", obj
.data
, "rigify_colors_index")
844 col
= row
.column(align
=True)
845 col
.operator("armature.rigify_color_set_add", icon
='ADD', text
="")
846 col
.operator("armature.rigify_color_set_remove", icon
='REMOVE', text
="").idx
= idx
847 col
.menu("DATA_MT_rigify_color_sets_context_menu", icon
='DOWNARROW_HLT', text
="")
849 row
.prop(armature
, 'rigify_theme_to_add', text
='Theme')
850 op
= row
.operator("armature.rigify_color_set_add_theme", text
="Add From Theme")
853 row
.operator("armature.rigify_add_color_sets", text
="Add Standard")
856 # noinspection PyPep8Naming
857 class BONE_PT_rigify_buttons(bpy
.types
.Panel
):
858 bl_label
= "Rigify Type"
859 bl_space_type
= 'PROPERTIES'
860 bl_region_type
= 'WINDOW'
862 # bl_options = {'DEFAULT_OPEN'}
865 def poll(cls
, context
):
866 return is_valid_metarig(context
) and context
.active_pose_bone
868 def draw(self
, context
):
870 id_store
= C
.window_manager
871 bone
= context
.active_pose_bone
872 rig_name
= get_rigify_type(bone
)
875 rig_types
= get_rigify_types(id_store
)
878 build_type_list(context
, rig_types
)
881 if len(feature_set_list
.get_enabled_modules_names()) > 0:
883 row
.prop(context
.object.data
, "active_feature_set")
885 row
.prop_search(bone
, "rigify_type", id_store
, "rigify_types", text
="Rig type")
887 # Rig type parameters / Rig type non-exist alert
890 rig
= rig_lists
.rigs
[rig_name
]['module']
891 except (ImportError, AttributeError, KeyError):
894 box
.label(text
="ERROR: type \"%s\" does not exist!" % rig_name
, icon
='ERROR')
896 if hasattr(rig
.Rig
, 'parameters_ui'):
900 param_cb
= rig
.parameters_ui
902 # Ignore the known empty base method
903 if getattr(param_cb
, '__func__', None) == \
904 getattr(base_rig
.BaseRig
.parameters_ui
, '__func__'):
906 except AttributeError:
910 col
= layout
.column()
911 col
.label(text
="No options")
913 col
= layout
.column()
914 col
.label(text
="Options:")
916 param_cb(box
, get_rigify_params(bone
))
919 # noinspection PyPep8Naming
920 class VIEW3D_PT_tools_rigify_dev(bpy
.types
.Panel
):
921 bl_label
= "Rigify Dev Tools"
922 bl_space_type
= 'VIEW_3D'
923 bl_region_type
= 'UI'
924 bl_category
= "Rigify"
927 def poll(cls
, context
):
928 return context
.mode
in ['EDIT_ARMATURE', 'EDIT_MESH']
930 def draw(self
, context
):
931 obj
= context
.active_object
933 if context
.mode
== 'EDIT_ARMATURE':
934 r
= self
.layout
.row()
935 r
.operator("armature.rigify_encode_metarig", text
="Encode Metarig to Python")
936 r
= self
.layout
.row()
937 r
.operator("armature.rigify_encode_metarig_sample", text
="Encode Sample to Python")
939 if context
.mode
== 'EDIT_MESH':
940 r
= self
.layout
.row()
941 r
.operator("mesh.rigify_encode_mesh_widget", text
="Encode Mesh Widget to Python")
944 # noinspection PyPep8Naming
945 class VIEW3D_PT_rigify_animation_tools(bpy
.types
.Panel
):
946 bl_label
= "Rigify Animation Tools"
947 bl_context
= "posemode" # noqa
948 bl_space_type
= 'VIEW_3D'
949 bl_region_type
= 'UI'
950 bl_category
= "Rigify"
953 def poll(cls
, context
):
954 obj
= context
.active_object
955 if obj
and obj
.type == 'ARMATURE':
956 rig_id
= obj
.data
.get("rig_id")
957 if rig_id
is not None:
958 has_arm
= hasattr(bpy
.types
, 'POSE_OT_rigify_arm_ik2fk_' + rig_id
)
959 has_leg
= hasattr(bpy
.types
, 'POSE_OT_rigify_leg_ik2fk_' + rig_id
)
960 return has_arm
or has_leg
964 def draw(self
, context
):
965 obj
= context
.active_object
966 id_store
= context
.window_manager
968 row
= self
.layout
.row()
970 only_selected
= get_transfer_only_selected(id_store
)
973 icon
= 'OUTLINER_DATA_ARMATURE'
975 icon
= 'ARMATURE_DATA'
977 row
.prop(id_store
, 'rigify_transfer_only_selected', toggle
=True, icon
=icon
)
979 row
= self
.layout
.row(align
=True)
980 row
.operator("rigify.ik2fk", text
='IK2FK Pose', icon
='SNAP_ON')
981 row
.operator("rigify.fk2ik", text
='FK2IK Pose', icon
='SNAP_ON')
983 row
= self
.layout
.row(align
=True)
984 row
.operator("rigify.transfer_fk_to_ik", text
='IK2FK Action', icon
='ACTION_TWEAK')
985 row
.operator("rigify.transfer_ik_to_fk", text
='FK2IK Action', icon
='ACTION_TWEAK')
987 row
= self
.layout
.row(align
=True)
988 row
.operator("rigify.clear_animation", text
="Clear IK Action", icon
='CANCEL').anim_type
= "IK"
989 row
.operator("rigify.clear_animation", text
="Clear FK Action", icon
='CANCEL').anim_type
= "FK"
991 row
= self
.layout
.row(align
=True)
992 op
= row
.operator("rigify.rotation_pole", icon
='FORCE_HARMONIC', text
='Switch to pole')
996 op
= row
.operator("rigify.rotation_pole", icon
='FORCE_MAGNETIC', text
='Switch to rotation')
1000 RIGIFY_OT_get_frame_range
.draw_range_ui(context
, self
.layout
)
1003 def rigify_report_exception(operator
, exception
):
1007 # find the non-utils module name where the error happened
1008 # hint, this is the metarig type!
1009 _exception_type
, _exception_value
, exception_traceback
= sys
.exc_info()
1010 fns
= [item
.filename
for item
in traceback
.extract_tb(exception_traceback
)]
1011 fns_rig
= [fn
for fn
in fns
if os
.path
.basename(os
.path
.dirname(fn
)) != 'utils']
1013 fn
= os
.path
.basename(fn
)
1014 fn
= os
.path
.splitext(fn
)[0]
1016 if fn
.startswith("__"):
1017 message
.append("Incorrect armature...")
1019 message
.append("Incorrect armature for type '%s'" % fn
)
1020 message
.append(exception
.message
)
1022 message
.reverse() # XXX - stupid! menu's are upside down!
1024 operator
.report({'ERROR'}, '\n'.join(message
))
1027 def is_metarig(obj
):
1028 if not (obj
and obj
.data
and obj
.type == 'ARMATURE'):
1030 if 'rig_id' in obj
.data
:
1032 for b
in obj
.pose
.bones
:
1033 if b
.rigify_type
!= "":
1038 class Generate(bpy
.types
.Operator
):
1039 """Generates a rig from the active metarig armature"""
1041 bl_idname
= "pose.rigify_generate"
1042 bl_label
= "Rigify Generate Rig"
1043 bl_options
= {'UNDO'}
1044 bl_description
= 'Generates a rig from the active metarig armature'
1047 def poll(cls
, context
):
1048 return is_metarig(context
.object)
1050 def execute(self
, context
):
1051 metarig
= verify_armature_obj(context
.object)
1053 for bcoll
in metarig
.data
.collections_all
:
1054 if bcoll
.rigify_ui_row
> 0 and bcoll
.name
not in SPECIAL_COLLECTIONS
:
1059 'No bone collections have UI buttons assigned - please check the Bone Collections UI sub-panel.'
1061 return {'CANCELLED'}
1064 generate
.generate_rig(context
, metarig
)
1065 except MetarigError
as rig_exception
:
1067 traceback
.print_exc()
1069 rigify_report_exception(self
, rig_exception
)
1070 except Exception as rig_exception
:
1072 traceback
.print_exc()
1074 self
.report({'ERROR'}, 'Generation has thrown an exception: ' + str(rig_exception
))
1076 target_rig
= get_rigify_target_rig(metarig
.data
)
1077 self
.report({'INFO'}, f
'Successfully generated: "{target_rig.name}"')
1079 bpy
.ops
.object.mode_set(mode
='OBJECT')
1084 class UpgradeMetarigTypes(bpy
.types
.Operator
):
1085 """Upgrades metarig bones rigify_types"""
1087 bl_idname
= "pose.rigify_upgrade_types"
1088 bl_label
= "Rigify Upgrade Metarig Types"
1089 bl_description
= 'Upgrades the rigify types on the active metarig armature'
1090 bl_options
= {'UNDO'}
1092 def execute(self
, context
):
1093 upgrade_metarig_types(verify_armature_obj(context
.active_object
))
1097 class UpgradeMetarigLayers(bpy
.types
.Operator
):
1098 """Upgrades the metarig from bone layers to bone collections"""
1100 bl_idname
= "armature.rigify_upgrade_layers"
1101 bl_label
= "Rigify Upgrade Metarig Layers"
1102 bl_description
= 'Upgrades the metarig from bone layers to bone collections'
1103 bl_options
= {'UNDO'}
1105 def execute(self
, context
):
1106 upgrade_metarig_layers(verify_armature_obj(context
.active_object
))
1110 class ValidateMetarigLayers(bpy
.types
.Operator
):
1111 """Validates references from rig component settings to bone collections"""
1113 bl_idname
= "armature.rigify_validate_layers"
1114 bl_label
= "Validate Collection References"
1115 bl_description
= 'Validate references from rig component settings to bone collections. Always run this both '\
1116 'before and after joining two metarig armature objects into one to avoid glitches'
1117 bl_options
= {'UNDO'}
1120 def poll(cls
, context
):
1121 return is_valid_metarig(context
) and context
.object.mode
!= 'EDIT'
1123 def execute(self
, context
):
1124 obj
= verify_armature_obj(context
.object)
1125 messages
= validate_collection_references(obj
)
1126 for msg
in messages
:
1127 self
.report({'WARNING'}, msg
)
1129 self
.report({'INFO'}, "No issues detected.")
1133 class Sample(bpy
.types
.Operator
):
1134 """Create a sample metarig to be modified before generating the final rig"""
1136 bl_idname
= "armature.metarig_sample_add"
1137 bl_label
= "Add Metarig Sample"
1138 bl_options
= {'UNDO'}
1140 metarig_type
: StringProperty(
1142 description
="Name of the rig type to generate a sample of",
1144 options
={'SKIP_SAVE'}
1148 def poll(cls
, context
):
1149 return context
.mode
== 'EDIT_ARMATURE'
1151 def draw(self
, context
):
1152 layout
= self
.layout
1153 layout
.use_property_split
= True
1154 layout
.use_property_decorate
= False
1155 col
= layout
.column()
1156 build_type_list(context
, get_rigify_types(context
.window_manager
))
1157 col
.prop(context
.object.data
, "active_feature_set")
1158 col
.prop_search(self
, "metarig_type", context
.window_manager
, "rigify_types")
1160 def invoke(self
, context
, event
):
1161 if self
.metarig_type
== "":
1162 return context
.window_manager
.invoke_props_dialog(self
)
1163 return self
.execute(context
)
1165 def execute(self
, context
):
1166 if self
.metarig_type
== "":
1167 self
.report({'ERROR'}, "You must select a rig type to create a sample of.")
1168 return {'CANCELLED'}
1170 rig
= rig_lists
.rigs
[self
.metarig_type
]["module"]
1171 create_sample
= rig
.create_sample
1172 except (ImportError, AttributeError, KeyError):
1173 raise Exception("rig type '" + self
.metarig_type
+ "' has no sample.")
1175 create_sample(context
.active_object
)
1177 bpy
.ops
.object.mode_set(mode
='EDIT')
1182 class EncodeMetarig(bpy
.types
.Operator
):
1183 """Creates Python code that will generate the selected metarig"""
1184 bl_idname
= "armature.rigify_encode_metarig"
1185 bl_label
= "Rigify Encode Metarig"
1186 bl_options
= {'UNDO'}
1189 def poll(cls
, context
):
1190 return context
.mode
== 'EDIT_ARMATURE' and is_metarig(context
.object)
1192 def execute(self
, context
):
1195 if name
in bpy
.data
.texts
:
1196 text_block
= bpy
.data
.texts
[name
]
1199 text_block
= bpy
.data
.texts
.new(name
)
1201 obj
= verify_armature_obj(context
.active_object
)
1202 text
= write_metarig(obj
, layers
=True, func_name
="create", groups
=True, widgets
=True)
1203 text_block
.write(text
)
1204 bpy
.ops
.object.mode_set(mode
='EDIT')
1205 self
.report({'INFO'}, f
"Metarig written to text datablock: {text_block.name}")
1209 class EncodeMetarigSample(bpy
.types
.Operator
):
1210 """Creates Python code that will generate the selected metarig as a sample"""
1211 bl_idname
= "armature.rigify_encode_metarig_sample"
1212 bl_label
= "Rigify Encode Metarig Sample"
1213 bl_options
= {'UNDO'}
1216 def poll(cls
, context
):
1217 return context
.mode
== 'EDIT_ARMATURE' and is_metarig(context
.object)
1219 def execute(self
, context
):
1220 name
= "metarig_sample.py"
1222 if name
in bpy
.data
.texts
:
1223 text_block
= bpy
.data
.texts
[name
]
1226 text_block
= bpy
.data
.texts
.new(name
)
1228 obj
= verify_armature_obj(context
.active_object
)
1229 text
= write_metarig(obj
, layers
=False, func_name
="create_sample")
1230 text_block
.write(text
)
1231 bpy
.ops
.object.mode_set(mode
='EDIT')
1233 self
.report({'INFO'}, f
"Metarig Sample written to text datablock: {text_block.name}")
1237 # noinspection PyPep8Naming
1238 class VIEW3D_MT_rigify(bpy
.types
.Menu
):
1240 bl_idname
= "VIEW3D_MT_rigify"
1245 def draw(self
, context
):
1246 layout
= self
.layout
1247 obj
= verify_armature_obj(context
.object)
1248 target_rig
= get_rigify_target_rig(obj
.data
)
1250 text
= "Re-Generate Rig" if target_rig
else "Generate Rig"
1251 layout
.operator(Generate
.bl_idname
, text
=text
)
1253 if context
.mode
== 'EDIT_ARMATURE':
1255 layout
.operator(Sample
.bl_idname
)
1257 layout
.operator(EncodeMetarig
.bl_idname
, text
="Encode Metarig")
1258 layout
.operator(EncodeMetarigSample
.bl_idname
, text
="Encode Metarig Sample")
1261 def draw_rigify_menu(self
, context
):
1262 if is_metarig(context
.object):
1263 self
.layout
.menu(VIEW3D_MT_rigify
.bl_idname
)
1266 class EncodeWidget(bpy
.types
.Operator
):
1267 """ Creates Python code that will generate the selected metarig.
1269 bl_idname
= "mesh.rigify_encode_mesh_widget"
1270 bl_label
= "Rigify Encode Widget"
1271 bl_options
= {'UNDO'}
1274 def poll(cls
, context
):
1275 return context
.mode
== 'EDIT_MESH'
1277 def execute(self
, context
):
1280 if name
in bpy
.data
.texts
:
1281 text_block
= bpy
.data
.texts
[name
]
1284 text_block
= bpy
.data
.texts
.new(name
)
1286 text
= write_widget(context
.active_object
)
1287 text_block
.write(text
)
1288 bpy
.ops
.object.mode_set(mode
='EDIT')
1293 def draw_mesh_edit_menu(self
, _context
: bpy
.types
.Context
):
1294 self
.layout
.operator(EncodeWidget
.bl_idname
)
1295 self
.layout
.separator()
1298 def fk_to_ik(rig
: ArmatureObject
, window
='ALL'):
1299 scn
= bpy
.context
.scene
1300 id_store
= bpy
.context
.window_manager
1302 rig_id
= rig
.data
['rig_id']
1303 leg_ik2fk
= eval('bpy.ops.pose.rigify_leg_ik2fk_' + rig_id
)
1304 arm_ik2fk
= eval('bpy.ops.pose.rigify_arm_ik2fk_' + rig_id
)
1305 limb_generated_names
= get_limb_generated_names(rig
)
1308 frames
= get_keyed_frames_in_range(bpy
.context
, rig
)
1309 elif window
== 'CURRENT':
1310 frames
= [scn
.frame_current
]
1312 frames
= [scn
.frame_current
]
1314 only_selected
= get_transfer_only_selected(id_store
)
1316 if not only_selected
:
1317 pose_bones
= rig
.pose
.bones
1318 bpy
.ops
.pose
.select_all(action
='DESELECT')
1320 pose_bones
= bpy
.context
.selected_pose_bones
1321 bpy
.ops
.pose
.select_all(action
='DESELECT')
1323 for b
in pose_bones
:
1324 for group
in limb_generated_names
:
1325 if b
.name
in limb_generated_names
[group
].values() or b
.name
in limb_generated_names
[group
]['controls']\
1326 or b
.name
in limb_generated_names
[group
]['ik_ctrl']:
1327 names
= limb_generated_names
[group
]
1328 if names
['limb_type'] == 'arm':
1330 controls
= names
['controls']
1331 ik_ctrl
= names
['ik_ctrl']
1332 # fk_ctrl = names['fk_ctrl']
1333 parent
= names
['parent']
1334 pole
= names
['pole']
1335 rig
.pose
.bones
[controls
[0]].bone
.select
= True
1336 rig
.pose
.bones
[controls
[4]].bone
.select
= True
1337 rig
.pose
.bones
[pole
].bone
.select
= True
1338 rig
.pose
.bones
[parent
].bone
.select
= True
1339 kwargs
= {'uarm_fk': controls
[1], 'farm_fk': controls
[2], 'hand_fk': controls
[3],
1340 'uarm_ik': controls
[0], 'farm_ik': ik_ctrl
[1], 'hand_ik': controls
[4],
1341 'pole': pole
, 'main_parent': parent
}
1342 args
= (controls
[0], controls
[1], controls
[2], controls
[3],
1343 controls
[4], pole
, parent
)
1346 controls
= names
['controls']
1347 ik_ctrl
= names
['ik_ctrl']
1348 # fk_ctrl = names['fk_ctrl']
1349 parent
= names
['parent']
1350 pole
= names
['pole']
1351 rig
.pose
.bones
[controls
[0]].bone
.select
= True
1352 rig
.pose
.bones
[controls
[6]].bone
.select
= True
1353 rig
.pose
.bones
[controls
[5]].bone
.select
= True
1354 rig
.pose
.bones
[pole
].bone
.select
= True
1355 rig
.pose
.bones
[parent
].bone
.select
= True
1356 # noinspection SpellCheckingInspection
1357 kwargs
= {'thigh_fk': controls
[1], 'shin_fk': controls
[2], 'foot_fk': controls
[3],
1358 'mfoot_fk': controls
[7], 'thigh_ik': controls
[0], 'shin_ik': ik_ctrl
[1],
1359 'foot_ik': controls
[6], 'pole': pole
, 'footroll': controls
[5], 'mfoot_ik': ik_ctrl
[2],
1360 'main_parent': parent
}
1361 args
= (controls
[0], controls
[1], controls
[2], controls
[3],
1362 controls
[6], controls
[5], pole
, parent
)
1365 if not bones_in_frame(f
, rig
, *args
):
1369 bpy
.ops
.anim
.keyframe_insert_menu(type='BUILTIN_KSI_VisualLocRot')
1370 bpy
.ops
.anim
.keyframe_insert_menu(type='Scaling')
1372 bpy
.ops
.pose
.select_all(action
='DESELECT')
1373 limb_generated_names
.pop(group
)
1377 def ik_to_fk(rig
: ArmatureObject
, window
='ALL'):
1378 scn
= bpy
.context
.scene
1379 id_store
= bpy
.context
.window_manager
1381 rig_id
= rig
.data
['rig_id']
1382 leg_fk2ik
= eval('bpy.ops.pose.rigify_leg_fk2ik_' + rig_id
)
1383 arm_fk2ik
= eval('bpy.ops.pose.rigify_arm_fk2ik_' + rig_id
)
1384 limb_generated_names
= get_limb_generated_names(rig
)
1387 frames
= get_keyed_frames_in_range(bpy
.context
, rig
)
1388 elif window
== 'CURRENT':
1389 frames
= [scn
.frame_current
]
1391 frames
= [scn
.frame_current
]
1393 only_selected
= get_transfer_only_selected(id_store
)
1395 if not only_selected
:
1396 bpy
.ops
.pose
.select_all(action
='DESELECT')
1397 pose_bones
= rig
.pose
.bones
1399 pose_bones
= bpy
.context
.selected_pose_bones
1400 bpy
.ops
.pose
.select_all(action
='DESELECT')
1402 for b
in pose_bones
:
1403 for group
in limb_generated_names
:
1404 if b
.name
in limb_generated_names
[group
].values() or b
.name
in limb_generated_names
[group
]['controls']\
1405 or b
.name
in limb_generated_names
[group
]['ik_ctrl']:
1406 names
= limb_generated_names
[group
]
1407 if names
['limb_type'] == 'arm':
1409 controls
= names
['controls']
1410 ik_ctrl
= names
['ik_ctrl']
1411 # fk_ctrl = names['fk_ctrl']
1412 parent
= names
['parent']
1413 pole
= names
['pole']
1414 rig
.pose
.bones
[controls
[1]].bone
.select
= True
1415 rig
.pose
.bones
[controls
[2]].bone
.select
= True
1416 rig
.pose
.bones
[controls
[3]].bone
.select
= True
1417 kwargs
= {'uarm_fk': controls
[1], 'farm_fk': controls
[2], 'hand_fk': controls
[3],
1418 'uarm_ik': controls
[0], 'farm_ik': ik_ctrl
[1],
1419 'hand_ik': controls
[4]}
1420 args
= (controls
[0], controls
[1], controls
[2], controls
[3],
1421 controls
[4], pole
, parent
)
1424 controls
= names
['controls']
1425 ik_ctrl
= names
['ik_ctrl']
1426 # fk_ctrl = names['fk_ctrl']
1427 parent
= names
['parent']
1428 pole
= names
['pole']
1429 rig
.pose
.bones
[controls
[1]].bone
.select
= True
1430 rig
.pose
.bones
[controls
[2]].bone
.select
= True
1431 rig
.pose
.bones
[controls
[3]].bone
.select
= True
1432 # noinspection SpellCheckingInspection
1433 kwargs
= {'thigh_fk': controls
[1], 'shin_fk': controls
[2], 'foot_fk': controls
[3],
1434 'mfoot_fk': controls
[7], 'thigh_ik': controls
[0], 'shin_ik': ik_ctrl
[1],
1435 'foot_ik': ik_ctrl
[2], 'mfoot_ik': ik_ctrl
[2]}
1436 args
= (controls
[0], controls
[1], controls
[2], controls
[3],
1437 controls
[6], controls
[5], pole
, parent
)
1440 if not bones_in_frame(f
, rig
, *args
):
1444 bpy
.ops
.anim
.keyframe_insert_menu(type='BUILTIN_KSI_VisualLocRot')
1445 bpy
.ops
.anim
.keyframe_insert_menu(type='Scaling')
1447 bpy
.ops
.pose
.select_all(action
='DESELECT')
1448 limb_generated_names
.pop(group
)
1452 def clear_animation(act
, anim_type
, names
):
1455 if names
[group
]['limb_type'] == 'arm':
1456 if anim_type
== 'IK':
1457 bones
.extend([names
[group
]['controls'][0], names
[group
]['controls'][4]])
1458 elif anim_type
== 'FK':
1459 bones
.extend([names
[group
]['controls'][1], names
[group
]['controls'][2], names
[group
]['controls'][3]])
1461 if anim_type
== 'IK':
1462 bones
.extend([names
[group
]['controls'][0], names
[group
]['controls'][6], names
[group
]['controls'][5],
1463 names
[group
]['controls'][4]])
1464 elif anim_type
== 'FK':
1465 bones
.extend([names
[group
]['controls'][1], names
[group
]['controls'][2], names
[group
]['controls'][3],
1466 names
[group
]['controls'][4]])
1468 for fcu
in act
.fcurves
:
1469 words
= fcu
.data_path
.split('"')
1470 if words
[0] == "pose.bones[" and words
[1] in bones
:
1471 f_curves
.append(fcu
)
1476 for fcu
in f_curves
:
1477 act
.fcurves
.remove(fcu
)
1479 # Put cleared bones back to rest pose
1480 bpy
.ops
.pose
.loc_clear()
1481 bpy
.ops
.pose
.rot_clear()
1482 bpy
.ops
.pose
.scale_clear()
1487 def rot_pole_toggle(rig
: ArmatureObject
, window
='ALL', value
=False, toggle
=False, bake
=False):
1488 scn
= bpy
.context
.scene
1489 id_store
= bpy
.context
.window_manager
1491 rig_id
= rig
.data
['rig_id']
1492 leg_fk2ik
= eval('bpy.ops.pose.rigify_leg_fk2ik_' + rig_id
)
1493 arm_fk2ik
= eval('bpy.ops.pose.rigify_arm_fk2ik_' + rig_id
)
1494 leg_ik2fk
= eval('bpy.ops.pose.rigify_leg_ik2fk_' + rig_id
)
1495 arm_ik2fk
= eval('bpy.ops.pose.rigify_arm_ik2fk_' + rig_id
)
1496 limb_generated_names
= get_limb_generated_names(rig
)
1499 frames
= get_keyed_frames_in_range(bpy
.context
, rig
)
1500 elif window
== 'CURRENT':
1501 frames
= [scn
.frame_current
]
1503 frames
= [scn
.frame_current
]
1505 only_selected
= get_transfer_only_selected(id_store
)
1507 if not only_selected
:
1508 bpy
.ops
.pose
.select_all(action
='DESELECT')
1509 pose_bones
= rig
.pose
.bones
1511 pose_bones
= bpy
.context
.selected_pose_bones
1512 bpy
.ops
.pose
.select_all(action
='DESELECT')
1514 for b
in pose_bones
:
1515 for group
in limb_generated_names
:
1516 names
= limb_generated_names
[group
]
1519 new_pole_vector_value
= not rig
.pose
.bones
[names
['parent']]['pole_vector']
1521 new_pole_vector_value
= value
1523 if b
.name
in names
.values() or b
.name
in names
['controls'] or b
.name
in names
['ik_ctrl']:
1524 if names
['limb_type'] == 'arm':
1527 controls
= names
['controls']
1528 ik_ctrl
= names
['ik_ctrl']
1529 # fk_ctrl = names['fk_ctrl']
1530 parent
= names
['parent']
1531 pole
= names
['pole']
1532 rig
.pose
.bones
[controls
[0]].bone
.select
= not new_pole_vector_value
1533 rig
.pose
.bones
[controls
[4]].bone
.select
= not new_pole_vector_value
1534 rig
.pose
.bones
[parent
].bone
.select
= not new_pole_vector_value
1535 rig
.pose
.bones
[pole
].bone
.select
= new_pole_vector_value
1537 kwargs1
= {'uarm_fk': controls
[1], 'farm_fk': controls
[2], 'hand_fk': controls
[3],
1538 'uarm_ik': controls
[0], 'farm_ik': ik_ctrl
[1],
1539 'hand_ik': controls
[4]}
1540 kwargs2
= {'uarm_fk': controls
[1], 'farm_fk': controls
[2], 'hand_fk': controls
[3],
1541 'uarm_ik': controls
[0], 'farm_ik': ik_ctrl
[1], 'hand_ik': controls
[4],
1542 'pole': pole
, 'main_parent': parent
}
1543 args
= (controls
[0], controls
[4], pole
, parent
)
1547 controls
= names
['controls']
1548 ik_ctrl
= names
['ik_ctrl']
1549 # fk_ctrl = names['fk_ctrl']
1550 parent
= names
['parent']
1551 pole
= names
['pole']
1552 rig
.pose
.bones
[controls
[0]].bone
.select
= not new_pole_vector_value
1553 rig
.pose
.bones
[controls
[6]].bone
.select
= not new_pole_vector_value
1554 rig
.pose
.bones
[controls
[5]].bone
.select
= not new_pole_vector_value
1555 rig
.pose
.bones
[parent
].bone
.select
= not new_pole_vector_value
1556 rig
.pose
.bones
[pole
].bone
.select
= new_pole_vector_value
1558 # noinspection SpellCheckingInspection
1559 kwargs1
= {'thigh_fk': controls
[1], 'shin_fk': controls
[2], 'foot_fk': controls
[3],
1560 'mfoot_fk': controls
[7], 'thigh_ik': controls
[0], 'shin_ik': ik_ctrl
[1],
1561 'foot_ik': ik_ctrl
[2], 'mfoot_ik': ik_ctrl
[2]}
1562 # noinspection SpellCheckingInspection
1563 kwargs2
= {'thigh_fk': controls
[1], 'shin_fk': controls
[2], 'foot_fk': controls
[3],
1564 'mfoot_fk': controls
[7], 'thigh_ik': controls
[0], 'shin_ik': ik_ctrl
[1],
1565 'foot_ik': controls
[6], 'pole': pole
, 'footroll': controls
[5], 'mfoot_ik': ik_ctrl
[2],
1566 'main_parent': parent
}
1567 args
= (controls
[0], controls
[6], controls
[5], pole
, parent
)
1570 if bake
and not bones_in_frame(f
, rig
, *args
):
1574 rig
.pose
.bones
[names
['parent']]['pole_vector'] = new_pole_vector_value
1577 bpy
.ops
.anim
.keyframe_insert_menu(type='BUILTIN_KSI_VisualLocRot')
1578 bpy
.ops
.anim
.keyframe_insert_menu(type='Scaling')
1579 overwrite_prop_animation(rig
, rig
.pose
.bones
[parent
], 'pole_vector', new_pole_vector_value
, [f
])
1581 bpy
.ops
.pose
.select_all(action
='DESELECT')
1582 limb_generated_names
.pop(group
)
1587 # noinspection PyPep8Naming
1588 class OBJECT_OT_IK2FK(bpy
.types
.Operator
):
1589 """ Snaps IK limb on FK limb at current frame"""
1590 bl_idname
= "rigify.ik2fk"
1592 bl_description
= "Snaps IK limb on FK"
1593 bl_options
= {'INTERNAL'}
1595 def execute(self
, context
):
1596 rig
= verify_armature_obj(context
.object)
1598 fk_to_ik(rig
, window
='CURRENT')
1603 # noinspection PyPep8Naming
1604 class OBJECT_OT_FK2IK(bpy
.types
.Operator
):
1605 """ Snaps FK limb on IK limb at current frame"""
1606 bl_idname
= "rigify.fk2ik"
1608 bl_description
= "Snaps FK limb on IK"
1609 bl_options
= {'INTERNAL'}
1611 def execute(self
, context
):
1612 rig
= verify_armature_obj(context
.object)
1614 ik_to_fk(rig
, window
='CURRENT')
1619 # noinspection PyPep8Naming
1620 class OBJECT_OT_TransferFKtoIK(bpy
.types
.Operator
):
1621 """Transfers FK animation to IK"""
1622 bl_idname
= "rigify.transfer_fk_to_ik"
1623 bl_label
= "Transfer FK anim to IK"
1624 bl_description
= "Transfer FK animation to IK bones"
1625 bl_options
= {'INTERNAL'}
1627 def execute(self
, context
):
1628 rig
= verify_armature_obj(context
.object)
1635 # noinspection PyPep8Naming
1636 class OBJECT_OT_TransferIKtoFK(bpy
.types
.Operator
):
1637 """Transfers FK animation to IK"""
1638 bl_idname
= "rigify.transfer_ik_to_fk"
1639 bl_label
= "Transfer IK anim to FK"
1640 bl_description
= "Transfer IK animation to FK bones"
1641 bl_options
= {'INTERNAL'}
1643 def execute(self
, context
):
1644 rig
= verify_armature_obj(context
.object)
1651 # noinspection PyPep8Naming
1652 class OBJECT_OT_ClearAnimation(bpy
.types
.Operator
):
1653 bl_idname
= "rigify.clear_animation"
1654 bl_label
= "Clear Animation"
1655 bl_description
= "Clear Animation For FK or IK Bones"
1656 bl_options
= {'INTERNAL'}
1658 anim_type
: StringProperty()
1660 def execute(self
, context
):
1661 rig
= verify_armature_obj(context
.object)
1663 if not rig
.animation_data
:
1666 act
= rig
.animation_data
.action
1670 clear_animation(act
, self
.anim_type
, names
=get_limb_generated_names(rig
))
1674 # noinspection PyPep8Naming
1675 class OBJECT_OT_Rot2Pole(bpy
.types
.Operator
):
1676 bl_idname
= "rigify.rotation_pole"
1677 bl_label
= "Rotation - Pole toggle"
1678 bl_description
= "Toggles IK chain between rotation and pole target"
1679 bl_options
= {'INTERNAL'}
1681 bone_name
: StringProperty(default
='')
1682 window
: StringProperty(default
='ALL')
1683 toggle
: BoolProperty(default
=True)
1684 value
: BoolProperty(default
=True)
1685 bake
: BoolProperty(default
=True)
1687 def execute(self
, context
):
1688 rig
= verify_armature_obj(context
.object)
1691 bpy
.ops
.pose
.select_all(action
='DESELECT')
1692 rig
.pose
.bones
[self
.bone_name
].bone
.select
= True
1694 rot_pole_toggle(rig
, window
=self
.window
, toggle
=self
.toggle
, value
=self
.value
, bake
=self
.bake
)
1698 # noinspection PyPep8Naming
1699 class POSE_OT_rigify_collection_ref_add(bpy
.types
.Operator
):
1700 bl_idname
= "pose.rigify_collection_ref_add"
1701 bl_label
= "Add Bone Collection Reference"
1702 bl_description
= "Add a new row to the bone collection reference list"
1703 bl_options
= {'UNDO'}
1705 prop_name
: StringProperty(name
="Property Name")
1708 def poll(cls
, context
):
1709 return is_valid_metarig(context
) and context
.active_pose_bone
1711 def execute(self
, context
):
1712 params
= get_rigify_params(context
.active_pose_bone
)
1713 getattr(params
, self
.prop_name
).add()
1717 # noinspection PyPep8Naming
1718 class POSE_OT_rigify_collection_ref_remove(bpy
.types
.Operator
):
1719 bl_idname
= "pose.rigify_collection_ref_remove"
1720 bl_label
= "Remove Bone Collection Reference"
1721 bl_description
= "Remove this row from the bone collection reference list"
1722 bl_options
= {'UNDO'}
1724 prop_name
: StringProperty(name
="Property Name")
1725 index
: IntProperty(name
="Entry Index")
1728 def poll(cls
, context
):
1729 return is_valid_metarig(context
) and context
.active_pose_bone
1731 def execute(self
, context
):
1732 params
= get_rigify_params(context
.active_pose_bone
)
1733 getattr(params
, self
.prop_name
).remove(self
.index
)
1741 DATA_OT_rigify_add_color_sets
,
1742 DATA_OT_rigify_use_standard_colors
,
1743 DATA_OT_rigify_apply_selection_colors
,
1744 DATA_OT_rigify_color_set_add
,
1745 DATA_OT_rigify_color_set_add_theme
,
1746 DATA_OT_rigify_color_set_remove
,
1747 DATA_OT_rigify_color_set_remove_all
,
1748 DATA_UL_rigify_color_sets
,
1749 DATA_MT_rigify_color_sets_context_menu
,
1751 DATA_PT_rigify_advanced
,
1752 DATA_PT_rigify_color_sets
,
1753 DATA_UL_rigify_bone_collections
,
1754 DATA_PT_rigify_collection_list
,
1755 DATA_PT_rigify_collection_ui
,
1756 DATA_OT_rigify_collection_select
,
1757 DATA_OT_rigify_collection_set_ui_row
,
1758 DATA_OT_rigify_collection_add_ui_row
,
1759 DATA_PT_rigify_samples
,
1760 BONE_PT_rigify_buttons
,
1761 VIEW3D_PT_rigify_animation_tools
,
1762 VIEW3D_PT_tools_rigify_dev
,
1764 UpgradeMetarigTypes
,
1765 UpgradeMetarigLayers
,
1766 ValidateMetarigLayers
,
1770 EncodeMetarigSample
,
1774 OBJECT_OT_TransferFKtoIK
,
1775 OBJECT_OT_TransferIKtoFK
,
1776 OBJECT_OT_ClearAnimation
,
1778 POSE_OT_rigify_collection_ref_add
,
1779 POSE_OT_rigify_collection_ref_remove
,
1784 from bpy
.utils
import register_class
1786 animation_register()
1792 bpy
.types
.VIEW3D_MT_editor_menus
.append(draw_rigify_menu
)
1793 bpy
.types
.VIEW3D_MT_edit_mesh
.prepend(draw_mesh_edit_menu
)
1800 from bpy
.utils
import unregister_class
1803 rot_mode
.unregister()
1807 unregister_class(cls
)
1809 bpy
.types
.VIEW3D_MT_editor_menus
.remove(draw_rigify_menu
)
1810 bpy
.types
.VIEW3D_MT_edit_mesh
.remove(draw_mesh_edit_menu
)
1812 animation_unregister()