[Closed] Get/set transformation offset — what am I doing wrong?
What am I missing here? I thought this end up with p3 having the same transform as p2…
delete objects
p1 = point rotation:(eulerAngles (random -180 180) (random -180 180) (random -180 180)) size:10 wirecolor:(color 255 0 0)
p2 = point rotation:(eulerAngles (random -180 180) (random -180 180) (random -180 180)) size:15 wirecolor:(color 0 255 0)
offset = p2.transform * inverse p1.transform
p3 = point transform:(p1.transform * offset) wirecolor:(color 0 0 255) size:20
I’ve obviously either forgotten or am overlooking something…
Never mind… figured it out, I remember being confused by this before.
While I’m here, can someone explain how (p1.transform * offset) does not give the same result as (offset * p1.transform) ??
Okay, let’s make it a little more complicated. What’s going on with the following??
-- Reset the scene
delete objects
sliderTime=0
-- Make a random point
p1 = point rotation:(eulerAngles (random -180 180) (random -180 180) (random -180 180)) pos:[random -30 30, random -30 30, random -30 30] size:10 wirecolor:(color 255 0 0)
-- Make a second random point, parent it to the first
p2 = point rotation:(eulerAngles (random -180 180) (random -180 180) (random -180 180)) pos:[random -30 30, random -30 30, random -30 30] size:15 wirecolor:(color 0 255 0)
p2.parent = p1
-- Get the difference between the transformations of the two points
offset = at time 0 (inverse p1.transform) * p2.transform
-- Create a third point, and set its transformation using the offset between the first two
p3 = point wirecolor:(color 0 0 255) size:20
p3.transform = (p1.transform * offset)
-- EVERYTHING SO FAR WORKS
with animate on at time 20
(
-- Change the position and rotation of the first point
rotate p1 (eulerAngles (random -180 180) (random -180 180) (random -180 180))
move p1 [random -30 30, random -30 30, random -30 30]
-- Set the transformation of the third point again using the offset between the first two
p3.transform = (p1.transform * offset)
-- THIS DOES /NOT/ WORK
)
There has to be some way to do what I am trying to do here, can someone clue me in?
Yep, I’d already figured that first one out… but what about the second case??
yes ,your first step works but it is not correct. because:
p1.transform * offset=p1.transform*(inverse p1.transform) * p2.transform, which is
absolutely p2.transform. However, the correct method to get the offset of “p2 relative to –p1” is like this:
offset=at time 0 p2.transform*(inverse p1.transform)
which means the transform offsetfor “p2 relative to p1”. the multiply order is important and should not be switched.
at the same time, when using the offset, the correct method is :
p3.transform = (offset*p1.transform)
also the multiply order is critical and should not be
changed.
then why the multiply order is important? because the multiply of matrix
is non-commutative. normally,MatixAMatrixB !=MatrixBMatrixA.
the equation for p3 to keep the same offset with “p2 relative to p1” is:
(p3.transform)*inverse (p1.transform) =offset
to solve this equation we should POST-multiply both sides with p1.transform,
(p3.transform)*inverse (p1.transform)p1.transform =offsetp1.transform
then we get:
(p3.transform)= offset*p1.transform
thats it.
-- Reset the scene
delete objects
sliderTime=0
-- Make a random point
p1 = point rotation:(eulerAngles (random -180 180) (random -180 180) (random -180 180)) pos:[random -30 30, random -30 30, random -30 30] size:10 wirecolor:(color 255 0 0)
-- Make a second random point, parent it to the first
p2 = point rotation:(eulerAngles (random -180 180) (random -180 180) (random -180 180)) pos:[random -30 30, random -30 30, random -30 30] size:15 wirecolor:(color 0 255 0)
p2.parent = p1
-- Get the difference between the transformations of the two points
offset = at time 0 p2.transform*(inverse p1.transform)
-- Create a third point, and set its transformation using the offset between the first two
p3 = point wirecolor:(color 0 0 255) size:20
p3.transform = ( offset*p1.transform )
-- EVERYTHING SO FAR WORKS
with animate on at time 20
(
-- Change the position and rotation of the first point
rotate p1 (eulerAngles (random -180 180) (random -180 180) (random -180 180))
move p1 [random -30 30, random -30 30, random -30 30]
-- Set the transformation of the third point again using the offset between the first two
p3.transform = (offset*p1.transform)
-- THIS DOES WORK
)
Thanks, Java, you’re awesome! That’s exactly what I needed to know
I figured out matrix multiplications are non-commutative myself, but still don’t really get the “why” of it. I’m afraid that even after all these years of scripting, watching DVDs on the subject, etc, transformation matrices still seem like voodoo to me…
Awhile ago I checked out Khan Academy for a primer on matrix math. I didn’t finish all the videos, but it was very informative and goes into the Why and How questions you might have. It also shows you how to work out matrix operations by hand, which is overkill for our needs by and large but is still very edifying.
i am so glad it helps some.
ya, i totally agree with you:matrix is a most mysterious part of computer graphics, at the same time, most interesting!
To figure out the whys of matrix is a long story and should begin with the most bottom of the theory,in fact, i once had several questions on matrix before i really want to dig the deep most on this thing,and get some understanding of myself, although in fact, you could still use transform matrix without understanding but following the rules. Anyway, at that time,i was curious and here we go :
-
why use matrix to record transform?why not just use postion, rotation, scale vectors?
i feel we now use matrix as the fundamental method to record transform is for convenience, for computer`s convenience.
if we use position,rotation and scale vectors to describe transform, we need 3 kinds of data, each of them has its ownoperation method, for position, its add;for rotation, it is multiply and add; for scale, it is multiply and add.transform matrix solved all these operations into one operation: multiply, of course, it is multiply between transform matrices. if you want to move, just multiply current transform matix by a translation matrix;if you want to rotate,
just multiply current transform matrix by a rotation matrix; or if you want to scale, just multiply current transform matrix by a rotation matrix. This is efficient for computer to calculate the matrices multiplication instead of using different methods to deal with PRS vectors. Especially this is much more efficient for modern graphics hardware use parrallel computation for matrix multiplication. This doesnt mean reduce the calculation amount, this means a unified abstraction of transform,because the implementation of translation, rotation, scale matrix is still based on normal calculation of real number add, multiply, sin,cos functions.Tranform matrix is a great container, a set of rules, and in fact no mystery.the second convenience of using transform matrix is the mathematic of matrix operations were there, available, complete and ready for use. matrix is an important concept in linear algebra, its operations include: add, substract,multiply(no divide) ,inverse matrix, orthogonalize matrix, etc, and the definition of the operations is much older than computer graphics and in fact is a set of tools to solve the System of Linear Equations, and transform in cg is coincidently a System of Linear Equations,
so using matrix to solve this is reasonable.the third convenience of using transform matrix is for users like you and me, if we really understand the rules of transform matrix or at least know how to use the transform matrix in 3dsmax,or in other packages,which is similiar.
-
why transform matrix multiply is non-commutative, is there any clue on graphics?
Transform matrix multiply is non-commutative because the definition of matrix multiply. it is not the same as matrix add, which is element by element addition. matrix multiply is roughly a rowcolume method. that is , if matrixAmatrixB=matrixC, the first element of the matrixC is the sum of the multiply of the first ROW and the first Colume elements accordingly. this inevitably means that generally, matrixAmatrixB!= matrixBmatrixA.This is reasonable, because:
in world space move a teapot 10 units along z axis and then in world space rotate 45 degrees along y axis
is very different with:
in world space rotate a teapot 45 degree along y axis and then in world space move it 10 units along z axis. -
what is a transform matrix?
Generally, a matrix is like this, e.g. a 3X3 matrix:
1 ,2, 3
4, 5, 6
7, 8, 9
Matrix is an extremely abstract concept, it may means something or nothing. the key is how to interpret it, so what i write down is a good matrix, maybe not useful.
A 3d transform matrix is like this, this is an Identity matrix:
1, 0, 0, 0
0, 1, 0, 0
0, 0, 1, 0
0, 0, 0, 1
In 3dsmax, its implemented as matrix3 value, this is an Identity matrix3 value:
1, 0, 0
0, 1, 0
0, 0, 1
0, 0, 0
We will see that matrix3 value in maxscript is in fact a matrix4X3, in strict matrix multiplication, matrix3*matrix3 is illegal,because
the row number of the first matrix is 4, and the colume numberof the second matrix is 3, they just dont equal and could not multiply each other. and in fact maxscript interpret a matrix3 value as this:
1, 0, 0, 0
0, 1, 0, 0
0, 0, 1, 0
0, 0, 0, 1
fill the vacant positions with colume vector[0,0,0,1] and then multiply the matrices, matrix3 is just to save memory and reduce calculation.
so, we now understand transform matrix is a matrix that following some rules, what if we didnt follow the rules?
it will be interesting and in fact you could manually construct an arbitary matrix3 value to pre-multiply current transform, and max will not check the rules and give you the result of the transform,however, the result in most cases will be weired and streched. To get desired transform we should follow the rules of transform matrix construction.
and , what on earth information does transform matrix record?
an identity matrix3 is a great observing target, and in fact it tells almost everything!
1, 0, 0
0, 1, 0
0, 0, 1
0, 0, 0
“Identity” means it`s world original matrix, when you created a teapot from top view and set its position to [0,0,0], its transform is just an Identity matrix3.
it seems:
a. the first row is the vector of teapots x axis: (1,0,0); b. the second row is the vector of teapot
s y axis: (0,1,0);
c. the third row is the vector of teapot`s z axis: (0,0,1);
d. the fourth row is the postion of teapots POSITION! (0,0,0)
is this coincidence? there is a move transform gizmo underneath the matrix3 value? i am not sure, but i always imagine matrix3 value as a transform gizmo in different spaces. In fact, you could try move and rotate and scale the teapot and get the matrix3 value, and the above points are still true.
And this gives us a distinguished method to construct a transform matrix, especially you want to align one or two axis of the node to other nodes.
this method is not described in mascript help files but we could find a lot of examples in this forum.
-
what connections are between transform matrix and 3dsmax operations?
a. translation matrix, rotation matrix, scale matrix relate to move, rotation, scale;b. link to parent: record the offset at the time of linking, and apply the same offset to child any time parent changes its transform;
c. reset xform operation normalize the rotation and scale part of the matrix, it construct a new transform for the selected node, and transform
the vertex offset out of the original transform and into the new transform; -
how to understand(imagine) a transform matrix?
a. any transform matrix is relative to something:
i always imagine any transform matrix as an offset, this offset includes position, rotation, scale parts. for example, p1.transform is node p1`s offset from world origin, or offset for” p1 relative to world origin”. because p1.transform=p1.transform*inverse (IdentityMatrix). IdentiryMatrix is a transform matrix of positon[0,0,0], rotation[0,0,0], scale[1,1,1], i call it world origin matrix.
b. why p2.transform*(inverse p1.transform) is the offset for p2 relative to p1?
offset is also a transform matrix, this is obvious. it means and equals a transform that when applied to p1, p1 will move(and rotate,scale) to p2,
so we get the following initial condition equation:p2.transform= offset*p1.transform
note that we put offset at left and p1.transform at right , this is imortant, it means offset is relative to p1.transform,
or apply the offset in p1`s coordination system, which is a local system.
to solve “offset” in this equation we should eliminate p1.transform, by “right-multiplying” both sides by (inverse p1.transform),p2.transform*(inverse p1.transform)= offset*p1.transform*(inverse p1.transform) p2.transform*(inverse p1.transform)= offset
that is,
offset= p2.transform*(inverse p1.transform)
what if we set the initial condition equation like this? :
p2.transform=p1.transform*offset
which put offset at right and p1.transform at left, just as before, we could solve offset as before like this:
( inverse p1.transform )*p2.transform=( inverse p1.transform )*p1.transform*offset
offset = ( inverse p1.transform )*p2.transform
familiar,right? this is what you got in the first step. what i can say is ,this offset is also a correct offset, it is the offset transform recorded in “world coordinate”, no in “p1” local coordinate.
however in your case, you want p3 to keep track of the offset for “p2 relative to p1”, “relative to” means “local to”, so this offset is not what you really want.
according to all above discussion, we might be figuring out something that is about to emerge from the fog, what is the meaning of “left-multiplied by” and “right-multipied by”?(or you can put it “pre-multipied by” and “post-multipied by”).
c. "left-multiplied by" and "right-multiplied by"
just consider this expression:
tranformMatrixA*tranformMatrixB
it could be read as : tranformMatrixA “right-multiplied by” tranformMatrixB, it means
in coordsys WORLD apply ranformMatrixB to tranformMatrixA;
also it could be read as : tranformMatrixB ” left-multiplied by” tranformMatrixA, it means
in coordsys LOCAL (tranformMatrixB) apply tranformMatrixA to tranformMatrixB;
tongue twister, ya? the same result, both are correct, but it really has different meanings, choose which meaning depends on your situation and is the choice comfortable to you.
case A:
p2.transform*(inverse p1.transform)
it is the result of in coordsys WORLD apply (inverse p1.transform) to p2.transform, to visualize this process, just link p2 to p1, then transform p1 back to world origin, rotation to 0,0,0, scale to 1,1,1, you will see p2 changed accordingly, and now p2`s WORLD transform is absolutely the offset for” p2 relative to p1″ . you might say that this is talking tongue twister,
and in fact, i am. because the real and only mathematic process of get the offset of p2 relative to p1 is indeed what i said, link p2 to p1, transform p1 back to origin and see where p2 is.
we could consider this as a standard routine as relative transform or lingking or coordinate conversion by using inverse matrix.
case B:
rotationTransform*p1.transform
it is to in coordsys of p1 rotate some degrees, that is to rotate in local space of p1. transform. this is much more reasonable than interpreted as ” in world coordsys apply p1.transform on rotationTransform”.
By the way, the above is the multiply sequence in 3dsmax. The order is in fact depends on how the system treat the vector, generally, computer softwares treat vectors as row vectors, however, most of the academy materials such as cg theory books treat vectors as colume vectors, so the multiply sequence will be a transpose of 3dsmax sequence. According to matrix multiply definition, the transpose of multiply sequence at the same time inverse the multiply order, that is
Transpose(matrixA*matrixB)=Transpose(matrixB)*Transpose(matrixA)
this is just a reminder if you start with reading some books and videos in case of confusing.