Rotation matrix for a model where the forward axis is not +X

I'm trying to convert a normalized unit vector, representing an entity heading, into a rotation matrix for rendering.

The problem is that I'm using an (apparently) unusual forward vector for my entities. I use right-hand axes, with +Z pointing up and +Y pointing forward. (Rationale: My game has units moving across a map, and it's easier for me to deal with the map as X/Y positions with a +Z height)

Whenever I try to look up the math for building the rotation matrix, the examples always assume my forward vector is +X, which gives the wrong result. The math assumes my entity is facing +X, and so my entities look like they're moving sideways.

I've also tried a (temporary) 2D approach using atan2, but again it assumes angle 0 (forward) is +X, with the same result.

How does the math for a "look rotation" matrix work for an arbitrary axis? Or, if it's easier, are there examples of building this sort of matrix using a right-hand +Y axis?

You still handle it in the usual way. Make 3 perpendicular unit vectors (an orthonormal basis), expressing the directions your object's local axes should point after rotation:

// Forward

// Right
X = Normalize(Cross(Y, (0, 0, 1));

// Up
Z = Cross(X,Y);


And then use these three vectors as the columns of a matrix:

$$\vec {\text{rotated}} = \begin{bmatrix}X_x & Y_x & Z_x\\X_y & Y_y & Z_y\\X_z & Y_z & Z_z\\\end{bmatrix} \vec{\text{unrotated}}$$

Or, if you use a multiplication convention with the matrix on the right, use these vectors as the rows of your matrix instead of the columns.

You can verify that if you substitute any of the standard basis vectors (1, 0, 0), (0, 1, 0), or (0, 0, 1) for that $$\\vec{\text{unrotated}}\$$ variable, you get out your corresponding rotated basis vector, X, Y, or Z. That means that any linear combination of those standard basis vectors (ie. any point in 3D space) gets transformed to the matching linear combination of your rotated basis vectors (ie. it gets rotated to match the object's desired orientation).

You can use atan2 for this too - even though we conventionally label the arguments y, x, that does not restrict you to using those named axes in those slots. You can swizzle your coordinates any way you like to get the 0 angle and direction of positive rotation to match your intended scheme. So for example atan2(-forward.x, forward.y) will give you a yaw angle of 0° when pointing toward y+, 90° when pointing toward x-, and -90° when pointing toward x+, consistent with right-handed rotation about z+.

• Wonderfully thorough answer, just what I was looking for. Thanks! Commented Sep 13, 2021 at 1:52