The process:
I coded the entire system in Notepad++, rather than relying on the benefits of code completion and error checking in an IDE such as Eclipse. The result is that I've grown extremely adept at catching my own syntax errors, and quickly fixing those that do manage to slip through the cracks. Even some of the trickier and more insidious bugs didn't last much longer than 30 minutes or so of solid debugging.
The entire system is scripted in the maya.cmds implementation of python rather than pyMel. This has the effect of making the code much more verbose and quite a bit harder to read... but overall it wasn't that bad.
Insights:
The main thing that I learned was that, in a project of this magnitude, construction is king. I was reading Code Complete by Steve McConnell while working on the system, and it was a great combination. It really helped drive home the importance of well designed, well documented, well constructed coding. I'll get more into that in a second.
Since I had planned to take what I learned and apply it to a new autorigger of my own design, I noted a few things that I would change in my system. The first was the reliance on naming convention and namespaces to store and recall information about where a particular rig part came from and what it could do. This led to namespaces nested three layers deep, and an outliner that quickly became a solid wall of text. It worked, but storing information inside of metaNodes connected directly to rig parts would have been a more elegant, more powerful, and a cleaner solution. Second, once you 'lock' the rig and turn the blueprint modules into actual joints, that's it. the process is pretty much irreversible. It would have been nice to be able to see the joint locations, and then be able to go back and make changes. The other big design choice that I would avoid was the use of locked containers. Containers in Maya may be powerful, and theoretically awesome from a rigger's point of view (animators can't rename or delete important nodes, everything is bundled together), but in practice they're pretty annoying. Having to constantly unlock nested containers in order to debug things is slow and painful. In addition, attributes published to containers behave strangely in the channel box, disallowing middle-mouse scrubbing to change attribute values, and turning the clean list of attributes into an organizational nightmare.
Seen here: the arm controls? I'm not even sure...
The main thing I took away from this project though, was the importance of construction. I want to be clear: I mean no disrespect or insult to the guys who developed this system. It was a rapidly developed prototype, meant as a proof of concept and a learning tool -- not a production ready rigging pipeline. What the guys at 3DBuzz managed to pull off in this DVD was nothing short of awesome. That being said, the code itself is pretty rough.
There is almost no documentation whatsoever to the code. This was addressed directly in the videos a few times, as the addition of comments and documentation would have made the videos even longer, but even so, it makes the system a nightmare to read through. I considered adding documentation myself, but I would have never been able to keep up with the videos, and I wouldn't have truly understood the code well enough as I was writing it to comment it well anyways. Since the system was developed bit by bit, there were pieces of the code that weren't actually implemented until many hours later. They were only added when they were because the architect of the system knew that those functions would be needed eventually.
I would have liked to see more wrappers to Maya functions. There are arguments against this sort of thing (see the "Wrapping Commands in Maya" post on Sune Kempf's rigging page), but after you've written this for the umpteenth time:
cmds.xform(obj, worldSpace=True, absolute=True, translation=cmds.xform(target, q=True, worldSpace=True, translation=True))
You'll find yourself wishing you had declared a utility function:
utils.matchWorldSpacePosition(obj, target)
Stuff like this would have also made the routines smaller, since they tend to run on (and on and on).
The biggest issue I had with the code though, and the one thing that would really make this system solid, was the problem of information hiding, or more specifically, the lack thereof. Throughout the system, routines and functions made assumptions about the data that was being returned to them. This may have worked, since the assumptions were essentially hard-coded by the system's architect himself, but it made the code nigh impossible to read and follow in places. It also cause random bugs to pop up all over the place.
For instance, in one function, there was a check to see if a spine joint chain had 5 or more joints, and if it did, it could have a certain control rig installed on it. The code to check that condition was:
blueprintJoints = utils.findJointChain(blueprintJointsGrp)
if len(blueprintJoints) >= 6:
# do stuff
This seems like a typo. It seemed like a typo to me as I wrote it, and it took me a second to remember that the 'findJointChain' function returned the parent group of the joints in index one of the returned array. So instead of checking to see if there were 5 joints in the returned array, you had to add one, in order to account for that group. You'd never guess that was the case from the title of the routine, but this is one of many examples of this sort of thing. A better implementation of this check would be to write a routine that really did only return the joints (the existing routine could continue to return the group as well), and use a named constant instead of the magic number 6:
MIN_JOINTS_IN_SPINE_RIG = 5
blueprintJoints = utils.findOnlyJointChain(blueprintJointsGrp)
if len(blueprintJoints) >= MIN_JOINTS_IN_SPINE_RIG:
# do stuff
This makes the code much more readable, almost self-documenting.
ALL the learning:
This post is getting crazy long, so I'll try and wrap it up. What did I learn from this project? First, I got an INCREDIBLE amount of experience, writing the code, testing it, and debugging it. Second, I got to see how the whole system fits together. How multiple files and classes interrelate and work together to create an incredible amount of functionality. I learned some cool python tricks and techniques as well, such as using __import__ instead of the usual import when you need to bring in a module that is being stored as a variable, and you don't explicitly know which module it is. I also got to use a bit of vector math, which got me thinking of solutions to problems that stumped me a week ago (matching pole vector locations with a no-flip leg setup? I'll probably write a post about it). I also saw how the complexity of a system grows exponentially, and how the design is so import when it comes to the scope of a project, and determining which features get implemented or not.
Finally, I'd just like to say again, what an undertaking the creators of this system have pulled off. Not just building the autorig, but deconstructing it and rebuilding it in a tutorial format, constructing the system piece by piece, adding functionality and keeping it running and testable at every stage. Kudos to those guys at 3DBuzz. Would I recommend the DVD to others? If you've got the basics of python and scripting for maya down, and you're not intimidated by a challenge -- definitely. You'll learn a ton, I know I did.
I went through all that about a year ago and got side tracked, and yes it was a beast to come back to and try and debug, I'm in the process of simplifying it all, I have it based on pymel and Qt but combined all the UI's into one tool as well. Kudos for getting through it all.
ReplyDeleteHi Kyle,
ReplyDeleteI bought the DVD a while back and finally got around to doing it.
I'm about half way through and the amount of error checking I have to do is insane... Currently at the mirror expression stage video 91 and I keep getting strange snapping...
tried it on Maya 2016,2017 and 2018.
any ideas what it could be...
it works fine if I don't add everything into the summetryMove_container.... any ideas?