PSXDEV : Billboard system
Trying to devise a billboard system for this psx 3d engine I've been working on, I first used a trigonometric approach that used atan2, then found out that it was simpler and cheaper to use a inverse rotation matrix.
Trigonometry
So this was my first idea :
// Sprite system WIP
// Find angle between billboard object and camera object using atan2
objAngleToCam.vy = patan( posToCam.vx,posToCam.vz );
objAngleToCam.vx = patan( posToCam.vx,posToCam.vy );
// Apply rotation to billboard
curLvl.meshPlan->rot.vy = -( (objAngleToCam.vy >> 4) + 1024 ) ;
// Update billboard's relative position to camera object
posToCam.vx = -camera.pos.vx - curLvl.meshPlan->pos.vx ;
posToCam.vz = -camera.pos.vz - curLvl.meshPlan->pos.vz ;
posToCam.vy = -camera.pos.vy - curLvl.meshPlan->pos.vy ;
So here we're using patan()
, which is a function that uses a pre-calculated atan2 table for speed.
As is, this solution works, but only for the Y axis (PSX uses a Y-down coord system).
Trying to align it on the X and Z axis result in gimbal lock, which is not good looking.
Inverse rotation matrix
Fast forward a few weeks, and I have this realization that somewhere in my brain is a knowledge I can use to solve this another way !
I learned from my good psxdev pal @Nicolas Noble that finding an inverse rotation matrix is quite trivial if you already have a rotation matrix handy :
Here is the inverse matrix formula :
In plain english, that's : The inverse matrix is the transpose of the matrix's co-factors divided by the determinant.
We know that the determinant of a rotation matrix is 1 :
Rotation matrices are square matrices, with real entries. More specifically, they can be characterized as orthogonal matrices with determinant 1;
https://en.wikipedia.org/wiki/Rotation_matrix
thus :
The inverse of a rotation matrix is the adjugate of this matrix.
Adjugate of a rotation matrix
The adjoint or adjugate of a matrix is the "transpose of the cofactor matrix".
Let's try with a rotation matrix. We know that the rotation matrix on the X axis is
Let's isolate the rotation part in a 2x2 matrix :
According to this document, finding the adjugate means swapping the entries without the signs diagonally, then applying the sign matrix :
In this case, on the diagonals, we switch with ... , and with ... .
Nothing changed so far.
But applying the sign matrix changes the signs of the and we end up with :
Transpose of a rotation matrix
The transpose of a matrix is a new matrix whose rows are the columns of the original. [https://www.quora.com/What-is-the-geometric-interpretation-of-the-transpose-of-a-matrix]
Remember the rotation part of our rotation matrix on X ?
Let's try to find the transpose of that.
The first column of that matrix is and the second column is .
Let's make our first row : .
Now let's make our second row : .
The result is ... ! Looks familiar ?
So we can see that if :
hence :
The inverse of a rotation matrix is the transpose of this matrix.
Code
So we know that the adjugate of a rotation matrix is the same as its transpose...
We do have a TransposeMatrix()
function available in PsyQ, so let's use that !
// Find inverse rotation matrix so that the billboard object always faces camera
// working matrices
MATRIX curRot, invRot;
// Get current rotation matrix
ReadRotMatrix(&curRot);
// Get transpose of current rotation matrix and store it in invRot
TransposeMatrix(&curRot, &invRot);
// Multiply current rotation matrix by the inverse matrix
SetMulRotMatrix(&invRot);
Sources
https://discord.com/channels/642647820683444236/663664210525290507/826109895874838551
https://en.wikipedia.org/wiki/Rotation_matrix
https://en.wikipedia.org/wiki/Adjugate_matrix
http://www.macs.hw.ac.uk/~markl/teaching/Inverses.pdf
https://www.quora.com/What-is-the-geometric-interpretation-of-the-transpose-of-a-matrix