toji / gl-matrix

Javascript Matrix and Vector library for High Performance WebGL apps
glmatrix.net
MIT License
5.36k stars 719 forks source link

getOrigin? #412

Open dakom opened 3 years ago

dakom commented 3 years ago

If a Matrix is created via fromRotationTranslationScaleOrigin(), is it possible to recover the origin, or more specifically, each of:

?

magcius commented 3 years ago

No. There are infinitely many values of Rotation, Translation, Scale and Origin that combine to the same matrix. Origin is a convenience parameter, but it is impossible to recover. Decomposition only returns one of the many matrices it could be, using some heuristics to define its outputs.

bchirlsbiodigital commented 2 years ago

If we know the origin and it is not [0, 0, 0], is it possible to get the rotation, translation and scale?

magcius commented 2 years ago

Rotation and scale are identical, the only difference is the translation. If you know the origin you used to create the matrix with, you can extract the original translation with something like (untested):

translation[0] = mtx[12] - origin[0] + (mtx[0] * origin[0] + mtx[4] * origin[1] + mtx[8] * origin[2]);
translation[1] = mtx[13] - origin[1] + (mtx[1] * origin[0] + mtx[5] * origin[1] + mtx[9] * origin[2]);
translation[2] = mtx[14] - origin[2] + (mtx[2] * origin[0] + mtx[6] * origin[1] + mtx[10] * origin[2]);
bchirlsbiodigital commented 2 years ago

Here is a function in typescript:

function getTranslation(out: vec3, mat: mat4, origin?: vec3) {

    out[0] = mat[12];
    out[1] = mat[13];
    out[2] = mat[14];

    if (origin) {
        const ox = origin[0];
        const oy = origin[1];
        const oz = origin[2];

        /*
        Algebraically solve for translation vector, based on code in
        fromRotationTranslationScaleOrigin
        */
        out[0] += mat[0] * ox + mat[4] * oy + mat[8] * oz - ox;
        out[1] += mat[1] * ox + mat[5] * oy + mat[9] * oz - oy;
        out[2] += mat[2] * ox + mat[6] * oy + mat[10] * oz - oz;
    }

    return out;
}

And here are some tests (in addition to the existing getTranslation tests):

it('should get translation with zero origin', () => {
    const out = [0, 0, 0] as vec3;
    const matrix = mat4.create();
    fromTranslation(matrix, [1, 2, 3]);

    getTranslation(out, matrix, [0, 0, 0]);
    expect(out).toBeEqualish([1, 2, 3]);
});

it('should get translation with non-zero origin', () => {
    const out = [0, 0, 0] as vec3;
    const matrix = mat4.create();
    const quaternion = [-0.1767767, 0.3061862, 0.1767767, 0.9185587] as quat;
    const origin = [-4, -5, -6] as vec3;
    fromRotationTranslationScaleOrigin(matrix, quaternion, [1, 2, 3], [1.5, 2.5, 3.5], origin);

    getTranslation(out, matrix, origin);
    expect(out).toBeEqualish([1, 2, 3]);
});

it('should not modify origin', () => {
    const out = [0, 0, 0] as vec3;
    const matrix = mat4.create();
    const quaternion = [-0.1767767, 0.3061862, 0.1767767, 0.9185587] as quat;
    const origin = [-4, -5, -6] as vec3;
    fromRotationTranslationScaleOrigin(matrix, quaternion, [1, 2, 3], [1.5, 2.5, 3.5], origin);

    getTranslation(out, matrix, origin);
    expect(origin).toEqual([-4, -5, -6]);
});

Feel free to use and adjust for your styling needs.