Confused about rotation (Spaces and Quaternions)

Hi, I’m writing a tool that cleans up a Biped rig to be a little more engine friendly, mostly removing pointers, but also remaking and baking some joints. I’m not very familiar with Max rigging/scripting, but I’ve worked with Python in Maya, so I opted to use pymxs. Still I’m having some trouble understanding rotation issues I’m having.

I’ve genericized the issue to this: I have a hierarchy of dummies:

    1. dummy_root
      1. dummy_old
      1. dummy_constrained
      1. dummy_new

I want to bake the animation of dummy_old onto dummy_new. To do this in python I’m using dummy_constrained which is orient constrained to dummy_old and then each frame for a duration, copying the rotation from dummy_constrained to dummy_new, but this doesn’t result in matching rotation.

dummyRoot = rt.getNodeByName("dummy_root")
dummyOld= rt.getNodeByName("dummy_old")
dummyConstrained= rt.getNodeByName("dummy_constrained")
dummyNew= rt.getNodeByName("dummy_new")
dummyNew.rotation = dummyConstrained.rotation

I had it list out rotation for a single frame, and this is what I found:

(quat -0.359605 -0.315985 -0.130885 0.868163) - dummy_root
(quat -0.650749 0.157522 0.0287707 -0.742217) - dummy_old
(quat 0.286525 -0.466804 0.14015 0.824837) - dummy_constrained
(quat 0.286525 -0.466804 0.14015 0.824837) - dummy_new

Moreover if I instead say:

dummyNew.rotation = dummyOld.rotation

The rotation axis gizmo in the center of the object will show the X and Z axis as matching, but the Y axis is flipped in the opposite direction. If instead I manually match dummy_old and dummy_new’s EulerXYZ values in the transform type-in under the timeline, they match up without the axis flipping ( do dummy_old and dummy_constrained’s EulerXYZ values in the UI also match even though the quat doesn’t)

dummy_constrained and dummy_new are in the same space hierarchy-wise, but giving them matching rotation values doesn’t match their rotation in the viewport. I feel like I’m running into an issue with different spaces and trying to convert them or not understand the way quaternions work. I can’t work out what spaces these objects are in or how to match them up using quaternions, with the same result achieved by using the Euler UI options (which should just turn back into quaternions under the hood anyway.)

Does anyone have any advice on where I’m going wrong? Thanks!

I would make sure dummyConstrained.rotation is the value I expect.
Max has its wonderful animation controller stack hierarchy which tends to include a node for each space/matrix involved in the final transform result. A constraint adds a node to the animation controller stack (I believe.) So, dummyConstrained.rotation may only be the rot value in the space of its immediate controller stack parent.

You may need to find its world space transform and apply that, perhaps.

It’s been almost a decade since I have have had to navigate the Max controller stack, so I don’t recall off the top of my head how to view and examine it.

Hiya, I’m no long on Max - however it is rather tricky with its coordinate spacing and changing things. Have you looked into or tried using 3ds Max 2023 Developer Help | coordsys | Autodesk

There is also a very helpful function posted on the 3ds_mach Tech-Artists Slack from the amazing Marc Robert that might be helpful as well if you go to the channel and pop in - coordsys_context as a search you’ll find it :slight_smile:

If you want the completed rotation value of the controller stack use:
Node.Transform.Rotation.

Using just Node.Rotation will get the value of the currently active controller on the controller stack.

I would personally advise against using the coordsys syntax and use matrix maths instead.

Setting rotation or position directly will give unexpected behaviour where as setting a transform directly works.
Ie NodeA.Rotation = NodeB.Rotation will often lead to position changing (madness I know but there is a logical reason for it that I cannot remember)
NodeA.Transform = NodeB.Transform will work

1 Like