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);