Sunday, December 25, 2005

Finishing the prototype

I think the prototype rig is done. In the last week, I've added a pseudo forward kinematic style forearm orientation to my hand control. As well as getting a true FK arm working, with synchronization to the IK arm after switching, and vice versa.

The forearm orientation works especially well. I've gotten it optimized and working without an expression. This means the hand controller rotates with forearm, and the rotation channels are completely relative. So if you rotate the arm in IK or FK 90 degrees, but haven't rotated the hand, the hand rotates that same 90 degrees but the channels stay at 0 0 0. Based on the comments at Jason Schleifer's web site, a lot of people have not had any luck accomplishing this.

Keeping to my rigging philosophy of being able to alter the rig midstream in a shot and animating that alteration, I've added synchronization of the forearm follow and isolation settings so there's no jumping between states.

At first I borrowed Daniel PK's solution, which required an expression. But using part of his methodology, I was able to make an optimized direct connection instead.

The only limitation I'll state on the FK arm, is that stretchy doesn't work in FK...it snaps to an unstretched state if you had the arm stretched out. Also, you can't switch from bending the elbow backwards (as in "successive breaking of the joints") in FK, to IK without some unfortunate side effects. IK just doesn't support backward bending on the elbow. I could make a pseudo IK controller that would enable such things, but I think the arms already have enough choices for animators as it is, with IK/FK, FK rotation on IK hand, and six animatable and synchronized space switching choices, plus isolated shoulder rotation on both FK and IK.

I did a chopped up, lo res version of the character, which can be interactively switched (even animated between resolutions). Amazing as it seems to me, I had to actually write my own tool to chop the character up into separate objects to parent the segments directly to the bones. I posted this up at HighEnd3D (sg_polySeparateObject.mel). Really simple script, and amazing that this function isn't built into Maya.

In addition to the arm controls, I also completely altered the way the eye control works after input from Jeff Cooperman. It is now a rotational control, with an isolated option to aim at a control target, which itself can be constrained. Unconstrained, the eye position is synchronized between the isolated and rotational controls. Also added an eyelid shape control.

I optimized the rig speed Saturday, and saw a 6-8x increase in speed. It's now totally realtime in interaction, and tests so far play at full 24fps speeds.

I should begin working on a rigging demo Tuesday.

Planned Improvements:
Character interface
Ground-up skeleton rebuild with new tools.

Scripts
Besides the polySeparateObject script, I'm starting work on my skinning script. This will be used with my rigging tools. The rigging tools will create spokes eminating from the main bones, which can be user adjusted, to improve precision of proximity assignment. In the prototype this has worked well.

To facilitate reasonable rig speed, I'm working on a script to take assignments made to the so called "SPUR" bones, and add them to the parent bone, thus boiling out a great deal of soft skin cluster assignment that slows the rig noticably. This is an ambitious script, and the technique will significantly reduce the need to "paint" weights.

The challenge in this will be how to manage keeping SPURs that can be used for FFDs and pseudo muscle controls.

I've already gotten the portion of the script which allows me to read which vertices are assigned to what bones, and at what weights. I'll need to parse these, transfer and or add these values to the parent bone. Much of the code for parsing exists in syncOb.mel. Thanks go to Mike Hovland for pointing me in the right direction for locating the weights on the vertices/CVs.

I'm not including the various character specific scripts for synchronizing switching on the head, eyes, hands, shoulders and feet. Or the obsolete script for IK hand pseudo FK.

This week I should start working on graphic interfaces.

Sunday, December 11, 2005

Space Switching Madness

It seems that there are always unwritten, esoteric rules to Maya commands.

After my success with the space switching capabilities on the hands, I decided to add synchronizing scripts to the feet, tail and head, so when switching to isolation modes in those, they wouldn't jump. Or in the case of the feet, going from complete isolation to moving with the body.

Now, the isolation mechanism is similar, but sufficiently different on each of these, that I had some difficulty with every single one. That's because "xform -q -ws -a -t " doesn't actually give you the value you think you should be getting. If you read the docs, and just about everything out there on the command, you should be getting a full, world value for the location of the object, regardless of what's going on in the hierarchy above that object.

No dice. This is a horribly behaved command when, it turns out, the objects in the hierarchy above the one you seek a value on, have their transforms zeroed out. Especially if the object you're trying to get the information on also has the transforms zeroed.

But there's more. If the object you're trying to get information on is not zeroed, it won't work right either when you try to put the xform you were caching, back onto the the object so it doesn't move when you throw the switch.

Like everything else, things get easy once you know what you're doing. It took hours of detective work to figure out what was different between the head and the feet and the arms. Turned out, it was having a zeroed out parent. I arrived at this conclusion after trying every "xform" flag permutation. Absolutely nothing other than "-rp" would return a reasonable value. And "-rp" is useless if you want to transform the whole object. So I manually cut and pasted the values derived, and found the values were relative to the parent object's zeroed out transforms. Even though I demanded "world space" values with the "-ws" flag. Go figure.

So after hours of noodling with this, I completely reset the parent object to no offsets. The value in the translation box would be the exact ones that would come up on a "-rp" query.

And what do you know? With a real world value for the parent object, suddenly the script worked the way it was supposed to. The transforms were read, cached, and dumped back into the head, and the head didn't jump when the isolation mode changed. Amazing. Hopefully things will go smoother with my last two isolation controls tomorrow, on the clavicle.

It took awhile to find the pattern that had made things (IMO, unnecessarily) difficult, and turned each attempt at a self compensating space switching control into a cloak and dagger, sleuthing ordeal. And I don't think I've got all the answers. The clavicle controls are nested deep in a complex hierarchy, and I'm expecting to run into additional "gotchas" that may require some complicated vector math to solve. Or maybe the rigging gods will smile kindly on me, and things will work predictably instead of idiosyncratically.

The most amazing thing about this was that not one person I talked to on the Internet knew this. Usually you use "xform" for moving stuff like particles around a scene, or relating objects to other objects at the same hierarchy level, I suppose. At least, based on the tutes and books I have, that seems to be the case. It appears that I'm using this command to do something for which it was not intended.

But there's really good news. In getting this to work properly now, I've eliminated the "autokey" problem entirely. I no longer get keys arbitrarily added when scrubbing or paging frame to frame. I've also eliminated delays that occurred with the prior scripts which used locators and constraints to synchronize the elements. The "attribute" change is no longer detected at unwanted times, and objects no longer select and deselect themselves.

I'll have to ask some riggers if running all these scriptJobs has some drawback I don't know about. I'm sure it's wasting cycle time, but I haven't detected an overhead hit with the increased number of scripts. I've also combined them all into one "uber script" so I can easily change the object names when Maya adds the "namespace" prefix to everything when referencing. It's all good so far.

So now, I have a head isolation control that not only compensates for rotation, but if I translate the head for some reason, it still works. The feet work stunningly well, and I can see using this a lot more than I thought I would, especially for the walk I have in mind for KrazyCat, where he does a lot of swiveling with his foot out. In playing with the hands, I've found that the space switching is useful for something I hadn't anticipated: smoothing out bumps in arcs that occur from higher hierarchical movement. I can go to world space for a few frames, then switch to hierarchical mode, and now I have a compensated arc that works beautifully. I wish I had this tool on Tripping the Rift. I don't know how the tail is going to work yet, but I found that on the turnaround test, a little isolation at the beginning of the turn really alleviated the need to manually counteranimate (well, it was counteranimating itself really). So far, I'm really pleased with the way this is working.

Speaking of the tail, I'm going to have to re-do the architecture on it. More bones and probably a slightly different hierarchy.

Scripts
leftHandSwitch
rightHandSwitch - both of these are the finished space switching scripts
HandSelect - This selects both the hand control and the rotate x control I added to eliminate gimbal lock.
handSwitchOn - This was the script I used to lock out channels so they wouldn't automatically add undesired keys. However, now it's no longer needed.
lFootSync
rFootSync - These are the scripts that stop the feet from jumping when I switch from world space to body space.
tailSync - The script that stops the tail from jumping when switching from hierarchical to isolated modes.
headSync - The script that stops the head from jumping when switching from hierarchical rotation to isolated rotation.
Planned improvements:
I need to clean up the scripts to eliminate all the commented out features that are no longer used or needed, and delete scripts that aren't being used anymore, as well as their related, commented out, scriptJobs.

Thursday, December 08, 2005

Space Switching, animating topology, and eliminating gimbal lock

Among the features I've been trying to solve, space switching on the hands has been a big one.

What I wanted to end up with was a pulldown menu on the hand control that would let you switch seamlessly from the hand being parented to the clavicle, upper torso, hip, head and thigh. Without jumping position when the switch is thrown.

I'd planned to use the xform command. And I did to some extent. But I found using an interim object as a position marker (via my align script integrated into an expression in a scriptJob) and then realigning to it was a bit more reliable. I found that for xform to work, I had to run the same line twice. The first time would throw the hand off in space, and the second would do what it was supposed to. I'll probably revisit removing the interim synchronizer later, but it's working now.

For some reason, everything works perfectly with the autokeyer off. But when it's on, and you scrub, it sometimes drops a key when you stop scrubbing. On both hands, not just the one you have selected. Where the scriptJob should be actuating the expression only when the attribute is changed, as the flag suggests, it seems to detect the changed attribute that's been keyed.

I also found that using the script "live" was unreliable. That is, relying on the script to flip the switch as you went forward or backward isn't fast enough because of the order in which Maya evaluates nodes. So I added channels to the hand that reflect the channels on the parent constraint underlying this whole technique. Set those at the same time the other channels on the hand are set, and it's flawless. With the channels on the hand control itself, connected to the channels on the constraints, an animator can move keys around in the dope sheet without having to worry about channels on some remote object screwing them up.

So now I can animate, and if I want to rest a hand on the hip, I just pulldown "hip" on the "Wrist Parent" channel, and the hand stays on the hip. If the character wants to put his head in his hands and have the hands stick while shaking his head in woe, just pulldown the "head" option. If the character is leading the hand action by moving his shoulder first and you want the hand to follow a couple of frames later, just switch the Wrist Parent to "UpTorso". The other two options are "Thigh" and "World". "World" just does what people usually do with IK hands. The hands just sit in space, waiting for you to move them. The cool thing about this as a simple pull down option, is that when you get that bump from some combination of body moves, but you want to maintain a smooth arc for a few frames, it's just a quick switch away. Switching back with perfectly synchronized hand movement is simple and more important...fast.

This took two days to come up with. There are a lot of logical things that just don't work in Maya, or at least not the way you'd think they would. Of course, now that I know what works, it won't take as long next time ;)

Next up, was finishing off a simple topology control on the head. Basically just two controls on the side of the head that let you pull the upper face of this character like taffy, just as the control on top of the head pulls the top of the head like taffy.

Added rudimentary whisker controls.

Also added, anti-gimbal locking for the hands. First hand control operates Y and Z rotations only, second control handles x rotation only. ScriptJob to actuate automatic selection of both objects at the same time (something I've found quite useful, as used on the ear controls as well). I added a channel to display the X rotation of the gimbal control, but numeric input must be done in the non animatable X channel of first control. The rotation gizmo functions as you'd expect in "gimbal" mode. Only all the X rotation is being done on a completely separate object.

I wish I could make this a little more transparent. Like substituting the "Rotate X" channel for the one on the gimbal x rotate controller. But no dice, at least not yet if at all. At this point the compromise of functionality over transparent elegance will have to be sufficient.

Scripts
I put up my first script on HighEnd3D. sg_syncOb. This is a script I use a lot for making control objects, or just objects that automatically name themselves and locate themselves where a previously picked object is. No nifty GUI, which disappoints some people, but very powerful. Works with multiple selections, a lot of options, including color selection. Makes objects that are non-shading and non rendering. Can parent or constrain the original objects to the new ones. Or not. Automatically picks up the name, parses out mid name object type, and substitutes it for one that is user assigned. The naming scheme should be useful in other scripts.

I commented the script so others who use the script can part it out and add new features or functions. Also, so I could remember what I was thinking when I wrote it.

I'm writing scripts now trivially for searching and getting results line by line so I can paste things into a script faster, sometimes for quickly connecting large numbers of attributes. I've refined script testing so there's less trial and error, or at least the trial and error cycle is faster. Looks like I'll have to come up with my own attribute reorganizing script, because attributeMan.mel didn' t want to work on the hands. Or it would only work on one of the attributes and I couldn't move the new attributes further up the line. So I had to manually reorganize them.

A task for another day.

Friday, December 02, 2005

Scripts with flags

I've been busy for the last couple of days and haven't done any new rigging. But I've been putting a couple of hours here and there to address some script inefficiency and figure out some vital scripting tricks.

This has been bothering me for a few days, and I finally decided to do something about it.

Up to now, I've had a bunch of separate scripts with similar functionality. It goes something like this: make a locator, align the locator, constrain the object it was aligned to, to the new locator. Or a non-shading, non-rendering sphere. Or parent instead of constrain. Or align the point only. I just modified the script and saved it as a different script for each purpose.

This seemed inefficient. Especially at 2am, when I couldn't remember which script did what.

So I decided to start merging all these scripts into one uber script, and add some options along the way. Now, when I type the script name, a dialog box pops up, which lists all the flags, including the choice between sphere and locator, and I'll add poly cube, circles and squares (and maybe some other things as well) on Monday. Maybe I'll add color choice too.

Nice thing about this is that at 2am, I won't have to remember the flags either.

I've parsed the flags in such a way that I can list the flags in any order, including the radius option which is followed by a number, and sort them all afterward.

This will, of course, come in handy for other things, particularly my interim rig building tools until I start doing some interface building.
Drawbacks:
Lots of positives for this technique, but I'm annoyed by the fact that I can't just arrow up and recall the options. For now, I have it echoing the input options so I can copy and paste them at least. If this becomes an annoyance, I'll just make some sort of interface that stays available with radio buttons.