Shoemake's Code

The code presented in the last section works, but it's mostly a black box. Lets explore what the decomp_affine function does on a high level. We only ever use the t, q and k fields of the AffineParts structure. Let's take a look at the source of decomp_affine and see how these componetns are filled in.

void decomp_affine(HMatrix A, AffineParts *parts) {
    HMatrix Q, S, U;
    Quat p;
    float det;
    parts->t = Qt_(A[X][W], A[Y][W], A[Z][W], 0);
    det = polar_decomp(A, Q, S);
    if (det<0.f) {
        mat_copy(Q,=,-Q,3);
        parts->f = -1;
    } 
    else {
        parts->f = 1;
    }
    parts->q = Qt_FromMatrix(Q);
    parts->k = spect_decomp(S, U);
    parts->u = Qt_FromMatrix(U);
    p = snuggle(parts->u, &parts->k);
    parts->u = Qt_Mul(parts->u, p);
}

The t field is super simple, it just copies the fields of the matrix that hold the translation data.

The q field is obtained by calling the polar_decomp function, which performs a polar decomposition. This function takes a Matrix M as it's input argument and fills out two output arguments Q and S, the rotation (with potential flip data) and scale (with potential skew data) matrices respectivley. Finally, the function returns the determinent of the rotation matrix.

At this point, the rotation matrix Q might actually be a flip. If the determinant was less than 0, Q contains flip information and needs to be negated. Q now holds the final rotation matrix, it can be converted to a quaternion and stored in q.

The S matrix contains both scaling and skew data. This skew data is removed using the spect_decomp function, which breaks the matrix down into eigen vectors and eigen values. This function takes the S matrix containing scale and skew data as it's input argument and fills out a single output argument, U which is the "stretch" matrix. The function returns a Vector which holds the scale data (eigen values)for the transform on each axis.

The resulting k and u values are non-unique, there are 24 potential values for these. The snuggle function modifies the value of k and returns a quaternion that needs to multiply u in order to make sure these values are correct when interpolated. We don't actually care about the u component. After k is corrected, we have everything we need.