gingerBill/gb

Mat4 inverse

Closed this issue · 2 comments

jimon commented

Would be nice to have mat inverse functions, they are quite useful for projecting screen coordinates back to world/model space. In most cases just having mat4 affine inverse will suffice.

jimon commented

Hm, I made a prototype for generic 4x4 inverse:

bool gb_mat4_inverse(gbMat4 * out, gbMat4 * in)
{
    #define _M(__i, __j) in->col[__j].e[__i]
    #define _MO(__i, __j) out->col[__j].e[__i]
    #define _M3(a0, a1, a2, a3, a4, a5, a6, a7, a8) \
                ( a0 * (a4 * a8 - a5 * a7) \
                - a1 * (a3 * a8 - a5 * a6) \
                + a2 * (a3 * a7 - a4 * a6) ) * d

    float ka = _M(2, 2) * _M(3, 3) - _M(2, 3) * _M(3, 2);
    float kb = _M(2, 1) * _M(3, 3) - _M(2, 3) * _M(3, 1);
    float kc = _M(2, 1) * _M(3, 2) - _M(2, 2) * _M(3, 1);
    float kd = _M(2, 0) * _M(3, 3) - _M(2, 3) * _M(3, 0);
    float ke = _M(2, 0) * _M(3, 2) - _M(2, 2) * _M(3, 0);
    float kf = _M(2, 0) * _M(3, 1) - _M(2, 1) * _M(3, 0);

    float d = _M(0, 0) * (ka * _M(1, 1) - kb * _M(1, 2) + kc * _M(1, 3))
            - _M(0, 1) * (ka * _M(1, 0) - kd * _M(1, 2) + ke * _M(1, 3))
            + _M(0, 2) * (kb * _M(1, 0) - kd * _M(1, 1) + kf * _M(1, 3))
            - _M(0, 3) * (kc * _M(1, 0) - ke * _M(1, 1) + kf * _M(1, 2));

    if(fabs(d) <= 0.0000000001f) // find a good epsilon
    {
        gb_mat4_identity(out);
        return false;
    }

    d = 1.0f / d;

    _MO(0, 0) =  _M3(_M(1, 1), _M(1, 2), _M(1, 3), _M(2, 1), _M(2, 2), _M(2, 3), _M(3, 1), _M(3, 2), _M(3, 3));
    _MO(0, 1) = -_M3(_M(0, 1), _M(0, 2), _M(0, 3), _M(2, 1), _M(2, 2), _M(2, 3), _M(3, 1), _M(3, 2), _M(3, 3));
    _MO(0, 2) =  _M3(_M(0, 1), _M(0, 2), _M(0, 3), _M(1, 1), _M(1, 2), _M(1, 3), _M(3, 1), _M(3, 2), _M(3, 3));
    _MO(0, 3) = -_M3(_M(0, 1), _M(0, 2), _M(0, 3), _M(1, 1), _M(1, 2), _M(1, 3), _M(2, 1), _M(2, 2), _M(2, 3));
    _MO(1, 0) = -_M3(_M(1, 0), _M(1, 2), _M(1, 3), _M(2, 0), _M(2, 2), _M(2, 3), _M(3, 0), _M(3, 2), _M(3, 3));
    _MO(1, 1) =  _M3(_M(0, 0), _M(0, 2), _M(0, 3), _M(2, 0), _M(2, 2), _M(2, 3), _M(3, 0), _M(3, 2), _M(3, 3));
    _MO(1, 2) = -_M3(_M(0, 0), _M(0, 2), _M(0, 3), _M(1, 0), _M(1, 2), _M(1, 3), _M(3, 0), _M(3, 2), _M(3, 3));
    _MO(1, 3) =  _M3(_M(0, 0), _M(0, 2), _M(0, 3), _M(1, 0), _M(1, 2), _M(1, 3), _M(2, 0), _M(2, 2), _M(2, 3));
    _MO(2, 0) =  _M3(_M(1, 0), _M(1, 1), _M(1, 3), _M(2, 0), _M(2, 1), _M(2, 3), _M(3, 0), _M(3, 1), _M(3, 3));
    _MO(2, 1) = -_M3(_M(0, 0), _M(0, 1), _M(0, 3), _M(2, 0), _M(2, 1), _M(2, 3), _M(3, 0), _M(3, 1), _M(3, 3));
    _MO(2, 2) =  _M3(_M(0, 0), _M(0, 1), _M(0, 3), _M(1, 0), _M(1, 1), _M(1, 3), _M(3, 0), _M(3, 1), _M(3, 3));
    _MO(2, 3) = -_M3(_M(0, 0), _M(0, 1), _M(0, 3), _M(1, 0), _M(1, 1), _M(1, 3), _M(2, 0), _M(2, 1), _M(2, 3));
    _MO(3, 0) = -_M3(_M(1, 0), _M(1, 1), _M(1, 2), _M(2, 0), _M(2, 1), _M(2, 2), _M(3, 0), _M(3, 1), _M(3, 2));
    _MO(3, 1) =  _M3(_M(0, 0), _M(0, 1), _M(0, 2), _M(2, 0), _M(2, 1), _M(2, 2), _M(3, 0), _M(3, 1), _M(3, 2));
    _MO(3, 2) = -_M3(_M(0, 0), _M(0, 1), _M(0, 2), _M(1, 0), _M(1, 1), _M(1, 2), _M(3, 0), _M(3, 1), _M(3, 2));
    _MO(3, 3) =  _M3(_M(0, 0), _M(0, 1), _M(0, 2), _M(1, 0), _M(1, 1), _M(1, 2), _M(2, 0), _M(2, 1), _M(2, 2));

    #undef _M3
    #undef _M
    #undef _MO

    return true;
}

Is it something we can merge to upstream? Probably would be nice to have affine and generic versions for all implemented matrix sizes as well.

In the next update (v0.07), I will have inverse functions for all the matrix types.