diff options
Diffstat (limited to 'src/code/su_mtx.c')
| -rw-r--r-- | src/code/su_mtx.c | 361 |
1 files changed, 361 insertions, 0 deletions
diff --git a/src/code/su_mtx.c b/src/code/su_mtx.c new file mode 100644 index 000000000..09cbda90e --- /dev/null +++ b/src/code/su_mtx.c @@ -0,0 +1,361 @@ +/** + * @file su_mtx.c + * @brief "Fast" functions for constructing RSP-compatible matrices directly + * + * The three functions in this file construct scaling, rotation, and translation matrices, and combinations thereof. The + * intention appears to be to exploit the peculiar structure of the RSP's fixed-point matrix format, which we recall is + * + * typedef long int Mtx_t[4][4]; + * typedef union { + * Mtx_t m; + * struct { + * u16 intPart[4][4]; + * u16 fracPart[4][4]; + * }; + * long long int forc_structure_alignment; + * } Mtx; // size = 0x40 + * + * This means it can be written to as either words or two sets of shorts; the latter is the correct format for + * interpreting it the way the RSP does, but the first is likely faster to access and write to. The functions save + * writing halves by + * + * - writing a word 0 if two adjacent elements are 0 in a convenient place, or 1 if they are 0 and 1, + * - writing a word using a shift and overwriting the dirty half afterwards. + * + * Examples of both of these are seen in Mtx_SetTranslateScaleMtx(); we explain there in full detail due to the + * function's relative simplicity. The other two functions we merely point out the interesting parts rather than + * explaining the whole process. + * + * We use the following notation throughout: round brackets denote the word-sized elements, square the pairs they cover. + * The integer and fractional parts of a fixed-point number are denoted by i and f respectively, and these letters are + * also used to represent which half each of the pairs belongs to. + * + * (00) [i00,i01], (01) [i02,i03] + * (02) [i10,i11], (03) [i12,i13] + * (10) [i20,i21], (11) [i22,i23] + * (12) [i30,i31], (13) [i32,i33] + * (20) [f00,f01], (21) [f02,f03] + * (22) [f10,f11], (23) [f12,f13] + * (30) [f20,f21], (31) [f22,f23] + * (32) [f30,f31], (33) [f32,f33] + * + * We keep the RSP's column-major convention; this file is confusing enough without attempting to transpose everything. + * Bear in mind therefore that we are acting on *row vectors* on the *right*. + * + * @warning The behaviour of the output of the functions in this file is undefined in C89, since both members of the + * union are used to set the mtxs. (C99+ allow type-punning, but the behaviour is still implementation-defined because + * it relies on the storage being big-endian.) + * + * @remark Name inferred from shared Animal Forest functions, meaning of "su" is unclear. + */ + +#include "global.h" + +/** + * Constructs a matrix \$f ST \$f, i.e. a scaling \$f S \$f followed by a translation \$f T \$f. + * + * The final result is + * + * \f[ + * \begin{bmatrix} + * s_x & 0 & 0 & 0 \\ + * 0 & s_y & 0 & 0 \\ + * 0 & 0 & s_z & 0 \\ + * t_x & t_y & t_z & 1 + * \end{bmatrix} + * \f] + * + * @param[out] mtx Fixed-point matrix pointer to output to + * @param[in] scaleX Scale in the X direction + * @param[in] scaleY Scale in the Y direction + * @param[in] scaleZ Scale in the Z direction + * @param[in] translateX X component of translation + * @param[in] translateY Y component of translation + * @param[in] translateZ Z component of translation + * + * @remark Original name: "suMtxMakeTS" + */ +void Mtx_SetTranslateScaleMtx(Mtx* mtx, f32 scaleX, f32 scaleY, f32 scaleZ, f32 translateX, f32 translateY, + f32 translateZ) { + Mtx* m = mtx; + s32 fixedPoint; + + m->m[0][1] = 0; // [i02, i03] == [0, 0] + m->m[2][1] = 0; // [f02, f03] == [0, 0] + m->m[0][3] = 0; // [i12, i13] == [0, 0] + m->m[2][3] = 0; // [f22, f23] == [0, 0] + m->m[1][0] = 0; // [i20, i21] == [0, 0] + // + fixedPoint = scaleX * 0x10000; // + m->m[0][0] = fixedPoint; // [i00, i01] == [scaleX.i, scaleX.f]; i01 is now "dirty" + m->intPart[0][1] = 0; // Clean i01 by zeroing it: [i00, i01] == [scaleX.i, 0] + m->m[2][0] = (u32)fixedPoint << 16; // [f00, f01] == [scaleX.f, 0] + // + fixedPoint = scaleY * 0x10000; // + m->m[0][2] = (u32)fixedPoint >> 16; // [i10, i11] == [0, scaleY.i] + m->m[2][2] = fixedPoint & 0xFFFF; // [f10, f11] == [0, scaleY.f] + // + fixedPoint = scaleZ * 0x10000; // + m->m[1][1] = fixedPoint; // [i22, i23] == [scaleZ.i, scaleZ.f] + m->intPart[2][3] = 0; // [i22, i23] == [scaleZ.i, 0] + m->m[3][1] = (u32)fixedPoint << 16; // [f22, f23] == [scaleZ.f, 0] + // + m->m[3][0] = 0; // [f20, f21] == [0, 0] + // + fixedPoint = translateX * 0x10000; // + m->intPart[3][0] = ((u32)fixedPoint >> 16) & 0xFFFF; // [i30, i31] == [translateX.i, ?] + m->fracPart[3][0] = fixedPoint & 0xFFFF; // [f30, f31] == [translateX.f, ?] + // + fixedPoint = translateY * 0x10000; // + m->intPart[3][1] = ((u32)fixedPoint >> 16) & 0xFFFF; // [i30, i31] == [translateX.i, translateY.i] + m->fracPart[3][1] = fixedPoint & 0xFFFF; // [f30, f31] == [translateX.f, translateY.f] + // + fixedPoint = translateZ * 0x10000; // + m->intPart[3][2] = ((u32)fixedPoint >> 16) & 0xFFFF; // [i30, i31] == [translateZ.i, ?] + m->intPart[3][3] = 1; // [i32, i33] == [translateZ.i, 1] + m->m[3][3] = (u32)fixedPoint << 16; // [f32, f33] == [translateZ.f, 0] + + // So we end up with + // [scaleX.i, 0], [0, 0], + // [0, scaleY.i], [0, 0], + // [0, 0], [scaleZ.i, 0], + // [translateX.i, translateY.i], [translateZ.i, 1] + // + // [scaleX.f, 0], [0, 0], + // [0, scaleY.f], [0, 0], + // [0, 0], [scaleZ.f, 0] + // [translateX.f, translateY.f], [translateZ.f, 0] +} + +// Unused +/** + * Should create an axis-angle rotation matrix. + * + * @note The axis vector is expected to be normalised. + * + * The result should be: + * + * \f[ + * \begin{bmatrix} + * (1-a_x^2) \cos \theta + a_x^2 & a_x a_y (1 - \cos \theta) + a_z \sin \theta & a_z a_x (1 - \cos \theta) - a_y + * \sin \theta & 0 \\ + * a_x a_y (1 - \cos \theta) - a_z \sin \theta & (1-a_y^2) \cos \theta + a_y^2 & a_y a_z (1 - \cos \theta) + a_x + * \sin \theta & 0 \\ + * a_z a_x (1 - \cos \theta) + a_y \sin \theta & a_y a_z (1 - \cos \theta) - a_x \sin \theta & (1-a_z^2) \cos + * \theta + * + a_z^2 & 0 \\ + * t_x & t_y & t_z & 1 + * \end{bmatrix} + * \f] + * + * @warning There is a significant bug in this function, which means it does not actually produce this correct rotation + * matrix; see inline for details. + * + * @param[out] mtx Fixed-point matrix pointer to output to + * @param[in] angle angle to rotate about axis + * @param[in] axisX X component of axis to rotate about + * @param[in] axisY Y component of axis to rotate about + * @param[in] axisZ Z component of axis to rotate about + * + * @remark Original name: probably something like "suMtxMakeR" or "suMtxMakeRotateVector" + */ +void Mtx_SetRotationMtx(Mtx* mtx, s32 angle, f32 axisX, f32 axisY, f32 axisZ) { + //! FAKE? The somewhat peculiar distribution of temps in this function seems necessary to match? + f32 tempX; + f32 tempZ; + f32 tempY; + f32 sin = Math_SinS(angle); + f32 cos = Math_CosS(angle); + f32 tempXX; + f32 tempYY; + f32 tempZZ; + + s32 fixedPoint; + + mtx->m[1][2] = 0; // [i30, i31] == [0, 0] + mtx->m[1][3] = 1; // [i32, i33] == [0, 1] + mtx->m[3][2] = 0; // [f30, f31] == [0, 0] + mtx->m[3][3] = 0; // [f31, f32] == [0, 0] + + tempXX = axisX * axisX; + tempX = (axisY * axisZ) * (1.0f - cos); + + // i00 and f00 + fixedPoint = (((1.0f - tempXX) * cos) + tempXX) * 0x10000; + mtx->intPart[0][0] = ((u32)fixedPoint >> 0x10) & 0xFFFF; + mtx->fracPart[0][0] = fixedPoint & 0xFFFF; + + tempZ = ((axisX * axisY) * (1.0f - cos)); + tempZZ = axisZ * axisZ; + + // i21 and f21 + fixedPoint = (tempX - (axisX * sin)) * 0x10000; + mtx->intPart[2][1] = ((u32)fixedPoint >> 0x10) & 0xFFFF; + mtx->fracPart[2][1] = fixedPoint & 0xFFFF; + + tempY = (axisZ * axisX) * (1.0f - cos); + tempYY = axisY * axisY; + + // [i12, i13] and [f12, f13] + fixedPoint = (tempX + (axisX * sin)) * 0x10000; + mtx->m[0][3] = fixedPoint; // i13 dirty + mtx->intPart[1][3] = 0; // clean i13 + mtx->m[2][3] = fixedPoint << 0x10; + + // i11 and f11 + fixedPoint = ((((1.0f - tempYY) * cos) + tempYY) * 0x10000); + mtx->intPart[1][1] = ((u32)fixedPoint >> 0x10) & 0xFFFF; + mtx->fracPart[1][1] = fixedPoint & 0xFFFF; + + //! @bug The sign of the `axisY * sin` term is wrong on the next two elements (i/f20 and i/f02), which causes the + //! matrix to not be a rotation matrix (and indeed not even necessarily invertible). + // i20 and f20 + fixedPoint = (tempY - (axisY * sin)) * 0x10000; + mtx->intPart[2][0] = ((u32)fixedPoint >> 0x10) & 0xFFFF; + mtx->fracPart[2][0] = fixedPoint & 0xFFFF; + + // [i02, i03] and [f02, f03] + fixedPoint = (tempY + (axisY * sin)) * 0x10000; + mtx->m[0][1] = fixedPoint; // i03 dirty + mtx->intPart[0][3] = 0; // clean i03 + mtx->m[2][1] = fixedPoint << 0x10; + + // [i22, i23] and [f22, f23] + fixedPoint = ((((1.0f - tempZZ) * cos) + tempZZ) * 0x10000); + mtx->m[1][1] = fixedPoint; // i23 dirty + mtx->intPart[2][3] = 0; // clean i23 + mtx->m[3][1] = fixedPoint << 0x10; + + // i10 and f10 + fixedPoint = (tempZ - axisZ * sin) * 0x10000; + mtx->intPart[1][0] = ((u32)fixedPoint >> 0x10) & 0xFFFF; + mtx->fracPart[1][0] = fixedPoint & 0xFFFF; + + // i01 and f01 + fixedPoint = (tempZ + axisZ * sin) * 0x10000; + mtx->intPart[0][1] = ((u32)fixedPoint >> 0x10) & 0xFFFF; + mtx->fracPart[0][1] = fixedPoint & 0xFFFF; +} + +/** + * Creates a general scale, rotation, translation matrix. + * + * @note The axis vector is expected to be normalised. + * + * The transformations are applied in the order scale, rotate, translate: \$f SRT \$f + * + * The result should be + * \f[ + * \begin{bmatrix} + * s_x((1-a_x^2) \cos \theta + a_x^2) & s_x(a_x a_y (1 - \cos \theta) + a_z \sin \theta) & s_x(a_z a_x (1 - \cos + * \theta) - a_y \sin \theta & 0) \\ + * s_y(a_x a_y (1 - \cos \theta) - a_z \sin \theta) & s_y((1-a_y^2) \cos \theta + a_y^2) & s_z(a_y a_z (1 - \cos + * \theta) + a_x \sin \theta) & 0 \\ + * s_z(a_z a_x (1 - \cos \theta) + a_y \sin \theta) & s_z(a_y a_z (1 - \cos \theta) - a_x \sin \theta) & + * s_z((1-a_z^2) \cos \theta + a_z^2) & 0 \\ + * 0 & 0 & 0 & 1 + * \end{bmatrix} . + * \f] + * + * @warning There is a significant bug in this function, which means it does not actually produce a proper rotation + * matrix; see inline for details. + * + * @param[out] mtx Fixed-point matrix pointer to output to + * @param[in] scaleX Scale in the X direction + * @param[in] scaleY Scale in the Y direction + * @param[in] scaleZ Scale in the Z direction + * @param[in] angle angle to rotate about axis + * @param[in] axisX X component of axis to rotate about + * @param[in] axisY Y component of axis to rotate about + * @param[in] axisZ Z component of axis to rotate about + * @param[in] translateX X component of translation + * @param[in] translateY Y component of translation + * @param[in] translateZ Z component of translation + * + * @remark Original name: probably something like "suMtxMakeSRT", although Animal Forest function is a Tait-Bryan + * rotation rather than axis-angle. + */ +void Mtx_SetTranslationRotationScaleMtx(Mtx* mtx, f32 scaleX, f32 scaleY, f32 scaleZ, s32 angle, f32 axisX, f32 axisY, + f32 axisZ, f32 translateX, f32 translateY, f32 translateZ) { + f32 tempX; + f32 tempY; + f32 tempZ; + f32 sin = Math_SinS(angle); + f32 cos = Math_CosS(angle); + f32 tempXX = axisX * axisX; + f32 tempYY = axisY * axisY; + f32 tempZZ = axisZ * axisZ; + s32 fixedPoint; + + tempX = axisY * axisZ * (1.0f - cos); + + // i00 and f00 + fixedPoint = (tempXX + (1.0f - tempXX) * cos) * scaleX * 0x10000; + mtx->intPart[0][0] = ((u32)fixedPoint >> 0x10) & 0xFFFF; + mtx->fracPart[0][0] = fixedPoint & 0xFFFF; + + tempY = axisZ * axisX * (1.0f - cos); + + // i21 and f21 + fixedPoint = (tempX - axisX * sin) * scaleZ * 0x10000; + mtx->intPart[2][1] = ((u32)fixedPoint >> 0x10) & 0xFFFF; + mtx->fracPart[2][1] = fixedPoint & 0xFFFF; + + tempZ = axisX * axisY * (1.0f - cos); + + // [i12, i13] and [f12, f13] + fixedPoint = (tempX + axisX * sin) * scaleY * 0x10000; + mtx->m[0][3] = fixedPoint; // i13 dirty + mtx->intPart[1][3] = 0; // clean i13 + mtx->m[2][3] = fixedPoint << 0x10; + + // i11 and f11 + fixedPoint = (tempYY + (1.0f - tempYY) * cos) * scaleY * 0x10000; + mtx->intPart[1][1] = ((u32)fixedPoint >> 0x10) & 0xFFFF; + mtx->fracPart[1][1] = fixedPoint & 0xFFFF; + + //! @bug The sign of the `axisY * sin` term is wrong on the next two elements (i/f20 and i/f02), which causes the + //! matrix to not be a rotation matrix (and indeed not even necessarily invertible). + // i20, f20 + fixedPoint = (tempY - axisY * sin) * scaleZ * 0x10000; + mtx->intPart[2][0] = ((u32)fixedPoint >> 0x10) & 0xFFFF; + mtx->fracPart[2][0] = fixedPoint & 0xFFFF; + + // [i02, i03] and [f02, f03] + fixedPoint = (tempY + axisY * sin) * scaleX * 0x10000; + mtx->m[0][1] = fixedPoint; // [i03, i03], i03 dirty + mtx->intPart[0][3] = 0; // clean i03 + mtx->m[2][1] = fixedPoint << 0x10; // [f02, f03] + + // [i22, i23] and [f22, f23] + fixedPoint = (tempZZ + (1.0f - tempZZ) * cos) * scaleZ * 0x10000; + mtx->m[1][1] = fixedPoint; + mtx->intPart[2][3] = 0; + mtx->m[3][1] = fixedPoint << 0x10; + + // i10 and f10 + fixedPoint = (tempZ - axisZ * sin) * scaleY * 0x10000; + mtx->intPart[1][0] = ((u32)fixedPoint >> 0x10) & 0xFFFF; + mtx->fracPart[1][0] = fixedPoint & 0xFFFF; + + // i01 and f01 + fixedPoint = (tempZ + axisZ * sin) * scaleX * 0x10000; + mtx->intPart[0][1] = ((u32)fixedPoint >> 0x10) & 0xFFFF; + mtx->fracPart[0][1] = fixedPoint & 0xFFFF; + + // [i30, i31] and f30 + fixedPoint = translateX * 0x10000; + mtx->m[1][2] = fixedPoint; // [i30, i31], i31 dirty + mtx->fracPart[3][0] = fixedPoint & 0xFFFF; // f30 + + // i31 and f31 + fixedPoint = translateY * 0x10000; + mtx->intPart[3][1] = ((u32)fixedPoint >> 0x10) & 0xFFFF; // overwrite i31 + mtx->fracPart[3][1] = fixedPoint & 0xFFFF; // overwrite f31 + + // [i32, i33] and [f32, f33] + fixedPoint = translateZ * 0x10000; + mtx->m[1][3] = fixedPoint; // [i32, i33] + mtx->intPart[3][3] = 1; // clean i33 + mtx->m[3][3] = (fixedPoint << 0x10); // [f32, f33] +} |
