Archive for category maya

Weak References

Last year while I was working at Red Rover, I heard the term “weak reference” in reference to a technique for referencing objects in 3DS Max. The Max TD used them for a variety of things. I didn’t quite understand what he was talking about at the time, since the last version of Max I used was 2.5 and I never did rigging or coding for it then.

More recently I’ve come to the same technique on my own both in Maya and in Cinema 4D, and was reminded of the name for it by that same TD over beers a few weeks back.

Essentially, weak references are variables on objects that contain a pointer to an object, and not a reference to it’s name. In Maya, for example, you may see rigging scripts written by TDs referencing specific objects by name or saving names of objects as they’re created and using those names to connect attributes or add constraints. In a clean scene this works fine, as long as the rigger is meticulous in their naming scheme and runs different stages of the rigging script in proper order.

But what happens when Maya namespaces become involved? As soon as you reference in an asset, every object that makes up that asset gets a namespace prefixed onto it’s name. If you’ve written scripts that require specific names, they break. If your layout files aren’t perfect and the namespace is different between two or more shots (as Maya is wont to append a number, regardless of what you specify), useful tools like character GUIs and the like break and you’re left doing namespace surgery in a text editor.

Weak references sidestep all this by giving you a permanent connection to an object regardless of name changes or namespace prefixes.

A good example is how I’m currently handling cameras in scenes. A decision was made early on, on the current project at work, to name cameras in layout files by the name of the shot / sequence. Normally this isnt a problem, but we’re using a renderer that’s not linked into Maya directly and therefore needs a command line batch exporter written. If all the cameras are named differently, and the camera’s animation has to be baked and exported as well, how do you go about picking the right object?

Using weak references, the problem becomes trivial. You create them as follows:

addAttr -ln "cameraObj" -at "message";

You’ve probably seen attribute connections of type message into IK handles and other things. The message attribute carries no data– that is, it never changes and causes no DAG recalculations. (This is doubly useful because you can connect the message attributes of two objects to message-typed user attributes in a cycle without causing a cycle error– more on that later.) However, the attribute can be used to get the name of the connected object like so:

listConnections object.messageAttribute;

It will return an array of strings. If you rename an object, you can get the object’s current name through the above command.

So where do you store these attributes? For the moment I’m using a trick I saw on the Siggraph ’08 talk by Blue Sky on procedural rigging: I create non-executing script nodes and store connections on them. In the camera example above, every scene has a master script node. On that node are a few attributes, including its “type” and a .message connection to the render camera. It’s them trivial to find the camera’s name:

string $sel[] = `ls -type "script"`;
for ($s in $sel) {
	if (`attributeQuery -node $s -ex "snCamera"`) {
		// this should be the one you need
		// normally I search for the type, but this is an example
		string $conn[] = `listConnections ($s + ".snCamera")`;
		// if it's only one connection incoming, then you're done.
		print("Camera is named " + ($conn[0]) + "\n");
	}
}

This technique can be extended to include all kinds of objects. It can also be very helpful for scripts like character GUIs that need to know what characters are present in a scene, and be able to change the positions of all those controls.

One final note on this for now: In Cinema 4D, every object and tag in a scene can be named the same. Searches for objects or tags by name are often fruitless because of this; if two objects or tags have the same name there’s really no easy way to tell which is which in a COFFEE script. What you can do, however, is create a user data variable that is of the Link type. This allows you to drag and drop an object into that variable’s edit field, and provides a permanent pointer to that object regardless of name. This is very useful in rigging; for example, you can always tell which joints in a leg are control joints, and which are bind joints, by creating links. You can also expose the links in XPresso and use the pointers as if you’d dragged an object onto the XPresso node window.

a quick note on object snapping

A few weeks ago I got stuck while writing a foot snapping tool — the snapping code I used to use didn’t work on the rigs we inherited because there were a lot of pivot changes. I tried a bunch of more elegant ways to fix this but ended up doing the old standby:

delete `orientConstraint`;
delete `pointConstraint`;

Now, that solution wasn’t a satisfying one to me, so I kept looking for a better one.

Turns out there’s an easy fix. If you use getAttr on a transform node’s .worldMatrix, you get back the proper 4×4 set of float values that represents its final position in world space. Afterwards you can set the matrix of another object using xform. It’s pretty simple in either MEL or Python.


// this snaps one object to another using worldSpace matrix coordinates
string $sel[] = `ls -sl`;
float $m[] = `getAttr ($sel[1] + ".worldMatrix")`;
xform -ws -m $m[0] $m[1] $m[2] $m[3] $m[4] $m[5] $m[6] $m[7] $m[8] $m[9] $m[10] $m[11] $m[12] $m[13] $m[14] $m[15] ($sel[0]);

The MEL code is a bit ugly because of having to specifically reference each item in the array of floats, but it works. Here’s the Python equivalent. I keep it now as a button my shelf.


# uses matrices to snap one object to another
import maya
# yes, I put all my commands into the main namespace
from maya.cmds import *

sel = ls(sl=True)
mat = getAttr(sel[1] + ".worldMatrix")
xform(sel[0], ws=True, m=mat)

Katt's Mysterious 3D Lectures – Vector Application: A Better Rivet

You have the aim constraint under your belt. You can guess how a pole vector’s motion will change rotations with a look. You’re feeling a new sense of power and a desire to accomplish… things.

Now what?

Let’s start with something simple. Remember that old standby, the Rivet script, that I mentioned in my last post? Every good tutorial DVD I’ve bought over the years had it. Every rigger I’ve ever met uses it. As scripts go, it’s probably the most useful 2k on HighEnd3D.com.

But did you know it’s also broken?

Let me go back for a moment. A year ago, while I was working under a talented rigger who liked the Farenheit Digital system for creating nurbs ribbon rigs, was saddened by the fact that all licenses of Maya at our company were Maya Complete save two: his, and mine. This meant that the standard way of building ribbons, where you attach a Maya Hair follicle to the surface, wasn’t going to work as Maya Hair only comes with Unlimited. He mentioned something about using an aim constraint and a pointOnSurfaceNode or through the decomposeMatrix node to accomplish the same, although it didn’t work as well. So I was tasked with writing a python node plugin that accomplished the task. It worked well and quickly enough; 40 or so of them were live in the final rigs.

However I prefer to keep the number of non-standard nodes in rigs to a minimum. At my current place of work we realized a need for a follicle-like setup again, so I started researching.

At one point we’d thought we could solve the problem with the Rivet script. Rivet takes two edges, lofts them into a nurbs surface with history, then attaches to the surface a locator using a pointOnSurfaceInfo and an aim constraint. When the lofted surface is relatively square and doesn’t deform much, this works fine. When you try to use just the pointOnSurfaceInfo and aim constraint setup on a longer nurbs surface that deforms and bends, however, we found that the locators do not behave properly. Past a certain amount of twisting, the locators would rotate off in odd directions.

I played with the script and found that the pointOnSurfaceInfo node was feeding the surface normal into the aim constraint as the aim vector, with one of the tangent vectors as Up. Because of this, the aim constraint was causing the locator to flip. The way aim constraints work makes up vectors into suggestions, not rules. It also makes the third axis a product of the other two, as I showed in my last post.

In the end it was a simple fix: instead of using the surface normal (which wasn’t an illogical choice), I fed both surface tangents into the aim constraint and let the third axis, the normal, be the derived one. Since the tangent u and v vectors are always orthogonal regardless of how much you distort the surface, and since they always run in the right directions along the surface, you can be certain that the surface normal — a third orthogonal vector — will still end in the right place. (I bet the surface normal is derived from the cross product of the two tangent vectors anyway, internally.) No need for a custom node or to force the loading of decomposeMatrix; so far I haven’t seen any problems with this setup.

Steps for those who want to try this at home:

1) Create a pointOnSurfaceInfo node attached to your nurbs surface. Set it’s U and V parameters to get the output to the right place on your surface.

2) Use the createNode command to make an aimConstraint node.

3) Plug the pointOnSurfaceInfo’s tangentU into the aimConstraint’s target[0].targetTranslate, and the tangentV into the constraint’s up vector.