Friday, July 13, 2012

Question about tranforms on a frozen object. . .

A fella named Jason emailed me at Vimeo with a question about the IK 2 FK snapping video I did (here):

"I'm a student over at rigging dojo and my mentor turned me towrds your ik fk snapping tool that you created. The script works great. But I would ask how do you handle ctrl curves when they have been say zeroed out at (5,5,5) but when I query there position i get back (0,0,0) and when i apply the proper cordinates to the ctrl curve it goes the right amount but because it was zeroed out it isn't right... Any help would be appreciated"

Thought it was a good question (as I would expect from Rigging Dojo!) and thought it was worth talking about for two reasons: 1) the problem/solution are kind of interesting (if you're into this kinda thing) and 2) it reinforces some stuff I've said in the past, which bears repeating. Namely, you're kinda doing it wrong.

{slight digression (would make it footnote if knew how). . . When I say "wrong" here I'm using the term loosely. That is, you may have valid reasons for doing whatever you're doing and in your case it may make sense, BUT in most ordinary circumstances, you're probably creating more trouble than you're saving. end of digression}

So second things first . . . What is "wrong" and why?:

It has to do with freezing your transforms. And THAT has to do with the Matrix ("whooa . . ."). Each object has a transformation matrix that basically keeps track of where the object is in space, how it's rotated, scaled, etc. I won't make a fool out of you and me by trying to explain it in detail (Hamish Mckenzie has some cool posts on his blog about it), cuz I don't know it that well. But what happens, more or less, when you freeze transforms is that you "reset" the objects matrix (or perhaps more accurately, offset the matrix) and tell it that where it is NOW is at 0, 0, 0.  In the ordinary course of events, that may work, but when you come in with your cheeky little scripts and try to tell it to go somewhere specific, as you've discovered, there's a problem and Maya doesn't know what you mean (or again, to be more precise, Maya knows exactly what's going on, YOU don't know what you mean, or least I don't).
So that's what I mean when I say you're doing it wrong. Freezing transforms on controls is usually NOT the best way to zero them out. A much better way is to "group orient" them (some people call it "group freezing"). I even did a post and a video on the process. In short, you create your control at 0,0,0 and immediately group it. Then take the group and move THAT to the correct location + orientation, either with constraints that you then delete or better yet with "xform" and math and scripts and such. But you get two nice benefits: 1) the control stays at 0,0,0 because it's parented under the group that's doing all the moving and 2) because the control's matrix is then inheriting it's information from the group (which DOES have all the correct info with no offsets), both you and Maya are talking about the same world space, etc. and things like "xform" and "move" and stuff should work fine.

Sooooo. . . what if (as I imagine is the case) you've already done the freezing on your control and don't want to go back. Can you still use the IK2FK python code? The answer is "yes" (though I would recommend fixing the control using group orienting and being done with it. . .).
There are actually a few ways I would imagine. I read somewhere once that Maya stores the freeze transform offset matrix somewhere. I also bet that there's some way to get all smartypants and mathy and solve the problem by busting out some matrix voodoo. But since I can't do that (yet?), I can only offer a solution that will solve the translate problem the same way we did the rest of the script, with a lil elbow grease and vector math. It's not that hard:
Basically, the trick is to find the world space position of the IK control. If you just xform the transform node (the object), it obviously returns a weird value cuz the matrix has been offset. So instead, you need to do something like:
cmds.xform(ctrlName, q=True, rp=True) which will instead use something that's not part of the matrix business (rp is for the rotate pivot point). This will give you the world space of the object. When you compare THAT to the actual value you get from "xform" of the control, it gives you how far "off" you are from Maya's interpretation of the location. You now just need to know where to move it to, and since you've got the offset value, you just need to subtract THAT from the location of the FK ctrl to get the number you'd need to move the transform of the IK ctl. So putting those together and assuming everything else in the script is the same, the section where you move the IK wrist would look like this:

   #snap IK ctrl to wrist position
  
    trueIKraw = cmds.xform(ikWrist, ws=True, q=True, rp=True)  #get IK pos in true ws
    trueIK = om.MVector(trueIKraw[0], trueIKraw[1],trueIKraw[2]) #make it a vector
    frozenIKraw = cmds.xform(ikWrist, q=True, t=True) #get IK pos via matrix
    frozenIK = om.MVector(frozenIKraw[0], frozenIKraw[1],frozenIKraw[2]) #make vector
    offset = trueIK - frozenIK    #get the offset value for the Ik ctrl
    correctedFw = fw - offset   #this modifies the fk pos to account for the offset
    cmds.move(correctedFw.x, correctedFw.y, correctedFw.z, ikWrist, ws=True)
  
So all we're doing is figuring out the difference between the world space of the ik ctrl (trueIK) and the values the maya is giving us for the frozen transform (frozenIK) and applying that difference to the fk wrist position to get the final destination of the ik ctrl. This should also work when you HAVEN'T frozen the transforms (since the offset would then be 0).
Whew. Hope that makes sense ...

Thursday, July 12, 2012

Script for creating/editing message attrs . . .

I know I mentioned it in the 3rd part of the IK/FK snapping videos, so here it is for those of you who might be inclined to use it, the message mapping script . . .

If you're not sure what I'm talking about re: message attrs, watch this or look it up, sucka.

Here it is! zbw_messageMapper or go to the Downloads page above. . .

There are 2 tabs here:

 The first tab is for creating message attributes.

Select the object you want to add the message attrs to (the "base object") and click the "choose object" button to add it to the text field.
Click "add new attrs" to create a row that will allow you to type in the attr name and select the object the you want to send a message to that attr. You can do multiple attrs at a time, btw. If you want to delete the last row you created, click the red button (this just deletes the UI part, not any attrs). Once you've got what you need entered, then click the green button and the magic will happen. (it's really boring magic). A bit of text will pop up letting you know what you've got has been added and any problems or warnings should show up in your script editor. It will also run down what attr have been connected to what in the script editor.

The second tab is reviewing what message attrs you have on your selected base obj.

Select your base the same way you did in the last tab and click "list all message attrs". Any attributes of type "message" (however they were created) will be listed below in columns, one for the attr, one for the connected obj, if any, and a delete button.
The delete button is obvious, I hope, but if you right click on either of the other two columns you'll get a little menu giving you the option to change the name of the attr or change the obj connected to that attr. Clicking on either will open up a little window that gives you opportunity to change the respective end of the connection (and obviously reconnect things when done).

That's about it. I taken care of most of the dummy checking here (i.e. what happens when there are no message attrs or when you leave a field empty, etc), but there may be some situation that spits out an error or something. Be aware that creation side will force connect things, so it will overwrite any attrs that have the same name. It will show this warning in the script editor (though it just does anyways. Ha!). It will also show a little text message in the main window once you've created the messages, which will disappear when you change base objs. I chose not to reset the fields back to empty in case you wanted to do multiple base obj with the same attrs.

Hope it's useful to someone. And let me know if there are any big problems. I may try to fix them, you never know.

To run it, put it into your python scripts folder (maya doesn't automatically read all scripts folders for python files) and type:
import zbw_messageMapper

zbw_messageMapper.messageMapper()

You can drag that code to your shelf and run it from a button there.

Tuesday, July 03, 2012

IK to FK snapping

These videos are about how you might approach and execute IK to FK snapping. (For those that don't know or use different terminology, that's where you run a script and it snaps your IK limb position to your FK limb position). It's something that I had always looked at as a bit of luxury, something to get to if there was time when building a rig (hint: there rarely was time). As I mention in the video, Michael Cawood pointed out to me that it is actually a time saver when you're doing a lot of quick blocking. If you pose your characters in IK mode in blocking, you have a lot less counter animation to do when you change the body rotations, for example. You can pose away in IK, then switch to FK when it comes time to animate (if you need to switch, that is). As I was one of the lead animators on that job with Michael, not a rigger (thank god, there were 50+ characters for that one), I didn't bother to offer up my services:) But I thought it would be a good topic for a video tute, so here it is. . .
There are 3 parts. 1) covers the basic idea and some rudimentary vector math 2) covers the python scipting of those concepts in Maya and 3) introduces "message attributes" which are nifty little attrs that can be really useful to bump up functionality of your rigs/scripts.


Maya/Rigging: IK to FK snapping, part 1 from zeth willie on Vimeo.


Maya/Rigging: IK to FK snapping, part 2 from zeth willie on Vimeo.


Maya/Rigging: IK to FK snapping, part 3 from zeth willie on Vimeo.


Oh, yeah. Here is the code for the basic version of the IK/FK script in python (the script using the message attrs will depend on your specific attr names):
import maya.cmds as cmds
import maya.OpenMaya as om

#select the joints we need
sel = cmds.ls(sl=True)

#assign selection
fkWrist = sel[0]
fkShldr = sel[1]
fkElbow = sel[2]
ikWrist = sel[3]
ikPv = sel[4]

#get positions from fk
fkwRaw = cmds.xform(fkwrist, ws=True, q=True, t=True)
fkwPos = om.MVector(fkwRaw[0], fkwRaw[1], fkwRaw[2])

fkeRaw = cmds.xform(fkelbow, ws=True, q=True, t=True)
fkePos = om.MVector(fkeRaw[0], fkeRaw[1], fkeRaw[2])

fksRaw = cmds.xform(fkshldr, ws=True, q=True, t=True)
fksPos = om.MVector(fksRaw[0], fksRaw[1], fksRaw[2])

#set position of IK wrist ctrl
cmds.move(fkwPos.x, fkwPos.y, fkwPos.z, ikwrist)

#start figuring out pole vector pos

#find avg (midpoint) of shoulder and wrist
midpoint = (fkwPos + fksPos) / 2

#find pv direction
pvOrigin = fkePos - midpoint

#extend that length
pvRaw = pvOrigin * 2


#position pvRaw at midpoint
pvPos = pvRaw + midpoint

#stick pole vector at pvPos
cmds.move(pvPos.x, pvPos.y, pvPos.z, ikpv)
 

Once I get the little things hammered out in the zbw_messageMapper script that I showed in the video, I'll post that up too. . .

EDIT: One more thing! Here's the link to Ryan Trowbridge's site. http://www.rtrowbridge.com/blog/ Not sure how to access individual posts, but the post from March 09 has the video of the MasterClass on Vector Math in Maya. Good stuff. Also just bought his book on Python for Games and Film. Can't wait to read it!

Thursday, June 28, 2012

Follow up on getting work . . .

What this guys says:
http://effectscorner.blogspot.com/2012/06/getting-visual-effects-job.html#.T-zE3nAmZG-
How come he says it and it sounds smart and I say it and it comes out all curse words and aggressive?
. . .
F*&k him.

BTW, Scott Squires is seriously legit. Great blog too.


Friday, June 15, 2012

transfer UV's follow up . . .

A little while ago Nasheet posted a comment about solving some of the issues people were having with the transferUV script I posted here:

"""
It worked well for most of the meshes in my scene, but I did get the same error as teezy5 for a couple of them, and I think I found a solution. The error was thrown from:

`getAttr ($targetOrig+".intermediateObject")`;

The problem in my case was that there was more than one object named '$targetOrig' in my scene, and thus the return value of the 'getAttr' was an int[] containing the intermediateObject values for all matching objects, instead of just an int. In order to only get the unique intermediateObject for the selected geometry, 'listRelatives' needs to return the full dagpath. Basically, I just changed the line:


string $targetShapes[] = `listRelatives-s $target`;

to

string $targetShapes[] = `listRelatives-s -f $target`;


And it worked. That's it! Thank you again!
"""
Haven't had a chance to play with it, but sounds right to me. . .
Thanks Nasheet!

Here's the fixed script: zbw_cleanTransferUV.mel

Thursday, May 10, 2012

Autoswim-fish-expressions follow up . . .

A couple of months ago I had someone (Patrice Berube) e-mail me a question about some of the behavior in the autoswim fish rig. Been pretty busy with work and all so never got around to posting about the issue . . .
Basically, the problem was when one animated the frequency of the swimming fish (whole or part), over the frames where the animation occurs the sine wave function would freak out, go super fast, super slow, backwards, etc.
Since the rig was made remotely for a studio, I never really got a chance to play with it too much once I made it and no one ever brought up the issue (I actually knew more or less what the animation would be, and animating the frequency never really entered into it. I also suspect that a lot of the animation was done on the FK controls). But once Patrice pointed out what the issue he was having, the problem became more clear.
Essentially, through all of the expressions and such, time was constant (or moving constantly), but changing the frequency as time progressed would give kind of random values as you passed through the progression of frames (essentially, the sin values no longer had any sensible relationship to the values from the previous frame). My first reaction was, "uh oh! This will take some of that crazy math stuff to solve". But as we batted around a couple of emails about, we both kind of arrived at a similar solutions that were a bit more palatable (though I'd still like to know a math-y solution, I can't be bothered at this point).
Here's the problem illustrated:


As you can see, the values that are coming out of the sine function (which is the basis for the joint rotation) are fine if you don't animate the freq. Predictable values, each frame (in this case) changing by a value of 1 in oscillating directions.
But when you animate the freq it looks like it should make sense, but the values kind of suck. Starts out fine (delta of 1. Look I'm using mathy terms!), but then the changes become really unpredictable, sometimes almost 2, sometimes about zero. So the animation would look crazy.
 As it turns out the solution Patrice and I both realized independently (with Patrice actually implementing it, rather than just thinking about it :) is that rather than getting hard-core on the math, you could simply take the constant time out of the equation and not worry so much about animating the frequency. Instead, if you create an attribute to take the place of time (I thought to call it "time", he thought to call it "swimspeed". His is better) and simply animate that to make your fish swim, it would solve the problem. So in your expressions you'd simply replace "frame" with $swimspeed (which pulls the value of that attr), you could speed up or slow down the sine wave without fussing with the global frequency. A linear curve for "swimspeed" equals some version of normal time and messing with that curve will give you faster or slower time (as far as the fish rig is concerned).
Note that you'd still have problems if you tried to animate the frequency of the individual controls (because those frequencies would be animated relative to the new "swimspeed" attr). But that's less of an issue. You can just set those frequencies to be whatever you'd like, per control and control the rest of the animation globally.
Hope that helps anyone having this issue and big thanks to Patrice for pointing it out and working through the problem!