This week was a very busy one. I barely slept! But I got a lot done.
My first task was to build a quality rig for the animators - a rig I could easily update and fix and iterate on. In order to accomplish this task, I turned to Python. Here is a screenshot of my VSCodium workspace.
I'm dividing my work into two Python modules, ns_maya_utils and basic_autorig. ns_maya_utils is a collection of rigging tools and utilities, and the math helper functions that make it possible. The math functions are implemented in fast and portable NumPy, because I need to learn NumPy and this seemed like a good time to start.
| 1590 lines of Python |
The rigging script is written in the Maya cmds Python API, based on my own original design. It consists of an IK/FK switching limb, used by the arms and legs, an IK foot, an FK Hand with Curl Assist, a simple Neck/Head FK control, and a novel implementation of the classic Ribbon Spline IK spine rig .
| Generating the rig. This gif is not sped up. |
Generating the rig takes less than a second. It uses the widgets and meta-rig objects in the file as input and generates a complete control rig as output. The control rig and the deform rig are separated so it can export cleanly to the game engine.
The rig's IK foot features an intuitive heel roll control and a switch to enter FK mode. It has a pole-vector controller to set the knee direction.
The hands feature an auto-curl feature for easier posing.
The rig is stable in extreme poses and resists flipping even when turned around and on its side.
Here is an isolated view of the spine rig. I present a novel approach to the Ribbon Spline torso rig. If you look closely, you'll notice there is no ribbon! Instead, I use two identical NURBS curves, offset slightly on the X axis and hooked to the same cluster deform nodes. Because they always move together, they produce the same overall shape as a ribbon - without the overhead of calculating the spans, or the UVs, or the follicle positions on the ribbon. There is no risk of a rig component poking out of the mesh and getting rendered on accident. Instead, I use only two curves and a clever series of nodes.
In Maya's node editor, you can see the method behind my madness. First, I take the position, and tangent from each curve with a pointOnCurveInfo node. Using the positions from each curve, I calculate the normal. Taking the cross product of the tangent and normal gets me the binormal vector. Together, these three vectors make up an orthonormal basis matrix - just the thing to plug into the Offset Parent Matrix of this transform node (which has been set to not inherit transform). This lifts the curve's transformations into a simple node that I can use for constraints. The joints are parent-constrained to these curve-pin transform nodes. Easy!
My goal was to work on the rig for three days, from Saturday to Monday. In the end, it took four days - from Sunday to Wednesday. I didn't get it to my animators as fast as I wanted, and I didn't have time to fill out all the features or test it extensively -- but because I have a solid foundation, I can easily iterate through different versions of the rig.
My second task this week was to update the lighting in the level. This proved very challenging, because there were a lot of mesh light cylinders in the scene from the level designer's blockout. Because I already had a lot of light cylinders in the scene, I decided to use Blueprints to build a tool to take advantage of them. My tool places lights on the cylinders and creates a control to direct them. It is easy to art direct and modify.
The tool was made with these blueprints (arranged in a sequence):
I won't go into the tiny details, but the overall idea of the construction script here is to take the bounding box of the blockout light and use it to transform and place a nicer light object that uses proper Unreal light objects instead of mesh lighting.
I was having a lot of trouble getting the RectLight to point at the controller using the Look At Function node, so I ended up building my own constraint with vector nodes. Look familiar?
Just as in the Maya rig, I took the displacement between two points, crossed it with a known orthogonal vector, and used the normalized displacement, known orthogonal vector, and cross product to construct an orthonormal basis matrix (Unreal unhelpfully and very confusingly calls it a "rotator" instead of a basis).
Here is the result in the level: