Sign in to follow this  
gifaraj

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 this post


Link to post
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 this post


Link to post
Share on other sites
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 1
b:
0.54 0 -0.84 0
0.68 -0.59 0.44 0
-0.5 -0.81 -0.32 0
0 0 0 1
scale:
1 0 0 0
0 1 0 0
0 0 -1 0
0 0 0 1
b * scale:
0.54 0 0.84 0
0.68 -0.59 -0.44 0
-0.5 -0.81 0.32 0
0 0 0 1
a * scale:
-0.49 -0.87 0 0
0.87 -0.49 0 0
0 0 -1 0
3 1 -5 1
a * 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 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 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 1
c * 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 this post


Link to post
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 this post


Link to post
Share on other sites
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

Share this post


Link to post
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

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

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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
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 this post


Link to post
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 this post


Link to post
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 this post


Link to post
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 this post


Link to post
Share on other sites
Quote:
Original post by someusername
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?


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 someusername
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.


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 changes
translation.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 this post


Link to post
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 this post


Link to post
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 source
MTRand r;
// compute random rotation and translation matrix
Matrix4x4f 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 vector
Vec3f 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 matrix
void 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 vector
void 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 this post


Link to post
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 this post


Link to post
Share on other sites
whoops, missed that
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)

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

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 this post


Link to post
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 this post


Link to post
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.21
vertex * (a * b):
-335.88 -567.46 97.207
vertex2 * (c * d):
247.68 385.29 -471.91


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

Any idea?

Share this post


Link to post
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]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this