PDA

View Full Version : Aligning only the pivot of an object using MAXScript

 martinB05-28-2008, 11:29 PMFor 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-29-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-29-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 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 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

JHN
05-29-2008, 08:19 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. 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-29-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 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?

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

JHN
05-29-2008, 09:51 AM
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

JHN
05-29-2008, 10:15 AM
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-29-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 pre-rotated issue be fixed by collapsing or resetting the transform?

JHN
05-29-2008, 11:40 AM
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

JHN
05-29-2008, 12:04 PM
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-29-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-29-2008, 10:24 PM
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

Rorschach
06-03-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-05-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???

S-S
06-05-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

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-05-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

06-05-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-05-2008, 11:22 PM
This fails miserably for any objects having a negative or non-uniform 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-06-2008, 02:24 PM
[QUOTE=magicm]This fails miserably for any objects having a negative or non-uniform 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 non-uniform scale is not working?

But NU Scale is evil anyway... Try manually editing the pivot on a NU-scaled object... >:)

-- MartinB

TakuanDaikon
03-19-2009, 12:26 AM
This fails miserably for any objects having a negative or non-uniform 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-18-2010, 10: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-18-2010, 10: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.