Friday, November 25, 2005

Catching up, part 2

Scripting

As part of my dual front self-training methodology, I'm writing scripts while working on my rig. This gives me a way to focus my energies, rather than working on generalized scripting tutorials.

When I find that I'm doing something repetitively, I write a script. Since I'm taking use of a character in a pipeline into consideration, a big part of my goal is control of object and node names.
This is a major reason I'm working on my own rigging script. If I get a character TD job, I'd like to bring in my tools and know what part of the script to alter to accommodate an existing pipeline.

I'm also finding that as I learn how to write tools of my own, it gives me an idea of what to look for in existing scripts: commands, methodologies, naming conventions and processes. I'm trying to make my scripts clearer for others to follow, and that means eschewing the convenience of short string names for more descriptive ones that can be followed from one process to another.

The scripts I've created so far, of course, duplicate the utility of more elegant ones that can be found on places like High End 3D. I'm avoiding even looking at what's in those scripts so I can learn to build my own instead of just copying what already exists. Most are difficult to follow anyway, because they have nice, user friendly interfaces and the origin of variables is somewhat obscured.

Here's what I've got so far:

Start script job //activates a script job for a continually used command by character controls
autoJoint //this is the first version of my foot pivot switch. I've since deployed a version of this as an expression.
clippingPlane //sets the clipping planes on all cameras from the defaults when the units are changed so you can actually see objects.
makeFootPivot //creates all of the objects which represent the various foot axes, aligns them to the joints and creates the attributes on the foot controller to switch them.
Plans for improvements:
Generalize to accommodate different character names
Use loops instead of discreet commands
Automatically create expressions
Create foot object space switching.
pivot switching expression //adapted the autoJoint script to an expression to avoid need for script job
alignObjects //my version of the "align objects" tool in Max. I wrote this to be able to discreetly control alignment of object in space and orientation and can call this within other scripts when building and orienting objects.
clusterer
//creates clusters on CVs in the order they were picked. Still needs work because of the way Maya represents multi-selection of CVs. Works now only if I pick CVs in reverse order.
syncLoc //creates a locator for each object selected, aligns it, then constrains the original objects to the locator synchronized to it.
Plans for improments:
Flag system to choose point, orient, point and orient, or parent
Command line entry to choose an object class. Currently each new locator is given the class "LOC"
renamer //this allows a selected group of objects to be renamed, with each new object having a number suffix (_1, _2, _3) added to the end.
renamerA //same as renamer, but letters are added to the end instead to avoid confusion with Maya's auto numbering scheme for duplicate objects (_A, _B, _C). At present, it only allows for renaming of 26 objects before it runs out of letters, but I haven't come close to renaming more than that yet.
Plans for improvements:
Combine renamer and renamerA with flag system
Add "prefix only" mode
geoConstraint //this is a tool that will constrain a group of selected objects to the surface of another object.
multiConstraint //this works like geoConstraint, only it constrains the multiple selected objects with a point, orient, or point and orient constraint. I'll probably combine the two with expansion of my flag system later.
syncLocParent //like syncLoc, only does a parent. This will be folded into syncLoc when I get to it.
stretchySkel //actually less functional than it sounds. This was the first script I created to speed up creation of a stretchy skeleton. All it does it connect the mult/divide node to the .tx of selected joints.
syncLocNoneg //this creates synchronized locators but doesn't constrain or parent anything to them. It has the additional function of parsing the object name and automatically giving the new locator the same name, but with object class changed.
makeStretchyChain //my current version of this creates a joint chain between two objects, aligns the axes of the joints, adds an IKSpline solver, adds the nodes nodes and connects attributes that make the chain stretchy. I have two versions of this: one uses joint translation and one uses joint scaling to make the chain stretchy. Command line options allow setting the number of joints, and the appendage name. Joints are sequentially suffixed with letters (_A, _B, etc.).

This last one took some time to evolve and trouble shoot. While I'd figured out how to make a stretchy chain manually several months ago, automating the process took some figuring out.

The development of the script proceeded in logical parts. This all started sometime Tuesday (11/22) when I was making all sorts of stretchy chains to test out various ideas on the head distorting thing. This is essentially something you'd only do on a cartoon character, but that's what I wanted to do right now. I haven't gone back to the head tweaking problem solving yet, and the chain idea is probably overly complicated for the results I want to achieve. But if it takes 10 stretchy chains to achieve the results reliably without compromising other controls, so be it. I needed a fast way to draft chains, and I'd have to make a script like this eventually if I want to have my very own rig building script, right? And I'm not about to let a little fact like the one that I just started learning scripting less than two weeks before, and that this might be overly ambitious at the moment, stop me, am I?

I'll start from the beginning.

It took a while to figure out how to build the joints where I wanted them.

First I thought I'd just build them evenly between two objects. This meant calculating world x y z euclidean coordinates between two points. I cobbled the xform part of the joint switching script to get the world space coordinates of the spheres, and just did simple math. The approach worked OK, but wasn't all that flexible. Sometimes the joints don't follow a straight line path.

I decide to build the joints along a curve. So first I had to build the curve. That became the first part of the script, still using the xform lines from the first version. The second part would have been easy if I knew what I was doing. In this case, the search for the right node took longer than usual, probably because I was trying to figure this out at 3am. I wasn't going to go to sleep until I figured this out.

The magic bullet was the pointOnCurveInfo node. I did some manual experimenting with it to find out how to get the info I wanted. Finally, I figured out how to use a percentage to get the position info out. I just added a loop that divided 1 by the number of joints, got the percentage and successively added each joint to the new position. Anyone reading this can tell I'm proud of the fact that I figured this out. To me, it was a profound revelation, because I can think of all sorts of uses for this tool.

Now...I'm sure I could have just found out the answer to this by dumpster diving existing code (this is not to say existing code is trash, it's just hard to find what you're looking for when you don't know exactly what you're looking for). But when you wrack your brain to figure something out yourself, you tend to learn more thoroughly, and along the way think of other places the information can be useful. Sometimes you find stuff that's totally irrelevant to the task at hand, that comes in handy later on.

The rest of the first draft of the stretchy joint code was pretty straightforeward. I just manually built the stretchy part in the interface, looked at the script editor, and translated that into loops and conditional statements. Couple of glitches plagued me: I was using specific object names for curves and nodes the script was creating. Problem was, on repeated testing of the script and diagnosing failures to get the desired results, new nodes and curves were created, and subsequent ones created by the script were getting suffix ascended. So the script would try to use the old ones, which were wrongly assembled. It finally occurred to me that this was happening, and I made an objExists check. My first use of that command, though I knew it existed.

Finally, I get it all working. Except that it doesn't work as well as my hand assembled versions. Examining the entrails, I found that the joints were oriented wrong. And when I used Jason Schleifer's jsOrientJointUI2 script to fix them, they worked fine.

I tried to deconstruct jsOJUI2 to see how it worked and to add that scripting to my script. My effort at cheating was resoundingly thwarted by the complexity of the script. I also knew, deep down, that if it worked in the interface but not my script, even though it almost worked in my script, I was missing something fundamental. So I posted on Schleifer's forum just before midnight Wednesday (11/23). I stayed up another three hours trying to figure it out, but I was just Wile E. Coyote-ing: losing sight of the goal through obsession.

Yesterday morning, before Thanksgiving Linner and the family gathering, I sat down and systematically broke the problem down. I copied and pasted portions of the script in the script editor and executed them. I checked the orientation by turning on the display of the rotation axes. I compared them to the joints built manually. I looked at the script editor as I made the joints. Well what do you know? After the first joint, subsequent joints have a command that aligns the joints. DUH! All I needed to add to make the joints align properly was a conditional statment in the joint construction loop: if ($counter >0)
{joint -e -zso -oj xyz -sao yup $jointname[($counter - 1)];}

What's the point of explaining all this? Well...documenting the learning process isn't just about what I did and when for me. What I learn by the process of goal, failure, tenacity and finally success took me down all sorts of roads that I saw all sorts of sights on. I know a lot more ways of doing something wrong now, and I saw solutions to problems that I've only vaguely started thinking about. But I expect that if I work as a character TD somewhere, I'm going to have to improvise and solve puzzles all the time.

0 Comments:

Post a Comment

<< Home