1 #====================== BEGIN GPL LICENSE BLOCK ======================
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 #======================= END GPL LICENSE BLOCK ========================
24 "author": "Nathan Vegdahl, Lucio Rossi, Ivan Cappiello, Alexander Gavrilov",
25 "blender": (2, 81, 0),
26 "description": "Automatic rigging from building-block components",
27 "location": "Armature properties, Bone properties, View3d tools panel, Armature Add menu",
28 "wiki_url": "https://docs.blender.org/manual/en/dev/addons/"
29 "rigging/rigify/index.html",
30 "category": "Rigging"}
38 # The order in which core modules of the addon are loaded and reloaded.
39 # Modules not in this list are removed from memory upon reload.
40 # With the sole exception of 'utils', modules must be listed in the
41 # correct dependency order.
42 initial_load_order
= [
52 'utils.widgets_basic',
53 'utils.widgets_special',
72 def get_loaded_modules():
73 prefix
= __name__
+ '.'
74 return [name
for name
in sys
.modules
if name
.startswith(prefix
)]
77 fixed_modules
= set(reload_list
)
79 for name
in get_loaded_modules():
80 if name
not in fixed_modules
:
83 for name
in reload_list
:
84 importlib
.reload(sys
.modules
[name
])
86 def load_initial_modules():
87 load_list
= [ __name__
+ '.' + name
for name
in initial_load_order
]
89 for i
, name
in enumerate(load_list
):
90 importlib
.import_module(name
)
92 module_list
= get_loaded_modules()
93 expected_list
= load_list
[0 : max(11, i
+1)]
95 if module_list
!= expected_list
:
96 print('!!! RIGIFY: initial load order mismatch after '+name
+' - expected: \n', expected_list
, '\nGot:\n', module_list
)
101 if not legacy_loaded
:
102 rig_lists
.get_internal_rigs()
103 metarig_menu
.init_metarig_menu()
106 if "reload_list" in locals():
109 legacy_loaded
= False
111 load_list
= load_initial_modules()
113 from . import (base_rig
, base_generate
, rig_ui_template
, feature_set_list
, rig_lists
, generate
, ui
, metarig_menu
)
115 reload_list
= reload_list_init
= get_loaded_modules()
117 if reload_list
!= load_list
:
118 print('!!! RIGIFY: initial load order mismatch - expected: \n', load_list
, '\nGot:\n', reload_list
)
123 from bpy
.types
import AddonPreferences
124 from bpy
.props
import (
135 class RigifyPreferences(AddonPreferences
):
136 # this must match the addon name, use '__package__'
137 # when defining this in a submodule of a python package.
140 def update_legacy(self
, context
):
141 global legacy_loaded
, reload_list
144 if legacy_loaded
: # already in legacy mode. needed when rigify is reloaded
150 globals().pop('utils')
151 globals().pop('rig_lists')
152 globals().pop('generate')
154 globals().pop('metarig_menu')
156 from .legacy
import utils
, rig_lists
, generate
, ui
, metarig_menu
158 print("ENTERING RIGIFY LEGACY\r\n")
161 reload_list
+= [ m
.__name
__ for m
in [ legacy
, utils
, rig_lists
, generate
, ui
, metarig_menu
] ]
163 globals()['utils'] = legacy
.utils
164 globals()['rig_lists'] = legacy
.rig_lists
165 globals()['generate'] = legacy
.generate
166 globals()['ui'] = legacy
.ui
167 globals()['metarig_menu'] = legacy
.metarig_menu
174 globals().pop('utils')
175 globals().pop('rig_lists')
176 globals().pop('generate')
178 globals().pop('metarig_menu')
180 from . import utils
, rig_lists
, generate
, ui
, metarig_menu
182 print("EXIT RIGIFY LEGACY\r\n")
184 globals()['utils'] = utils
185 globals()['rig_lists'] = rig_lists
186 globals()['generate'] = generate
188 globals()['metarig_menu'] = metarig_menu
190 legacy_loaded
= False
191 reload_list
= reload_list_init
198 def update_external_rigs(self
, force
=False):
199 """Get external feature sets"""
203 set_list
= feature_set_list
.get_installed_list()
205 if force
or len(set_list
) > 0:
207 print('Reloading external rigs...')
208 rig_lists
.get_external_rigs(set_list
)
211 print('Reloading external metarigs...')
212 metarig_menu
.get_external_metarigs(set_list
)
214 # Re-register rig parameters
215 register_rig_parameters()
217 legacy_mode
: BoolProperty(
218 name
='Rigify Legacy Mode',
219 description
='Select if you want to use Rigify in legacy mode',
224 show_expanded
: BoolProperty()
226 show_rigs_folder_expanded
: BoolProperty()
228 def draw(self
, context
):
230 column
= layout
.column()
234 expand
= getattr(self
, 'show_expanded')
235 icon
= 'TRIA_DOWN' if expand
else 'TRIA_RIGHT'
239 sub
.context_pointer_set('addon_prefs', self
)
240 sub
.alignment
= 'LEFT'
241 op
= sub
.operator('wm.context_toggle', text
='', icon
=icon
,
243 op
.data_path
= 'addon_prefs.show_expanded'
244 sub
.label(text
='{}: {}'.format('Rigify', 'Enable Legacy Mode'))
246 sub
.alignment
= 'RIGHT'
247 sub
.prop(self
, 'legacy_mode')
250 split
= col
.row().split(factor
=0.15)
251 split
.label(text
='Description:')
252 split
.label(text
='When enabled the add-on will run in legacy mode using the old 2.76b feature set.')
255 rigs_expand
= getattr(self
, 'show_rigs_folder_expanded')
256 icon
= 'TRIA_DOWN' if rigs_expand
else 'TRIA_RIGHT'
260 sub
.context_pointer_set('addon_prefs', self
)
261 sub
.alignment
= 'LEFT'
262 op
= sub
.operator('wm.context_toggle', text
='', icon
=icon
,
264 op
.data_path
= 'addon_prefs.show_rigs_folder_expanded'
265 sub
.label(text
='{}: {}'.format('Rigify', 'External feature sets'))
267 for fs
in feature_set_list
.get_installed_list():
268 row
= col
.split(factor
=0.8)
269 row
.label(text
=feature_set_list
.get_ui_name(fs
))
270 op
= row
.operator("wm.rigify_remove_feature_set", text
="Remove", icon
='CANCEL')
272 row
= col
.row(align
=True)
273 row
.operator("wm.rigify_add_feature_set", text
="Install Feature Set from File...", icon
='FILEBROWSER')
275 split
= col
.row().split(factor
=0.15)
276 split
.label(text
='Description:')
277 split
.label(text
='External feature sets (rigs, metarigs, ui layouts)')
280 row
.label(text
="End of Rigify Preferences")
283 class RigifyName(bpy
.types
.PropertyGroup
):
284 name
: StringProperty()
287 class RigifyColorSet(bpy
.types
.PropertyGroup
):
288 name
: StringProperty(name
="Color Set", default
=" ")
289 active
: FloatVectorProperty(
292 default
=(1.0, 1.0, 1.0),
294 description
="color picker"
296 normal
: FloatVectorProperty(
299 default
=(1.0, 1.0, 1.0),
301 description
="color picker"
303 select
: FloatVectorProperty(
306 default
=(1.0, 1.0, 1.0),
308 description
="color picker"
310 standard_colors_lock
: BoolProperty(default
=True)
313 class RigifySelectionColors(bpy
.types
.PropertyGroup
):
315 select
: FloatVectorProperty(
318 default
=(0.314, 0.784, 1.0),
320 description
="color picker"
323 active
: FloatVectorProperty(
326 default
=(0.549, 1.0, 1.0),
328 description
="color picker"
332 class RigifyParameters(bpy
.types
.PropertyGroup
):
333 name
: StringProperty()
335 # Parameter update callback
339 def update_callback(prop_name
):
340 from .utils
.rig
import get_rigify_type
342 def callback(params
, context
):
344 # Do not recursively call if the callback updates other parameters
348 bone
= context
.active_pose_bone
350 if bone
and bone
.rigify_parameters
== params
:
351 rig_info
= rig_lists
.rigs
.get(get_rigify_type(bone
), None)
353 rig_cb
= getattr(rig_info
["module"].Rig
, 'on_parameter_update', None)
355 rig_cb(context
, bone
, params
, prop_name
)
361 # Remember the initial property set
362 RIGIFY_PARAMETERS_BASE_DIR
= set(dir(RigifyParameters
))
364 RIGIFY_PARAMETER_TABLE
= {'name': ('DEFAULT', StringProperty())}
366 def clear_rigify_parameters():
367 for name
in list(dir(RigifyParameters
)):
368 if name
not in RIGIFY_PARAMETERS_BASE_DIR
:
369 delattr(RigifyParameters
, name
)
370 if name
in RIGIFY_PARAMETER_TABLE
:
371 del RIGIFY_PARAMETER_TABLE
[name
]
374 def format_property_spec(spec
):
375 """Turns the return value of bpy.props.SomeProperty(...) into a readable string."""
376 callback
, params
= spec
377 param_str
= ["%s=%r" % (k
, v
) for k
, v
in params
.items()]
378 return "%s(%s)" % (callback
.__name
__, ', '.join(param_str
))
381 class RigifyParameterValidator(object):
383 A wrapper around RigifyParameters that verifies properties
384 defined from rigs for incompatible redefinitions using a table.
386 Relies on the implementation details of bpy.props return values:
387 specifically, they just return a tuple containing the real define
388 function, and a dictionary with parameters. This allows comparing
389 parameters before the property is actually defined.
395 def __init__(self
, params
, rig_name
, prop_table
):
396 self
.__params
= params
397 self
.__rig
_name
= rig_name
398 self
.__prop
_table
= prop_table
400 def __getattr__(self
, name
):
401 return getattr(self
.__params
, name
)
403 def __setattr__(self
, name
, val
):
404 # allow __init__ to work correctly
405 if hasattr(RigifyParameterValidator
, name
):
406 return object.__setattr
__(self
, name
, val
)
408 if not (isinstance(val
, tuple) and callable(val
[0]) and isinstance(val
[1], dict)):
409 print("!!! RIGIFY RIG %s: INVALID DEFINITION FOR RIG PARAMETER %s: %r\n" % (self
.__rig
_name
, name
, val
))
412 if name
in self
.__prop
_table
:
413 cur_rig
, cur_info
= self
.__prop
_table
[name
]
415 print("!!! RIGIFY RIG %s: REDEFINING PARAMETER %s AS:\n\n %s\n" % (self
.__rig
_name
, name
, format_property_spec(val
)))
416 print("!!! PREVIOUS DEFINITION BY %s:\n\n %s\n" % (cur_rig
, format_property_spec(cur_info
)))
418 # actually defining the property modifies the dictionary with new parameters, so copy it now
419 new_def
= (val
[0], val
[1].copy())
421 # inject a generic update callback that calls the appropriate rig classmethod
422 val
[1]['update'] = update_callback(name
)
424 setattr(self
.__params
, name
, val
)
425 self
.__prop
_table
[name
] = (self
.__rig
_name
, new_def
)
428 class RigifyArmatureLayer(bpy
.types
.PropertyGroup
):
431 if 'group_prop' in self
.keys():
432 return self
['group_prop']
436 def set_group(self
, value
):
437 arm
= bpy
.context
.object.data
438 if value
> len(arm
.rigify_colors
):
439 self
['group_prop'] = len(arm
.rigify_colors
)
441 self
['group_prop'] = value
443 name
: StringProperty(name
="Layer Name", default
=" ")
444 row
: IntProperty(name
="Layer Row", default
=1, min=1, max=32, description
='UI row for this layer')
445 selset
: BoolProperty(name
="Selection Set", default
=False, description
='Add Selection Set for this layer')
446 group
: IntProperty(name
="Bone Group", default
=0, min=0, max=32,
447 get
=get_group
, set=set_group
, description
='Assign Bone Group to this layer')
456 RigifySelectionColors
,
463 from bpy
.utils
import register_class
467 feature_set_list
.register()
468 metarig_menu
.register()
475 bpy
.types
.Armature
.rigify_layers
= CollectionProperty(type=RigifyArmatureLayer
)
477 bpy
.types
.Armature
.active_feature_set
= EnumProperty(
478 items
=feature_set_list
.feature_set_items
,
480 description
="Restrict the rig list to a specific custom feature set"
483 bpy
.types
.PoseBone
.rigify_type
= StringProperty(name
="Rigify Type", description
="Rig type for this bone")
484 bpy
.types
.PoseBone
.rigify_parameters
= PointerProperty(type=RigifyParameters
)
486 bpy
.types
.Armature
.rigify_colors
= CollectionProperty(type=RigifyColorSet
)
488 bpy
.types
.Armature
.rigify_selection_colors
= PointerProperty(type=RigifySelectionColors
)
490 bpy
.types
.Armature
.rigify_colors_index
= IntProperty(default
=-1)
491 bpy
.types
.Armature
.rigify_colors_lock
= BoolProperty(default
=True)
492 bpy
.types
.Armature
.rigify_theme_to_add
= EnumProperty(items
=(
493 ('THEME01', 'THEME01', ''),
494 ('THEME02', 'THEME02', ''),
495 ('THEME03', 'THEME03', ''),
496 ('THEME04', 'THEME04', ''),
497 ('THEME05', 'THEME05', ''),
498 ('THEME06', 'THEME06', ''),
499 ('THEME07', 'THEME07', ''),
500 ('THEME08', 'THEME08', ''),
501 ('THEME09', 'THEME09', ''),
502 ('THEME10', 'THEME10', ''),
503 ('THEME11', 'THEME11', ''),
504 ('THEME12', 'THEME12', ''),
505 ('THEME13', 'THEME13', ''),
506 ('THEME14', 'THEME14', ''),
507 ('THEME15', 'THEME15', ''),
508 ('THEME16', 'THEME16', ''),
509 ('THEME17', 'THEME17', ''),
510 ('THEME18', 'THEME18', ''),
511 ('THEME19', 'THEME19', ''),
512 ('THEME20', 'THEME20', '')
515 IDStore
= bpy
.types
.WindowManager
516 IDStore
.rigify_collection
= EnumProperty(items
=(("All", "All", "All"),), default
="All",
517 name
="Rigify Active Collection",
518 description
="The selected rig collection")
520 IDStore
.rigify_types
= CollectionProperty(type=RigifyName
)
521 IDStore
.rigify_active_type
= IntProperty(name
="Rigify Active Type", description
="The selected rig type")
523 bpy
.types
.Armature
.rigify_advanced_generation
= BoolProperty(name
="Advanced Options",
524 description
="Enables/disables advanced options for Rigify rig generation",
527 def update_mode(self
, context
):
528 if self
.rigify_generate_mode
== 'new':
529 self
.rigify_force_widget_update
= False
531 bpy
.types
.Armature
.rigify_generate_mode
= EnumProperty(name
="Rigify Generate Rig Mode",
532 description
="'Generate Rig' mode. In 'overwrite' mode the features of the target rig will be updated as defined by the metarig. In 'new' mode a new rig will be created as defined by the metarig. Current mode",
534 items
=( ('overwrite', 'overwrite', ''),
537 bpy
.types
.Armature
.rigify_force_widget_update
= BoolProperty(name
="Force Widget Update",
538 description
="Forces Rigify to delete and rebuild all the rig widgets. if unset, only missing widgets will be created",
541 bpy
.types
.Armature
.rigify_target_rig
= PointerProperty(type=bpy
.types
.Object
,
542 name
="Rigify Target Rig",
543 description
="Defines which rig to overwrite. If unset, a new one called 'rig' will be created",
544 poll
=lambda self
, obj
: obj
.type == 'ARMATURE' and obj
.data
is not self
)
546 bpy
.types
.Armature
.rigify_rig_ui
= PointerProperty(type=bpy
.types
.Text
,
547 name
="Rigify Target Rig UI",
548 description
="Defines the UI to overwrite. If unset, 'rig_ui.py' will be used")
550 bpy
.types
.Armature
.rigify_rig_basename
= StringProperty(name
="Rigify Rig Name",
551 description
="Defines the name of the Rig. If unset, in 'new' mode 'rig' will be used, in 'overwrite' mode the target rig name will be used",
554 IDStore
.rigify_transfer_only_selected
= BoolProperty(
555 name
="Transfer Only Selected",
556 description
="Transfer selected bones only", default
=True)
558 # Update legacy on restart or reload.
559 if legacy_loaded
or bpy
.context
.preferences
.addons
['rigify'].preferences
.legacy_mode
:
560 bpy
.context
.preferences
.addons
['rigify'].preferences
.legacy_mode
= True
562 bpy
.context
.preferences
.addons
['rigify'].preferences
.update_external_rigs()
565 register_rig_parameters()
568 def register_rig_parameters():
569 if bpy
.context
.preferences
.addons
['rigify'].preferences
.legacy_mode
:
570 for rig
in rig_lists
.rig_list
:
571 r
= utils
.get_rig_type(rig
)
573 r
.add_parameters(RigifyParameterValidator(RigifyParameters
, rig
, RIGIFY_PARAMETER_TABLE
))
574 except AttributeError:
577 for rig
in rig_lists
.rigs
:
578 rig_module
= rig_lists
.rigs
[rig
]['module']
579 rig_class
= rig_module
.Rig
580 r
= rig_class
if hasattr(rig_class
, 'add_parameters') else rig_module
582 if hasattr(r
, 'add_parameters'):
583 r
.add_parameters(RigifyParameterValidator(RigifyParameters
, rig
, RIGIFY_PARAMETER_TABLE
))
586 traceback
.print_exc()
590 from bpy
.utils
import unregister_class
592 # Properties on PoseBones and Armature.
593 del bpy
.types
.PoseBone
.rigify_type
594 del bpy
.types
.PoseBone
.rigify_parameters
596 ArmStore
= bpy
.types
.Armature
597 del ArmStore
.rigify_layers
598 del ArmStore
.active_feature_set
599 del ArmStore
.rigify_colors
600 del ArmStore
.rigify_selection_colors
601 del ArmStore
.rigify_colors_index
602 del ArmStore
.rigify_colors_lock
603 del ArmStore
.rigify_theme_to_add
604 del ArmStore
.rigify_advanced_generation
605 del ArmStore
.rigify_generate_mode
606 del ArmStore
.rigify_force_widget_update
607 del ArmStore
.rigify_target_rig
608 del ArmStore
.rigify_rig_ui
609 del ArmStore
.rigify_rig_basename
611 IDStore
= bpy
.types
.WindowManager
612 del IDStore
.rigify_collection
613 del IDStore
.rigify_types
614 del IDStore
.rigify_active_type
615 del IDStore
.rigify_transfer_only_selected
619 unregister_class(cls
)
621 clear_rigify_parameters()
624 metarig_menu
.unregister()
626 feature_set_list
.unregister()