Maya Aim Constraint Offset: Confused

So I was getting ready to post up code from my GDC presentation and I noticed this little problem that I’m hoping someone can help me solve.

Basically: pointConstraints, orientConstraints, and aimConstraints all have an offset attribute.

For a pointConstraint, this does exactly what you expect, which is that it applies a local-space translation offset from the target position.

For an orientConstraint, the offset attribute represents an Euler angle offset from the target orientation. This offset is composited to the target orientation (as a posttransformation), and the rotation order is tied to the rotateOrder attribute of the constrained object. As such, if your constrained object is XYZ, the offset will be XYZ, and so on. You can see this in action by executing the following short script, whereby the tiny cone and the large cone should both line up, no matter what you set as the rotateOrder on the large cone.

import maya.cmds as cmds
cmds.file(f=True, new=True)
cone1 = cmds.polyCone()
cone2 = cmds.polyCone()
cone3 = cmds.polyCone()
target = cmds.polyCone(n='target')
cmds.setAttr('%s.translate'%target[0], 2, 0, 0)
cmds.setAttr('%s.rotate'%target[0], 45, 45, 45)
cmds.setAttr('%s.scale'%cone2[0], 0.5, 0.5, 0.5)
cmds.setAttr('%s.scale'%cone3[0], 0.25, 0.25, 0.25)
oc1 = cmds.orientConstraint(target[0], cone1[0], o=[45,45,45])
oc2 = cmds.orientConstraint(target[0], cone2[0], o=[0,0,0])
cmds.parent(cone3[0], cone2[0])
cmds.connectAttr('%s.offset'%oc1[0], '%s.rotate'%cone3[0])
cmds.connectAttr('%s.rotateOrder'%cone1[0], '%s.rotateOrder'%cone3[0])
cmds.connectAttr('%s.rotateOrder'%cone1[0], '%s.rotateOrder'%cone2[0])
cmds.select(cone1[0])

So now the problem: the aimConstraint. The documentation says:

Offset
Specifies an offset position (translate X, Y, and Z) for the constrained object relative to the target point. Note that the target point is the position of the target object’s rotate pivot, or the average position of the rotate pivots of the target objects. Default values are all 0.

If you actually use this value, you can see this is complete nonsense, and that the offset attribute represents some type of Euler angle offset. The problem, however, is that it does not appear to do the same thing as the orientConstraint, which you can see by running this variant of the script.

import maya.cmds as cmds
cmds.file(f=True, new=True)
cone1 = cmds.polyCone()
cone2 = cmds.polyCone()
cone3 = cmds.polyCone()
target = cmds.polySphere(n='target')
cmds.setAttr('%s.translate'%target[0], 0, -2, 2)
cmds.setAttr('%s.scale'%cone2[0], 0.5, 0.5, 0.5)
cmds.setAttr('%s.scale'%cone3[0], 0.25, 0.25, 0.25)
aim1 = cmds.aimConstraint(target[0], cone1[0], aim=[0,1,0], u=[0,0,1], wu=[1,0,0], wut='vector', o=[45,45,45])
aim2 = cmds.aimConstraint(target[0], cone2[0], aim=[0,1,0], u=[0,0,1], wu=[1,0,0], wut='vector', o=[0,0,0])
cmds.parent(cone3[0], cone2[0])
cmds.connectAttr('%s.offset'%aim1[0], '%s.rotate'%cone3[0])
cmds.connectAttr('%s.rotateOrder'%cone1[0], '%s.rotateOrder'%cone3[0])
cmds.connectAttr('%s.rotateOrder'%cone1[0], '%s.rotateOrder'%cone2[0])
cmds.select(cone1[0])

In this case the large cone and tiny cone only appear to align when the rotateOrder is XYZ. So what does this mean?

Basically, the (world-space) output for an orientConstraint is constraintRotation * offsetRotation while the (world-space) output for an aimConstraint follows the same pattern when the object is rotated XYZ, but not for any other rotateOrder. I’m wondering if anyone either knows what the offset attribute is doing differently for the aimConstraint, or has some idea just by glancing at the output. Thanks in advance!

For anyone interested, I’m guessing at this point it is just a bug, as providing a small nonzero value in one of the offset channels produces the behavior. For example, enter the following Python script:

import maya.cmds as cmds
cmds.file(f=True, new=True)
cone1 = cmds.polyCone()
target = cmds.polySphere(n='target')
cmds.setAttr('%s.translate'%target[0], 0, -2, 2)
aim1 = cmds.aimConstraint(target[0], cone1[0], aim=[0,1,0], u=[0,0,1], wu=[1,0,0], wut='vector', o=[0.00001, 0.0, 0.0])
cmds.select(cone1[0])

At this point, if you cycle through the different rotateOrder values for the cone’s transform node, you’ll see a bunch of wild orientations. It’s pretty strange since aimConstraints are such an old feature, but that’s my best guess at this point.

1 Like