Implementing a Rig
Each rig component is implemented as a
Rig class located in a separate module
rigs sub-package of a feature set or Rigify itself. This explains
how it is done.
All new rigs should inherit either directly or indirectly from
which implements the basic interface of a rig component. Classes that do not inherit from
BaseRig are considered to be using the deprecated legacy interface.
This class manages a number of standard fields that represents relations between the rig, bones, and other rigs. It also provides a set of utility methods, as well as empty methods to be overridden.
All rigs have the following standard fields provided by the base class:
- The armature Object of the rig being generated.
- Name of the main ORG bone of the rig component (the one holding its type and parameters).
- Parameter property collection for the rig component.
- Reference to the main object managing the generation process.
- Reference to the object managing generation of the UI script.
- A dictionary for storing names of the bones. Uses BoneDict, which allows access to contents via attribute syntax instead of requiring the regular dictionary access syntax. The standard layout is to store ORG bones coming from the metarig in
bones.org, control bones in
bones.ctrl, mechanism in
bones.mchand deformation bones in
bones.deform. The dictionary may contain strings, lists and nested
- Parent rig of the current rig, i.e. the rig that owns the parent of the main bone.
- List of child rigs.
- Set of ORG bones owned by the rig; equal to flattened
- Set of ORG bones that are children of ORG bones owned by this rig, and aren't owned by any other rig.
- Dictionary of bones generated by the rig, mapping them to the name of the bone they were copied from, if applicable.
Note: The term 'ORG bones' refers to bones that were copied directly from the metarig, as their names are prefixed with
ORG- during the initial stages of the generation process. By convention, Rigify also prefixes internal 'mechanism' bones with
MCH-, and deforming bones with
BaseRig by itself primarily provides the following methods:
- Initializes the rig component. Unlike the legacy rig interface, normal rig components are not expected to override
- This method should be overridden to define which ORG bones are exclusively owned by this rig, and will be used in the generation process. It is called by
bones.org, and indirectly
- May return a single name, a list of names, or a
It also provides many more utility and callback methods by inheriting from other mixin classes. For example:
rig.report_error(message, ...)from rigify.utils.errors.RaiseErrorMixin
- Throws an exception reporting a generation error, using the message with added information about the rig component itself. The message is presented using
- Returns the
EditBonewith the specified name, based on the current mode.
rig.make_constraint(bone_name, con_type, subtarget=None, ...)from MechanismUtilityMixin
- Adds and returns a new constraint to the given bone. Named arguments are used to initialize properties of the constraint.
For more details see the information about the mentioned mixin classes.
In order to avoid frequent object mode switches, as well as to facilitate interaction between rigs, the generation process is performed in stages. Each stage is represented by a callback method, and all callbacks of the same stage are called for all rigs before proceeding to the next stage.
Each stage is called in a specific mode, and is intended for a specific kind of activity. Rigs should not switch modes themselves.
Before all stage processing starts, Rigify copies the metarig, renames bones to
ORG-, instantiates all rig components, and links them to each other and bones via the above mentioned parent-child fields. After that it proceeds to execute stages in order:
- This stage is called in Object mode to allow rigs to collect information about the bones and other rigs, so rigs may not change the armature in any way. This stage takes the place of
__init__in really initializing the rig.
- Called in Edit mode to allow rigs to align and adjust ORG bone positions and orientations before generating new bones.
- Called in Edit mode to let the rigs generate new bones. This is the only stage that can add bones.
- Called in Edit mode to allow setting the parent links and other Edit mode properties after all bones have been generated.
- Called in Object mode to set bone transformation mode, layers, create custom properties. Existing rigs also generate the UI script panels here. Can be used to add constraints that need to be applied during rig creation.
- Called in Object mode to allow reading bone positions after constraints that were added during the
configure_bonesstage are evaluated.
- Called in Edit mode to allow final adjustments to the rest pose, e.g. to apply a temporary constraint added in the
- Called in Object mode to create all constraints, drivers and other mechanics of the rig.
- Called in Object mode to create all bone widget objects.
- Called in Object mode at the very end of generation to do final processing, e.g. used internally to emit the UI script.
The rig classes can run code in a stage in two ways. The simplest is to override the method with the name of the stage. The more advanced way is to use a decorator to mark an arbitrary method as belonging to a stage.
The direct callback is called first, followed by the custom decorated methods:
def generate_bones(self): print('first') @stage.generate_bones def foo(self): print('second')
If decorated methods are overridden in a subclass, the decorator must be repeated for code clarity reasons, or a warning will be printed to the console.
Multiple methods decorated with the same stage are called in the order of first definition, with class bodies scanned in reverse MRO order in case of inheritance:
class Base(...): @stage.generate_bones def first(self):... @stage.generate_bones def second(self):... class Derived(Base): @stage.generate_bones def third(self):... # Was first defined in Base so still first: @stage.generate_bones def first(self):... @stage.generate_bones def fourth(self):...
The callbacks of any rig instance are called after all callbacks for all of its parent rigs for that stage are completed.
However, for clarity, it is best not to rely on this order unless absolutely necessary, and write code as if the order within the stage was undefined, especially for the decorated callbacks. In some case this can be achieved by pre-calculating some data in a previous stage, or in the non-decorated direct callback.
Rig Parameters and UI
The rig components can have custom parameters, which can be set by the user via a panel in metarig Bone properties that appears when the matching rig type is selected. Registration of the parameters and UI layout of the panel are implemented via special callbacks.
- Called to allow rigs to register their parameters via assigning
bpy.propsdefinitions to attributes of the parameter group.
- All rigs share the same property group, so it is necessary to use descriptive property names to avoid conflicts. Rigify will detect incompatible redefinitions and print a warning to the console.
- Called from the panel's
drawmethod to present the rig-specific parameters.
Rig.on_parameter_update(context, pose_bone, params, param_name)classmethod
- Called when a parameter is changed by the user.
Rigs normally provide a usage sample that can be added to a metarig in edit mode via a property panel. The sample is implemented by using the 'Encode Sample to Python' operator in armature edit mode, and pasting the resulting
create_sample function into the module of the rig component.
This describes some organization guidelines used for the new style Rigify rig code.
Store bone names in rig.bones
Since mode switches invalidate bone references, only names can be stored between stages. As a common convention, store names of all generated bones inside
rig.bones, separated by their purpose into control, mechanism and deform groups.
Fields of the class may be used to store extra redundant references for stage interaction purposes.
Stage decorators are provided so that all code related to a specific group of bones could be kept together, even though in reality it is split into stages, and executed interleaved with other groups of bones.
Thus, all bones of a rig should be split into logical groups, and all code for a group implemented as decorated stage callbacks placed together in the logical sequence.
prepare_bones stages may/should be implemented as regular method overrides, since they only deal with the initial group of bones. The following stages should only use the regular override callbacks to collect data before any changes are applied. The
finalize stage is expected to be only rarely used, and no guideline is provided.
Avoid big loop bodies
Bodies of loops over bones should at most consist of one function call or assignment. More complex code should be split into a separate method, and the loop counter parameter provided even if not needed by the loop body. For standard rigs included in Rigify, the body should always be split into a method.
This makes it easier to subclass the rig later and slightly modify the behavior of the loop body for specific bones, without having to replace the whole loop.
This demonstrates the above guidelines via a slightly simplified version of the
class Rig(BaseRig): """A rig that consists of 3 connected chains of control, org and deform bones.""" def find_org_bones(self, bone): return [bone.name] + connected_children_names(self.obj, bone.name) def initialize(self): if len(self.bones.org) <= 1: self.raise_error("Input to rig type must be a chain of 2 or more bones.") ############################## # BONES # # org: # ORG bones # ctrl: # Control chain. # deform: # DEF bones # ############################## ############################## # Control chain @stage.generate_bones def make_control_chain(self): self.bones.ctrl = map_list(self.make_control_bone, count(0), self.bones.org) def make_control_bone(self, i, org): return self.copy_bone(org, make_derived_name(org, 'ctrl'), parent=True) @stage.parent_bones def parent_control_chain(self): self.parent_bone_chain(self.bones.ctrl, use_connect=True) @stage.configure_bones def configure_control_chain(self): for args in zip(count(0), self.bones.ctrl, self.bones.org): self.configure_control_bone(*args) def configure_control_bone(self, i, ctrl, org): self.copy_bone_properties(org, ctrl) @stage.generate_widgets def make_control_widgets(self): for ctrl in self.bones.ctrl: self.make_control_widget(ctrl) def make_control_widget(self, ctrl): create_bone_widget(self.obj, ctrl) ############################## # ORG chain @stage.rig_bones def rig_org_chain(self): for args in zip(count(0), self.bones.org, self.bones.ctrl): self.rig_org_bone(*args) def rig_org_bone(self, i, org, ctrl): self.make_constraint(org, 'COPY_TRANSFORMS', ctrl) ############################## # Deform chain @stage.generate_bones def make_deform_chain(self): self.bones.deform = map_list(self.make_deform_bone, count(0), self.bones.org) def make_deform_bone(self, i, org): return self.copy_bone(org, make_derived_name(org, 'def'), parent=True, bbone=True) @stage.parent_bones def parent_deform_chain(self): self.parent_bone_chain(self.bones.deform, use_connect=True) @stage.rig_bones def rig_deform_chain(self): for args in zip(count(0), self.bones.deform, self.bones.org): self.rig_deform_bone(*args) def rig_deform_bone(self, i, deform, org): self.make_constraint(deform, 'COPY_TRANSFORMS', org)