By design any nodes placed into the Max scene are oriented so their local z-axis is perpendicular to the construction plane of the viewport in which they are placed. In order to have objects with their +z-axis aligned with the world coordinate system +z-axis you have to place the object in either the perspective or the top viewport. This recently became an issue at Volition, as we wanted to use a scripted helper object to specify snap-points for objects in game. The scripted helper’s axis orientation is directly relevant to how objects are snapped to it. Having the helper placed at different rotations based on something as arbitrary as a viewport selection was counterintuitive. Having the artists constantly reorienting their helper nodes after creation was becoming time consuming.
I figured it would be an easy thing to fix. All I needed to do was to set the rotation of the new node to quat (0 0 0 1) as it was being created…
tool create
(
on mousePoint clickno do (
if (clickno == 1) then (
delegate.shape = “sh-snappoint”
nodeTM.pos = gridPoint
nodeTM.rotation = (quat 0 0 0 1)
) else (
#stop
)
)
)
This didn’t change the behavior at all. The nodes were still coming in oriented relative to the viewport in which they were placed. Confused, I put a “print (nodeTM.rotation as string)” line after the nodeTM.rotation = … line. The script started telling me that each placed node had a rotation of (quat 0 0 0 1), which was good. Each node still ended up in viewport relative orientations, though. querying their .rotation property after they were in the scene yielded rotations other than the default. Somehow, somewhere, Max was overriding my desired rotation before the node was attached to the scene.
After some forum posts with Autodesk it turns out that you have to use getCPTM(), which returns the construction plane to world transform matrix, set the view transformation matrix to that, and then set nodeTM to be equal to the transform matrix of the mouse position on the active grid in world coordinates multiplied by the inverse of the newly set view transformation matrix. Then a placed node will always be oriented to the world.
You might need to read that through a few times. I know I did!
The correct code is:
tool create
(
on mousePoint clickno do (
if (clickno == 1) then (
delegate.shape = “sh-snappoint”
viewTM = getCPTM()
nodeTM = (transMatrix worldPoint) * (inverse viewTM)
) else (
#stop
)
)
)