glm::dmat2 cofactor(const glm::dmat2& A)
{
return {A[1][1], -A[0][1], -A[1][0], A[0][0]};
}
glm::dmat3 cofactor(const glm::dmat3& A)
{
glm::dmat3 B;
B[0][0] = A[1][1]*A[2][2] - A[1][2]*A[2][1]; // +M00
B[0][1] = -(A[1][0]*A[2][2] - A[1][2]*A[2][0]); // -M01
B[0][2] = A[1][0]*A[2][1] - A[1][1]*A[2][0]; // +M02
B[1][0] = -(A[0][1]*A[2][2] - A[0][2]*A[2][1]); // -M10
B[1][1] = A[0][0]*A[2][2] - A[0][2]*A[2][0]; // +M11
B[1][2] = -(A[0][0]*A[2][1] - A[0][1]*A[2][0]); // -M12
B[2][0] = A[0][1]*A[1][2] - A[0][2]*A[1][1]; // +M20
B[2][1] = -(A[0][0]*A[1][2] - A[0][2]*A[1][0]); // -M21
B[2][2] = A[0][0]*A[1][1] - A[0][1]*A[1][0]; // +M22
return B;
}
glm::dmat4 cofactor(const glm::dmat4& A)
{
glm::dmat4 C;
// Cofactor matrix C[i][j] = (-1)^(i+j) * det(minor(i, j))
// glm is column-major, so C[col][row] = C[j][i]
// === Row 0 ===
C[0][0] = glm::determinant(glm::mat3(
A[1][1], A[2][1], A[3][1],
A[1][2], A[2][2], A[3][2],
A[1][3], A[2][3], A[3][3]));
C[1][0] = -glm::determinant(glm::mat3(
A[0][1], A[2][1], A[3][1],
A[0][2], A[2][2], A[3][2],
A[0][3], A[2][3], A[3][3]));
C[2][0] = glm::determinant(glm::mat3(
A[0][1], A[1][1], A[3][1],
A[0][2], A[1][2], A[3][2],
A[0][3], A[1][3], A[3][3]));
C[3][0] = -glm::determinant(glm::mat3(
A[0][1], A[1][1], A[2][1],
A[0][2], A[1][2], A[2][2],
A[0][3], A[1][3], A[2][3]));
// === Row 1 ===
C[0][1] = -glm::determinant(glm::mat3(
A[1][0], A[2][0], A[3][0],
A[1][2], A[2][2], A[3][2],
A[1][3], A[2][3], A[3][3]));
C[1][1] = glm::determinant(glm::mat3(
A[0][0], A[2][0], A[3][0],
A[0][2], A[2][2], A[3][2],
A[0][3], A[2][3], A[3][3]));
C[2][1] = -glm::determinant(glm::mat3(
A[0][0], A[1][0], A[3][0],
A[0][2], A[1][2], A[3][2],
A[0][3], A[1][3], A[3][3]));
C[3][1] = glm::determinant(glm::mat3(
A[0][0], A[1][0], A[2][0],
A[0][2], A[1][2], A[2][2],
A[0][3], A[1][3], A[2][3]));
// === Row 2 ===
C[0][2] = glm::determinant(glm::mat3(
A[1][0], A[2][0], A[3][0],
A[1][1], A[2][1], A[3][1],
A[1][3], A[2][3], A[3][3]));
C[1][2] = -glm::determinant(glm::mat3(
A[0][0], A[2][0], A[3][0],
A[0][1], A[2][1], A[3][1],
A[0][3], A[2][3], A[3][3]));
C[2][2] = glm::determinant(glm::mat3(
A[0][0], A[1][0], A[3][0],
A[0][1], A[1][1], A[3][1],
A[0][3], A[1][3], A[3][3]));
C[3][2] = -glm::determinant(glm::mat3(
A[0][0], A[1][0], A[2][0],
A[0][1], A[1][1], A[2][1],
A[0][3], A[1][3], A[2][3]));
// === Row 3 ===
C[0][3] = -glm::determinant(glm::mat3(
A[1][0], A[2][0], A[3][0],
A[1][1], A[2][1], A[3][1],
A[1][2], A[2][2], A[3][2]));
C[1][3] = glm::determinant(glm::mat3(
A[0][0], A[2][0], A[3][0],
A[0][1], A[2][1], A[3][1],
A[0][2], A[2][2], A[3][2]));
C[2][3] = -glm::determinant(glm::mat3(
A[0][0], A[1][0], A[3][0],
A[0][1], A[1][1], A[3][1],
A[0][2], A[1][2], A[3][2]));
C[3][3] = glm::determinant(glm::mat3(
A[0][0], A[1][0], A[2][0],
A[0][1], A[1][1], A[2][1],
A[0][2], A[1][2], A[2][2]));
return C;
}
On a separate note, for convenience the following adjoint functions could be added:
glm::dmat2 adjoint(const glm::dmat2& m) {return glm::transpose(cofactor(m));}
glm::dmat3 adjoint(const glm::dmat3& m) {return glm::transpose(cofactor(m));}
glm::dmat4 adjoint(const glm::dmat4& m) {return glm::transpose(cofactor(m));}
//or perhaps more generally:
template<typename Matrix>
Matrix adjoint(const Matrix& m){return glm::transpose(cofactor(m));};
glm::determinant(glm::mat)is very useful...However, it is a product of the cofactor matrix, which computes sub-dimensional determinants, eventually outputting a single scalar. (There are physical and photonic methods that require tensor intermediates).
Striving for
modularity:The cofactor matrix should be the basis of computing determinants, so that developers don't have to roll their own and the machinery used to compute cofactors can be optimized and reused for determinants.
Below is an unrolled implementation of 2d, 3d, 4d cofactors:
On a separate note, for convenience the following adjoint functions could be added: