Wednesday, August 1, 2012
Monday, July 30, 2012
Vector Angle setup
For the past two days or so I've been thinking about vector math, trying to pull off a node setup like this one. I've got the concept in my head, but I'm still working on the details of the implementation, as far as the actual math goes. I've been studying the Maya API as well, and while I'm understanding how everything fits together (the general structure of API plugins), I just don't have enough experience with the API classes to know which MObjects and functions to use to get what's in my head into the code and onto the screen. So I figured I'd mock up a simple node network to see what I could do, albeit with a limited understanding of the math involved. I'd still like to get an API locator node implementation figured out, but I figured I'd share what I've come up with so far.
Ranged Vector Angle Setup from kyleMR on Vimeo.
Basically, I'm getting an aim vector from an object (in this case, the cone) by using a locator. I subtract the position of the cone from the position of the locator to get the aim vector (which is a normalized vector, since the locator is one unit from the cone) and then I do the same for the target locator, which gives me a target vector. I take the dot product of those vectors and pipe them into a setRange node, which allows me to remap the value between 0 and 1, as well as change the high and low boundaries of the calculation. Pretty simple. Basically, this sort of setup can work as a pose space deformer, although I'm just driving the scale of a sphere for the sake of visualization. The process only takes a few minutes to set up by hand, but I scripted it out just for the hell of it. If anyone is interested in the code just let me know.
This problem has been churning in my mind for a few days now, and I'll definitely be taking the solution further.
Basically, I'm getting an aim vector from an object (in this case, the cone) by using a locator. I subtract the position of the cone from the position of the locator to get the aim vector (which is a normalized vector, since the locator is one unit from the cone) and then I do the same for the target locator, which gives me a target vector. I take the dot product of those vectors and pipe them into a setRange node, which allows me to remap the value between 0 and 1, as well as change the high and low boundaries of the calculation. Pretty simple. Basically, this sort of setup can work as a pose space deformer, although I'm just driving the scale of a sphere for the sake of visualization. The process only takes a few minutes to set up by hand, but I scripted it out just for the hell of it. If anyone is interested in the code just let me know.
This problem has been churning in my mind for a few days now, and I'll definitely be taking the solution further.
Saturday, July 28, 2012
Check Symmetry Utility
A little while ago I worked out this little melscript utility to check for non-symmetrical vertices in your models, and today I decided to turn it into a python script and rework some of the logic. Basically, you run the script and it returns the number of non-symmetrical vertices in the mesh you have selected. It also selects those vertices for you, so you can see them in the viewport. If you want, you can also specify a certain mesh, mirror axis (the plane across which the symmetry will be determined, default is the YZ plane), tolerance, and turn off the selection of the verts at the end. That way you could use this as a utility to another script that deals with symmetrical meshes. Grab it here: checkNonSymmetricalVerts.
Friday, July 27, 2012
Stick a fork in it
... because I finally finished my modular autorig. Developed by following along with 3DBuzz's monstrous 50+ hour 'Developing Modular Rigging Systems with Python' DVD, this autorig is by far the most elaborate and involved scripting project I've ever taken on. This all started because I wanted to build my own autorig, but didn't really know exactly where to start, so I picked up the learning DVD to get a better idea of the process. I definitely learned a ton doing this project, so I wanted to share some of those insights here. Below is a quick demo of the system's functionality.
The autorig itself spans over 30 python files, written from scratch, and it took a little over 2 months working off and on to finish. The process was pretty simple at first; watch the videos and write the code as they write it, testing frequently. However, it quickly became apparent that the process wouldn't just be a walk in the park, copying code the whole way. Discrepancies in the way UI code is handled by the pre-QT interface of Maya 210 (which was the version the system was developed in) and Maya 2012 meant that subtle changes had to be made in order for all of the elements of the user interface to be displayed correctly. Also, fixing any bugs that I discovered while testing (most due to my own typos, I'll admit) was totally up to me.
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.
Since I took this on as a learning experience, I wanted to really break down what I learned, the good, the bad, and the ugly, from this project.
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.
You'll find yourself wishing you had declared a utility function:
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:
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:
This makes the code much more readable, almost self-documenting.
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 stuffThis 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 = 5blueprintJoints = utils.findOnlyJointChain(blueprintJointsGrp)if len(blueprintJoints) >= MIN_JOINTS_IN_SPINE_RIG: # do stuffThis 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.
Tuesday, July 3, 2012
mr_motionTrail is available now!
The mr_motionTrail utility that I demo at the end of my current rigging reel is now available for download at CreativeCrash! I had some people expressing interest in using it for their own animation work, so I decided to put it online for download. Go get it, it's free!
Saturday, June 23, 2012
Facial rigging class at CGSociety
I just signed up for Judd Simantov's character facial rigging workshop on CGSociety! I've been super excited for this class ever since I saw his demo reel. The class starts next month, and I'm looking forward to learning all sorts of fun things that I can use for my upcoming rigs for a short film I'm working on.
Check it out!
Check it out!
Monday, June 4, 2012
Rigging Reel
Kyle Mistlin-Rude Rigging Demo 2012 from kyleMR on Vimeo.
These are a few of the things I've been working on in the past couple of weeks. I'm currently also developing a modular auto-rigging setup, and there's another rig or two coming down the pipeline, so expect to see some more updates soon!
A few notes on the utilities/scripts I wrote:
SpareParts: Rig Utilities:
Fk/Ik matching is handled using metaNodes and a total of 3 functions, and the setup could conceivably be reworked into an even more generic framework. As it is right now, one function handles getting the selection and connecting to the metaNode, which in turn connects to rig components involved in the fk/ik switching process. The other two functions take care of fk to ik switching and vice versa. Any hinge jointed limb with a similar blending setup would work with these functions, allowing for a lot of functionality with very limited script. There's also an option to set keys to 'bake' the blend state and controller positions.
Space switching is handled using a similar node system, and the same set keys option is available for baking the parent space state.
Baking animation from the control rig to the bind rig, as well as rig export will also be handled by this script, but at the time of this reel, it was not implemented in the UI. (I had yet to get my hands on the final character model and do some engine tests, and I didn't want to distribute an untested script to the animators.)
The UI updates based on the current selection, so that only the options that apply to the selected control are available to the user. For instance, any selecting any control on one of the arms will allow the user to switch fk/ik state, but if any other control is selected, the button will be disabled. The same goes for the space switching and export panels.
mr Motion Trails:
This is a handy tool for animators tracking arcs. It uses Maya's built in motionTrails, but in a hierarchy independent of the animated control. This way, the point from which the trail is generated can be moved. In the example on the reel, I created a trail on the head control, which by default tracks an arc from the pivot point of the head joint. I then re-position the trail to draw from the tip of the character's nose, so the trail picks up head rotation and gives me a nice arc. In addition, the user can choose to add spacing markers to the trail, in order to better see slow-ins and slow-outs. The interface also gives the user an easy way of editing trail parameters, as well as removing the trails from the scene. The trails are not selectable in the viewport, so they don't interfere with animation control selection.
The script was written using pyMel, which seems to lack support for certain motionTrail nodes, causing errors whenever they are cast into PyNodes and variables. Finding a workaround to this was a fun challenge, since I needed to be able to access those nodes in order to edit their settings on the fly via the tool. I'm planning a future update for the script which will move away from Maya's motion trails and instead utilize a system of my own. I'll also include support for user selected colors and more display options.
Subscribe to:
Posts (Atom)
