This was an experimentation into runtime rigging - the process of calculating skeletal transforms on the fly rather than processing baked forward kinematics.
Runtime Rigging is particularly useful in situations where variants of a skeletal mesh are required and animations can be described at a higher level than joint transforms. In this scenario, it becomes possible to continuously increase the variations of an asset, including the skeletal structure without having to pay the development overhead of offline retargeting as well as minimising the footprint of files you need to distribute for each variant.
In this video, we build a 'rig' using a Shard graph, where each node is a calculated behavior. Behaviors can be anything, but in this case, most the behaviors are relatively simple such as multipliers, inverters etc.
The original implementation of shard is written solely in python and was demonstrated as a generic graph execution framework being utilised to invoke the building of rig components. To utilise the same approach at runtime I have re-written the execution framework in C++ and defined the graph format as being consistently digestible between the two (generating a python module from the c-api might be a next step!).
Because the behavior of the rig exists only as a Shard Graph (a JSON markup of node descriptions and connection states), it is completely agnostic of application/host. Ultimately, it’s a logic graph which takes in some inputs, evaluates the graph (following backward dependencies for optimization) and provides some output values. All of which make it incredibly portable.
In Maya, we implement a custom C++ node which links to the shard library. The node exposes a file path attribute and reads the JSON data from the file. During the digestion of the JSON data it dynamically populates the node attributes as defined in the JSON data – leaving us with a Maya Attribute per input and output plug in the Shard graph. This allows the Rigger/Artist to connect control connections to the user facing plugs in the Shard graph. The approach means we only need to write one custom Maya node to handle all possible Shard Graphs.
In the example of the door, each skeleton has its own Shard Graph definition, which determines how the various parts of the door will move. However, they each share the same input plug’s, meaning they can utilise animations defined on any door. In this example, the only data being passed in is the a translateX value of a single bone. Therefore, with one animator defined curve we can have drastically different doors each of which contain bespoke details that move in specific ways.
Finally, we create a C# wrapper in Unity3d which exposes our C++ shard library. Just as with Maya, we instance Shard, feed it our JSON rig and apply our animation (which contains a single float track). In this example, for each door we write a small C# component which feeds in the graph on initialisation, updates the inputs and passes the outputs to the desired unity objects.
We can then interact with the door using animation, but also through the direct manipulation of the single bones translateX value. A huge benefit here is that the door be driven via curves or via any procedural process we may want to utilise within the engine.
The overall goal of this experiment was to demonstrate application agnostic rigging as well as an exploration of building a pipeline which allows for vast amounts of variation without the overhead of additional animation data. The side benefit to this approach is that the skeleton can be manipulated and tweaked continuously by a rigger without having to re-export the animations to see the new result within a game engine.
This example utilised a door as its simple and kept the goal focused on executing a graph at runtime in multiple host environments. But the scope is certainly far wider than that, and given a rich set of behaviors (IK, constraints etc.) it would be possible to expand to characters requiring skeletal variation.
Big thanks to Vinh Dieu Lam for his guidance on wrapping my C++ library in C# for use in Unity!