Eight years ago, Nico Strobbe posted a rigging challenge to CG Society – a mechanical cat with over 400 parts, and every joint’s rotations decomposed in many various ways. Jorn-Harald Paulsen has one of the better known reels showing off a finished rig. His site also has a link to download the model.
In the past eight years I have tried several times to rig this mechanical monstrosity, usually getting no further than halfway down the front leg before getting frustrated and giving up. You can only get so far with the “traditional” approach to rigging (see below).
Those of you who follow me on Twitter may have noticed that I have been the unofficial herald for Cult Of Rig, a rigging oriented live stream by Raffaele Fragapane that espouses a thoughtful, methodical, functional approach to rigging vis a vis using math to explicitly construct the transform matrices of the rig.
As it turns out, this math based “nodeling” approach is ideal for mechanical rigging where a complete Euler rotation needs to be separated into its individual components, and where simple single axis rotations of an FK control end up driving half the leg to keep it “on model”.
I’ve been working on the rig on and off for about a month now, and have gotten the left front leg 75% working. In that time I have had several false starts. But my starting point has been the same each time: a basic FK limb. Each control is positioned at the best pivot based on the model. At first I wanted to make each limb segment locked to it’s respective start and end joint, but several iterations has shown me that it is better to try to solve the limb so the ankle stays fixed and the knee does what it has to.
The base of the leg is fairly simple; a hinge (rotate Z), a swivel (rotate Y), and an axle (rotate X).
In order to drive the rotations I needed to consider the FK hip control in a local space. In the graph below, the FK hip control’s result – hip_srt – and the leg’s “parent” space – shoulder_srt – are the inputs of this sub-graph. Using a
multMatrix node and a
decomposeMatrix node, I get the FK hip’s transform local to the shoulder.
By being able to control the axis order of the Euler rotation, I can ensure that the rotations happen in the correct order – first Z, then Y. This is the default rotate order; XYZ. (Due to a bug in the
decomposeMatrix node in Maya 2016, I had to use a
quatToEuler node to re-order rotations on other components).
composeMatrix node and a
multMatrix node, I calculate a new world matrix that represents just the hinge’s transform
The swivel follows the hinge as a child, but I eschew relying on the transform hierarchy to make the behavior work. The reasons why are gone over in detail in the Cult Of Rig videos. Instead, a compose a matrix with just the rotate Y of the FK hip control and multiply it by the world matrix of the hinge. In the end, I have a transform world matrix for both the hip hinge and hip swivel.
The hip axle is calculated a differently. Do to it’s pivot being offset from the pivot of the FK hip control, taking the rotate X of the control directly would not have the end of the axle pointing at the knee. Instead, I have to calculate the rotation necessary to aim the axle at the knee. Because this rotation is happening in a single axis, it can be calculated using an
I used an
aimConstraint for prototyping. Normally Maya creates a rat’s nest of connections when you create a constraint through the menu. If you create the node explicitly, you can wire up the exact connections you need. In this case, the only connections need are to
.constraintTranslate. The outputs of the constraint are used to compose a matrix which is multiplied by the hip swivel world matrix to obtain the hip axle world matrix.
And that’s just the hip rotation*. Things get wierder the further down the leg you go. But by keeping the sub-graphs isolated to a single isolated rotation, it is not too difficult to wrangle. In the examples above you can see there are
network nodes with the “pass” suffix – these are just dummy nodes to act as the “boundary’ of a sub graph, passing the world matrix and world inverse matrix of one isolated rotation to the next.
Unfortunately, doing a tab per isolated rotation means I will have between fifteen and twenty tabs when I am done. And that’s just the left front leg.
* A shrewed observer may note that I am repeating some calculations. Yes, I am. This is still a prototype so I am being very explicit in keeping my graphs isolated. If and when I get the leg working I can try to optimize it.