I've been working on some rig generation scripts for biped characters. They started their life as scripts that were driven by shelf buttons. The options were hardcoded into the shelf button. You wanted to change the angle/offset/size of a controller, you had to edit the shelf button. Building a complete rig consisted of pushing several shelf buttons. You can see it in action building several rigs in my demo reel.
When I started building a UI around the scripts, I had two main design goals which were:
1. Easy to change the rig on a limb by just changing a selection in a list. You have IK and you want switchable FK/IK? No problem. Change a selection, regen the rig and off you go.
2. New rig designs were easy to code and you just drop them in. Each rig subsytem was self-contained - no code changes to hook them in.
When I first started out the project, I couldn't figure out how to meet the second goal. I had a number of .mel scripts. How to tell when a new one had been added? How would I find new rig scripts and figure out what they were? Since MEL doesn't have anything resembling reflection (except for the 'whatIs' command), getting this done seemed pretty difficult.
So, not wanting to get blocked, I punted on the problem. I made the interface to the rig files as simple as possible (each limb e.g. arms, legs, back, etc) had a specific signature for building and that was that. To add a new arm type, you had to go to the menu for arm types, add the string you wanted and then go to the code that looked at the options and add a new clause to the if-then-else and call the new function. Not great, but not terrible.
As work progressed, I accidentally did a really good thing. I separated the scripts into subdirectories: one for the arm modules, one for the leg modules, etc. Then I explictly traversed the directories with 'getFileList' and sourced the .mel files I found.
As I was finishing up the demo reel, it hit me. I could use the 'whatIs' command and query if certain methods existed. Since I was explicitly sourcing the .mel files, I had their name. If the methods I was interested in were prefixed with the file name, that was all I needed.
So, now the interface to a subsystem is pretty simple:
- a build proc
- a display options proc
- a save options proc
- a couple of procs to get menu strings and stuff
Now, the main generator is completely generic. You want to add a new a new rig type for a limb? Just code up the functions and drop it in the correct directory. It automatically shows up in the UI and is available.
Now of course, the connections are more subtle. Particularly between the skeleton and the rig types. I've eliminated a bunch of the naming problems by automatically selecting the best joint I can find, based on name. However, if the skeleton has a different naming scheme, the rigger can just select the right joint in the rig builder UI. But what if you want a ribbon spine and there's no nurbs plane for the ribbon? You the build just fails. The whole connection between the skeleton and the code is too tight and too implicit.
Something to work on for the next version.