Implementing a Rig¶
Each rig component is implemented as a Rig
class located in a
separate module within the rigs
sub-package of a feature set or
Rigify itself. This explains how it is done.
BaseRig class¶
All new rigs should inherit either directly or indirectly from
rigify.base_rig.BaseRig
, 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.
Fields¶
All rigs have the following standard fields provided by the base class:
obj
- The armature Object of the rig being generated.
base_bone
- Name of the main ORG bone of the rig component (the one holding its type and parameters).
params
- Parameter property collection for the rig component.
generator
- Reference to the main object managing the generation process.
script
- Reference to the object managing generation of the UI script.
bones
- 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 inbones.ctrl
, mechanism inbones.mch
and deformation bones inbones.deform
. The dictionary may contain strings, lists and nestedBoneDict
s. rigify_parent
- Parent rig of the current rig, i.e. the rig that owns the parent of the main bone.
rigify_children
- List of child rigs.
rigify_org_bones
- Set of ORG bones owned by the rig; equal to flattened
bones.org
. rigify_child_bones
- Set of ORG bones that are children of ORG bones owned by this rig, and aren't owned by any other rig.
rigify_new_bones
- Dictionary of bones generated by the rig, mapping them to the name of the bone they were copied from, if applicable.
rigify_derived_bones
Reverse of the rigify_new_bones
dictionary, mapping originals to
sets of bones copied from them.
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 DEF-
.
Methods¶
BaseRig
by itself primarily provides the following methods:
rig.__init__(generator, pose_bone)
- Initializes the rig component. Unlike the legacy rig interface, normal
rig components are not expected to override
__init__
itself. rig.find_org_bones(pose_bone)
callback- 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
__init__
to computebones.org
, and indirectlyrigify_org_bones
.May return a single name, a list of names, or a
BoneDict
.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
message.format(...)
. rig.get_bone(name)
from BoneUtilityMixin- Returns the
PoseBone
orEditBone
with 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.
Generation Stages¶
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.
Stage Order¶
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:
initialize
- 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. prepare_bones
- Called in Edit mode to allow rigs to align and adjust ORG bone positions and orientations before generating new bones.
generate_bones
- Called in Edit mode to let the rigs generate new bones. This is the only stage that can add bones.
parent_bones
- Called in Edit mode to allow setting the parent links and other Edit mode properties after all bones have been generated.
configure_bones
- 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.
preapply_bones
- Called in Object mode to allow reading bone positions after constraints
that were added during the
configure_bones
stage are evaluated. apply_bones
- Called in Edit mode to allow final adjustments to the rest pose, e.g. to
apply a temporary constraint added in the
configure_bones
stage. rig_bones
- Called in Object mode to create all constraints, drivers and other mechanics of the rig.
generate_widgets
- Called in Object mode to create all bone widget objects.
finalize
- Called in Object mode at the very end of generation to do final processing, e.g. used internally to emit the UI script.
Stage Callbacks¶
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:
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.
Staged Sub-Components¶
All classes that support stages (i.e. inherit from
GenerateCallbackHost
) also support having staged sub-objects. This
is achieved via two attributes:
rigify_sub_objects
- A sequence of staged sub-objects of this object, used for recursive walk
by the
rigify_invoke_stage
method. The default static value is an empty tuple, so adding items requires replacing it with a list first. rigify_sub_object_run_late
- When false, this sub-object's stages are executed after its owner's
direct callback and before the decorated methods. If true, this
sub-object is processed after the decorated methods.
Two classes are provided to aid implementing sub-objects of rig classes:
RigComponent
andLazyRigComponent
. They have the following attributes and methods: owner
- The rig object that owns this component.
obj
- The armature Object of the rig being generated.
is_component_enabled
- This
LazyRigComponent
is already added to its owner's sub-objects. component.__init__(owner)
- Initializes the component. For
RigComponent
immediately adds itself to the owner's list. component.enable_component()
- Adds this
LazyRigComponent
to its owner's list (unless it is already added).
The components also inherit from BoneUtilityMixin and MechanismUtilityMixin to provide the usual utility methods.
It is not strictly necessary to inherit from these two classes if the
owner's sub-object list is managed in some other way; the minimum
necessary base for a sub-object is just GenerateCallbackHost
. This
base class also means sub-objects can have sub-objects of their own.
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.
Rig.add_parameters(params)
classmethod- Called to allow rigs to register their parameters via assigning
bpy.props
definitions 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.
Rig.parameters_ui(layout, params)
classmethod- Called from the panel's
draw
method 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.
Rig Sample¶
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.
Guidelines¶
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.
Use decorators to group related code¶
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.
The initialize
and 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.
Example¶
This demonstrates the above guidelines via a slightly simplified version
of the SimpleChainRig
class.
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)