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!