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

## Recommended Posts

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]

##### Share on other sites
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?

##### Share on other sites
Quote:
 Original post by someusernameTry 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 someusernameIf 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 someusernameHave 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.

##### Share on other sites
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]

##### Share on other sites
Quote:
 Original post by someusernameActually, 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 someusernameCan'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

##### Share on other sites
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

Assuming, of course, I understood the question. =)

##### Share on other sites
Quote:
 Original post by someusernameClearly, 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 NotAYakkAssuming, of course, I understood the question. =)

I don't think you did. ;)
Thanks though.

##### Share on other sites
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.

##### Share on other sites
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]

##### Share on other sites
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 :-(

##### Share on other sites
pre-muptiplying top matrix should give you practically no extra cost whatsoever, and will work.

As for "doing it right": change of handness is like mirroring.
With hierarchies you have something like
a*b*c*d * v
where a,b,c,d is matrices and v is certex
and if all matrices mirror the space you get
a mirrors, a*b doesn't mirror, a*b*c mirror, a*b*c*d doesn't mirror.
You absolutely can not "evenly distribute" mirroring over several matrices.[lol] matrix either mirrors the space (has negative determinant) or it does not (has nonnegative determinant)

If you want to convert whole thing, that is, vertex coordinates(&normals), matrices, and everything else so that now whole thing is just like what'd happen if Maya would use same-handed coordinate system as your engine:

1:Negate all z coordinates of vertices and normals of model.(!)
2:In all matrices, negate following elements
  x  x -x  x  x  x -x  x -x -x  x -x  x  x -x  x

(maybe 3rd x on bottom row shouldn't matter as your matrices should all have zero there)

Note: I've derived that geometrically by imagining this thing (matrices is simple to visualize btw. just imagine 3 xyz vector arrows corresponding from columns. arrows should be drawn from point given by fourth point. when flipping coordinates, you need to flip z of all things, and also negate z arrow). I think it must work but haven't tried.

[Edited by - Dmytry on April 4, 2006 11:27:00 AM]

##### Share on other sites
Quote:
 Original post by someusernameI 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?

I kind of see what you mean. I don't know why this is so difficult to do, though. I mean, isn't there a transformation in LH that does the SAME transformation as in RH?

What I want then is, the transformations that Maya would give me if it used a LH system. That's all I want. Is it not possible?

Quote:
 Original post by someusernameIn 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.

That site says exactly what I my problem is: 'This works well except for custom programs which import .X files and expect that all coordinates and matrix frames be in LHCS; for the latter case go and enable the "Flip Z of Vectors and Reverse Angle of Quaternions" option.' I need to use my animations on objects that don't know (and shouldn't need to know) about the coordinate system change.

A rotation matrix can be converted to a quaternion and back, so I tried doing exactly that:

// retrieve translation/rotation/scale from transform matrix...// make changestranslation.z *= -1.0;rotation.x *= -1.0;rotation.y *= -1.0;// set my exported matrix using this translation/rotation/scale...

But it doesn't work correctly, the geometry transforms into a weird blob.

Another thing I've considered is just using a RH system in my engine as well. Is it really as simple as changing the view matrix and the DirectX Culling order render state?

Quote:
 Original post by Dmytry 1:Negate all z coordinates of vertices and normals of model.(!)2:In all matrices, negate following elements

You posted just when I was about to post this. I tried doing what you said though, and it still didn't work. Look at the screenshot: http://66.253.147.225/images/export_problem.jpg

Thanks :D

##### Share on other sites
404 error on image...

sorry, fixed.

##### Share on other sites
This is strange. I'm writing unit test.

##### Share on other sites
Oh. I get it.

"Handedness" refers to the direction of the z-axis. I thought you where talking about left and right matrix multiplication. =)

Lets invent another operator "^z" which changes the space of a matrix and vector from one handedness to the other.

if R and V are vectors and A is a transformation:
R^z = A^zV^z
then
R = AV
where for any vector V = (V_x, V_y, V_z, V_w), then V^z = (V_x, V_y, -V_z, V_w).

Problem: Find what "^z" does to matrixes.

Does that describe the problem?

##### Share on other sites
my solution surely works. I wrote little test:

#include <iostream>#include <cmath>#include <MersenneTwister.h>#include "../math3/math3.h"using namespace math3;using namespace std;// random number sourceMTRand r;// compute random rotation and translation matrixMatrix4x4f RandomMatrix(){    Quaternionf q;// make random quaternion, normalize it, and convert to matrix    q.a=r()-0.5;    q.b=r()-0.5;    q.c=r()-0.5;    q.d=r()-0.5;    Normalize(q);// append random translation    Matrix4x4f a(QuaternionToMatrix(q));    a.At(0,3)=r()*10.0-5.0;    a.At(1,3)=r()*10.0-5.0;    a.At(2,3)=r()*10.0-5.0;    return a;}// compute random vectorVec3f RandomVector(){    Vec3f result;    result.x=r()*10.0-5.0;    result.y=r()*10.0-5.0;    result.z=r()*10.0-5.0;    return result;}// Mirror hand of matrixvoid MirrorHand(Matrix4x4f &a){    a.At(2,0)=-a.At(2,0);    a.At(2,1)=-a.At(2,1);    a.At(2,3)=-a.At(2,3);    a.At(0,2)=-a.At(0,2);    a.At(1,2)=-a.At(1,2);    a.At(3,2)=-a.At(3,2);}// mirror hand of vectorvoid MirrorHand(Vec3f &v){    v.z=-v.z;};using namespace std;int main(){    Matrix4x4f    a=RandomMatrix(),    b=RandomMatrix(),    c=RandomMatrix(),    d=RandomMatrix();    Vec3f vertex=RandomVector();    cout<<Mulw1(a*b*c*d,vertex)<<endl;// mirror hands of all matrices and vertices    MirrorHand(a);    MirrorHand(b);    MirrorHand(c);    MirrorHand(d);    MirrorHand(vertex);// should have z negated    cout<<Mulw1(a*b*c*d,vertex)<<endl;	//std::cout << "Hello world!" << std::endl;	return 0;}

output:
vector:[3.15286,	7.46052,	-6.50599]vector:[3.15286,	7.46052,	6.50599]

notes: Mulw1 (Matrix4x4 m,Vector3 m)
multiplies 4x4 matrix with vector {v.x,v.y,v.z,1}

if you want i can provide dependencies for compilation
edit: by the way, to convert quaternion, negate i and j components (or real and k, doesn't matter).

[Edited by - Dmytry on April 4, 2006 12:10:24 PM]

##### Share on other sites
NotAYakk: this ^z is my MirrorHand operator (i replied before seeing your message) :-)
My test prints
A*V
and
A^z * V^z
so you can see whever (A*V)^z = A^z * V^z or not.

##### Share on other sites
whoops, missed that
Quote:
 Original post by someusernameTry 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)

Almost right, except it's XOR and there shouldn't be (3,3) :)
Quote:
 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->upHave 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?

And this is right, should work too, but only if vertex's z coordinate is also flipped (i.e. if not only all matrices is converted to other handness this way but also all vectors too). It is better idea to just flip signs as described above, it is equivalent but faster. Left multiplication flips signs of third row, and right of third column (so intersection is flipped twice and thus has sign unchanged).

Actually, i was bit uneasy about being unable make simple proof that my method works, but your second thing lets do it elegantly:
we have
M*a*M * M*b*M * M*c*M ... * M*v
(last term is negation of Z in v)
and M*M = identity so it is equal to
M*a*b*c ...*v
and that is exactly what we want. So correctness is proved.

##### Share on other sites
(sorry, Nth post in the row)
as for what OP want, i have no idea, but there's only two conversions that make sense:
a: convert everything (transformation matrices and vectors e.g. vertices, normals, etc) from one handness to other handness so after conversion everything will be just like what'd be if Maya had same handness as you use.(note: you may need to flip vertex winding order too.)
b: put some root transform that flips z when working with things from maya (not really a conversion)
As i understand b is undesired(also it clutters things up), so the only sensible alternative is a. Which is what my example code are doing using MirrorHand() on matrices and vectors, you can try doing that with some custom converter.

##### Share on other sites
Dmytry, thanks for taking the time to setup a test case. The strange thing is, I set up a similar test case and get different results...

#include <buitre/math/math.hpp>#include <iostream>#include <iomanip>using namespace BuitreEngine::Math;void correct(BeMatrix4& matrix){    matrix(2,0) *= -1.0f;    matrix(2,1) *= -1.0f;    matrix(2,3) *= -1.0f;    matrix(0,2) *= -1.0f;    matrix(1,2) *= -1.0f;    matrix(3,2) *= -1.0f;}void correct(BeVector3& v){    v.z() *= 1.0f;}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));    BeVector3 vertex(2.5f, -304.1f, 589.21f);    BeMatrix4 c = a;    BeMatrix4 d = b;    BeVector3 vertex2 = vertex;    correct(c);    correct(d);    correct(vertex2);    std::cout << "vertex: \n" << vertex << std::endl;    std::cout << "vertex * (a * b): \n" << vertex * (a * b) << std::endl;    std::cout << "vertex2 * (c * d): \n" << vertex2 * (c * d) << std::endl;}

Which outputs:

vertex:       2.5    -304.1    589.21vertex * (a * b):   -335.88   -567.46    97.207vertex2 * (c * d):    247.68    385.29   -471.91

operator*(BeVector3 const&, BeMatrix4 const&) just transforms the vector by the matrix.

Any idea?

typo:
v.z() *= 1.0f;
should be
v.z() *= -1.0f;
:-)

##### Share on other sites
doh ;)
thanks.

Well, that seems to be working fine then, but the rendered image says otherwise.. Hmm...

edit:
would that work for bone offset matrices too?

[Edited by - gifaraj on April 4, 2006 1:41:32 PM]

## Create an account

Register a new account

• ## Partner Spotlight

• ### Forum Statistics

• Total Topics
627671
• Total Posts
2978551

• 11
• 10
• 10
• 12
• 22