Convert from z-up to y-up world

I recently face-palmed when realizing that once again, I had set my own application up with Y as the up axis, but was importing all my assets from 3dsmax where Z is the up axis. I am using the Physx exporter and importing using the PXU library. Rather than re-write large portions of my code, I decided to just iterate through imported matricies and convert them from Z up to Y up.

When you are no matricies expert, this can be tough, there are a lot of people asking how to do this and there isn’t a whole lot out there to help you, which is why I am writing this post.

Physx uses 4×3 matricies for transformation, being a 3×3 rotation matrix with a 1×3 translation vector.

Converting the position is easy, just swap the Y and Z values:

vector3 position;
float temp = position.y;
position.y = position.z;
position.z = temp;

But transforming the rotation vector is a whole lot harder. At first thought you may think “hey, just rotate 90 degrees on the X axis” but no, unfortunately this doesn’t work. The actual solution is just like fixing the position, swap the Y and Z values.

On a 3×3 matrix this is slightly more confusing. What are the Y and Z values? Well, I will assume that you realise a 3×3 matrix is a 3×3 grid of numbers. To swap the Y and Z values, you must swap the second and third columns AND the second and third rows:

matrix33 rotation;
vector3 temp;

temp = rotation.column2;
rotation.column2 = rotation.column3;
rotation.column3 = temp;

temp = rotation.row2;
rotation.row2 = rotation.row3;
rotation.row3 = temp;

On a 4x4 matrix:

/**
 * The method that moves the matrix from Z-up axis to Y-up axis space is as follows:
 * - load the matrix directly from blender (it has the Z-up axis orientation)
 * - switch the second and third rows in the matrix
 * - switch the second and third column in the matrix
 * - multiply the values in the third row by -1
 * - multiply the values in the third column by -1
 * 
 * The result matrix is now in Y-up axis orientation.
 * The procedure was discovered by experimenting but it looks like it's working :)
 * The previous procedure transformet the loaded matrix into component (loc, rot, scale),
 * switched several values and pu the back into the matrix.
 * It worked fine until models with negative scale are used.
 * The current method is not touched by that flaw.
 * 
 * @param store
 *            the matrix where the result will pe placed
 * @return the required matrix
 */
public static Matrix4f getMatrix(Matrix4f store) {
    // first switch the second and third row
    for (int i = 0; i < 4; ++i) {
        float temp = store.get(1, i);
        store.set(1, i, store.get(2, i));
        store.set(2, i, temp);
    }

    // then switch the second and third column
    for (int i = 0; i < 4; ++i) {
        float temp = store.get(i, 1);
        store.set(i, 1, store.get(i, 2));
        store.set(i, 2, temp);
    }

    // multiply the values in the third row by -1
    store.m20 *= -1;
    store.m21 *= -1;
    store.m22 *= -1;
    store.m23 *= -1;

    // multiply the values in the third column by -1
    store.m02 *= -1;
    store.m12 *= -1;
    store.m22 *= -1;
    store.m32 *= -1;

    return store;
}