Process/Addons/Rigify/RigClass

< Process‎ | Addons‎ | Rigify

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 in bones.ctrl, mechanism in bones.mch and deformation bones in bones.deform. The dictionary may contain strings, lists and nested BoneDicts.
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.

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 compute bones.org, and indirectly rigify_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 or EditBone 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:

    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.

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)