Rigging as a programming language
November 07, 2022There are a lot of parallels that can be made between rigging and programming, especially when working with node-based frameworks
Riggers are programmers
I don't mean that in the sense that riggers are writing python code to generate their rigs. This is just a byproduct of the lack of good rigging tools out there.
I mean that rigging is programming.
The job of the rigger is to build the tool (The rig) that the animator will use to move and animate an asset.
That comes with the same considerations needed when building a piece of software.
To name a few:
- Good User experience Animators need rigs that are well designed and that suit their purposes.
- Performance.
A rig that has all the features but runs at 2 FPS is just not usable. - Scalability of deployment.
Whether you're an indie dev or working on a AAA Game or Big budget movie, Making a rig and "deploying" it to the animators should be a process that you can trust and that doesn't get in the way. - Probably more.
Nodes are functions
Function Definitions
Most node graphs will let you make a node out of other nodes. When you do that, you are essentially creating a function definition. The contents of your node is its definition:
- The "Input Node" holds the function arguments
- The "Output Node" holds the return values
- Everything in between is the body of the function.
Here's a mock up graph of a Dot Product Function: Which is exactly the same as this python function:
def dot_product(a: Vector3, b: Vector3) -> float:
return a.x * b.x + a.y * b.y + a.z * b.z
Function Calls
Creating a new "instance" of a node is the same as calling that function:
- The node itself is the Function Call
- Connecting or setting the input attributes is the same as passing values to the function arguments.
- Connecting/setting the output attributes is the same as using the return values of the function.
Again, here's an example of a graph that calls the Dot Product function we just made:
And here's the python code equivalent to this graph
vector_a = Vector3(1, 0, 0)
vector_b = Vector3(0, 1, 0)
output = dot_product(vector_a, vector_b)
Objects are... uh... Objects
Things like joints transforms, geometries, etc are not much more than Structs.
Every object you create in your scene is an instance of that struct.
They have attributes that can be manipulated by the Nodes/functions
So what are the advantages of thinking of it this way?
Scalability
In theory a rigging framework built this way would only need to come with low level functions (basic math operations and types)
Then from that higher level functions can be built: IK Solvers, constraints, twist extractors, etc.
Then you have even higher level functions, these are your typical rig components: Arms, Legs, Wings, Spines, etc.
Then finally the highest level functions are your full rigs: Bipeds, Quadrupeds, Birds, etc.
And just like that you have a very complete Rigging Library (Oh look another programming term!)
Iteration speed
Most DCCs require some sort of custom tooling to make rigging viable in producion.
The workflow tends to be like this:
- Prototype the rig manually
- Automate the rig creation
When it should be this:
- Create the rig.
A system like this would most likely make build scripts or rigging systems (mGear, Rigify, etc) obselete.
In a way, the rig is the build script.
It is the program that takes all the control transformations and solves the positions of the vertices.
An approach that works for everyone
The beauty of such a system is it can work for all types of users.
On one hand, the power users will want to mess around with the low level stuff and have precise control over everything.
Oh the other hand the less tech savy will simply want to create a rig for their characters and will just need to use the Biped function.
Versatile
Warning
I am not a Software Engineer so some details here might not be accurate.
I do think the overall Ideas I'm describing are true though.
A Programming Language is at its core an Abstract Syntax Tree (The data structure that represents the order of all the operations of the program)
Basically, this is a Generic representation of the source code that can be used to do many different ways:
- It can be Interpreted to actually run the Program/Rig.
- It can be "compiled" back to a Source file of the same language (This is typically what Code Formatters do)
Note
The AST would be the bridge between the node graph and the source code, letting users define and call functions in their preferred way.
- It can be "compiled" (or transpiled) to a source file of a different language.
This is what TypeScript does to end up generating JavaScript code.
Tip
Imagine a rig transpiled to GLSH or HLSL shaders that runs entirely on the GPU.
- It can be "compiled" to something else. The same AST could be used to Build a Maya rig as well as Building the equivalent Blender Rig