[Closed] Setting node-based coordsys rotation
In the process of saving poses to a file, I’m using
mainShape is the “main controller shape”, and is defined elsewhere.
pnt is a struct with the listed properties.
in coordsys mainShape (
pnt.px = p.pos.x
pnt.py = p.pos.y
pnt.pz = p.pos.z
pnt.rx = (inverse p.transform).rotation.X
pnt.ry = (inverse p.transform).rotation.Y
pnt.rz = (inverse p.transform).rotation.Z
pnt.sx = p.scale.x
pnt.sy = p.scale.y
pnt.sz = p.scale.z
)
And it seems to save out fine (I’ve tried various other ways of getting the rotation, so that’s subject to change if what I’m doing is wrong).
I can’t find a good way to SET the rotation of a node in the coordsys of another specified node again – at least not a way that lets me reproduce the rotation the node had when it was saved! I hope that makes sense, my head is starting to melt at this point :banghead:
My goal is to be able to save a pose for a character to file, and later restore that pose – no matter where in worldspace the character may be at “restore time”.
Any pointers, anyone?
don’t save the position and rotation separate. save the transform matrix. the rotation as eulerangles in any space than world or local doesn’t make any sense.
What if you calculate the world transform of both objects and get the difference between them, then add the difference back in world coordinates?
-Eric
Will give it a go, will that work for rotation though? Position and scale are trivial, by comparison. Well, nothing like trying to find out if it works I suppose!
Position is trivial as it is the 4th row of the matrix3, but scale and rotation are combined in the first 3 rows. Using quatToEuler2 (inverse obj.rotation) should give you the same values as you see in the UI.
For more information follow my link and posts in this thread and the posts/threads it links to.
-Eric
Will this work?
obj.rotation.x_rotation
obj.rotation.y_rotation
obj.rotation.z_rotation
Thanks Pixel_Monkey, this works to get some good values out:
in coordsys mainshape (
pnt.rx = ((QuatToEuler2 (inverse hand.rotation)).x_rotation)
pnt.ry = ((QuatToEuler2 (inverse hand.rotation)).y_rotation)
pnt.rz = ((QuatToEuler2 (inverse hand.rotation)).z_rotation)
)
Now I just need a way to set those values again!
If I just use
hand.rotation.x_rotation = pnt.rx
hand.rotation.y_rotation = pnt.ry
hand.rotation.z_rotation = pnt.rz
it gives strange results – sometimes flipped, sometimes it seems to look right. Apparently random* results are not something I’m comfortable with heh!
*I know it isn’t random, it just looks that way when I don’t know what’s going on!
Is there a reason why you are getting explicit euler values instead of the Quat? If you need to display the values for users, then yes Euler are more conventional and user readable, but if you are simply storing values it should always be done in Quats.
in coordsys mainshape (
pntQuat = (inverse hand.rotation)
pnt.rx = (QuatToEuler2 pntQuat).x_rotation
pnt.ry = (QuatToEuler2 pntQuat).y_rotation
pnt.rz = (QuatToEuler2 pntQuat).z_rotation
)
hand.rotation = pntQuat
All angles in 3ds max are internally stored as a Quat, and you have a chance of inaccuracy everytime you do a conversion. Also, euler rotations have order dependency. So XYZ, YXZ, ZXY, etc can have different results. With quats you avoid the order dependecy. From the Maxscript Help Quat Values:
Quaternions are used to store object rotations in 3ds Max.
…
Interpolation between two key frame orientations is much easier using quaternions and produces smooth and natural motion. Unlike Euler angles, no numerical integration is necessary; quaternions provide an analytic result (no approximations).
…
When converting a series of quat values to eulerAngles values, it is possible for sign flips to occur in the eulerAngles values. This is due to the fact that one single quat value can be expressed through many different eulerAngles values.
Hope that helps some,
-Eric
EDIT: Quats also support Algebraic equations. So you could easily get the difference. This example will rotate obj2 by the difference of the initial rotation:
rotOffset = obj1.rotation - obj2.rotation
obj2.rotation += rotOffset
Only that I’m not sure what the most efficient way would be, and so I thought it simpler to develop it with values I can double-check manually. I only need to store and retrieve the values programatically, so I don’t need them to look pretty at all!
Will setting the rotation to the stored quat behave correctly if the model’s mainshape has been rotated after the “in coordsys mainshape (…” values were stored ?
Maybe this answers the above? I’ll check as soon as I get the opportunity to!
In any case, many thanks for your help so far!
Denis beat me to it,
Store the data as the inverse transform:
inverseTm = obj.transform * inverse target.transform
and apply it back:
obj.transform = inverseTm * target.transform
On a side note, it might be more wise to store the world transforms of the objects (source, target) – its more data but its raw. For instance if you store a hierarchy in world space it essentially allows you rebuild the hierarchy in any order you like.
Aha, that’s actually the first thing I tried – saving the transform, but I must have been doing it wrong – but that’s so much simpler, and more elegant – and it seems to work just fine! Excellent – I’ll have to do more testing, but so far it’s looking good.