So I haven’t really posted anything on my rigging project since that first post, but I’ve actually been doing a ton of scripting for it, in any spare time I can find. I haven’t started any designing, modelling, joint placement, or any of that, but I’ve begun to lay the foundation for how the rig will be built.
There’s 3 main ideas I’ve recently got working and wanted to share: Naming, Python Objects and Inheritance, and Rigging Interface.
How nodes in a rig are named of course is really important. A good rig should have every node helpfully named, without any duplicates. A good name should be consistent, easily read, searchable and parsable. Individuals and studios have their own naming conventions that they like to use, and in some cases it will be so ingrained and hard-coded into scripts and tools that it can be a real pain to update. For that reason I didn’t want to do any explicit naming of nodes in individual scripts, but instead write one python function that is used to generate the name for everything that gets created. The function takes all the different parts of names as arguments, and then uses a template string to determines how the parts of names should be organized. So, for example, say you have a simple name template that looks like this:
This template can be interpreted by python’s “string.Template” object, to substitute the different parts for passed-in arguments. So using that template to name a joint in the arm on the left side, with an underscore as a separator, you would generate something like: “joint_Left_Arm”. The neat thing is, if you use the same function for every name in the rig, then if you wanted to get rid of the underscores in all your names, or put the node type last, you only have to change the template. I’m just using one naming convention right now, but ultimately it could even be determined in a project file, so that two different shows running in parallel could have two completely different naming conventions. I’m not sure how useful that would be, but at the very least the rigging tools could be adapted to work at different studios.
Python Objects and Inheritence
The next big revelation for me was using object-oriented programming as the structure for the different rig elements. I wanted to make a modular system, which is to say the different parts should be structured the same way, and be potentially interchangeable. It also means that the individual elements are more general, and certain ones can be used regardless of what the puppet is: biped, quadruped, snake, bird, etc.
The benefit of having a solid, consistent structure is that it makes it quicker and easier to write a new rig element and have it work with the rest of the system. And, as always, consistency helps the animator to know where to find controls and generally how to use each part of the puppet. Everything that is the same among the different elements of the puppet, things like where to group local controls versus world controls, space switching, attributes to control visibility, ect., can all be defined in a core class, which other classes inherit from. And it doesn’t have to stop there, since more complicated rigs may be combinations of simple rigs. For example, I started with writing an Fk class, and then an Ik class, which are very basic and both inherit from the same PuppetElement class. Now the more complicated FkIk switch class, in addition to inheriting from PuppetElement, also inherits from the Fk and the Ik class. This means that the fk and ik portion of the rig will be built the exact same way as their stand-alone counterparts. So it will be consistent, but it also means that if you upgrade somewhere along the line from an Fk rig to an FkIk rig, references wont break and animation wont be lost (hopefully, anyway) because the Fk portion of the rigs will have been built the same way.
The last thing I wanted was some sort of interface for setting up the rig. I have always preferred a rig that is “compiled” or built by scripts all at once, rather than something that you set up one piece at a time. The benefits of a fully scripted approach are consistency between puppets (there’s that word again!), and the ability to update code for a certain part of the puppet and push the change to all the puppets by rebuilding them. The alternative of building individual parts with individual tools can be a quick and flexible process, but I think it makes maintaining and updating multiple puppets more work.
The issue with a fully scripted rigging process is how to communicate which systems are built to control which joints. In my experience in the past, this was done in the form of a master build script for each puppet, which was just basically the instructions for which scripts to run on which joints in the scene. This works and is flexible, but can take a while to write, requires an additional file for each puppet, and can be a bit unintuitive. Some autoriggers I’ve seen run off a template skeleton, or specific names in a scene. This can be more user-friendly, but isn’t always very flexible or scaleable, since you’re potentially constrained to whatever the template skeleton looks like. Adding extra arms or rigging a snake may require creating a new template skeleton. There’s nothing wrong with a template, but my thinking was that to avoid having to maintain potentially a lot of skeletons, it would be better to disassociate the building process from a template. Basically, I liked the flexibility of a script, but without the extra file or the reliance on names.
So, if I was building a puppet from a script file, it would be a bunch of lines that said something like:
createIkElement(joints=(‘joint1’, ‘joint2’), createPoleVector=True, stretchy=True)
The idea was to recreate this function call as a node in maya, with the arguments being attributes that a user could edit as options. So as a basic example, the way this works is a user selects the joints to be included in the element, and then creates an Ik “Guide”. The guide is basically just an annotation node, and in the channel box it would have a couple boolean attributes called “createPoleVector” and “stretchy.” A “joints” message attribute is created and hooked up to the joints involved. The user can still position and orient joints, rename and reparent everything. In addition, there’s an attribute that records the python module, and the class or function that this guide represents. When the time comes to build the puppet, the builder script finds all the guides, imports the module and then runs the function, using the attributes and their values as keyword arguments.
So far it’s working pretty well, and I like having everything to do with the puppet encapsulated in one scene file, and represented a bit more visually than a script.
And So On
I’m not sure how quickly things will progress from here, but if I can I’d like to try to motivate myself to post progress every weekend. One of the reasons I started posting again was to share my rig development process, so I had better at least do that!