Posts Tagged programming

autorig theory

A friend has been pushing me to write about how my Maya autorig works, and my thought process on how to write modular autorigs as a whole. Been putting it off for far too long, so here we go!

Problems with Autorigs

I look at modules in an autorigs as simple, replicable pieces that serve a specific purpose without overlapping feature-wise with another module. They’re Lego pieces, and when connected together you get something predictable that can be rebuilt and repurposed across multiple characters.

However, most autorigs I’ve seen or used split their workflow into three stages:

  • The Guide (using Gear parlance): usually a simpler rig setup that allows the user to position the rig without worrying about joint orientation, joint counts, or how modules might need to be connected under the hood to expose certain features.
  • The Bind Joints: generated by the guide, these are used for skinning the character.
  • The Rig: also generated from the guide, these are the controls that move the bind joints.

Usually the rig and bind joints are generated at the same time from the guide. This leads you to the first problem that I’ve tried to solve: when you have to modify the rig in any way, you end up having to delete the rig and the bind joints, modify the guide, regenerate the entire rig from scratch, and then reload the skin weights. We had a character at work two years back that was hella complex, and when the client asked for changes the build alone took a half hour, with weight re-loading taking even longer.

Having to regenerate the rig whenever changes are made at the guide stage is also, in my opinion, quite limiting. You can’t fix a single module in rigs that work like this, and you can’t do iterations as quickly when you’re running through every module’s build and reattaching skins to change a control type.

My Process

I wanted to overcome these specific limitations, so my goal initially was to be able to build, unbuild, rebuild, or upgrade any module in the rig without affecting other modules, within reason (which is a caveat we’ll get to). I also wanted to not delete bind joints unless it was absolutely necessary, so that rig development could continue with skinned characters without having to reload skins every five minutes.

To do this, one thing I’ve done in my system is to remove guides. Instead, modules are built off of the bind joints, each module receiving its own chain. None of the chains are parented to each other, but are instead constrained to each other after module build. Also, as much as possible I try to set up parenting relationships that have control spaces / follows driven by bind joints in other modules. These are special message connections.

Unbuilding, or removing a single module, requires that the module and all nodes related to the rig are deleted from the scene, and the original chain is reset to its default position. This means that a mechanism is needed for saving and restoring those default positions, as well as for keeping track of all nodes added to the scene by a module’s build.

Because constraints between modules are largely between bind joints, one nice feature of the system is that unbuilding a module and resetting its joints to their default position leaves the rigs of other modules in tact. You can change a IK Limb into an FK chain, for example, and modules that rely on its bind joints will follow the new setup without needing to be rebuilt.

Tagging and Upgrading

To identify joint chains in the scene which can be turned into rigs, the system can be passed a selection of joints or can search through all joints; in both cases, the roots of the chains are tagged with Params, which are special user attributes applied before the build. Params could be things like the token used to identify the module and name controls uniquely, control scale, number of controls (for things like Spine rigs), and so on. Params don’t get removed during normal usage, so settings used to build a module persist in the Maya file on the root joints and don’t require a separate offline file. A template for a rig can be just the bind joints and their connections, saved as a .ma file.

A benefit of this is that when the code for the module changes and Params are added or removed, they can be upgraded on joint roots easily. I’ve used this trick to update parts of older rigs while maintaining animation compatibility in scenes with references.

Controllers and Orientation

Controllers pull their setup information from the Params as well. This includes type, size, color, and orientation. As all bones in the system are oriented X-down, Y-up (to provide a predictable set of inputs as well as to match Maya’s defaults), orientations allow the controllers to be turned in their default position with axes oriented in a different manner. More simply, if the animators want fingers to be X down and Z up, or Z down and -Y up, this preference can be set in Params on the root joint of the chain and will take effect at build time. Different control types and their Parameters are registered in the __init__() of a rig module.

Postbuilds and Seaming

One of the hardest things to reconcile when making modules work together is when dependencies from one module must exist before another can be built. An example: a reverse-foot setup requires that the leg module it’s going to drive and be driven by (depending on whether the combination is in IK or FK mode) exists prior to connection.

I solved this by having builds run in stages: build, postbuild, and seam.

The build is what you expect: it sets up the control structure for the module, constraining the bind hierarchy to it and returning the module group. If you’ve ever scripted a rig setup you’ll find the build() function of a module easy to follow.

Postbuild happens as the name suggests: after the build. In fact, the postbuild functions of the modules are run after all module build() functions have returned. This guarantees that the pre-conditions that might be required for the postbuild() will be complete without having to explicitly track the order in which modules are built. That aforementioned Foot module, for example, can depend on the Leg it’s attached to existing by the time the master build function runs its postbuild(), and it can use that knowledge to ask the built Leg module for controllers and other information.

Seaming is the constraining together of modules. Each module has one or more targets for constraints. The root input moves the entire rig, so you can be sure that if it is constrained your module’s controls will move (and scale) properly with it. There might also be an IK goal input that gets constrained to another module to move the IK goal: the tip of a limb, or a Spine.

My previous design had seaming as a function outside the module that’s run by the master build process, but I’ve moved it into the modules themselves so that custom seaming overrides can be created. Each module also handles unseaming, or the removal of input constraints and the resetting of the input objects to their original positions. Also, with seaming moving into its own module-level function, while I may be keeping the postbuild() method in the base class I’m not sure currently how much it will be needed moving forwards.

Try It Out For Yourself

In writing this I’ve also gone and written an entirely new system from the ground up in an effort to showcase my thoughts in working code. It requires Maya 2014 or greater and is Python-only. The first version only has two modules and no UI, but there are example files to test the builds. If there’s enough interest I’ll fill out the module library a bit more and possibly throw a UI on it. I’ll update this post soon with a link to the Github repo.

Tags: , , ,

fun with python modules

New scripters I speak with don’t often know how to easily load code from external folders / modules in Python scripts. Today’s post will give you a few pointers in both Cinema 4D and Maya.

Setting Your Script Paths

Python’s import command should be no stranger to you if this topic interests you– it was probably the second thing most people learn in Python, after writing “Hello, World!” to the console. Import allows you to bring in code from libraries external to the source file in which you’re currently working. For example:

# commonly seen in Cinema 4D
import c4d
from c4d import *

# commonly seen in Maya
import maya
from maya import cmds as mc
import pymel.all as pm

These statements bring whatever module you specify into the current namespace, or bring in functions, classes, and variables. But where are the libraries that the import method references? Where do they live on disk?

The easiest way to find out is to list Python’s sys.path:

import sys
print(sys.path)

In my case, in Maya, it outputs the following:

[
'/Applications/Autodesk/maya2012/Maya.app/Contents/MacOS',
'/Applications/Autodesk/maya2012',
'/Users/Carney/scripts/mayaScripts/scripts',

...

u'/Users/Carney/Library/Preferences/Autodesk/maya/2012-x64/prefs/scripts',
u'/Users/Carney/Library/Preferences/Autodesk/maya/2012-x64/scripts',
u'/Users/Carney/Library/Preferences/Autodesk/maya/scripts'
]

Looking in the folders that are listed, you may find any number of Python source files and Python object files (*.pyc). sys.path is great because it’s just a regular Python list, which means it’s editable. You can append any number of paths that you like to it, and when import is called it will look through each and every path for the name specified.

Let’s try this in Cinema 4D. First, make a folder somewhere on your machine. I’m going to use /Users/Carney/Desktop/scripts for this example (I’m on a Mac.) In that folder, create a new file called util.py and add the following text:

# util.py
# simple utility functions

def ut_log(*args):
    msg =''
    for arg in args:
        msg += ' ' + str(arg)
    print(msg)

Now back in Cinema 4D, open the Console and the Script Manager from the Scripts menu. In the Script Manager, paste the following and hit Execute, replacing the path with the folder you chose above:

import sys
sys.path.append('/Users/Carney/Desktop/scripts')

import util
util.ut_log('Fun', 'Bunnies', 'are', 'fun.')

If all’s well, you should get a single sentence printed to the console output. If you made a mistake, you may get an error about not being able to import the util file. Check your path and try again.

With this setup, you can write scripts in your application that reference lengthy libraries of external code or other utility functions that you’ve created. At the moment I’m organizing code into single files with multiple functions for shorter utilities. Any function that’s more complex gets it’s own file and a class to contain it. This leads to funny lines like the following:

import module_generic
mod = module_generic.GenericModule()

But the organization and the ability to quickly update code without having to sift through a monolithic file inside the C4D Script Manager is worth the headache.

Automatically Adding Python Search Paths in Maya

The only thing to remember about this setup is that you have to make sure you add that folder you created to sys.path every time your application starts. In Maya this is a simple and well-known trick, but I had to do some digging to find out the same for Cinema 4D.

In both applications there’s a special Python file that gets loaded every time the Python VM restarts. In Maya, the file is called

Automatically Adding Search Paths in Maya

and it must live in one of three places:

  • ~/Library/Preferences/Autodesk/maya/2012-x64/prefs/scripts
  • ~/Library/Preferences/Autodesk/maya/2012-x64/scripts
  • ~/Library/Preferences/Autodesk/maya/scripts

You may notice that these are listed in the above printout of sys.path. When Maya starts it will execute userSetup.py files it finds. If you put the userSetup.py file in a folder that has the version of Maya specified (for example, in maya/2012-x64/scripts), then only that version will load the userSetup. If you place it in the general scripts folder — the last folder listed above — then it will be loaded by all Maya versions that support Python.

You can also use the MEL command internalVar -usd to find a folder that will work.

The userSetup file can execute any code you like when Maya loads, allowing you to have custom menus auto-created or to modify your sys.path variable so that you can access your code at any time. Try this: save a userSetup.py file into , and make sure it contains the following (after modifying the path to point to where you saved your util.py file):

import sys
sys.path.append('/Users/Carney/Desktop/scripts')

Now in Maya, open the script editor and copy, paste, and run the following:

import util
util.ut_log('Fun', 'Bunnies', 'are', 'funner.')

If all worked, you’ll see a line printed from your userSetup.py file letting you know that the custom setup succeeded on Maya startup, and you’ll see the proper output from this file.

Automatically Adding Python Search Paths in Cinema 4D

The way to do the above in Cinema 4D seems to be less well-known: you have to find the folder that is appropriate for your particular version of Cinema 4D and copy in a file called python_init.py.

The easiest way to find this folder is to go to the Scripts menu and choose User Scripts > Script Folder. This is the folder where scripts you’ve edited and saved through the Script Manager live. Now, go up one folder to Library, then go up again. This is the main folder for your version of Cinema 4D; it may have an oddly long name, but it contains your prefs and libraries. The python_init.py file belongs in prefs/python. As an example, my python_init.py folder is:

  • /Users/Carney/Library/MAXON/Cinema 4D R13/prefs/python/

Final Notes

This is just the start of organizing your code. If you want to know more, I recommend reading the official documentation on Python modules.

Tags: , , ,

batch wrangling – FBX

At work we’re moving from a MotionBuilder pipeline to Maya, which I’m responsible for creating. One of the tasks on the list I was handed was to move animated takes out of MB and into Maya so that all exported files can be run through the same export steps, and also so that moving forwards animators would have the same tools for working on older animations as they will on files using my new rig.

I have Maya Creation Suite 2012 on my work machine, which is touted as being able to seamlessly transfer data between the included apps– Maya, MotionBuilder, and Mudbox. I haven’t had need to open Mudbox yet, but in my limited testing so far the Human IK rigs never come through properly from MB to Maya and on the Maya side the HIK characterization gets broken. This means that to do any animation fixes or retargeting inside Maya, the characterization needs to be deleted and rebuilt. Also funny: the automatic naming templates in Maya don’t always work, making recharacterization a tedious manual process. Keyed transforms (joints, etc.) and meshes come through just fine, however, with all skinning and materials in tact. Updating previously-sent objects did not.

Apart from all that, the first file I’ve been working on has 50 animations in it. I didn’t relish the idea of converting all of them by hand, so I set about seeing what’s possible on the scripting side.

The FBX import/export plugin comes with a number of commands for massaging how files are read in. A full list of the commands is on Autodesk’s site. Note that the Python versions of these functions fail; I think they’re being generated improperly at plugin load. Could be that I’m just not calling them properly; I didn’t bug-hunt because I found doing the HIK post-import steps didn’t work in Python, either.

I’ve written more MEL in the last two days than I have in the last four years, not that it’s a lot of code!

The important commands for what I needed are:

FBXRead -f [filename] — Doesn’t do any loading. Instead, it sets the specified file as the source for all queries.

FBXGetTakeCount — Self-explanatory; on the file I was editing it returned 50.

FBXGetTakeName [index]: returns the name of the take for a specified index. The indices are 1-based. I saved all of these in a string array.

FBXImportSetMayaFrameRate -v [true|false] — Important in my case because the animation was all at 30 frames per second, while Maya is usually set to 24.

FBXImportFillTimeline -v [true|false] — Makes sure the timeline length matches the length of the imported animation (although see below for a gotcha)

FBXImport -file "c:/myfile.fbx" -t [take index] — Does the actual loading of the scene. By specifying a take index, you can be sure to get only the animation you want.

With this information, I was able to loop through the animations in the FBX and, after a bit of post-import processing, spit out a series of .ma files.

There were few gotchas. One problem was that while the script was running, sometimes the setting for having the FBX file’s time length override the same in Maya would get truncated– the animation length got set but the [bar that shortens the animation] would get set to half the length. I couldn’t replicate the issue running the MEL script in small chunks. A call to playbackOptions in my loop fixed this, but it’s still weird.

Part two of this discussion will be about man-handling Maya’s Human IK in a batch process.

Tags: , , , ,