Keywords: Maya, Rig Control Curve, Control Rig, Layered Controls, Animation Layers

Maya Rigging series of articles:

Summary

Why we need the Layered Controls? two reasons:

  • We want to prevent the animator from touching the skeleton, because we don’t want others to add or remove joints which will lead to potential issues.
  • If we add other nodes(Curve, Locator, etc.) directly into skeleton hierarchy, it will cause issues when it comes to export to the Unreal Engine.

So we need a top layer to drive joints, that’s Layered Controls!

Take shoulder joint as example.

1, Create Curve: Create -> NURBS Primitives -> Circle

2, Scale the curve.

3, Freeze the transfrom and delete history:

  • Modify -> Freeze Transformations.
  • Edit -> Delete by Type -> History. (Alt + Shift + D)

4, Match Transforms.
Select shoulder joint and curve together, Modify -> Match Transformations -> Match All Transforms.

Another way to snap Control to Joint: Hold V and drag Control to target joint.

5, Tweak the curve’s rotation.

6, Parent Constraints.
Select both shoulder joint and curve, Hold Space -> Constrain -> Parent.

Now you can drive shoulder joint using controler.

More control curve templates: Control Icons

Create Controller Method 2nd: Merge Curve’s Transform Node

1, Create a curve and tweak the transform.

Which axis is modified depends on the target joint coordinate axis.

2, Freeze the transfrom and delete history:

  • Modify -> Freeze Transformations.
  • Edit -> Delete by Type -> History. (Alt + Shift + D)

3, Display the transform node: Right click hierarchy viewport -> Shapes.

4, Parent the curve to the target joint:

parent -r -s

5, Delete curve’s transform node, and hide the Shapes, now you can drive the joint using curve which likes Handle.

Controller Tweaking

Change the display color:

Change the line width:

Hide Joint for Controler

In the process of rigging, we can hide the joints to highlight the controllers: Show -> Viewport -> Uncheck Joints.

But it causes to hide controllers when the visibility of joint was off.

Q: As mentioned before, to prevent animator from touching skeleton, so we need to hide the joint, is there any way to display the controllers while joint was hidden?
A: Change the joint’s draw sytle.

We can change only one joint at a time on GUI, but can do it in batch using script.

Hide all joints:

string $target = "root";
select -r $target;
string $joints[] = `ls -type joint`;
for($joint in $joints)
{
	setAttr ($joint + ".drawStyle") 2;
	//print($joint + "\n");
}

Show all joints:

//change drawStyle from 2 to 0
setAttr ($joint + ".drawStyle") 0;
Transform Offset Method 1st: Group

As the Create Controller Method 1st say, when you tweaked the transform of controller, Translate and Rotation are non-Zero, thus it’s not friendly for animator.

Solution:
1, Create an empty Group. (shortcut: Ctrl + G)

2, Select Group first, then select Controller, then Match All Transforms.

Then you can see the Group’s transform changed.

3, Drag the Controller into Group, then you can see the Controller’s transform was zeroed.

Transform Offset Method 2nd: Offset Parent Matrix

Take Create Controller Method 1st as an example again.

From Maya 2020, it provide a new feature named Offset Parent Matrix to offset transform.
You can just copy the transform from Transform Attributes to Offset Parent Matrix, then zero the transform from Transform Attributes.

Now you can found that the controller’s transform was zeroed.

Script to copy Offset Parent Matirx and zero Transformation (credits to Muream):

import maya.api.OpenMaya as om
import maya.cmds as cmds

TRANSFORM_NODETYPES = ["transform", "joint"]

def has_non_default_locked_attributes(node):
    locked_attributes = []
    for attribute in ["translate", "rotate", "scale", "jointOrient"]:
        default_value = 1 if attribute == "scale" else 0
        for axis in "XYZ":
            if cmds.attributeQuery(attribute + axis, node=node, exists=True):
                attribute_name = "{}.{}{}".format(node, attribute, axis)
                current_value = cmds.getAttr(attribute_name)
                if cmds.getAttr(attribute_name, lock=True) and current_value != default_value:
                    return True

def reset_transforms(node):
    for attribute in ["translate", "rotate", "scale", "jointOrient"]:
        value = 1 if attribute == "scale" else 0
        for axis in "XYZ":
            if cmds.attributeQuery(attribute + axis, node=node, exists=True):
                attribute_name = "{}.{}{}".format(node, attribute, axis)
                if not cmds.getAttr(attribute_name, lock=True):
                    cmds.setAttr(attribute_name, value)

def bake_transform_to_offset_parent_matrix(node):
    if cmds.nodeType(node) not in TRANSFORM_NODETYPES:
        raise ValueError("Node {} is not a transform node".format(node))

    if has_non_default_locked_attributes(node):
        raise RuntimeError("Node {} has at least one non default locked attribute(s)".format(node))

    local_matrix = om.MMatrix(cmds.xform(node, q=True, m=True, ws=False))
    offset_parent_matrix = om.MMatrix(cmds.getAttr(node + ".offsetParentMatrix"))
    baked_matrix = local_matrix * offset_parent_matrix
    cmds.setAttr(node + ".offsetParentMatrix", baked_matrix, type="matrix")

    reset_transforms(node)

def bake_transform_to_offset_parent_matrix_selection():
    for node in cmds.ls(sl=True):
        bake_transform_to_offset_parent_matrix(node)

if __name__ == "__main__":
    bake_transform_to_offset_parent_matrix_selection()

Q: why don’t we zero transform offset using Freeze Transformations before Parent Constraints?
A: Freeze Transformations will change the axis direction of pivot.

References

Character Rigging for Beginners: 03 Primary Controls
https://www.youtube.com/watch?v=m37fhS9MNSI&list=PLbvsJz5ZcmxHEPiw_kF3vHjR023rIjR05&index=3

Character Rigging for Beginners: 04 Secondary Controls
https://www.youtube.com/watch?v=tsOXYOoGBNY&list=PLbvsJz5ZcmxHEPiw_kF3vHjR023rIjR05&index=4

Realtime Creature Rigging Workshop (7 / 19) : Control creation
https://www.youtube.com/watch?v=6jO9jQQfSL8

Plugins

Maya control curve UI
https://peerke.gumroad.com/l/WRtX


紫光阁名录:多拉尔·海兰察
海兰察(满语:ᡥᠠᡳ᠌ᠯᠠᠨᠴᠠ,穆麟德转写:Hailanca;1739—1793年),多拉尔氏,索伦部(今鄂温克族)人,后入满洲镶黄旗,世居黑龙江,生于呼伦贝尔阿荣旗霍尔奇镇,乾隆中后期名将,因屡立战功,官至正一品领侍卫内大臣,爵封一等超勇公;卒谥武壮。清朝惯例,阵亡者方能入祀昭忠祠,乾隆御准海兰察加恩入祀,旌扬战功。