Unit Conversions In Maya

Contents


Overview

Maya supports four distinct types of numerical values: linear values are used to measure distances and are specified in centimeters, feet, etc; angular values are used to measure angles and are specified in degrees or radians; time values are used to measure time and are specified in seconds, minutes, etc; and dimensionless values are simply values (e.g. float, int, double) which have no units associated with them.

For any particular attribute, you can determine the type of value that it contains by using the getAttr -type command:

CommandResult
getAttr -type sphere1.translateXdoubleLinear
getAttr -type sphere1.rotateYdoubleAngle
getAttr -type time1.outTimetime
getAttr -type sphere1.scaleZdouble

Maya allows you to set your preferred units for the first three types of values, either through the Options->General Preferences->Units window, or using the currentUnit command. Thus, you can set your preferred linear units to be feet, your preferred angular units to be radians, and your preferred time units to be minutes.

No matter what you set your preferred units to, Maya will continue to store values in its internal units, which are as follows:

Value TypeMaya Internal Units
linearcentimeters
angleradians
time0.00166667 seconds (6,000fps)
dimensionlessNone

So, if Maya always stores values in its internal units, then what does it mean when you specify preferred units which are different from those?

It means that whenever Maya displays a value to you, for example in the Attribute Editor or as the result from a getAttr command,  it will convert the value to your preferred units before displaying it. And whenever you give a value to Maya, for example in the Channel Box or in a setAttr command, Maya will interpret that value as being in your preferred units and will convert it to its internal units before storing it.

For example, let's say that you set your preferred linear units to be feet. You then issue the following two commands:

    setAttr sphere1.translateX 2;
    getAttr sphere1.translateX;

Since translateX is a linear attribute, and you have set your preferred linear units to be feet, Maya will interpret the 2 in the first command as 2 feet. Since Maya's internal linear units are centimeters, it will convert this value to 60.96 centimeters before storing it in sphere1.translateX.

In the second command, the reverse process takes place. Maya retrieves a value of 60.96 centimeters from sphere1's translateX attribute. Since your preferred linear units are feet, Maya will convert the value to feet, giving a result of 2, which is what it displays back to you.


Unit Conversion Nodes

So you get back the same value that you put in. That's not too exciting. But what happens when you start connecting attributes together in the Dependency Graph? Specifically, what happens when you connect together attributes of different types?

For example, let's say that you want sphere1 to rotate around its Y axis while it moves in the X direction. To do this, you connect its translateX, which is a linear attribute, to its rotateY, which is an angular attribute.

    connectAttr sphere1.translateX sphere1.rotateY;

Keeping in mind that sphere1.translateX was set to 2 earlier, what angle should sphere1.rotateY now contain?

Most people would think that rotateY should be 2 as well. However, remember that that 2 was interpreted as 2 feet, and Maya converted it to 60.96 centimeters before storing it. So the value that would end up being fed into sphere1's rotateY is 60.96, not 2. Since that would almost certainly not be the intended result, Maya does a bit of black magic to make the results come out the way that people would expect them to.

It does this by inserting a unitConversion node into the connection between sphere1.translateX and sphere1.rotateY. The unitConversion node multiplies its input value by a conversionFactor and sends the result to its output. In our example, the conversionFactor would be set to 0.0328084 which, when multiplied by 60.96, will give a result of 2. Thus, rotateY gets set to a value of 2 radians, as expected.

To make things even more interesting, let's assume that your preferred angular units are currently set to degrees. If you try to look at the value of rotateY, Maya will convert it into degrees for you, displaying a result of 114.591559 degrees. Once again, this isn't the result that most people would expect: they have what they think is a direct connection between two attributes, so if they set the source side of that connection to 2, they expect to see 2 on the destination side as well. So when Maya creates a conversion node, it has to calculate the factor required to convert the source value from your preferred units (feet)  into internal units for that value type (centimeters), and the factor to convert internal units for the destination value (radians) into your preferred units (degrees), then multiply those two factors together to get the final conversionFactor for the unitConversion node. E.g:

0.0328084(feet to centimeters)
*0.01745329(radians to degrees)
=0.0005726146

So when you enter a value of 2 feet into sphere1.translateX, Maya will convert that to 60.96 centimeters and store that result in translateX. The unitConversion node will then multiply that by 0.0005726146, giving a value of 0.034906586016 radians, which will be stored in rotateY. When you retrieve the value of rotateY with a getAttr command, or look at it in the Attribute Editor, Maya will convert it to degrees for you, and you'll see the expected value of 2 degrees. Thus, you entered 2 feet into the source attribute and saw 2 degrees appear at the destination attribute.

It is important to note that unitConversion nodes are set up to convert between the preferred units that were in force at the time that you made the connection. If you subsequently change your preferred units, any new unitConversion nodes you create will use conversion factors which make the results come out right for your new preferred units. But any existing unitConversion nodes will continue to use their old conversion factors, meaning that the results will not look right under the new preferred units. In the example above, if you changed your preferred angular units to radians, then looked at the values of sphere1.translateX and sphere1.rotateY again, you would still see a value of 2 feet displayed for translateX, but now rotateY would show a value of 0.034906586016 radians. There really isn't anything that Maya can do to get around this: if it changed the conversion factors of existing unitConversion nodes every time you changed your preferred units, then the real, internal values of the attributes that they are connected to would suddenly change as well, and your geometry would get all screwed up.

So as a general rule, pick your preferred units when you begin a new scene and then stick with them. Otherwise you could end up with unexpected results.

One other thing to note is that Maya does not always insert unitConversion nodes into Dependency Graph connections. It only does so when the following conditions are all true:

  1. The connection is between two numeric attributes.
  2. The source and the destination are of different types (e.g. one is linear and the other angular).
  3. The current preferred units for at least one of the types involved in the connection are different from Maya's default units for that type.

If any one of these conditions is not true, then the conversion factor would end up being 1.0, so Maya just makes a straight connection between the two attributes without any intervening unitConversion node.

If you're trying to walk the Dependency Graph, the uncertainty of whether a unitConversion node is or is not present in any given connection can prove annoying. Fortunately, Maya provides a way to sidestep this uncertainty by providing the listConnections command with the -skipConversionNodes flag. By setting this flag to true, you can skip right over the unitConversion node, if it's there, and get to the attribute on the other side, which is generally what you're after. E.g:

    listConnections -plugs yes sphere1.translateX;
    // Result: unitConversion1.input //
    listConnections -plugs yes -skipConversionNodes true
        sphere1.translateX;
    // Result: sphere1.rotateY //


Time

Time is handled a bit differently from the other numerical values. First of all, instead of using the same unitConversion nodes as for the other numerical values, time values have their own timeToUnitConversion nodes.

Second, the internal units that Maya uses for time -- 0.00016666667 seconds, equivalent to a frame rate of 6,000fps -- are not available for you to use as your preferred units. This means that connections between time and non-time attributes will always have timeToUnitConversion nodes in them.

One other confusion about time units should be cleared up here. When you look at the list of available time units, you'll see hours, minutes and seconds, which all make sense, but also film, game, pal and others which are all frame rates. How can a frame rate represent a unit of time?

The trick here is that the units are really the time for a single frame at the given rate. Thus, if your preferred time units are set to pal, then a time value of 4 would represent four frames at the PAL rate of 25fps, which gives you 0.16 seconds of time.


Bugs In Maya's Unit Handling

All of the above might work quite nicely if it were not for a number of bugs in Maya's handling of units. Most of these are the result of node attributes which have been given the incorrect value type. To see an example of this, create an arcLengthDimension node and check the type of its arcLength attribute:

    createNode -n arc1 arcLengthDimension;
    getAttr -type arc1.arcLength;
    // Result: double //

Since the arcLength attribute represents a distance, it should return a type of doubleLinear. But instead, it returns the dimensionless type of double. This means that no matter what you set your preferred linear units to, Maya will always display the arcLength value in centimeters.

You might think that nothing more than an annoyance, but more insidious errors can result from it. Consider the following fragment of a MEL script:

    float $length;

    $length = getAttr("arc1.arcLength");
    setAttr("sphere1.translateX", $length);

Let's say that arc1.arcLength is currently 5 centimeters. If your preferred linear units are centimeters, then $length will be set to 5 and sphere1.translateX will in turn be set to the same value. All is well.

But watch what happens if your preferred linear units are feet. Since arc1.arcLength has the dimensionless type of double, Maya will just retrieve its value straight up, without any conversion. So $length will still be set to 5. In the setAttr command, sphere1.translateX is of a linear type, so Maya will interpret the value in $length as being in your preferred linear units. Thus, sphere1.translateX ends up being set to 5 feet, or 304.8 centimeters.

So the script will generate different results depending upon the current setting for your preferred linear units. This is nasty because it means that you can test your script out using Maya's default units and find everything working fine. Then suddenly, one day, a user decides to change her linear units to something different and the script begins generating erroneous results.

Below is a table listing some of the known errors in Maya's attribute types. This listing is not exhaustive, it merely represents those errors which Gooroos Software has run into so far. If you encounter any others, please let us know and we will add them to the table.

Node Attribute Actual Type Correct Type
arcLengthDimension arcLength double doubleLinear
arcLengthInV double doubleLinear
motionPath uValue doubleLinear double

Gooroos Software's freely available unitCorrection node can be used to adjust for these errors. It gives you the ability to convert a value from one type to another, without introducing any unitConversion nodes or otherwise changing the value.

Looking at our earlier example, a unitCorrection node could be used to eliminate the errors as follows:

    createNode -name "uc" unitCorrection;
    connectAttr arc1.arcLength uc.doubleIn;

    float $length;

    $length = getAttr("uc.linearOut");
    setAttr("sphere1.translateX", $length);

This will give the correct results, regardless of what the user's current linear units are.

Return to Thoughts and Ruminations