[Closed] Aligning only the pivot of an object using MAXScript
For an object in my scene, I want to only modify the pivot such that it’s orientation matches a predefined set of axis. More precisely, I know how the X, Y and Z axis of the pivot should be oriented, as I have a matrix3 value that describes them in row1, row2 and row3.
Given this, the pivot can be easily oriented that way by simply using
myNode.transform = matrix3value
Of course, that will not only change the pivot but the entire object including geometry will change.
Unfortunately, maxOps.pivotMode = #pivotOnly does not work for node transformations through MAXScript, it seems only to deal with interactive transformations in the viewport.
So now my question is:
How do I have to calculate myNode.objectoffsetrot, myNode.objectoffsetpos and myNode.objectoffsetscale in such a way that the geometry is backtransformed and ends up in the same world coordinates as before the transform change.
Any advice much appreciated, as well as other ideas how to achieve this.
Thanks!
– MartinB
If I get what you’re asking, you might want to check out “Using Node Transform Properties” in the maxscript manual. While I was hunting up a solution for you I came across an example function called rotatePivotOnly, which might do what you’re after.
In fact that entire page probably answers your question quite succintly!
Thanks. I have seen the page and the function, unfortunately, the function only does a relative/incremental rotation, whereas I need an absolute orientation.
The function is great to rotate the pivot 90 degrees around the X axis, for example, but when the pivot’s z-Axis should point exactly along [0.83205,0.5547,0] and the x-Axis along [-0.5547,0.83205,0], for example, some more math is needed.
Oh, and sorry for being vague with what I am asking for, it’s a bit hard to explain. But basically I want to transform the pivot into fixed orientation that is given in a matrix3 value, without moving the geometry. Very much like if you interactively turn on “Affect Pivot Only” and then use the Align tool to transform the pivot, with all align options (especially “Align Orientation (Local)”) on.
In theory yes, in practice no, I must admit. Some some help with this would be great.
Thanks
– MartinB
What I have found out about transforming the pivot is that you’ll need to retransform the objectTransform matrix or any part of it. I think transforming the pivot in “Affect Pivot Only” mode is also a two step proces, transforming the node(transform+baseObject), counter transform the baseObject.
If you rotate in pivot only mode the pivot, the objectTransform, gets inverse transform coords.
$.transform
$.objectTransform
or
$.objectoffsetpos
$.objectoffsetrot
$.objectoffsetscale
Lookup “Using Node Transform Properties” in the mxs help.
-Johan
Yes, that is exactly what I want to do, I just need to find out how to do it.
The problem is that $.objectTransform is read-only, so I need to use $.objectoffsetpos etc. in order to get the same effect. But how to decompose a matrix3 into those individual parts?
I have read that help page but found it not extremly helpful for solving my specific problem. That’s why I am hoping someone here can help me fill in the missing pieces.
Thanks!
– MartinB
Rotation seems simple enough
pRot = eulerAngles 45 15 0
pM = (pRot as quat) as matrix3
$.transform = pM
$.objectOffsetRot = inverse pM.rotationPart
Translation i can’t get my head around at this time, since it will work in a different space since we rotated the transform matrix… there’s probably a simple solution for this… but my matrix/vector math is just not good enough :\
-Johan
Ok, got a bit further
(
pRot = eulerAngles 355 15 0
pPos = [50,-20,15]
pM = (pRot as quat) as matrix3
pM.translation = pPos
oldM = $.transform
$.transform = pM
$.objectOffsetPos = (inverse (XFormMat (transMatrix pPos) $.transform )).pos
$.objectOffsetRot = inverse pM.rotationPart
$.pos += oldM.pos
)
It seems to correctly put the object back in it’s old spot upon translation of the pivot, only fails if the object was rotated to start with… question is the values you put in the pivot offset, they have to be in world space or are the offsets?
With XFormMat function you can transform a matrix in another transform space…
-Johan
Sorry, I didn’t mean to imply you hadn’t read the manual (which I know full well you would have, since you’re THE Martin Breidt!), I just thought it was along the lines.
This is where we need Bobo. He’s down with the transform math!
JHN, would the pre-rotated issue be fixed by collapsing or resetting the transform?
That’s a good question, and I will look into it… but now work calls Hopefully someone comes along and just puts out 2 lines of code and it should be fixed… but for know my head is still hurting about all these different transform spaces…
For now, rotating is pretty trivial offsetting is not.
-Johan
Almost directly from the manual:
(
-- define function for rotating only the pivot point
fn RotatePivotOnly obj rotation = (
local rotValInv=inverse (rotation as quat)
animate off in coordsys local obj.rotation *= RotValInv
obj.objectoffsetrot *=RotValInv
obj.objectoffsetpos *= RotValInv
)
if (b = selection as array)[1] == undefined then
b = box()
b.pivot=[12.5,12.5,25] -- Translate pivot
RotatePivotOnly b (EulerAngles 0 0 35) -- Additive rotate pivot only 35 degrees about local Z
)
So moving the pivot with a straight .pivot approach and rotating everything back in the rotatePivotOnly function… Only problem is it now only does additive rotation. I see that it works… i don’t quit understand it yet… with the rotation values multiplying translate values…
-Johan
Exactly. If I now want the z axis of the pivot to be rotated in a specific way, I have to find out what relative rotation this would do. And then the same for the x and y axis
Yep, same here. It seems to be fairly simple matrix transformation math but I cannot get my head around it.
Thanks again!
– MartinB
Here is something I cooked up which seems to work correctly for non-scaled objects:
fn xform_pivot_only obj newX = (
-- Sets the transformation of the pivot of an object without moving the geometry
-- v0.1 - (c) Martin Breidt - 30.05.08
--
-- obj: object whose pivot should be transformed
-- newX: matrix3 value with new transform for pivot
--
-- !! currently works only correctly for non-scaled objects !!
--
with redraw off (
-- store old transform
local oldX = obj.transform
-- transform entire object such that pivot matches target transform matrix
-- this will also move the geometry
obj.transform = newX
-- compute "difference" transformation between old and new
local delta = oldX * (inverse newX)
-- counter-rotate to remove rotation from geometry
obj.objectOffsetRot = delta as quat
-- compute translation vector in world space
local dvec_world = oldX.row4 - newX.row4
-- transform translation vector from "world" to "new" coordinate system
local new_op = (newX * (transMatrix dvec_world) * (inverse newX)).row4
-- counter-translate to remove translation from geometry
obj.objectOffsetPos = new_op
)
)
--- now create some test objects
-- clear scene
delete $*
-- create some initial geometry and transform it somehow as a starting point
t = teapot pos:[150,-70,30] wirecolor:red
rotate t (eulerangles 45 31 70)
-- create copy so we can check later in the viewport whether the geometry has moved
t2 = copy t
t2.wirecolor = blue
-- define a target transform matrix for pivot:
-- the pivot's x axis should be [0,0,-1] (negative z axis),
-- the pivot's y axis should be [1,0,0] (positive x axis),
-- the pivot's z axis should be [0,-1,0] (negative y axis),
-- and the pivot should be at position [110,0,50]
new = (matrix3 [0,0,-1] [1,0,0] [0,-1,0] [110,0,50])
-- transform only the pivot of "t" so it matches "new"
xform_pivot_only t new
select t
The main addition to what we already had is the transformation of the objectOffsetPos value into the new local coordinate system.
This does not take scale into account, so if you have a scaled object whose pivot you want to transform, but the scale of the target matrix does not match, you get incorrect results.
Even without scale, I am not entirely sure this really works in all cases, I have not done any extensive testing. If anyone gives it a try here, please let me know if it isn’t working in some cases, and if so in which cases.
Any other comments most welcome, too!
Cheers!
– MartinB