Wednesday, February 29, 2012

Triple Switch!

That's gonna be the title for my new movie. . . Or the managerial move I make that loses my kid the little league championship game. . . Or a utility node in Maya.
The triple switch is such a weird node. . . Not sure I've ever seen anyone use it in production. But in certain circumstances it can be useful and on rare occasions can be very handy indeed.
Basically the idea of the node is that you can create a geometry-specific attribute of a single material. So, fer example, you could put one shader on 100 objects and have, say, the color be different for each object. Still using just one shader, mind you. . . the color is different for each object, but all the other attrs of the shaders behave as normal. Here's the vid about how that works.

Maya: Using the Triple Switch utility node from zeth willie on Vimeo.


It's kind of hard to imagine getting the most out of this node without some coding, since by definition the node becomes more useful the more stuff (and therefore connections) you have in your scene. . . Here's the code I used to set up the basic random color thingy from the video. To use it, hook the triple switch up to the material, apply the material shader to the objects. Select the TS, then the objects and run the code. Obviously, if you want different results (which you should, probably) then adjust whatever parameters and values you'd like:
//triple switch should already be created and connected to shader
//select triple then objects
string $sel[] = `ls -sl`;
int $sizeObj = size($sel);
string $triple = $sel[0];

for ($i=1; $i<$sizeObj; $i++) {
    string $me = $sel[$i];
    select -r $me;

    //these are the per object attrs we're adding
    addAttr -at "float" -k 1 -ln "redMult";
    addAttr -at "float" -k 1 -ln "greenMult";
    addAttr -at "float" -k 1 -ln "blueMult";

    //these are the random values for those attrs
    float $rRand = `rand .75 1.25`;
    float $gRand = `rand .75 1.25`;
    float $bRand = `rand .75 1.25`;

    //assign the values to the attrs
    setAttr ($me + ".redMult") $rRand;
    setAttr ($me + ".greenMult") $gRand;
    setAttr ($me + ".blueMult") $bRand;

    //connect the objects to the TS
    string $child[] = `listRelatives -c $me`;
    string $shape = $child[0];
    string $meShape = ($shape + ".instObjGroups[0]");

    connectAttr ($meShape) ($triple+".input["+($i-1)+"].inShape");

    //connect the attrs to the TS at the same index the obj is connected
    connectAttr ($me + ".redMult") ($triple+".input["+($i-1)+"].inComp1");
    connectAttr ($me + ".greenMult") ($triple+".input["+($i-1)+"].inComp2");
    connectAttr ($me + ".blueMult") ($triple+".input["+($i-1)+"].inComp3");
    }

}

EDIT: Here's the code I used to create the distance setup. Seems a bit wonky to do things this way, but it more or less works . . . The point here being that since you can use an attr to drive the triple switch, you can control that attr with any of the usual (or unusual means) you control attrs in Maya. For this one you'll need to create a ".tightenFalloff" attr on the measure object first. (BTW, you actually don't need to drive an attr with the distance numbers, you could just plug them into the triple switch directly, but that's a little mysterious for anyone who might use this. The point, again, was simply to illustrate that you don't need colors to drive the triple switch):
///////////////////////
//color by distance
//////////////////////
{
//triple switch should already be created and connected to shader
//select triple then measure object, then textured objects
string $sel[] = `ls -sl`;
int $sizeObj = size($sel);
string $triple = $sel[0];
string $mObject = $sel[1];

for ($i=2; $i<$sizeObj; $i++) {
    string $me = $sel[$i];
    string $meDist = $me + "Distance";
    select -r $me;
    addAttr -at "float" -min 0 -max 1 -k 1 -ln "distance";
  
    //create distance node
    shadingNode -asUtility distanceBetween -n $meDist;
    connectAttr ($me + ".worldMatrix") ($meDist + ".inMatrix1");
    connectAttr ($mObject + ".worldMatrix") ($meDist + ".inMatrix2");
    connectAttr ($me + ".rotatePivotTranslate") ($meDist + ".point1");
    connectAttr ($mObject + ".rotatePivotTranslate") ($meDist + ".point2");
  
    //connect the distance to a mult node
    shadingNode -asUtility multiplyDivide -n ($me+"mult");
    connectAttr ($mObject+".tightenFalloff") (($me+"mult")+".input1X");  
    connectAttr ($meDist+".distance") (($me+"mult")+".input2X");
  
    //connect the dist node to the attr on the object
    connectAttr (($me+"mult")+".outputX") ($me+".distance");

    string $child[] = `listRelatives -c $me`;
    string $shape = $child[0];
    string $meShape = ($shape + ".instObjGroups[0]");

    connectAttr ($meShape) ($triple+".input["+($i-2)+"].inShape");

    connectAttr ($me + ".distance") ($triple+".input["+($i-2)+"].inComp1");
    connectAttr ($me + ".distance") ($triple+".input["+($i-2)+"].inComp2");
    connectAttr ($me + ".distance") ($triple+".input["+($i-2)+"].inComp3");
    }

}

Tuesday, February 28, 2012

Geo caching for easier animation

Some students of mine were doing things that suggested geo-caching to me and I just used this on a job, so I was all like "wooohooo!". And then I made this video.
Basically talks about the general idea of geo-caching and then I show one example of what I used this on recently. General gist of it: Maya, Geometry, Cached.
Slightly less general gist: You'd use this to lighten some of the processing load in your scene, either by allowing you to eliminate heavy rigs or allowing you to use light version of your characters (i.e. just geo) for multiple instances of an animation. Basically baking animation into the geo itself for use later in various ways. So check it:



Edit/Addendum - Totally did not get back around to the bit about why the eyes and teeth didn't come along for the ride in the geo cache! Oops. That's Ok. The reading will do you good.
Here's the deal. . . geo won't get written to the cache (at least the default Maya cache) if it doesn't have something driving the shape node itself (like skinning or other deformers). Simply translating or rotating the geo won't cut it since that stuff operates on the transform node and leaves the shape node to rest a bit. Soooo. . . since I usually connect things like eyes and gums/teeth into the rig via some combo of parenting to groups and constraining those groups (it's easy, clean), when I try to include those in the cache, they sit completely still, unaffected by caching party going on around them. So the easiest way around that is to just bind them to a joint, either existing or created just for that purpose. If you do it carefully, you won't even have to weight them (bind them separately from the rest of the geo, just to one selected joint). But they'll then be included in the caching. The other option is to set up some kind of follicle system on your cache-catching rig, the simple one, whereby you'd use follicles or rivets to stick things like the eyes and teeth to the cache-animated geo. A bit wonkier, but whatever gets ya through the night, amiright?