View Full Version : Aligning only the pivot of an object using MAXScript
martinB 05 May 2008, 11:29 PM 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


erilaz
05 May 2008, 12:15 AM
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! :D
martinB
05 May 2008, 08:07 AM
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.
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 zAxis should point exactly along [0.83205,0.5547,0] and the xAxis 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 fact that entire page probably answers your question quite succintly! :D
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
martinB
05 May 2008, 08:37 AM
What I have found out about transforming the pivot is that you'll need to retransform the objectTransform matrix or any part of it.
Yes, that is exactly what I want to do, I just need to find out how to do 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
The problem is that $.objectTransform is readonly, so I need to use $.objectoffsetpos etc. in order to get the same effect. But how to decompose a matrix3 into those individual parts?
Lookup "Using Node Transform Properties" in the mxs help.
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
erilaz
05 May 2008, 10:48 AM
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! :D
JHN, would the prerotated 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
martinB
05 May 2008, 08:29 PM
Almost directly from the manual:
snip
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.
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
I see that it works.. i don't quit understand it yet.. :) with the rotation values multiplying translate values...
Yep, same here. It seems to be fairly simple matrix transformation math but I cannot get my head around it.
Thanks again!
 MartinB
martinB
05 May 2008, 10:24 PM
Here is something I cooked up which seems to work correctly for nonscaled 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 nonscaled 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)
 counterrotate 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
 countertranslate 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
Rorschach
06 June 2008, 11:37 AM
hey, I've been battling with this too... seems so complicated in maxscript.
I want to set an objects pivot (rotation and position) to that of another. i use a bit of a dirty method smoetimes where I take the object with the required pivot, delelte all the faces and attach the other object. This has it's own problems and is a bit heavy too, but often does the trick.
This question seems to have come up before, but no decent solution... surely it has to be doable?
IkerCLoN
06 June 2008, 05:53 PM
Hey, Martin, I tried your code and it works fine... the first time you execute it. Just tried it with a box an a point, and aligning the pivot of the box to the one in the point:
1st time  it aligns ONLY the pivot of the box to the point.
2nd time  it allows also the geometry of the box to the point.
If you move the point around and run the function, the pivot of the box will align to that point... but the position of the box (i mean, the offset position) seems to be equal to the old position of the point (i refresh the variable that holds the transform of the point). And weird things happen if I move the box around and run the script again.
Am I doing something wrong???
SS
06 June 2008, 06:03 PM
martin:
I'm not sure if this is any help to you, but here's what i've done sometimes; I have two objects visually on top of each other, and then i want to have pivot of first object aligned in position and rotation to other object's pivot. I think this could be more like what Rorschach is doing...
obj = $Teapot01  xyz aligned pivot
obj2 = $Teapot02  rotated and moved pivot
 Store parent info
obj_par = $teapot01.parent
obj2_par = $teapot02.parent
 Store transformation info
obj_tra = obj.transform
 unlink
obj.parent = ()
obj2.parent = ()
rotate and move pivot
obj.objectoffsetrot = obj2.rotation
obj.rotation = obj2.objectoffsetRot
obj.pos = obj_tra.pos
obj.pivot = obj2.pivot
 reparent
obj.parent = obj_par
obj2.parent = obj2_par
Rorschach:
It would be nice to have some similar command for object pivots like working pivot has:
<void>WorkingPivot.setTM <matrix3>tm
martinB
06 June 2008, 08:42 PM
Hey, Martin, I tried your code and it works fine... the first time you execute it. Just tried it with a box an a point, and aligning the pivot of the box to the one in the point:
1st time  it aligns ONLY the pivot of the box to the point.
2nd time  it allows also the geometry of the box to the point.
If you move the point around and run the function, the pivot of the box will align to that point... but the position of the box (i mean, the offset position) seems to be equal to the old position of the point (i refresh the variable that holds the transform of the point). And weird things happen if I move the box around and run the script again.
Yes, you are right, and no, I don't think you are doing something wrong. I have now added a Reset Pivot operation in the beginning, so the object/pivot always has a well defined start. This seems to work better.
So just insert a ResetPivot obj right after with redraw off ( in the old script above.
Does this help?
 MartinB
mirvadim
06 June 2008, 10:28 PM
But basically I want to transform the pivot into fixed orientation that is given in a matrix3 value, without moving the geometry.
Hi, Martin. Maybe this is not exactly You want but, anyway, just decided to put my 2 cents. :)
fn fnSetPivot nod TM = (
If canConvertTo nod Editable_Poly do (
If classof nod != Editable_Poly do convertToPoly nod
vertsPos = For i = 1 to (polyOp.getNumverts nod) collect (polyOP.getvert nod i)
nod.transform = TM
for i = 1 to vertsPos.count do polyOP.setvert nod i vertsPos[i]
)
)
nod = teapot position:[50,70,90] dir:[50,70,90]
TM = (matrix3 [1,0,0] [0,1,0] [0,0,1] [0,0,0])
fnSetPivot nod TM
So, this function just stores position for each vertex, then sets object transform and then moves each vertex to it`s original position. Nothing special, but works fine for me.
Deficiency is that it works only with geometry objects and it converts them into
Editable poly.
magicm
06 June 2008, 11:22 PM
This fails miserably for any objects having a negative or nonuniform scale, but it seems to work well in any other case I have tested:
(
fn AlignPivotTo Obj Trgt =
(
 Get matrix from object
if classOf Trgt != matrix3 then Trgt = Trgt.transform
 Store child transforms
local ChldTms = in coordSys Trgt ( for Chld in Obj.children collect Chld.transform )
 Current offset transform matrix
local TmScale = scaleMatrix Obj.objectOffsetScale
local TmRot = Obj.objectOffsetRot as matrix3
local TmPos = transMatrix Obj.objectOffsetPos
local TmOffset = TmScale * TmRot * TmPos
 New offset transform matrix
TmOffset *= obj.transform * inverse Trgt
 Apply matrix
Obj.transform = Trgt
 Restore offsets
Obj.objectOffsetPos = TmOffset.translation
Obj.objectOffsetRot = TmOffset.rotation
Obj.objectOffsetScale = TmOffset.scale
 Restore child transforms
for i = 1 to Obj.children.count do Obj.children[i].transform = ChldTms[i] * inverse Trgt * Obj.transform
)
 Examples
AlignPivotTo $Box01 $Point01
AlignPivotTo $Box02 (matrix3 1)
)
Cheers,
Martijn
martinB
06 June 2008, 02:24 PM
[QUOTE=magicm]This fails miserably for any objects having a negative or nonuniform scale, but it seems to work well in any other case I have tested:
(
fn AlignPivotTo Obj Trgt =
( ... )
Thanks a lot! That looks quite good! Any idea why nonuniform scale is not working?
But NU Scale is evil anyway... Try manually editing the pivot on a NUscaled object... >:)
 MartinB
TakuanDaikon
03 March 2009, 12:26 AM
This fails miserably for any objects having a negative or nonuniform scale, but it seems to work well in any other case I have tested:
fn AlignPivotTo
Cheers,
Martijn
I just wanted to express my thanks for that code, since it works absolutely perfect for what I was trying to accomplish, and probably saved me many hours of work.
Thanks!
stoot
01 January 2010, 11:58 AM
I just wanted to express my thanks for that code, since it works absolutely perfect for what I was trying to accomplish, and probably saved me many hours of work.
Thanks!
Sorry for resurrecting an old post but credit where credit's is due! I echo the above post's sentiment :thumbsup:
CGTalk Moderation
01 January 2010, 11:58 AM
This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.