1 # SPDX-FileCopyrightText: 2010-2023 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
8 "author": "Nathan Vegdahl, Lucio Rossi, Ivan Cappiello, Alexander Gavrilov", # noqa
10 "description": "Automatic rigging from building-block components",
11 "location": "Armature properties, Bone properties, View3d tools panel, Armature Add menu",
12 "doc_url": "{BLENDER_MANUAL_URL}/addons/rigging/rigify/index.html",
13 "category": "Rigging",
21 from bpy
.app
.translations
import pgettext_iface
as iface_
24 # The order in which core modules of the addon are loaded and reloaded.
25 # Modules not in this list are removed from memory upon reload.
26 # With the sole exception of 'utils', modules must be listed in the
27 # correct dependency order.
28 initial_load_order
= [
37 'utils.widgets_basic',
38 'utils.widgets_special',
53 'utils.action_layers',
61 def get_loaded_modules():
62 prefix
= __name__
+ '.'
63 return [name
for name
in sys
.modules
if name
.startswith(prefix
)]
67 fixed_modules
= set(reload_list
)
69 for name
in get_loaded_modules():
70 if name
not in fixed_modules
:
73 for name
in reload_list
:
74 importlib
.reload(sys
.modules
[name
])
77 def compare_module_list(a
: list[str], b
: list[str]):
78 # HACK: ignore the "utils" module when comparing module load orders,
79 # because it is inconsistent for reasons unknown.
80 # See rBAa918332cc3f821f5a70b1de53b65dd9ca596b093.
81 utils_module_name
= __name__
+ '.utils'
83 a_copy
.remove(utils_module_name
)
85 b_copy
.remove(utils_module_name
)
86 return a_copy
== b_copy
89 def load_initial_modules() -> list[str]:
90 names
= [__name__
+ '.' + name
for name
in initial_load_order
]
92 for i
, name
in enumerate(names
):
93 importlib
.import_module(name
)
95 module_list
= get_loaded_modules()
96 expected_list
= names
[0: max(11, i
+1)]
98 if not compare_module_list(module_list
, expected_list
):
99 print(f
'!!! RIGIFY: initial load order mismatch after {name} - expected: \n',
100 expected_list
, '\nGot:\n', module_list
)
106 rig_lists
.get_internal_rigs()
107 metarig_menu
.init_metarig_menu()
110 if "reload_list" in locals():
113 load_list
= load_initial_modules()
115 from . import (utils
, base_rig
, base_generate
, rig_ui_template
, feature_set_list
, rig_lists
,
116 generate
, ui
, metarig_menu
, operators
)
118 reload_list
= reload_list_init
= get_loaded_modules()
120 if not compare_module_list(reload_list
, load_list
):
121 print('!!! RIGIFY: initial load order mismatch - expected: \n',
122 load_list
, '\nGot:\n', reload_list
)
127 from bpy
.types
import AddonPreferences
# noqa: E402
128 from bpy
.props
import ( # noqa: E402
140 """Returns the currently active generator instance."""
141 return base_generate
.BaseGenerator
.instance
144 class RigifyFeatureSets(bpy
.types
.PropertyGroup
):
145 name
: bpy
.props
.StringProperty()
146 module_name
: bpy
.props
.StringProperty()
147 link
: bpy
.props
.StringProperty()
148 has_errors
: bpy
.props
.BoolProperty()
149 has_exceptions
: bpy
.props
.BoolProperty()
151 def toggle_feature_set(self
, context
):
152 feature_set_list
.call_register_function(self
.module_name
, self
.enabled
)
153 RigifyPreferences
.get_instance(context
).update_external_rigs()
155 enabled
: bpy
.props
.BoolProperty(
157 description
="Whether this feature-set is registered or not",
158 update
=toggle_feature_set
,
163 # noinspection PyPep8Naming
164 class RIGIFY_UL_FeatureSets(bpy
.types
.UIList
):
165 def draw_item(self
, context
, layout
, data
, item
, icon
, active_data
, active_propname
, _index
=0, _flag
=0):
166 # rigify_prefs: RigifyPreferences = data
167 # feature_sets = rigify_prefs.rigify_feature_sets
168 # active_set: RigifyFeatureSets = feature_sets[rigify_prefs.active_feature_set_index]
169 feature_set_entry
: RigifyFeatureSets
= item
170 if self
.layout_type
in {'DEFAULT', 'COMPACT'}:
173 name
= feature_set_entry
.name
176 if not feature_set_entry
.module_name
:
177 name
+= iface_(" (not installed)")
179 elif feature_set_entry
.has_errors
or feature_set_entry
.has_exceptions
:
183 row
.label(text
=name
, icon
=icon
, translate
=False)
185 if feature_set_entry
.module_name
:
186 icon
= 'CHECKBOX_HLT' if feature_set_entry
.enabled
else 'CHECKBOX_DEHLT'
187 row
.enabled
= feature_set_entry
.enabled
188 layout
.prop(feature_set_entry
, 'enabled', text
="", icon
=icon
, emboss
=False)
191 elif self
.layout_type
in {'GRID'}:
195 class RigifyPreferences(AddonPreferences
):
196 # this must match the addon name, use '__package__'
197 # when defining this in a submodule of a python package.
201 def get_instance(context
: bpy
.types
.Context
= None) -> 'RigifyPreferences':
202 prefs
= (context
or bpy
.context
).preferences
.addons
[__package__
].preferences
203 assert isinstance(prefs
, RigifyPreferences
)
206 def register_feature_sets(self
, do_register
: bool):
207 """Call register or unregister of external feature sets"""
208 self
.refresh_installed_feature_sets()
209 for set_name
in feature_set_list
.get_enabled_modules_names():
210 feature_set_list
.call_register_function(set_name
, do_register
)
212 def refresh_installed_feature_sets(self
):
213 """Synchronize preferences entries with what's actually in the file system."""
214 feature_set_prefs
= self
.rigify_feature_sets
216 module_names
= feature_set_list
.get_installed_modules_names()
218 # If there is a feature set preferences entry with no corresponding
219 # installed module, user must've manually removed it from the filesystem,
220 # so let's remove such entries.
221 to_delete
= [i
for i
, fs
in enumerate(feature_set_prefs
)
222 if fs
.module_name
not in module_names
]
223 for i
in reversed(to_delete
):
224 feature_set_prefs
.remove(i
)
226 # If there is an installed feature set in the file system but no corresponding
227 # entry, user must've installed it manually. Make sure it has an entry.
228 for module_name
in module_names
:
229 for fs
in feature_set_prefs
:
230 if module_name
== fs
.module_name
:
233 fs
= feature_set_prefs
.add()
234 fs
.name
= feature_set_list
.get_ui_name(module_name
)
235 fs
.module_name
= module_name
237 # Update the feature set info
238 fs_info
= [feature_set_list
.get_info_dict(fs
.module_name
) for fs
in feature_set_prefs
]
240 for fs
, info
in zip(feature_set_prefs
, fs_info
):
242 fs
.name
= info
["name"]
244 fs
.link
= info
["link"]
246 for fs
, info
in zip(feature_set_prefs
, fs_info
):
247 fs
.has_errors
= check_feature_set_error(fs
, info
, None)
248 fs
.has_exceptions
= False
250 # Add dummy entries for promoted feature sets
251 used_links
= set(fs
.link
for fs
in feature_set_prefs
)
253 for info
in feature_set_list
.PROMOTED_FEATURE_SETS
:
254 if info
["link"] not in used_links
:
255 fs
= feature_set_prefs
.add()
257 fs
.name
= info
["name"]
258 fs
.link
= info
["link"]
261 def update_external_rigs():
262 """Get external feature sets"""
264 set_list
= feature_set_list
.get_enabled_modules_names()
267 print('Reloading external rigs...')
268 rig_lists
.get_external_rigs(set_list
)
271 print('Reloading external metarigs...')
272 metarig_menu
.get_external_metarigs(set_list
)
274 # Re-register rig parameters
275 register_rig_parameters()
277 rigify_feature_sets
: bpy
.props
.CollectionProperty(type=RigifyFeatureSets
)
278 active_feature_set_index
: IntProperty()
280 def draw(self
, context
: bpy
.types
.Context
):
281 layout
: bpy
.types
.UILayout
= self
.layout
283 layout
.label(text
="Feature Sets:")
285 layout
.operator("wm.rigify_add_feature_set", text
="Install Feature Set from File...", icon
='FILEBROWSER')
289 'RIGIFY_UL_FeatureSets',
291 self
, "rigify_feature_sets",
292 self
, 'active_feature_set_index'
295 # Clamp active index to ensure it is in bounds.
296 self
.active_feature_set_index
= max(0, min(self
.active_feature_set_index
, len(self
.rigify_feature_sets
)-1))
298 if len(self
.rigify_feature_sets
) > 0:
299 active_fs
= self
.rigify_feature_sets
[self
.active_feature_set_index
]
302 draw_feature_set_prefs(layout
, context
, active_fs
)
305 def check_feature_set_error(_feature_set
: RigifyFeatureSets
, info
: dict, layout
: bpy
.types
.UILayout |
None):
309 if 'blender' in info
and info
['blender'] > bpy
.app
.version
:
312 split
= layout
.row().split(factor
=split_factor
)
313 split
.label(text
="Error:")
317 text
=iface_("This feature set requires Blender %s or newer to work properly."
318 ) % ".".join(str(x
) for x
in info
['blender']),
319 icon
='ERROR', translate
=False
322 for dep_link
in info
.get("dependencies", []):
323 if not feature_set_list
.get_module_by_link_safe(dep_link
):
326 split
= layout
.row().split(factor
=split_factor
)
327 split
.label(text
="Error:")
332 text
="This feature set depends on the following feature set to work properly:",
335 sub_split
= col
.split(factor
=0.8)
336 sub
= sub_split
.row()
338 sub
.label(text
=dep_link
, translate
=False, icon
='BLANK1')
339 op
= sub_split
.operator('wm.url_open', text
="Repository", icon
='URL')
345 def draw_feature_set_prefs(layout
: bpy
.types
.UILayout
, _context
: bpy
.types
.Context
, feature_set
: RigifyFeatureSets
):
346 if feature_set
.module_name
:
347 info
= feature_set_list
.get_info_dict(feature_set
.module_name
)
350 for item
in feature_set_list
.PROMOTED_FEATURE_SETS
:
351 if item
["link"] == feature_set
.link
:
355 description
= feature_set
.name
356 if 'description' in info
:
357 description
= info
['description']
359 col
= layout
.column()
362 check_feature_set_error(feature_set
, info
, col
)
364 if feature_set
.has_exceptions
:
365 split
= col
.row().split(factor
=split_factor
)
366 split
.label(text
="Error:")
369 sub
.label(text
="This feature set failed to load correctly.", icon
='ERROR')
371 split
= col
.row().split(factor
=split_factor
)
372 split
.label(text
="Description:")
373 col_desc
= split
.column()
374 for description_line
in description
.split("\n"):
375 col_desc
.label(text
=description_line
)
378 split
= col
.row().split(factor
=split_factor
)
379 split
.label(text
="Author:")
380 split
.label(text
=info
["author"])
382 if 'version' in info
:
383 split
= col
.row().split(factor
=split_factor
)
384 split
.label(text
="Version:")
385 split
.label(text
=".".join(str(x
) for x
in info
['version']), translate
=False)
387 if 'warning' in info
:
388 split
= col
.row().split(factor
=split_factor
)
389 split
.label(text
="Warning:")
390 split
.label(text
=" " + info
['warning'], icon
='ERROR')
392 split
= col
.row().split(factor
=split_factor
)
393 split
.label(text
="Internet:")
396 op
= row
.operator('wm.url_open', text
="Repository", icon
='URL')
397 op
.url
= info
['link']
398 if 'doc_url' in info
:
399 op
= row
.operator('wm.url_open', text
="Documentation", icon
='HELP')
400 op
.url
= info
['doc_url']
401 if 'tracker_url' in info
:
402 op
= row
.operator('wm.url_open', text
="Report a Bug", icon
='URL')
403 op
.url
= info
['tracker_url']
405 if feature_set
.module_name
:
406 mod
= feature_set_list
.get_module_safe(feature_set
.module_name
)
408 split
= col
.row().split(factor
=split_factor
)
409 split
.label(text
="File:")
410 split
.label(text
=mod
.__file
__, translate
=False)
412 split
= col
.row().split(factor
=split_factor
)
414 split
.operator("wm.rigify_remove_feature_set", text
="Remove", icon
='CANCEL')
417 class RigifyName(bpy
.types
.PropertyGroup
):
418 name
: StringProperty()
421 class RigifyColorSet(bpy
.types
.PropertyGroup
):
422 name
: StringProperty(name
="Color Set", default
=" ")
423 active
: FloatVectorProperty(
426 default
=(1.0, 1.0, 1.0),
428 description
="color picker"
430 normal
: FloatVectorProperty(
433 default
=(1.0, 1.0, 1.0),
435 description
="color picker"
437 select
: FloatVectorProperty(
440 default
=(1.0, 1.0, 1.0),
442 description
="color picker"
444 standard_colors_lock
: BoolProperty(default
=True)
446 def apply(self
, color
: bpy
.types
.BoneColor
):
447 color
.palette
= 'CUSTOM'
448 color
.custom
.normal
= utils
.misc
.gamma_correct(self
.normal
)
449 color
.custom
.select
= utils
.misc
.gamma_correct(self
.select
)
450 color
.custom
.active
= utils
.misc
.gamma_correct(self
.active
)
453 class RigifySelectionColors(bpy
.types
.PropertyGroup
):
454 select
: FloatVectorProperty(
457 default
=(0.314, 0.784, 1.0),
459 description
="color picker"
462 active
: FloatVectorProperty(
465 default
=(0.549, 1.0, 1.0),
467 description
="color picker"
471 class RigifyParameters(bpy
.types
.PropertyGroup
):
472 name
: StringProperty()
475 class RigifyBoneCollectionReference(bpy
.types
.PropertyGroup
):
476 """Reference from a RigifyParameters field to a bone collection."""
478 uid
: IntProperty(name
="Unique ID", default
=-1)
480 def find_collection(self
, *, update
=False, raise_error
=False) -> bpy
.types
.BoneCollection |
None:
481 return utils
.layers
.resolve_collection_reference(self
.id_data
, self
, update
=update
, raise_error
=raise_error
)
483 def set_collection(self
, coll
: bpy
.types
.BoneCollection |
None):
488 self
.uid
= utils
.layers
.ensure_collection_uid(coll
)
489 self
["name"] = coll
.name
492 if coll
:= self
.find_collection(update
=False):
496 return self
.get('name') or '?'
500 def _name_set(self
, new_val
):
502 self
.set_collection(None)
505 arm
= self
.id_data
.data
507 if new_coll
:= arm
.collections_all
.get(new_val
):
508 self
.set_collection(new_coll
)
510 self
.find_collection(update
=True)
512 def _name_search(self
, _context
, _edit
):
513 arm
= self
.id_data
.data
514 return [coll
.name
for coll
in utils
.misc
.flatten_children(arm
.collections
)]
516 name
: StringProperty(
517 name
="Collection Name", description
="Name of the referenced bone collection",
518 get
=_name_get
, set=_name_set
, search
=_name_search
522 # Parameter update callback
527 def update_callback(prop_name
):
528 from .utils
.rig
import get_rigify_type
530 def callback(params
, context
):
532 # Do not recursively call if the callback updates other parameters
536 bone
= context
.active_pose_bone
538 if bone
and bone
.rigify_parameters
== params
:
539 rig_info
= rig_lists
.rigs
.get(get_rigify_type(bone
), None)
541 rig_cb
= getattr(rig_info
["module"].Rig
, 'on_parameter_update', None)
543 rig_cb(context
, bone
, params
, prop_name
)
550 # Remember the initial property set
551 RIGIFY_PARAMETERS_BASE_DIR
= set(dir(RigifyParameters
))
552 RIGIFY_PARAMETER_TABLE
= {'name': ('DEFAULT', StringProperty())}
555 def clear_rigify_parameters():
556 for name
in list(dir(RigifyParameters
)):
557 if name
not in RIGIFY_PARAMETERS_BASE_DIR
:
558 delattr(RigifyParameters
, name
)
559 if name
in RIGIFY_PARAMETER_TABLE
:
560 del RIGIFY_PARAMETER_TABLE
[name
]
563 def format_property_spec(spec
):
564 """Turns the return value of bpy.props.SomeProperty(...) into a readable string."""
565 callback
, params
= spec
566 param_str
= ["%s=%r" % (k
, v
) for k
, v
in params
.items()]
567 return "%s(%s)" % (callback
.__name
__, ', '.join(param_str
))
570 class RigifyParameterValidator(object):
572 A wrapper around RigifyParameters that verifies properties
573 defined from rigs for incompatible redefinitions using a table.
575 Relies on the implementation details of bpy.props.* return values:
576 specifically, they just return a tuple containing the real define
577 function, and a dictionary with parameters. This allows comparing
578 parameters before the property is actually defined.
584 def __init__(self
, params
, rig_name
, prop_table
):
585 self
.__params
= params
586 self
.__rig
_name
= rig_name
587 self
.__prop
_table
= prop_table
589 def __getattr__(self
, name
):
590 return getattr(self
.__params
, name
)
592 def __setattr__(self
, name
, val_original
):
593 # allow __init__ to work correctly
594 if hasattr(RigifyParameterValidator
, name
):
595 return object.__setattr
__(self
, name
, val_original
)
597 if not isinstance(val_original
, bpy
.props
._PropertyDeferred
): # noqa
598 print(f
"!!! RIGIFY RIG {self.__rig_name}: "
599 f
"INVALID DEFINITION FOR RIG PARAMETER {name}: {repr(val_original)}\n")
602 # actually defining the property modifies the dictionary with new parameters, so copy it now
603 val
= (val_original
.function
, val_original
.keywords
)
604 new_def
= (val
[0], val
[1].copy())
606 if 'poll' in new_def
[1]:
607 del new_def
[1]['poll']
609 if name
in self
.__prop
_table
:
610 cur_rig
, cur_info
= self
.__prop
_table
[name
]
611 if new_def
!= cur_info
:
612 print(f
"!!! RIGIFY RIG {self.__rig_name}: REDEFINING PARAMETER {name} AS:\n\n"
613 f
" {format_property_spec(val)}\n"
614 f
"!!! PREVIOUS DEFINITION BY {cur_rig}:\n\n"
615 f
" {format_property_spec(cur_info)}\n")
617 # inject a generic update callback that calls the appropriate rig class method
618 if val
[0] != bpy
.props
.CollectionProperty
:
619 val
[1]['update'] = update_callback(name
)
621 setattr(self
.__params
, name
, val_original
)
622 self
.__prop
_table
[name
] = (self
.__rig
_name
, new_def
)
631 RigifyBoneCollectionReference
,
633 RigifySelectionColors
,
634 RIGIFY_UL_FeatureSets
,
641 from bpy
.utils
import register_class
645 feature_set_list
.register()
646 metarig_menu
.register()
654 bpy
.types
.Armature
.active_feature_set
= EnumProperty(
655 items
=feature_set_list
.feature_set_items
,
657 description
="Restrict the rig list to a specific custom feature set"
660 bpy
.types
.PoseBone
.rigify_type
= StringProperty(name
="Rigify Type", description
="Rig type for this bone")
661 bpy
.types
.PoseBone
.rigify_parameters
= PointerProperty(type=RigifyParameters
)
663 bpy
.types
.Armature
.rigify_colors
= CollectionProperty(type=RigifyColorSet
)
665 bpy
.types
.Armature
.rigify_selection_colors
= PointerProperty(type=RigifySelectionColors
)
667 bpy
.types
.Armature
.rigify_colors_index
= IntProperty(default
=-1)
668 bpy
.types
.Armature
.rigify_colors_lock
= BoolProperty(default
=True)
669 bpy
.types
.Armature
.rigify_theme_to_add
= EnumProperty(items
=(
670 ('THEME01', 'THEME01', ''),
671 ('THEME02', 'THEME02', ''),
672 ('THEME03', 'THEME03', ''),
673 ('THEME04', 'THEME04', ''),
674 ('THEME05', 'THEME05', ''),
675 ('THEME06', 'THEME06', ''),
676 ('THEME07', 'THEME07', ''),
677 ('THEME08', 'THEME08', ''),
678 ('THEME09', 'THEME09', ''),
679 ('THEME10', 'THEME10', ''),
680 ('THEME11', 'THEME11', ''),
681 ('THEME12', 'THEME12', ''),
682 ('THEME13', 'THEME13', ''),
683 ('THEME14', 'THEME14', ''),
684 ('THEME15', 'THEME15', ''),
685 ('THEME16', 'THEME16', ''),
686 ('THEME17', 'THEME17', ''),
687 ('THEME18', 'THEME18', ''),
688 ('THEME19', 'THEME19', ''),
689 ('THEME20', 'THEME20', '')
692 id_store
= bpy
.types
.WindowManager
693 id_store
.rigify_collection
= EnumProperty(
694 items
=(("All", "All", "All"),), default
="All",
695 name
="Rigify Active Collection",
696 description
="The selected rig collection")
698 id_store
.rigify_widgets
= CollectionProperty(type=RigifyName
)
699 id_store
.rigify_types
= CollectionProperty(type=RigifyName
)
700 id_store
.rigify_active_type
= IntProperty(name
="Rigify Active Type",
701 description
="The selected rig type")
703 bpy
.types
.Armature
.rigify_force_widget_update
= BoolProperty(
704 name
="Overwrite Widget Meshes",
705 description
="Forces Rigify to delete and rebuild all of the rig widget objects. By "
706 "default, already existing widgets are reused as-is to facilitate manual "
710 bpy
.types
.Armature
.rigify_mirror_widgets
= BoolProperty(
711 name
="Mirror Widgets",
712 description
="Make widgets for left and right side bones linked duplicates with negative "
713 "X scale for the right side, based on bone name symmetry",
716 bpy
.types
.Armature
.rigify_widgets_collection
= PointerProperty(
717 type=bpy
.types
.Collection
,
718 name
="Widgets Collection",
719 description
="Defines which collection to place widget objects in. If unset, a new one "
720 "will be created based on the name of the rig")
722 bpy
.types
.Armature
.rigify_rig_basename
= StringProperty(
723 name
="Rigify Rig Name",
724 description
="Optional. If specified, this name will be used for the newly generated rig, "
725 "widget collection and script. Otherwise, a name is generated based on the "
726 "name of the metarig object by replacing 'metarig' with 'rig', 'META' with "
727 "'RIG', or prefixing with 'RIG-'. When updating an already generated rig its "
728 "name is never changed",
731 bpy
.types
.Armature
.rigify_target_rig
= PointerProperty(
732 type=bpy
.types
.Object
,
733 name
="Rigify Target Rig",
734 description
="Defines which rig to overwrite. If unset, a new one will be created with "
735 "name based on the Rig Name option or the name of the metarig",
736 poll
=lambda self
, obj
: obj
.type == 'ARMATURE' and obj
.data
is not self
)
738 bpy
.types
.Armature
.rigify_rig_ui
= PointerProperty(
740 name
="Rigify Target Rig UI",
741 description
="Defines the UI to overwrite. If unset, a new one will be created and named "
742 "based on the name of the rig")
744 bpy
.types
.Armature
.rigify_finalize_script
= PointerProperty(
746 name
="Finalize Script",
747 description
="Run this script after generation to apply user-specific changes")
749 id_store
.rigify_transfer_only_selected
= BoolProperty(
750 name
="Transfer Only Selected",
751 description
="Transfer selected bones only", default
=True)
753 # BoneCollection properties
754 coll_store
= bpy
.types
.BoneCollection
756 coll_store
.rigify_uid
= IntProperty(name
="Unique ID", default
=-1)
757 coll_store
.rigify_ui_row
= IntProperty(
758 name
="UI Row", default
=0, min=0,
759 description
="If not zero, row of the UI panel where the button for this collection is shown")
760 coll_store
.rigify_ui_title
= StringProperty(
761 name
="UI Title", description
="Text to use on the UI panel button instead of the collection name")
762 coll_store
.rigify_sel_set
= BoolProperty(
763 name
="Add Selection Set", default
=False, description
='Add Selection Set for this collection')
764 coll_store
.rigify_color_set_id
= IntProperty(name
="Color Set ID", default
=0, min=0)
766 def ui_title_get(coll
):
767 return coll
.rigify_ui_title
or coll
.name
769 def ui_title_set(coll
, new_val
):
770 coll
.rigify_ui_title
= "" if new_val
== coll
.name
else new_val
772 coll_store
.rigify_ui_title_name
= StringProperty(
773 name
="UI Title", description
="Text to use on the UI panel button (does not edit the collection name)",
774 get
=ui_title_get
, set=ui_title_set
777 def color_set_get(coll
):
778 idx
= coll
.rigify_color_set_id
782 sets
= utils
.rig
.get_rigify_colors(coll
.id_data
)
783 return sets
[idx
- 1].name
if idx
<= len(sets
) else f
"? {idx}"
785 def color_set_set(coll
, new_val
):
787 coll
.rigify_color_set_id
= 0
789 sets
= utils
.rig
.get_rigify_colors(coll
.id_data
)
790 for i
, cset
in enumerate(sets
):
791 if cset
.name
== new_val
:
792 coll
.rigify_color_set_id
= i
+ 1
795 def color_set_search(coll
, _ctx
, _edit
):
796 return [cset
.name
for cset
in utils
.rig
.get_rigify_colors(coll
.id_data
)]
798 coll_store
.rigify_color_set_name
= StringProperty(
799 name
="Color Set", description
="Color set specifying bone colors for this group",
800 get
=color_set_get
, set=color_set_set
, search
=color_set_search
804 obj_store
= bpy
.types
.Object
806 obj_store
.rigify_owner_rig
= PointerProperty(
807 type=bpy
.types
.Object
,
808 name
="Rigify Owner Rig",
809 description
="Rig that owns this object and may delete or overwrite it upon re-generation")
811 prefs
= RigifyPreferences
.get_instance()
812 prefs
.register_feature_sets(True)
813 prefs
.update_external_rigs()
816 register_rig_parameters()
819 def register_rig_parameters():
820 for rig
in rig_lists
.rigs
:
821 rig_module
= rig_lists
.rigs
[rig
]['module']
822 rig_class
= rig_module
.Rig
823 rig_def
= rig_class
if hasattr(rig_class
, 'add_parameters') else rig_module
824 # noinspection PyBroadException
826 if hasattr(rig_def
, 'add_parameters'):
827 validator
= RigifyParameterValidator(RigifyParameters
, rig
, RIGIFY_PARAMETER_TABLE
)
828 rig_def
.add_parameters(validator
)
831 traceback
.print_exc()
835 from bpy
.utils
import unregister_class
837 prefs
= RigifyPreferences
.get_instance()
838 prefs
.register_feature_sets(False)
840 # Properties on PoseBones and Armature. (Annotated to suppress unknown attribute warnings.)
841 pose_bone
: typing
.Any
= bpy
.types
.PoseBone
843 del pose_bone
.rigify_type
844 del pose_bone
.rigify_parameters
846 arm_store
: typing
.Any
= bpy
.types
.Armature
848 del arm_store
.active_feature_set
849 del arm_store
.rigify_colors
850 del arm_store
.rigify_selection_colors
851 del arm_store
.rigify_colors_index
852 del arm_store
.rigify_colors_lock
853 del arm_store
.rigify_theme_to_add
854 del arm_store
.rigify_force_widget_update
855 del arm_store
.rigify_target_rig
856 del arm_store
.rigify_rig_ui
858 id_store
: typing
.Any
= bpy
.types
.WindowManager
860 del id_store
.rigify_collection
861 del id_store
.rigify_types
862 del id_store
.rigify_active_type
863 del id_store
.rigify_transfer_only_selected
865 coll_store
: typing
.Any
= bpy
.types
.BoneCollection
867 del coll_store
.rigify_uid
868 del coll_store
.rigify_ui_row
869 del coll_store
.rigify_ui_title
870 del coll_store
.rigify_ui_title_name
871 del coll_store
.rigify_sel_set
872 del coll_store
.rigify_color_set_id
873 del coll_store
.rigify_color_set_name
875 obj_store
: typing
.Any
= bpy
.types
.Object
877 del obj_store
.rigify_owner_rig
881 unregister_class(cls
)
883 clear_rigify_parameters()
886 operators
.unregister()
887 metarig_menu
.unregister()
889 feature_set_list
.unregister()