Matrix Splines in Maya
By: Cole O'Brien
Tags: Curves, Math, Rigging, Maya, Techart

The secret ingredient here is Maya’s wtAddMatrix nodes. We use these nodes to obtain position and tangents on a curve in matrix form. However neither of these images are showing the entire picture. These nodes use specific weights generated from static parameter, degree and knot values.

I won’t get into the math just yet, but essentially by baking these parameters into weight values we can reduce the required operations down to weighted sums. While this simplifies our networks, it does unfortunately mean these values cannot be driven by attribute connections. Although it is possible for these operations to be replicated in node form, it would require far more connections and would hurt performance significantly.

Usage

While the actual node network setup is fairly simple, the matrix weights themselves are best generated through code. Fortunately I’ve wrapped all the math in as a python module.

This module contains several functions designed to generate control point or tangent weights for either spline curves or surfaces. Given a list of control points they will each return a mapping of control points to weight values. These functions are all written entirely in vanilla python as well, so they’re theoretically useable outside of a Maya context.

This output weight mapping can then be used to create a weighted sum. In Maya you essentially have two options for this. For matrices you’re limited to the previously mentioned wtAddMatrix node, but they theoretically could also work with the blendWeighted node for single dimensional values.

Keep in mind when using matrices the entire matrix will be blended. This can be handy using blended rotation for up vectors, but often scale will need to be picked out. Also the tangent matrices store the tangent value in the matrix translation rather than rotation. So in order to access this vector directly you will need to use a decomposeMatrix node.

The provided module also contains three functions for generating the example setups used for this article. Each function should work in both Maya 2020 and pre-2020. These example functions are intended for testing purposes or to serve as a starting point for use elsewhere. They are not designed to be fully functional auto-riggers though.

Advantages and Limitations

This method has a lot of advantages but its not the solution for every problem. While I’ve tested a similar method in production for the past year, I think its important to consider the pros and cons before implementing it.

Advantages

Node Simplicity. There are many alternative methods to attach transforms to curves, but they often involve awkward conversions to and from NURBS nodes. All the heavy lifting here is done with a couple matrix nodes.

There are many alternative methods to attach transforms to curves, but they often involve awkward conversions to and from NURBS nodes. All the heavy lifting here is done with a couple matrix nodes. Matrix Friendly. This method includes matrix inputs and outputs rather than vectors so its is perfect for use with the new matrix nodes in Maya 2020.

This method includes matrix inputs and outputs rather than vectors so its is perfect for use with the new matrix nodes in Maya 2020. No DAG Nodes. While hiding DAG nodes from your animators isn’t a huge problem, eliminating them certainly reduces outliner clutter and avoids infamous DO_NOT_TOUCH groups. This is also helpful when building rigs through code as there are no residual DAG nodes to worry about.

While hiding DAG nodes from your animators isn’t a huge problem, eliminating them certainly reduces outliner clutter and avoids infamous DO_NOT_TOUCH groups. This is also helpful when building rigs through code as there are no residual DAG nodes to worry about. Performance. I’m not an expert on testing performance but in my simple tests using dgtimer this setup performs almost 40x faster than using curveInfo nodes. Optimization wasn’t the goal of this workflow, but it is a nice perk!

Limitations

Static parameter and knots. This is probably the biggest deal-breaker for anyone looking to implement this method. This means you cannot create rigs that slide along splines, or splines with locked lengths.

This is probably the biggest deal-breaker for anyone looking to implement this method. This means you cannot create rigs that slide along splines, or splines with locked lengths. Built-in Tool Support . Maya’s spline nodes include a lot of built-in utilities and tools that are incompatible with this workflow. This means we cannot drive curves with deformers or simulations, or use Maya’s API for querying curve data.

. Maya’s spline nodes include a lot of built-in utilities and tools that are incompatible with this workflow. This means we cannot drive curves with deformers or simulations, or use Maya’s API for querying curve data. Python Setup. While the provided module should be trivial to copy and paste where needed, if you’re used to rigging by hand this can be a bit of a burden.

Also I should also mention, a node plugin would probably be a lot more performant and create even simpler node networks. However if there’s one thing I’ve learned from working professionally is to avoid external dependencies. Creating rigs with plugins makes them harder to share and locks them to versions of Maya. For some pipelines plugins will be the solution but personally I always prefer a vanilla option.

Math Time

So far I’ve focused primarily on the practical applications of this method. But in the process of figuring this all out I’ve learned a lot about the math behind splines. While there are some great resources available online(this primer in particular was a huge help), they are often generalized or require extensive prior knowledge. So while I’m no expert, I feel like I should at least do my best to explain the math from a Maya rigging perspective.

Bezier Curves

Before I discuss splines, I should first go over Bezier curves. While these aren’t the same, understanding curves is fundamental to understanding splines.

One way to interpret a Bezier curve is as a weighted sum of control points. The amount of control points determines the curves order. In Maya though we often describe curves by their degree instead, which is one less than the order. So a curve with a degree of three will have four control points.

A position on a curve is described by a parameter or time(t) value. This single value often ranges from 0–1, starting at the first control point and terminating at the last. Bezier functions are essentially interpolation functions. In fact a degree two curve is essential a linear interpolation or a LERP function.

The equation itself for a Bezier curve is fairly simple. The formula on wikipedia for a degree 3 curve looks like this:

Where t is the parameter value, P are the input control points and B is the output Bezier point.

There’s really nothing too crazy about this formula, and should be pretty simple to convert into python code. Unfortunately, this format isn’t the most convenient to represent as a node network in Maya. Getting a single point will require around a dozen nodes just to add and multiply everything. Instead if we use a static time value we can simplify the equation into a weighted sum like this: