Monday, January 01, 2007

Damn!

I've been looking at my early scripts I've written for my own use and my job. I now see a lot of ways to make those scripts a lot more efficient, or at least shorter.

It seems I've written 14,000 lines in scripts in the last year or so. Jeebus but that's a lot of updating I'll need to do. Most likely, when I leave my current job.


Some interesting tools I've written lately:
Transfer shapes and connections from an outsourced facial rigs to the most recent company models and rigs, then delete the outsourced character model.

Face control and face camera windows that don't allow the other characters to interfere. Each has a pull down menu with the names of all the referenced characters n the scene. There's also a checkbox on the face camera to turn off orthographic front view so the face can be rotated around, and an adjustable clipping plane in the non-orthographic view to clip out interfering body parts.

A more specialized user interface for loading and saving the outsourced work so it'll more easily transfer to referenced characters.

A floating tool window with the tools likely to be used by other people.

A new, simpler FBX2Maya tool that expects the FBX rigs to actually be updated to the current rigs.

A multi-proxy tool that puts all the lores proxies in one group, and all the hires proxies in another. It's stupid how by default Maya makes a wordily named group for each object, making it more difficult to access them as a group in their lores and hires manifestations.

I made a tool for eye movement. It can simulate small eye movements to get rid of that cg "dead eye" look, with presets for camera angle and character emotional state. Sliders allow more fine tuning of eye movement magnitude and timing. Keyframing tools that allow an eye direction to be set on any selected character in the scene, but doesn't use a wired target that hangs in space (meaning multiple retargets and keys need to be used when the head is moving, but it eliminates that creepy "sliding eye" thing) with adjustable range for working on smaller segments of the overall scene, and sliders to adjust the eyes, buttons for adding keys or deleting them. I might make a version someday that also does eyelids with similar timing controls.

Nice thing about the tool is you can always edit the channels for more individualized control, but it gives a nice leg up for a quick and usable sub-eye movement when time is short. Sometimes there just isn't time to hand key everything that needs it.

While I was testing my face control tools, I needed to mock up a face controller real quick, so I created a face control maker tool. This makes two dimensional geometry boxes with moveable geometry in the middle limited to the interior of the boxes, text to identify them, and a slider to make it all in scale to the character. I might add a control interface to hook the controls up to channels later.

Updated crapBeGone to a scorched earth version that leaves the skinning intact with a separate button. crapBeGone eliminates unused nodes (for real) as well as other cruft, which the built in Maya tools just don't seem to do.

Been working on final skinning strategies now that models seem to be stabilizing and things might actually be moving into the Maya side of the pipe. Up to now, rigs have had to stay pretty simple so they'd be compatible with Motion Builder. I'm now working on rigs that can employ wrap deformers and other Maya-only trickery. Because of the large number of characters, we'll probably eschew dynamics and just leave the secondary stuff to hand keying, which seems to go really fast with a knowledgable animator at the stick. You'd think it would be faster to do the stuff with dynamics, but setting up dynamics for expected results takes a lot longer than the hand keying would on any given character. I've also been working with one of the animators to develop topological control of various aspects of the characters.


Well, that's the update. I kinda wanted to take my blog off the "non-updated blog" list ;)

Saturday, July 22, 2006

The other part of my job

All the stuff I mentioned in the previous post is part of the job I've kind of had to take handling the animation pipeline. There's just no one else to handle that.

Those familiar with Motion Builder might wonder why we're exporting skeletons. The answer is simple: It's a safety check. The director has signed off the performance in Motion Builder. By having the MB skeleton, it provides a visual cross reference as proof that the performance of the Control/IK version of the character is a 1:1 match with the approved performance when there's no divergence between the two skeletons, except maybe small spacial differences between dissimilar rigs used in the mocap process. Even so, when there's time the most recent version of the character is "characterized" in MB, so the match is pretty close to start with.

But here's an example of how far astray this process can go: We had a scene mocapped with an early version of the character, and that pipeline skipped Motion Builder entirely. So all the hand animation done on top of the mocap was done in Maya. The system I came up with works with that as well. Even though in the latest version of this particular character, the rotations on the bones have been zeroed and realigned, the control structure is entirely different, and even the locations of the joints has changed. So part of the job of my conversion system is to take legacy animation and load the most recent versions of a character with that legacy animation. It's a really useful hat trick.

OK, which brings me to the job I was actually hired to do: rigging. Lately I've been working on a facial rig architecture, that doesn't depend on blend shapes. I had to try a lot of things and burn them to the ground, because every one of them had some annoying pathology. 6.5 doesn't have the ability to paint weights on wrap or wire deformers, and influence objects give me a little more control over weight assignment, at least for the whole curve I'm using for deformation. It was pretty hard to figure out how to get the rotational information of the curves to be picked up by the object when the character is reoriented, but it was only hard because I had no idea what I was doing at first ;)

But there are lots of other little issues that need solving. Things that should be cloth simmed won't be, like a character's leather jacket. Leather isn't like other kinds of cloth, and it presents special challenges. Fortunately, I have about 10 leather jackets to use as reference.

Another is the typical problem of the shoulder area. It isn't hard to defeat some of the problems, but some of the actors who were used in the mocap sessions are [i]limber[\n]. Human limitations weren't a concern to them, and I have to accommodate the exceptional flexibility of a contortionist. As my supervisor says, "Fun, fun, fun". I'll probably be using influence objects a lot to create a selective "muscle system" where these problems occur. I've also got overlapping joints to accommodate controlled twist, a finger control system that accommodates mocapped finger data but lets you animate on top of it and a lot of little tweak controls that will let an animator or TD solve all sorts of problems with the character's, most of whom are not human proportioned.

One of the things I have to deal with, is that with all these multiple characters, I can't use scriptJobs to pull off some of the character TD's magic tricks. I'm working on ways around that. But at least I've now boiled the rigging process down to a "one button operation" for the main controls, connections, constraints and expressions. The face and assorted compensation controls are the only part that has to be done by hand.

It's all getting pretty exciting. At least in that way that a TD's job can be exciting.

Finally getting down to brass tacks...

I'm finally starting to get some real scenes to work with. And my animation tools are getting a trial by fire.

What I found out: Reality is always different from the test cases.

First, the skeleton fbx files that I'm getting have joints differently named from the standard, with two characters in the scene. One character will have joints named with a suffix like ...J_1 instead of ...J. The tools were set up to look for _J, and when they're differently named, that a problem.

Second, those suffixes weren't always consistent within the character. The ball of the foot and toe would sometimes be just _J where the rest of the character had J_1. So with two characters in the scene, one would have J_1 for everything and one would have J_2, but their toes and balls of the feet would be _J and J_1 respectively. Pain in the butt.

Third, my method for loading a saved animation file saved under one character name or reference to another that might be referenced or not, or dissimilarly named joints or controls, required hand matching from the name used in the file to each joint or control in a character. There are a lot of controls and joints in characters. This would take a long time in practice even though I'd only have to do it once. It turns out that there is no luxury of time unless I want to put in a lot of OT to get things done, and I sometimes have minutes instead of hours to do things.

Fourth, one method I had for handling conversion of mocap data to a fully rigged IK character worked, but when I'd try to save the referenced file would crash Maya as soon as I'd go to save the scene.

Fifth, that method required separating the characters from the converted fbx scene into individual characters. This turned out to be time consuming also.

So I needed to make tools a one stop affair.

I created a new tool which would save the exact spacial location, regardless of hierarchy, of the IK character's driver transforms and rotation order. This could be used to convert the data from the mocap characters, even if they were based on a different skeletal setup and hierarchy. This file fits into my .sganm file format (which currently has three different versions that are automatically detected: ANIMATION which saves all of the keyframe data including tangents, tanget type, tangent weight, and infinity and is hierarchy dependent; MOCAP, which is just the raw keyframe number, and keyframe value for each channel and is still hierarchical; and a spatial location format that just puts everthing exactly where it is in the animation file in world space, which is non hierarchical but has to be implemented in hierarchical order or it results in a transporter accident. In otherwords, I already had a tool that could be deployed to store the data from the new rig. I save this in the master folder that contains the character's old mocap rig, new mocap rig and the IK character that has all the controls installed.

In order for all this to work, the two skeletons have to be aligned on frame zero in the hero pose. The same code that's used for the translation from one older state to a newer one, or joint based animation to control based IK animation, is also used to create a master "zero frame" hero pose file. This is also stored in the master directory.

I wrote a tool for automatic creation of a "map" file. This would use TD defined wildcards to match similarly named joints and controls to the one used in the animation file. Whether that file is a "zero frame" hero file, or a control location file. This worked great until I found the joint naming inconsistency.

So I need to create a "descendants" based system that looks for matches regardless of the added delimiters and indexing based on the selected character name. I have bits and pieces of the code in use for other purposes, so it should be too difficult to implement.

On top of all that, even if I get it all working smoothly, there's still the problem of the manual handling of the various steps in the conversion process. What's needed is yet another file format, this one being a "Rosetta Stone" so to speak, for all the characters that have that control space file and "zero frame" file already saved in the master directory. That way, I could load an fbx scene, select the characters in that scene from a list, and it would automatically set a zero frame, load the appropriate reference file for each character, and map the animation to the character with controls with a "one button push" and could be used by anyone, rather than having to have it done by a TD.

There are a lot of details to making a system like that work. But I figure it could take a half hour of hand messaging and turn it into maybe five minutes. Which would be good since there are 1400 shots.

The reason all this is necessary, is that Motion Builder doesn't handle the complex rigs with all their constraints and esoteric expressions, at least not the versions we're using in production. The rigs also can overload hand animation over the mocap, and have space switching capabilities, and MB can't handle those either, and you can't apply the motion directly to the joints because of the IK, so conversion is an absolute necessity.

Saturday, May 20, 2006

More tools, interfaces and refinements

I've been writing some minor tools, and going GUI crazy the last couple of weeks.

Removing Print Commands
After the discovery of how major an effect using the print command has, I decided to go through all my major scripts and removing print commands from them. Unless I'm debugging I don't need them. I also added a "Done" window that tells me how long a script takes to my longer scripts like the Animation Save/Load utility, and the spur to joint utility.

In the process, I created a new standalone script called printWin. It consists of three processes: printWin initializes the script, you invoke "addPrintWin " to add the contents of a variable to a line in the text scroll list, and showPrintWin to show the window. I didn't combine the commands because I don't want the window to show up until all the data is added, and I don't want to add showWindow to addPrintWin because it can be used in highly iterative loops, and I don't want to call showWindow over and over again hundreds of times.

I also added a log utility to the printWin window, so I could highlight some or all lines and then save them out to a log file for copying, pasting and testing if there's a malfunction. I can also open it in a text file to search for problem lines in thousands of commands to figure out what the problem is. The name and path of the log file can be changed.

This is how I located a problem with my venerable "spur to joint" script, a major script that takes all the vertices in a character, analyzes their weights, removes them from spurs and adds them to the parent bone's weight assignment. Even if it doesn't have any weights assigned to it (which is pretty typical).

In working on the problem, I decided to rewrite a bit of the code to make it more efficient. A lot of repetitive loops could be combined, and instead of parsing the name of the joint to get rid of the suffix, I could use the substitute command. In the months since I wrote the script originally, I've discovered I don't have to put `gmatch` statements into a variable, but can use them directly in "if" statements without an equal. I didn't know that before.

I also made it so I don't have to select the vertices. I can select the objects or the vertices, and the script will figure it out.

So I got the spur to joint script down to between 2 and 2 1/2 minutes running time from 5 minutes or more.

Cleanup and Windows
I wrote a cleanup utility to conform all 17 current characters to a unified naming convention. This utility forced me to learn how to manipulate GUIs in a much more coherent fashion than I'd been doing before. I needed to throw up separate analytical windows, and I didn't want them all over the place.

Window # 1 has the basic controls with 5 buttons. I used the rowColumnLayout command to make the buttons span evenly across the window, two rows of two and a close button at the bottom. There's a textField to enter a character name, and all the changes executed by the script are logged in a textfile, with new entries appearing below existing ones.

Window # 2 appears directly below it and the left edge lines up with the first. This window lists all the joints that are different from the standard model, so I can examine them and see if there's a misnaming, a difference in the hierarchy that shouldn't be there, or additional bones like tails, ears, ponytails, trunks, etc. I found a lot of names of objects that were out of standard that way.

While Window # 2 originally was designed to be used with one character at a time, I had an occassion to check multiple characters. So I rewrote it yesterday to give me the parent transform node of each character, so I'd know which character had what problem. I'm finding I have to do this often enough that I'm going to have to write a standalone script to find the character names in the scene so I don't keep writing the same code over and over again. I can see many uses for this when I do the scene building scripts.

I align the 2nd window with the first by querying the first. For example:
window -tlc (`window -q -te "window_1"` + `window -q -h "window_1`)
`window -q -le "window_1"` -h 400 "window_2";

And I'd do something like that for all the windows. I also have it so all the windows are minimized if the first one is minimized, and the close window closes all the diagnostic windows associated with the tool.

Window_2 has text on the top, followed by a scrollLayout, followed by text, a scrollLayout and then the buttons. This is pretty handy. But it forced me to learn how to put all of this together.

At the end of the script for window_2, it does a query to find out how wide the text lines are, and scales the window and scroll lists to that size. Since all of the windows next to it use a call like `window -q -w "window_2"` as part of the formula to determine their position when opened, this means that all the windows fit together like pieces of a puzzle, and self assemble even if something is moved.

This all means that I am going back to my old scripts to neaten them up. If I have a row of buttons, rather than explicitly placing them with formLayout -e, I'm using automatic calculation to determine how to get the buttons exactly spaced for the width of the window or frameLayout.

The way I'm accomplishing this is with a line like this:
int $windowWidth = `window -q -w "window_2"`;
int $offset = 3;
int $columnSpace = 3;
rowColumnLayout -e
-cw 1 (($windowWidth/5)-$offset)
-cw 2 (($windowWidth/5)-$offset)
-cw 3 (($windowWidth/5)-$offset)
-cw 4 (($windowWidth/5)-$offset)
-cw 5 (($windowWidth/5)-$offset)
-cs 1 $columnSpace
-cs 2 $columnSpace
-cs 3 $columnSpace
-cs 4 $columnSpace
-cs 5 $columnSpace
"buttonRCL";

$offset is the amount subtracted to compensate for the scroll bar and/or frameLayout margins
I don't subtract that from the window value directly so I can alter the widths of the columns to make the buttons as wide as they need to be to fit the text in each button.

Using these techniques, if a window is resized because it's contents are wider than the initial window size, that's the value that's used to resize all the components that weren't responsible for resizing the window. It's a profound discovery for me.

What Comes First, The Code or the UI?
For a simple tool, it's pretty easy to just write the code. But when I need something that does a lot of complicated things, I find myself dummying up the UI first, which then tells me what I have to write into the script. What do I want the tool to do? How do I want it to work. Writing the UI first is sort of an advanced combination of an outline and flow chart. It's actually saved me a lot of time except when I have to learn new UI tricks, but I only have to learn them once. And I write them very quickly now. It also gives me a good idea of what needs to be broken into separate global procs and what can be combined into one.

But I'm getting to the point where I'm starting to think about writing a UI to build UIs. There are some out there, but as usual, I like the control and know what I want.

Converting Motion Data
What I'm currently working on is a method for converting data on a character done before the character was all zeroed out, and when the character's primary pose was done in an "A" pose instead of a "T" pose. I've chosen to go with a "brute force" quasi-mechanical methodology over actually trying to do matrix conversions. This involves posing the old skeleton in the new skeleton's pose, creating a huge number of transforms, synchronizing one with the old skeleton, synchronizing the other with the new skeleton, parenting new to old and then synchronizing the whole thing frame by frame with the old animation. The data is then extracted from the new skeleton transforms as world space information, and transplanted either onto the skeleton itself or controls that drive the IK. A lot of flexibility can be achieved with various parenting schemes.

That's the theory anyway.

Saturday, May 06, 2006

Scripting is like cooking...Part 1

I've posted out of sequence this week, since this is a two parter, and I thought Part 1 should be posted on top for more natural reading. Part 2 is below.

Before I start a big THANK YOU to Robert "Rob The Bloke" Bateman. He posted a reply on my CG Talk Thread that gave me the insight to solve a major, show stopping problem.

I don't think an analogy to my rapid, seat of the pants invention methodologies in the garage will be very accessible to many people. To sum it up, I need to make something that I can't readily purchase, so I scour the garage in search of discarded parts and leftover materials from other projects, and bins of fasteners, and cobble something together by using the lathe, drillpress, grinder, saws, files, oxy-acetylene torch and arc welder.

But almost everyone has a refrigerator and stove.

I like food to taste good, and I get easily bored. So when I need to cook something, I look in the fridge for raw materials for my project. Meats, veggies, cheese and sauces are the fundamental building blocks of all meals. I've got lots of dried spices and herbs, and will sometimes employ a canned good that has something tomatoey. Even peanut butter is useful if you want to make something with a thai peanut sauce. No cookbooks, just an idea where you grab ingredients, mix sauces together, and in the end come up with some sort of dish or sandwich that isn't boring.

It's amazing what kind of variety you can get when you do that.

Until this week, I never really thought much about there being a relationship between the garage mad scientist/engineer/junk-yard warrior and the cook. But it's a consistent theme: see what resources you have available, and combine them to get the result you want.

What made me realize that this is a way of thinking that becomes a repeated theme no matter what you're doing, so long as you're making something, is: I realized how often I do this in scripting.

I'll get to that in a minute. First, I'll revisit last week's Animation Save/Load script.

It's not enough to get something working. You have to test it with the most dire conceivable scene you can find. In this case, I used our gold standard character (to which all other characters must be compared), and a 2000 frame mocap file with keys on every available channel on every joint. That's a lot of data.

Well, it worked. Sorta. Turned out it would take 15 minutes to save, and 45 minutes to load. Not very feasible in production. Since I anticipate using the base functional code in a custome character/shot/scene loader with multiple characters in a scene, this speed was highly impractical.

In the course of revision and restarting the interface, I had to do a lot of things repetitively. This led to refinements of the interface:
1. The animation file is now persistent. On repeated loads, it automatically loads the last file as long as the same Maya startup is still being used.
2. Clicking on the source, then the destination objects, and then hitting the "Map to New Character" button gets really tiresome when you have to do it 30 or more times for every animated object in the character. My first pass at resolving this led to making it a click, click and it's mapped solution: now the "Map to New Character" button was obsolete.
3. Clicking the "Get info from File" and "Get Character Controls" buttons was getting old too, so I made those load automatically when the interface starts. Now those buttons were obsolete.

So I had three obsolete buttons. And I still had to do a "click, click" for each object in the character. The couple of dozen times I had to do this, I pretty much thought the solution for the long file loads was just over the horizon. When I realized it wasn't, I created a "Save Map" and "Load Map" function in the interface. I used two of the old button positions and eliminated the third.

Here's where the cooking ingredients analogy comes in. I didn't want to spend a lot of time on this stuff. So I grabbed the code from the animation save/load global procs off the shelf and added some different sauces, and now I had a "Save Map" and "Load Map" function, with a text field with the file name. And because I solved the persistent file name problem, they'd automatically load and update the interface every time I started it.

This was a huge time saver.

So I was able, over repeated refinements, to speed up the code. I got it down to 10 minutes for a save, and 13 minutes for a load. Still unacceptable for production, but I was getting closer. By loading everything into an array, and working from the array, it sped things up and saved multiple iterations of code. Disabling the "undo cue" also saved a bunch of time and freed up some memory. That really adds up when you've got a 300,000 line file that you're saving/reading.

But I couldn't figure out how to get the time down from that. And if I repeated the operation with the same Maya interface open, that 13 minutes would jump up to 30 minutes. And suck up a lot of memory that never came back. Something was wrong, and I was thinking about learning to use the API. But I still had to solve this problem right away, and finally posted to CG Talk about it. Prospects of solving the problem seemed dismal at first, and then Rob The Bloke came to my rescue.

It turns out that printing to the script editor is unreasonably inefficient. It sucks up huge amounts of memory and time. Not really noticeable with small code, but very very noticeable with something large and highly iterative. For debugging, I had it print out the commands that were being used to set the keys on the new character. That's 300,000 lines of printing in this case. It would take up 500 megs of memory, and not give that back. Read Rob's post in the thread I posted at the top to see what he said.

This was a profound insight. Deleting the print commands on both writing and reading the files sped writes to 38 seconds (also using fflush as Rob advised contributed), and reads to 2 minutes and 30 seconds or less. Despite his advice to get rid of the array, I found that it consistently knocked 10 seconds off the read time, so I went back to it. I may be revisiting this in the future for further optimization, but WHAT A DIFFERENCE!

I'm now going to have to go back to all my highly iterative code that, for example, reads the data off every single vertex in a character, and eliminate the print commands for all of it. It's already made a huge difference in my "Save Weights" and "Read Weights" tools. Once a tool works and you don't have to debug anymore, get rid of the print commands!

Scripting is like cooking...Part 2

So the second bit of this Cooking/Scripting analogy...

This week I was instructed to start looking over a bunch of characters that were done before my time. What I noticed was inconsistencies in the hierarchies, a lot of transforms without children, and different naming conventions for the same joints.

If I'm going to employ tools in the future that can parse and deal with the various body parts, it would sure be nice if they were always named the same thing.

So I wrote a quicky tool in a few hours to deal with this. Doing it by hand was out of the question, because I had a big checklist and I was sure to miss some detail and continue propogating the problem. My current list has 17 characters, so the time to do this by hand would really add up.

I settled on a "gold standard" character whose hierarchy would be used to compare all the other hierarchies.

And I went to the "fridge" of previously written code to borrow code which could be redeployed in the tool. This is a real time saver, when you can look at how you handled a problem before, and not have to reinvent the wheel. I borrowed and modified parsers, cobbled quick interfaces together, pop-up windows and file writers. It does all the functions and adds them to a log file so I have a record of what was done.

What I wound up with:
1. A "promptDialog" window pops up asking me to name the character which leads all the info written to the log file.
2. It automatically renames a bunch of standard non-conforming names of joints like the pelvis, fingers and foot parts.
3. It deletes all transforms that have no children, but not joints, ikHandles or effectors.
4. It pops up a scroll window that gives me a summary of what was done. If it looks like something necessary was deleted, then I'll reload the character and fix the code. So far, I haven't had this problem since the third iteration of the code.
5. It pops up a scroll window that gives me a list on top of joints that exist in the "gold standard" character that don't exist in the character that is being fixed. On the bottom, it gives me a list of joints in the character being fixed that don't exist in the "gold standard" character. Sometimes it's because the character being fixed has joints that the "gold standard" doesn't have like a tail and ears. Or some joints are named with joints that have their lettered incrementers jumping sequence because at one point intermediate joints were removed for redundancy.

But this all gives me a visual list to investigate. It alerts me to name formats that are out of conformity with the general naming convention. It points out joints that were never renamed from the default. It's a very good summary of things that might need to be looked at, and appropriately targeted.
6. It pops up another window with a final checklist for hierarchy conformity that also includes what objects need to have "inherit transforms" turned off.

When all this is done, it writes to a log file. I go and hand edit the log file with any additional steps I take with the lines of hand edited items indented, and finally, I add the file name that the newly fixed character was saved under.

With this, I was able to conform 16 of the 17 characters in under 4 hours (which only took that long because I was updating the code as I found problems, and wrote a new sg_rig utility which I'll need for a different stage of zeroing rotations later).

And then there's the new sg_rig utility. This pops up a summary of the rotation, rotation axis, joint orient and rotation order for specific joints I need to be certain conform to standards we need to meet. I borrowed some of the window code from the naming and hierarchy cleanup of the earlier utility script for that.

I wrote the utility because, while I had the characters up, I wanted to get a preview of the work I had ahead of me and to anticipate some upcoming problems I see with correcting them. I'll have my work cut out for me next week. I'm getting some ideas for maybe logging old data and correcting animation files being loaded from earlier work based on that old data (just in case dealing with this becomes my problem).

Saturday, April 29, 2006

This is how it turned out...


So this is how it turned out. It has common wildcards for control names, a textfield to enter combinations of control names or animated objects which may not be controls. The list will only show objects which have animated attributes. If "none" is selected, all the animated objects in the scene will be listed.

For saving animation, the saver will by default save the animation for everything in the list. If you select objects from the list, it will save animation only from those objects, and only on channels that are animated.

There is an option to save animation only from the selected referenced character, so if there are multiple characters in a scene, only the selected reference gets saved. This is currently only a single selection option, but if there's a need, I can make it multiple selection.

I've added an "Animation Offset" option, which allows animation that may not start until the middle of the scene to be synchronized with the scene on load, and a "Motion Capture" option for saving, which makes for file sizes that are 25% or less the size of the full animation file which has weight and tangent information.

I also added a "No Character Reference" mode, so that animation can be transferred to and from characters which are not referenced, as many files coming to me through motion builder haven't been referenced yet. When "No Character Reference" is checked, the text that says "Reference Character Controls" changes to "Character Controls".

The remapper automatically loads the available animated channels from the file on read into the left hand column below in the "Anim File Controls" column, and selecting a character from the "Reference List" above automatically loads its available objects based on the "Select Wildcard" choices at the top of the interface (including the wildcards entered into the text field).

I've also made the selection process much faster. A single click on either of the objects in the "Anim File Controls" or "Reference Character Controls" text boxes will add the from => to mapping to the Animation File => Character text box. The Animation File => Character display is self correcting, so if you make a mistake and change the object mapping to the "Reference Character Controls" object, it will replace the map you previously entered with the updated one.

I've left all the manual buttons for conveying data in the interface like "Get Info from File" , "Get Character Controls" and "Map to New Character" even though I've added more automation for those features so they generally aren't necessary. If all the automated stuff continues to work under production use, then I might take the buttons out.

Reading mapped files is a "What you see is what you get" process, since the information is actually taken from the "Animation File => Character" text box, and not the variables used to create the box.

Making everything in these text boxes update automatically when options were changed turned out to be more challenging than I thought. It's still not perfect, and sometimes to get the "Character List" (at the top) to update with the proper amount of rows, I have to turn "Show Objects Selected With Wildcard" checkbox on and off.

Another challenge was getting a perfect match on tangents and weights on curves. It turned out that if the animation had options like "Weighted Tangents" or "Lock Tangent Weights" turned on or off, I'd get incorrect information in the saved file. It was solved by caching the current state of the tangents and weights, turning weighted tangents on, unlocking tangent weights, breaking the tangents, saving the information, and then turning them back on with the cached state. All of that information is saved unless the "Motion Capture" checkbox is checked, in which case no tangent information is saved (everything is assumed to be linear since there's a key on every frame), and no "infinity" information is saved ("infinity" is assumed to be constant).

I'm using my own file format rather than an existing one. It doesn't seem like there's a truly universal animation file format being used, and I can add to this one or subtract from it at will based on the needs of the production. I've already had to add additional variables for accurate tangent weight replication. Since the format easy to parse out, it can be translated to anything else that's ASCII based with pretty little effort, though the translation can be time consuming if a lot of recursion is needed. The downside is that the files can be huge, but they are "human readable".

The "Name" basis of this file format makes it particularly suited to production, since hierarchies can change during production, particularly the number of children of a particular hierarchy, which I've found throws things off a great deal and can make old animation incompatible with new animation. The remapping feature seems to be a pretty obvious need, and it's amazing that it's not something that's become a standard feature set in Maya.

I would really like to resolve a memory issue with using the animation file loader, particularly with motion capture files, which have a huge amount of data. During loading, it will suck up a ton of memory, even sending my machine into "swap" virtual memory space. And it doesn't give it back when the process is finished. The only solution I have right now is to save the scene, kill Maya and then restart Maya. If anyone has any solutions to this problem, I'd love to hear them.

Plans for the future:
The underlying code can be used for scene loading, bypassing the reference process in Maya if it becomes necessary, providing the code proves to be reliable enough and I resolve the annoying memory issues. If there's a use for it, I can use a "Save by timeline" feature, and maybe a "hide until first frame" feature for characters that have no animation until the middle of a shot.

I may also add an "animation copy" feature, to copy animation between two characters loaded at the same time, possibly deploying the techniques I'm using in another tool that turns hierarchical straight FK rotation keys into IK translational keys between dissimilarly proportioned characters.

If it can be made more efficient, I might make a multi-select reference mode that can load the same animation on multiple characters for crowd scenes, with a random range of time offset to the characters aren't all moving at the same time. I've thought of ways to also randomize initial character placement so the characters are at a fixed proximity to the other characters, but it would be hard to duplicate some of the features I've heard about in Massive. Also, I'd have to work on my use of matrices to determine location within a bound distance.

Monday, April 24, 2006

A little something I'm working on


This is the start of an interface for animation tools I'm coming up with. It allows animators to save in a format I came up with that is entirely name based, and allows remapping to characters that are dissimilarly named.

The advantage of using a custom animation file format is that I can add features to it as needed and put parsing hooks in that lower the coding time.

I've already thought of a lot of features to add to this interface, including attribute break downs, detection of missing attributes and an ability to add them or skip them, and various failure and recovery modes.

I've also thought about ways to write a remap into the referenced character file so this doesn't have to be repeated if the remapping is something that's done repeatedly.

Saturday, April 08, 2006

Bet you thought I'd never update this thing

So I got a job as a character rigger at Threshold Animation Studios. It turns out everything I learned along the way has come in handy.

I walked in with a suite of rigging tools I created. This turned out to be a real good thing. The first thing I had to do was mod them to conform to the in-house naming conventions. This would have been harder using downloadable rigging tools, since most of them have multiple library calls and tracking down the naming conventions in all the disparate global procs would have been daunting.

The other thing, is that the studio has special requirements which required serious modifications to the functionality of the rigging scripts.

If you look at http://casadiablos.com/steph/stephInterfaces.htm, you can see the base interfaces I use in my tool kit. I've added a spur builder, which creates customized bone spurs to tailor autoskinning. I can interactively resize them to fit the character, then move them around to where the autoskinning needs the greatest influence from the parent bone.

The other advantages to having a whole suite of tools, is naming conformity. This makes it easier to have all other tools act on the given objects. In the case of the spurs, it's [bone name]_A(B,C,D,etc)_SPUR.

I wrote a script that takes the skin assignments for every vertex and CV, and writes them to a file. All of them. For every object. This kind of duplicates the functionality of skin maps, but at the same time, it's a one stop process. Not a bunch of individual files for each object. I had to create my own file format for that.

This was a fortuitous development, because the character models are frequently revised. If the modifications are minor, like character proportions, with no added vertices/CVs, then I just save out the weights on the old character, reposition the skeleton and reload the file. Voila!

While objects that are modified severely or have new vertices added can't necessarily use the old weight assignments, often it's only part of the character that's been modded, and I can go back to the spurred version of the character before the skin weighting spurs were removed, and reassign that part of the character.

Another handy tool I developed allows me to de-assign unwanted weights. You don't want the abdomen to deform when you move the arm. I have a checkbox arrangement for the common bones, and a text entry field for bones added later. These bones include all the child spurs.

I updated sg_syncOb. I haven't uploaded it yet, since it uses the house naming convention and is proprietary at this time. But it now includes a zooty arrow-ball as a controller type. When it's fully updated, it will also offer the option of creating a joint with selectable shapes to select that joint as a controller. This had the advantage of being able to use the jointOrient attribute for externally rotating the controller, such as using the forearm to rotate an IK control foreward kinematically, while still being able to animate that controller's rotation on top of it. Very useful, but it's tricky to get working and not without it's pathologies.

So here are the features of the rigging suite:
Skeleton: creates guide spheres that are interactively scaleable to get close to the character's proportions, and creates a base FK skeleton conforming to the house naming convention. Options are selectable number of fingers, and number of bones in the fingers, back, head, neck, arms and legs. The code is there for tail, ears and pony tail, but I haven't connected it to the interface. There's also a checkbox to turn axes on and off for the entire skeleton interactively. The guide sphere portion has a mirror function so I only have to align half the spheres in the appendages. All numeric options for number of bones and scaling of the guide spheres use sliders but numbers can be manually entered as well.
Make Control Objects: This just brings up the latest revision of syncOb.
Create Spurs: This makes the bone spurs for skinning. It arranges the bones in a circle around the parent bone, and names them as mentioned above. There's a selectable number of bones, the axis of the bone to arrange them in a circle, and interactive radius adjustment. The interactive radius can be used by selecting the parent bone at a later time and using the slider as well. Buttons are "Create Spurs", "Spur to Joint" (which takes all the spur assignments and adds them to the parent bone), and "Delete Spurs" (which deletes the no-longer needed spurs after using "Spur to Joint").
Rename Skeleton and Control Objects: This allows me to create joints by hand, say additional spurs going in a non-circular orientation, and then rename them to conform to the naming convention. The text fields include "Jointname", starting letter, and suffix (JNT, CTL, etc). Works on any number of selected object and indexes them with increasing alphabetical representation (B, C, D, etc).
Remove Unwanted Weights: As mentioned above, this removes weight influences from bones that you don't want influencing that section of the character. There are checkboxes for non-left-right bones, and individual checkboxes for the left and right appendages so I can remove the influences from autoskinning like the left femur, tibia, and foot bones from influencing the right. There's also the aformentioned text field for entering bone names not represented by a check box. Buttons are "Delete Weights" and "Clear Check Boxes" (which also clears the text field).
Save/Read Weights: This has a filename field that you can manually type, as well as buttons that call up a file requester to set your directory path and name for the file. I have a filetype I created, and the suffix is automatically added. There's a "Get" button to get the file with the same type of requester. Unfortunately, I couldn't customize the default filetype, so you have to enter *.weight (that's not the actual suffix name) to see the .weight files. Buttons are "Save Weights", "Delete Existing Weights" (removes all current skin assignments, required to read the file or it will error on read if it doesn't double assign, a bad thing), "Read/Reset Weights" (which will delete and read the weights), and "Read Weights Only".
Make Chain Stretchy: This allows me to select the start joint and end joint for a stretchy chain, using either RP (standard IK) or SplineIK. It also strictly names (with the joint base names) all the various nodes it takes to make a stretchy chain so they can be easily identified when manually editing the conditional, multiply/divide, distance and curveInfo nodes. Radio buttons for the axes select the the axes along the joint that are scaled by the stretchy command. There are also radio buttons for RP and Spline construction, and an option to create a stretchy single bone.

I'm also working on a conversion utility to take the "animation" information off of a skeleton with House of Moves channel data brought in to Maya through the FBX importer, and convert that to combined rotational and positional data to drive an advanced IK rig for hand keying over motion capture without a slow, double rig being needed (the way you can do it in Motion Builder). The tool also takes frames numbered using a time code scheme and puts them at frame 1. I'll add a feature to the interface that lets you set the start frame at an arbitrary number. This also allows for importation of the data onto reskinned characters, and I'll probably figure out a way to allow the data to be put onto non proportionally similar characters for crowd scenes.

A parallel project of mine is an addition to the autorigger that sets up all of the controls automatically (usually a standard feature of autoriggers), with the addition of a double hierarchy so the mocap data can be imported, but you can animate using stretchy IK on top of that. I'm dubious about the ability of this system to use space switching techniques because of the underlying motion capture data, but we'll see. As far as I can plan, if there is space switching, it has to be done before the mocap data is applied. This represents a similar problem for foot axis pivot switching.

I've started learning about dynamics. My initial impression is that to get the results you want for complex limbs and such, it takes so much tweaking that it would just be faster to hand animate it. But we'll see where I can make it useful.

At this time, I'm not going to be doing tutorials. I don't really have the time, and I'd really rather wait until my tools and techniques have been battle tested a bit more.

Thanks for reading. Hopefully I'll have a chance to update this a little more often.