Converting a transformation matrix from Right-handed to Left-handed coord system

Started by
21 comments, last by gifaraj 18 years ago
Hi, I'm exporting my geometry and animations from Maya and they use a right-handed coordinate system, while I use a left-handed one in my engine. I used to solve this problem by having a parent matrix of: 1 0 0 0 0 1 0 0 0 0 -1 0 0 0 0 1 which flipped the Z axis correctly. But, currently I'm required to apply animations to things that have not been exported using my maya plug-in, and those things don't have the above parent matrix. What I need, then, is to convert each individual matrix from a right-handed transformation to a left-handed one. I have tried many things but with no success. My most recent attempt used this code to convert the matrix:

                MQuaternion rotation;
                double scale[3];
                MVector translation;

                fnTransform.getRotation(rotation);
                fnTransform.getScale(scale);
                translation = fnTransform.translation(MSpace::kWorld);

                translation.z *= -1.0;
                rotation.x *= -1.0;
                rotation.y *= -1.0;

                MTransformationMatrix matrix = MTransformationMatrix::identity;
                matrix.rotateBy(rotation, MSpace::kWorld);
                matrix.setScale(scale, MSpace::kObject);
                matrix.setTranslation(translation, MSpace::kWorld);
Does anyone know how do this? Thanks a lot. edit: I want to add, the actual problem is that my characters are exported in a hierarchy of nodes, each of which has a transformation matrix. So, if I have a hierarchy like:

- A
  - B
    - C
I applied the matrices like this: C * B * A * scale (where scale is the above matrix) what I now want to do is convert each matrix individually, without having to rely on having a "scale" parent matrix to correct the problem. ANY help will be greatly appreciated! [Edited by - gifaraj on April 3, 2006 9:14:13 AM]
Advertisement
Try negating all the members of the matrix that are either in the third row *or* third column of the matrix. Thus negate the members at positions (3,1), (3,2), (3,3), (3,4), (1,3), (2,3), (4,3)

If it doesn't seem to work, can you tell us exactly how the axes are setup in both systems? E.g. the right-handed is: X->right, Y->into the screen, Z->up

Have you tried multiplying the animation matrix with
[ 1  0  0  0 ][ 0  1  0  0 ][ 0  0 -1  0 ][ 0  0  0  1 ]
from both sides?
Quote:Original post by someusername
Try negating all the members of the matrix that are either in the third row *or* third column of the matrix. Thus negate the members at positions (3,1), (3,2), (3,3), (3,4), (1,3), (2,3), (4,3)


I tried that now, but doesn't work when working with a hierarchy of matrices (see my edit in the original post).

Quote:Original post by someusername
If it doesn't seem to work, can you tell us exactly how the axes are setup in both systems? E.g. the right-handed is: X->right, Y->into the screen, Z->up


The left-handed is +X to the right, +Y up, +Z into screen. The right-handed has the +Z out of the screen.

Quote:Original post by someusername
Have you tried multiplying the animation matrix with
[ 1  0  0  0 ][ 0  1  0  0 ][ 0  0 -1  0 ][ 0  0  0  1 ]
from both sides?


Well, I tried doing that in a tester program, not in the actual maya plug-in. This is the program:

#include <buitre/math/math.hpp>#include <iostream>#include <iomanip>using namespace BuitreEngine::Math;void correct_matrix(BeMatrix4& matrix){    BeMatrix4 fix(1, 0, 0, 0,                  0, 1, 0, 0,                  0, 0, -1, 0,                  0, 0, 0, 1);    matrix = fix * matrix * fix;}int main(){    BeMatrix4 a = BeMatrix4::ZRot(4.2f) * BeMatrix4::Translation(BeVector3(3, 1, 5));    BeMatrix4 b = BeMatrix4::XRot(2.2f) * BeMatrix4::YRot(1.0f);    BeMatrix4 scale = BeMatrix4::Scale(BeVector3(1,1,-1));    BeMatrix4 c = a;    BeMatrix4 d = b;    correct_matrix(c);    correct_matrix(d);    std::cout << "a: \n" << a << std::endl;    std::cout << "b: \n" << b << std::endl;    std::cout << "scale: \n" << scale << std::endl;    std::cout << "b * scale: \n" << b * scale << std::endl;    std::cout << "a * scale: \n" << a * scale << std::endl;    std::cout << "a * b: \n" << a * b << std::endl;    std::cout << "(a * b) * scale: \n" << (a * b) * scale << std::endl;    std::cout << "a * (b * scale): \n" << a * (b * scale) << std::endl;    std::cout << "(a * scale) * (b * scale): \n" << (a * scale) * (b * scale) << std::endl;    std::cout << "c * d: \n" << c * d << std::endl;}


Which outputs:

a:   -0.49   -0.87       0       0    0.87   -0.49       0       0       0       0       1       0       3       1       5       1b:    0.54       0   -0.84       0    0.68   -0.59    0.44       0    -0.5   -0.81   -0.32       0       0       0       0       1scale:       1       0       0       0       0       1       0       0       0       0      -1       0       0       0       0       1b * scale:    0.54       0    0.84       0    0.68   -0.59   -0.44       0    -0.5   -0.81    0.32       0       0       0       0       1a * scale:   -0.49   -0.87       0       0    0.87   -0.49       0       0       0       0      -1       0       3       1      -5       1a * b:   -0.86    0.51   0.032       0    0.14    0.29   -0.95       0    -0.5   -0.81   -0.32       0   -0.17    -4.6    -3.7       1(a * b) * scale:   -0.86    0.51  -0.032       0    0.14    0.29    0.95       0    -0.5   -0.81    0.32       0   -0.17    -4.6     3.7       1a * (b * scale):   -0.86    0.51  -0.032       0    0.14    0.29    0.95       0    -0.5   -0.81    0.32       0   -0.17    -4.6     3.7       1(a * scale) * (b * scale):   -0.86    0.51  -0.032       0    0.14    0.29    0.95       0     0.5    0.81   -0.32       0     4.8     3.5     0.5       1c * d:   -0.86    0.51  -0.032       0    0.14    0.29    0.95       0     0.5    0.81   -0.32       0   -0.17    -4.6     3.7       1


What I'm trying to do is make
c * d
the same as
a * b * scale

In this case, it looks like only the 3rd column remains incorrect.

If there's anything else you need to know, please ask. Thanks a lot.
Actually, it holds that:
scale*a*b*scale == c*d, not only for the given matrices, but for any 4x4 matrices, assuming of course that the correct_matrix() function is just like in your code.

Can't you multiply the total transformation of any specific node in the hierarchy, by "scale" from both sides?

edit:
I've been going over the thread once more, and it seems you've got me confused here. :) What I suggested above, not only doesn't seem to be what you want, it's also obvious because the square of the "scale" matrix is the identity matrix.

Clearly, you want to negate the Z axis in the matrices.
Multiplying from the left with "scale" negates the Z column (the entire Z axis), and multiplying from the right negates the Z components of all axes and translation (which is necessary, because we've negated Z)
If that doesn't seem to work, I'm sorry, but -having no means of testing these- I probably can't help you.


[Edited by - someusername on April 3, 2006 12:56:15 PM]
Quote:Original post by someusername
Actually, it holds that:
scale*a*b*scale == c*d, not only for the given matrices, but for any 4x4 matrices, assuming of course that the correct_matrix() function is just like in your code.


You are right.
I'm looking for a way to make c*d == a*b*scale though.

Quote:Original post by someusername
Can't you multiply the total transformation of any specific node in the hierarchy, by "scale" from both sides?


Well, I could do it in the rendering engine, but.. that would make me rely on the fact that the matrices are still exported right-handed. This is something I want to avoid, and the reason for the desire of converting them to left-handed individually in the first place.

And would that be the correct transformation though? I already get a correct transformation when multiplying by "scale" at the end, would it still be correct multiplying it at the other end as well?

Thanks

Define: If A is a matrix, then A^T is the transpose of the matrix.
If V is a column-vector, then V^T is the corresponding row-vector.
If V is a row-vector, then V^T is the corresponding column-vector.

Note A^T^T=A and V^T^T=V.

Solution:
Then, for all matrixes A and vectors V:
AV = (V^T A^T)^T
VA = (A^T V^T)^T

The answer is "transpose your matrix, and all will be well."

Assuming, of course, I understood the question. =)
Quote:Original post by someusername
Clearly, you want to negate the Z axis in the matrices.
Multiplying from the left with "scale" negates the Z column (the entire Z axis), and multiplying from the right negates the Z components of all axes and translation (which is necessary, because we've negated Z)
If that doesn't seem to work, I'm sorry, but -having no means of testing these- I probably can't help you.


Applying the -1 Z-scale in the top-level matrix works fine. But, the problem is I don't want to have that top-level matrix, I want to convert every single matrix in the hierarchy individually.

Look at http://www.okino.com/conv/exp_xof.htm the section where it says "Method to Convert from Right-Handed Coordinate System to Left-Handed Coordinates". The last method, "Flip Z of Top-Level Matrix", is what I have been doing before. I want to do what the other method, "Flip Z of Vectors and Reverse Angle of Quaternions", does.

Any ideas?

Quote:Original post by NotAYakk
Assuming, of course, I understood the question. =)


I don't think you did. ;)
Thanks though.
Gifaraj, I think you're trying to solve something that -even if it *is* soluble- it doesn't need to be solved!

You say that multiplying the top order matrix by the scale matrix would fix the problem, just like you do when you export things on your own. This multiplication however, doesn't alter the product of the matrices by affecting each single one of them.
What is the point in looking for changes in each matrix, when only 4 numbers in the right-most one have changed sign? The others don't change!

I believe that trying to figure out how each matrix is affected by this negation, and counteract it, is meaningless. The other matrices are not affected directly. They will only yield a different product. Trying to find some formula to predict the effects of this on the other matrices is utopic.

The problem you present is pretty much the same as having a sequence of integers that are to be multiplied with each other, and trying to find out, how negating one of them would affect all others, in order to be able to compensate for it when calculating their product, without negating the original number.
Only one number is being affected, nothing happened to the others. It will only affect their product. What is to compensate for in the other numbers, when only one of them has changed?
I hope you see what I mean. The actual problem is that there is no problem. How you will deal with it in software, I don't know. Can't you just negate the top level matrix of each frame-hierarchy upon import?

[edit:
You want to be able to calculate the effects of the change of system's handedness in each matrix in a hierarchy, when there aren't any. Only the first one needs adjustment.]

Quote:
Look at http://www.okino.com/conv/exp_xof.htm the section where it says "Method to Convert from Right-Handed Coordinate System to Left-Handed Coordinates". The last method, "Flip Z of Top-Level Matrix", is what I have been doing before. I want to do what the other method, "Flip Z of Vectors and Reverse Angle of Quaternions", does.

Negating the angle of some rotations is necessary when porting data from CSs of different handedness. E.g. if you have negated the Z axis, all previous Z rotations must now act on the negated Z axis. This is achieved easier, by simply negating the angle of rotation. If you had swapped axes too, you should also adjust them to act on the new axes.

In your case, implicit rotations that are hardcoded in matrices are handled through negation of Z, just like the "Flip Z of top-level". Explicit rotations, like "Quaternion(cos(π/4), sin(π/4)*{0,0,1})" need either the axis or the angle to be negated. In any case, you replace all quaternions with their conjugate.
I don't think that this application does anything more than what it says it does.
Currently I have a LHS system running under OpenGL (what uses RHS), and most imported file formats are also in RHS. I do just that: The (belonging) importers swap the handiness during import directly on the mesh/animation data, and the "top level" matrix reverses the viewing system's handiness.

[Edited by - haegarr on April 4, 2006 9:58:33 AM]
Interesting - we have _exactly_ the same problem as gifaraj. We do what gifaraj is presently doing, modifying the top-level matrix at run-time but obviously we want to avoid this extra cost.
If we know the root of the hierarchy, we can pre-multiply the handedness conversion transform but what if that matrix is directly affected by an animation... the handedness conversion will be undone. So you modify the transform for that bone, right? Not quite so easy because we can retarget our animations to other hierarchies/skeletons :-(
I too am very much interested in any solutions/suggestions that you folks may have... sorry I don't have the answer :-(

This topic is closed to new replies.

Advertisement