Changing a matrix from right-handed to left-handed coordinate system

I would like to change a 4x4 matrix from a right handed system where:
x is left and right, y is front and back and z is up and down

to a left-handed system where:
x is left and right, z is front and back and y is up and down.

For a vector it's easy, just swap the y and z values, but how do you do it for a matrix?


Solution 1:

Let me try to explain it a little better. I need to export a model from Blender, in which the z axis faces up, into OpenGL, where the y axis faces up.

For every coordinate (x, y, z) it's simple; just swap the y and z values: (x, z, y).
Because I have swapped the all the y and z values, any matrix that I use also needs to be flipped so that it has the same effect.

After a lot of searching I've eventually found a solution at gamedev:

If your matrix looks like this:

{ rx, ry, rz, 0 }  
{ ux, uy, uz, 0 }  
{ lx, ly, lz, 0 }  
{ px, py, pz, 1 }

To change it from left to right or right to left, flip it like this:

{ rx, rz, ry, 0 }  
{ lx, lz, ly, 0 }  
{ ux, uz, uy, 0 }  
{ px, pz, py, 1 }

Solution 2:

I think I understand your problem because I am currently facing a similar one.

  • You start with a world matrix which transforms a vector in a space where Z is up (e.g. a world matrix).

  • Now you have a space where Y is up and you want to know what to do with your old matrix.

Try this:

There is a given world matrix

Matrix world = ...  //space where Z is up

This Matrix changes the Y and Z components of a Vector

Matrix mToggle_YZ = new Matrix(
{1, 0, 0, 0}
{0, 0, 1, 0}
{0, 1, 0, 0}
{0, 0, 0, 1})

You are searching for this:

//same world transformation in a space where Y is up
Matrix world2 = mToggle_YZ * world * mToggle_YZ;

The result is the same matrix cmann posted below. But I think this is more understandable as it combines the following calculation:

1) Switch Y and Z

2) Do the old transformation

3) Switch back Z and Y

Solution 3:

It is often the case that you want to change a matrix from one set of forward/right/up conventions to another set of forward/right/up conventions. For example, ROS uses z-up, and Unreal uses y-up. The process works whether or not you need to do a handedness-flip.

Note that the phrase "switch from right-handed to left-handed" is ambiguous. There are many left-handed forward/right/up conventions. For example: forward=z, right=x, up=y; and forward=x, right=y, up=z. You should really think of it as "how do I convert ROS' notion of forward/right/up to Unreal's notion of forward/right/up".

So, it's a straightforward job to create a matrix that converts between conventions. Let's assume we've done that and we now have

mat4x4 unrealFromRos = /* construct this by hand */;
mat4x4 rosFromUnreal = unrealFromRos.inverse();

Let's say the OP has a matrix that comes from ROS, and she wants to use it in Unreal. Her original matrix takes a ROS-style vector, does some stuff to it, and emits a ROS-style vector. She needs a matrix that takes an Unreal-style vector, does the same stuff, and emits an Unreal-style vector. That looks like this:

mat4x4 turnLeft10Degrees_ROS = ...;
mat4x4 turnLeft10Degrees_Unreal = unrealFromRos * turnLeft10Degrees_ROS * rosFromUnreal;

It should be pretty clear why this works. You take a Unreal vector, convert it to ROS-style, and now you can use the ROS-style matrix on it. That gives you a ROS vector, which you convert back to Unreal style.

Gerrit's answer is not quite fully general, because in the general case, rosFromUnreal != unrealFromRos. It's true if you're just inverting a single axis, but not true if you're doing something like converting X->Y, Y->Z, Z->X. I've found that it's less error-prone to always use a matrix and its inverse to do these convention switches, rather than to try to write special functions that flip just the right members.

This kind of matrix operation inverse(M) * X * M comes up a lot. You can think of it as a "change of basis" operation; to learn more about it, see https://en.wikipedia.org/wiki/Matrix_similarity.

Solution 4:

I have been working on converting the Unity SteamVR_Utils.RigidTransform to ROS geometry_msgs/Pose and needed to convert Unity left handed coordinate system to the ROS right handed coordinate system.

This was the code I ended up writing to convert coordinate systems.

var device = SteamVR_Controller.Input(index);
// Modify the unity controller to be in the same coordinate system as ROS.
Vector3 ros_position = new Vector3(
    device.transform.pos.z,
    -1 * device.transform.pos.x,
    device.transform.pos.y);
Quaternion ros_orientation = new Quaternion(
    -1 * device.transform.rot.z,
    device.transform.rot.x,
    -1 * device.transform.rot.y,
    device.transform.rot.w);

Originally I tried using the matrix example from @bleater, but I couldn't seem to get it to work. Would love to know if I made a mistake somewhere.

HmdMatrix44_t m = device.transform.ToHmdMatrix44();
HmdMatrix44_t m2 = new HmdMatrix44_t();
m2.m = new float[16];
// left -> right
m2.m[0] = m.m[0]; m2.m[1] = m.m[2]; m2.m[2] = m.m[1]; m2.m[3] = m.m[3];
m2.m[4] = m.m[8]; m2.m[5] = m.m[10]; m2.m[6] = m.m[9]; m2.m[7] = m.m[7];
m2.m[8] = m.m[4]; m2.m[9] = m.m[6]; m2.m[10] = m.m[5]; m2.m[11] = m.m[11];
m2.m[12] = m.m[12]; m2.m[13] = m.m[14]; m2.m[14] = m.m[13]; m2.m[15] = m.m[15];

SteamVR_Utils.RigidTransform rt = new SteamVR_Utils.RigidTransform(m2);

Vector3 ros_position = new Vector3(
    rt.pos.x,
    rt.pos.y,
    rt.pos.z);
Quaternion ros_orientation = new Quaternion(
    rt.rot.x,
    rt.rot.y,
    rt.rot.z,
    rt.rot.w);