Affine Decomposition
All that's left to do is to implement the actual AffineDecompose function. This page is mostly just a code-dump of how to put everything together. Let's start with the structures and function signatures of what we have already written. The only decomposition method that isn't needed here is QRDecompositon, because it is only used as a helper function for SpectralDecompositon.
struct FactorTranslationResult {
Matrix T; // Translation
Matrix X; // Linear transformation
}
struct PolarDecompResult {
Matrix Q; // Q is a rotation, or the negative of a rotation
Matrix S; // Scale and skew matrix
}
struct FactorRotationResult {
Matrix F; // Positive or negative identity (flip if negative)
Matrix R; // Rotation matrix
}
struct SpectralDecompositionResult {
Matrix U; // Each basis vector is an eigenvector
Matrix K; // Contains eigenvalues on main diagonal
Matrix Ut; // Transpose of U
}
struct SpectralAdjustmentResult {
Matrix U; // Each basis vector is an eigenvector
Matrix K; // Contains eigenvalues on main diagonal
Matrix Ut; // Transpose of U
}
FactorTranslationResult FactorTranslation(Matrix M);
PolarDecompResult PolarDecomposition(Matrix X);
FactorRotationResult FactorRotation(Matrix Q);
SpectralDecompositionResult SpectralDecomposition(Matrix S);
SpectralAdjustmentResult SpectralDecompositonAdjustment(SpectralDecompositionResult input)
Having already written all of the above functions, implementing AffineDecomposition becomes a matter of calling the helper functions in the appropriate order, like so:
struct AffineDecompositionResult {
Matrix T; // Holds translation of the matrix
Matrix F; // Flip data (positive or negative identity)
Matrix R; // Holds rotation of the matrix
Matrix U; // Holds eigenvectors of the matrix
Matrix K; // Holds eigenvalues (scale) of the matrix
Matrix Ut;// Transpose of U
}
AffineDecompositionResult AffineDecomposition(Matrix M) {
FactorTranslationResult factorTranslation = FactorTranslation(M);
PolarDecompResult polarDecomposition = PolarDecomposition(factorTranslation.X);
FactorRotationResult factorRotation = FactorRotation(polarDecomposition.Q);
SpectralDecompositionResult spectralDecomp = SpectralDecomposition(factorRotation.R);
SpectralAdjustmentResult spectralAdjustment = SpectralDecompositonAdjustment(spectralDecomp);
AffineDecompositionResult result;
result.T = factorTranslation.T;
result.F = factorRotation.F;
result.R = factorRotation.R;
result.U = spectralAdjustment.U;
result.K = spectralAdjustment.K;
result.Ut = spectralAdjustment.Ut;
return result;
}
We might want to re-format the output to match shoemake's reference code a bit more. For the most part this is just a matter of converting some matrices to quaternions, like so:
struct ShoemakeResult {
Vector3 T; // Translation
float F; // Sign of determinant
Quaternion R; // Rotation (q in shoemake's code)
Quaternion U; //Stretch matrix
Vector3 K; // Scale info
}
ShoemakeResult ConvertResultToShoemakeFormat(AffineDecompositionResult affine) {
ShoemakeResult result;
result.T = Vector3(affine.T[12], affine.T[13], affine.T[14]);
result.F = affine.F[0];
result.R = ToQuaternion(affine.R);
result.U = ToQuaternion(affine.U);
result.K = Vector3(affine.K[0], affine.K[5], affine.K[10]);
return result;
}