opengl math matrix layout in c

Hi

I am writing a c matrix 4 library to do my opengl rendering. I created some simple data structures and wanted to make sure that I setup the memory in the correct way so that I do not have to transpose the data when I send it over to the GPU.

typedef struct mat4
{
    double m[16];
}mat4;

mat4* newMat4(const vec3f *c1, const vec3f *c2, const vec3f *c3, const vec3f *c4);
mat4* newMat4identity(void);

void newMat4x(mat4* out, const vec3f *c1, const vec3f *c2, const vec3f *c3, const vec3f *c4);
void newMat4xc(mat4* out, const mat4* m4);
void newMat4identityx(mat4* out);


void mat4add(mat4* out, const mat4* a, const mat4* b);
void mat4sub(mat4* out, const mat4* a, const mat4* b);
void mat4mul(mat4* out, const mat4* a, const mat4* b);

void mat4mulvec3(vec3f* out, const vec3f* v, const mat4* a);

void mat4trans(mat4* out, const double x, const double y, const double z);
void mat4transv(mat4* out, const vec3f* v);

void mat4rotate(mat4* out, const double angle, const double x, const double y, const double z);
void mat4rotatev(mat4* out, const double angle, const vec3f* v);
void mat4xrotate(mat4* out, const double angle, const double x);
void mat4yrotate(mat4* out, const double angle, const double y);
void mat4zrotate(mat4* out, const double angle, const double z);

void mat4scale(mat4* out, const double x, const double y, const double z);
void mat4scalev(mat4* out, const vec3f* v);

int mat4inverse(mat4* out, const mat4* in);

void mat4transpose(mat4* out, const mat4* in);

static double iData[] = { 1, 0, 0, 0,
                          0, 1, 0, 0,
                          0, 0, 1, 0,
                          0, 0, 0, 1 };

static mat4*copyMatrix(mat4 *out, const double *data)
{
    out->m[0]  = data[0];
    out->m[4]  = data[4];
    out->m[8]  = data[8];
    out->m[12] = data[12];

    out->m[1]  = data[1];
    out->m[5]  = data[5];
    out->m[9]  = data[9];
    out->m[13] = data[13];

    out->m[2]  = data[2];
    out->m[6]  = data[6];
    out->m[10] = data[10];
    out->m[14] = data[14];

    out->m[3]  = data[3];
    out->m[7]  = data[7];
    out->m[11] = data[11];
    out->m[15] = data[15];
    return out;

}

mat4* newMat4(const vec3f *c1, const vec3f *c2, const vec3f *c3, const vec3f *c4)
{
    double data[] = { c1->p[0], c2->p[0], c3->p[0], c4->p[0],
                      c1->p[1], c2->p[1], c3->p[1], c4->p[1],
                      c1->p[2], c2->p[2], c3->p[2], c4->p[2],
                         0,        0,       0,        1      };

    mat4* v = (mat4*)malloc(sizeof(mat4));
    return copyMatrix(v, data);
}

mat4* newMat4c(const mat4* m4)
{
    mat4* v = (mat4*)malloc(sizeof(mat4));
    return copyMatrix(v, m4->m);
}

mat4* newMat4identity(void)
{
    mat4* v = (mat4*)malloc(sizeof(mat4));
    return copyMatrix(v, iData);
}

void newMat4x(mat4* out, const vec3f *c1, const vec3f *c2, const vec3f *c3, const vec3f *c4)
{
    double data[] = { c1->p[0], c2->p[0], c3->p[0], c4->p[0],
                      c1->p[1], c2->p[1], c3->p[1], c4->p[1],
                      c1->p[2], c2->p[2], c3->p[2], c4->p[2],
                      0,        0,       0,        1      };
    copyMatrix(out, data);
}

void newMat4xc(mat4* out, const mat4* m4)
{
    copyMatrix(out, m4->m);
}

void newMat4identityx(mat4* out)
{
    copyMatrix(out, iData);
}

void mat4add(mat4* out, const mat4* a, const mat4* b)
{
    double d[16];
    for(size_t i = 0; i < 16; i++)
    {
        d[i] = a->m[i] + b->m[i];
    }
    copyMatrix(out, d);
}

void mat4sub(mat4* out, const mat4* a, const mat4* b)
{
    double d[16];
    for(size_t i = 0; i < 16; i++)
    {
        d[i] = a->m[i] - b->m[i];
    }
    copyMatrix(out, d);
}

void mat4mul(mat4* out, const mat4* a, const mat4* b)
{
    out->m[0]  = ( a->m[0]  *  b->m[0]) + (a->m[1]  * b->m[4]) + (a->m[2]  *  b->m[8]) + (a->m[3]  * b->m[12] );
    out->m[4]  = ( a->m[4]  *  b->m[0]) + (a->m[5]  * b->m[4]) + (a->m[6]  *  b->m[8]) + (a->m[7]  * b->m[12] );
    out->m[8]  = ( a->m[8]  *  b->m[0]) + (a->m[9]  * b->m[4]) + (a->m[10] *  b->m[8]) + (a->m[11] * b->m[12] );
    out->m[12] = ( a->m[12] *  b->m[0]) + (a->m[13] * b->m[4]) + (a->m[14] *  b->m[8]) + (a->m[15] * b->m[12] );

    out->m[1]  = ( a->m[0]  *  b->m[1]) + (a->m[1]  * b->m[5]) + (a->m[2]  * b->m[9])  + (a->m[3]  * b->m[13] );
    out->m[5]  = ( a->m[4]  *  b->m[1]) + (a->m[5]  * b->m[5]) + (a->m[6]  * b->m[9])  + (a->m[7]  * b->m[13] );
    out->m[9]  = ( a->m[8]  *  b->m[1]) + (a->m[9]  * b->m[5]) + (a->m[10] * b->m[9])  + (a->m[11] * b->m[13] );
    out->m[13] = ( a->m[12] *  b->m[1]) + (a->m[13] * b->m[5]) + (a->m[14] * b->m[9])  + (a->m[15] * b->m[13] );

    out->m[2]  = ( a->m[0]  *  b->m[2]) + (a->m[1]  * b->m[6]) + (a->m[2]  * b->m[10]) + (a->m[3]  * b->m[14] );
    out->m[6]  = ( a->m[4]  *  b->m[2]) + (a->m[5]  * b->m[6]) + (a->m[6]  * b->m[10]) + (a->m[7]  * b->m[14] );
    out->m[10] = ( a->m[8]  *  b->m[2]) + (a->m[9]  * b->m[6]) + (a->m[10] * b->m[10]) + (a->m[11] * b->m[14] );
    out->m[14] = ( a->m[12] *  b->m[2]) + (a->m[13] * b->m[6]) + (a->m[14] * b->m[10]) + (a->m[15] * b->m[14] );

    out->m[3]  = ( a->m[0]  *  b->m[3]) + (a->m[1]  * b->m[7]) + (a->m[2]  * b->m[11]) + (a->m[3]  * b->m[15] );
    out->m[7]  = ( a->m[4]  *  b->m[3]) + (a->m[5]  * b->m[7]) + (a->m[6]  * b->m[11]) + (a->m[7]  * b->m[15] );
    out->m[11] = ( a->m[8]  *  b->m[3]) + (a->m[9]  * b->m[7]) + (a->m[10] * b->m[11]) + (a->m[11] * b->m[15] );
    out->m[15] = ( a->m[12] *  b->m[3]) + (a->m[13] * b->m[7]) + (a->m[14] * b->m[11]) + (a->m[15] * b->m[15] );
}

void mat4mulvec3(vec3f* out, const vec3f* v, const mat4* a)
{
    out->p[0] = a->m[0] * v->p[0] + a->m[1] * v->p[1] + a->m[2]  * v->p[2];
    out->p[1] = a->m[4] * v->p[0] + a->m[5] * v->p[1] + a->m[6]  * v->p[2];
    out->p[2] = a->m[8] * v->p[0] + a->m[9] * v->p[1] + a->m[10] * v->p[2];
}

void mat4trans(mat4* out, const double x, const double y, const double z)
{
    out->m[3]  += x;
    out->m[7]  += y;
    out->m[11] += z;
}

void mat4transv(mat4* out, const vec3f* v)
{
    out->m[3]  += v->p[0];
    out->m[7]  += v->p[1];
    out->m[11] += v->p[2];
}

void mat4rotate(mat4*  out, const double angle, const double x, const double y, const double z)
{
    double mAng = toRadiansr(angle);
    double cosZangle = cos(z);
    double sinZangle = sin(z);

    double sinYangle = sin(z);
    double cosYangle = cos(z);

    double sinXangle = sin(x);
    double cosXangle = cos(x);

    out->m[0]   = cos(mAng) * cos(mAng);
    out->m[4]   = cos(mAng) * sin(mAng);
    out->m[8]   = sin(mAng);

    out->m[1]   = ((cos(mAng) * sin(mAng)) + (sin(mAng) * sin(mAng) * cos(mAng)));
    out->m[5]   = ((cos(mAng) * cos(mAng)) - (sin(mAng) * sin(mAng) * sin(mAng)));
    out->m[9]   = -sin(mAng)  * cos(mAng);

    out->m[2]   = ((sin(mAng) * sin(mAng)) - (cos(mAng) * sin(mAng) * cos(mAng)));
    out->m[6]   = ((sin(mAng) * sin(mAng)) + (cos(mAng) * sin(mAng) * sin(mAng)));
    out->m[10]  = ((cos(mAng)  * cos(mAng)));
}

void mat4rotatev(mat4* out, const double angle, const vec3f* v)
{
    double mAng = toRadiansr(angle);
    double x, y, z;
    x = v->p[0];
    y = v->p[1];
    z = v->p[2];

    out->m[0]   = cos(y) * cos(z * mAng);
    out->m[4]   = cos(y) * sin(z * mAng);
    out->m[8]   = sin(y);

    out->m[1]   = ((cos(y) * sin(z)) + (sin(x) * sin(y) * cos(z)));
    out->m[5]   = ((cos(y) * cos(z)) - (sin(x) * sin(y) * sin(z)));
    out->m[9]   = -sin(x)  * cos(y);

    out->m[2]   = ((sin(x) * sin(z)) - (cos(x) * sin(y) * cos(z)));
    out->m[6]   = ((sin(x) * sin(z)) + (cos(x) * sin(y) * sin(z)));
    out->m[10]  = (cos(x)  * cos(y));
}


void mat4xrotate(mat4* out, const double angle, const double x)
{
    double mAng = toRadiansr(angle);
    out->m[5]  = cos(mAng);
    out->m[9]  = -sin(mAng);

    out->m[6]  = sin(mAng);
    out->m[10] = cos(mAng);
}

void mat4yrotate(mat4* out, const double angle, const double y)
{
    double mAng = toRadiansr(angle);
    out->m[0]  = cos(mAng);
    out->m[8]  = sin(mAng);

    out->m[2]  = -sin(mAng);
    out->m[10] = cos(mAng);
}

void mat4zrotate(mat4* out, const double angle, const double z)
{
    double mAng = toRadiansr(angle);
    out->m[0] = cos(mAng);
    out->m[4] = sin(mAng);

    out->m[1] = -sin(mAng);
    out->m[5] = cos(mAng);
}

void mat4scale(mat4* out, const double x, const double y, const double z)
{
    out->m[0]   *= x;
    out->m[5]   *= y;
    out->m[10]  *= z;
}

void mat4scalev(mat4* out, const vec3f* v)
{
    out->m[0]   *= v->p[0];
    out->m[5]   *= v->p[1];
    out->m[10]  *= v->p[2];
}

int mat4inverse(mat4* out, const mat4* in)
{
    double inv[16], det;
    int i;

    inv[0] =  in->m[5]  * in->m[10] * in->m[15] -
              in->m[5]  * in->m[11] * in->m[14] -
              in->m[9]  * in->m[6]  * in->m[15] +
              in->m[9]  * in->m[7]  * in->m[14] +
              in->m[13] * in->m[6]  * in->m[11] -
              in->m[13] * in->m[7]  * in->m[10];

    inv[4] = -in->m[4]  * in->m[10] * in->m[15] +
              in->m[4]  * in->m[11] * in->m[14] +
              in->m[8]  * in->m[6]  * in->m[15] -
              in->m[8]  * in->m[7]  * in->m[14] -
              in->m[12] * in->m[6]  * in->m[11] +
              in->m[12] * in->m[7]  * in->m[10];

    inv[8] =  in->m[4]  * in->m[9]  * in->m[15] -
              in->m[4]  * in->m[11] * in->m[13] -
              in->m[8]  * in->m[5]  * in->m[15] +
              in->m[8]  * in->m[7]  * in->m[13] +
              in->m[12] * in->m[5]  * in->m[11] -
              in->m[12] * in->m[7]  * in->m[9];

    inv[12] = -in->m[4]  * in->m[9] * in->m[14] +
               in->m[4]  * in->m[10] * in->m[13] +
               in->m[8]  * in->m[5] * in->m[14] -
               in->m[8]  * in->m[6] * in->m[13] -
               in->m[12] * in->m[5] * in->m[10] +
               in->m[12] * in->m[6] * in->m[9];

    inv[1] =  -in->m[1]  * in->m[10] * in->m[15] +
              in->m[1]  * in->m[11] * in->m[14] +
              in->m[9]  * in->m[2] * in->m[15] -
              in->m[9]  * in->m[3] * in->m[14] -
              in->m[13] * in->m[2] * in->m[11] +
              in->m[13] * in->m[3] * in->m[10];

    inv[5] =  in->m[0]  * in->m[10] * in->m[15] -
              in->m[0]  * in->m[11] * in->m[14] -
              in->m[8]  * in->m[2] * in->m[15] +
              in->m[8]  * in->m[3] * in->m[14] +
              in->m[12] * in->m[2] * in->m[11] -
              in->m[12] * in->m[3] * in->m[10];

    inv[9] =  -in->m[0]  * in->m[9] * in->m[15] +
              in->m[0]  * in->m[11] * in->m[13] +
              in->m[8]  * in->m[1] * in->m[15] -
              in->m[8]  * in->m[3] * in->m[13] -
              in->m[12] * in->m[1] * in->m[11] +
              in->m[12] * in->m[3] * in->m[9];

    inv[13] =  in->m[0]  * in->m[9] * in->m[14] -
               in->m[0]  * in->m[10] * in->m[13] -
               in->m[8]  * in->m[1] * in->m[14] +
               in->m[8]  * in->m[2] * in->m[13] +
               in->m[12] * in->m[1] * in->m[10] -
               in->m[12] * in->m[2] * in->m[9];

    inv[2] =  in->m[1]  * in->m[6] * in->m[15] -
              in->m[1]  * in->m[7] * in->m[14] -
              in->m[5]  * in->m[2] * in->m[15] +
              in->m[5]  * in->m[3] * in->m[14] +
              in->m[13] * in->m[2] * in->m[7] -
              in->m[13] * in->m[3] * in->m[6];

    inv[6] =  -in->m[0]  * in->m[6] * in->m[15] +
              in->m[0]  * in->m[7] * in->m[14] +
              in->m[4]  * in->m[2] * in->m[15] -
              in->m[4]  * in->m[3] * in->m[14] -
              in->m[12] * in->m[2] * in->m[7] +
              in->m[12] * in->m[3] * in->m[6];

    inv[10] =  in->m[0]  * in->m[5] * in->m[15] -
               in->m[0]  * in->m[7] * in->m[13] -
               in->m[4]  * in->m[1] * in->m[15] +
               in->m[4]  * in->m[3] * in->m[13] +
               in->m[12] * in->m[1] * in->m[7] -
               in->m[12] * in->m[3] * in->m[5];

    inv[14] =  -in->m[0]  * in->m[5] * in->m[14] +
               in->m[0]  * in->m[6] * in->m[13] +
               in->m[4]  * in->m[1] * in->m[14] -
               in->m[4]  * in->m[2] * in->m[13] -
               in->m[12] * in->m[1] * in->m[6] +
               in->m[12] * in->m[2] * in->m[5];

    inv[3] =  -in->m[1] * in->m[6] * in->m[11] +
              in->m[1] * in->m[7] * in->m[10] +
              in->m[5] * in->m[2] * in->m[11] -
              in->m[5] * in->m[3] * in->m[10] -
              in->m[9] * in->m[2] * in->m[7] +
              in->m[9] * in->m[3] * in->m[6];

    inv[7] =  in->m[0] * in->m[6] * in->m[11] -
              in->m[0] * in->m[7] * in->m[10] -
              in->m[4] * in->m[2] * in->m[11] +
              in->m[4] * in->m[3] * in->m[10] +
              in->m[8] * in->m[2] * in->m[7] -
              in->m[8] * in->m[3] * in->m[6];

    inv[11] = -in->m[0] * in->m[5] * in->m[11] +
              in->m[0] * in->m[7] * in->m[9] +
              in->m[4] * in->m[1] * in->m[11] -
              in->m[4] * in->m[3] * in->m[9] -
              in->m[8] * in->m[1] * in->m[7] +
              in->m[8] * in->m[3] * in->m[5];

    inv[15] = in->m[0] * in->m[5] * in->m[10] -
              in->m[0] * in->m[6] * in->m[9] -
              in->m[4] * in->m[1] * in->m[10] +
              in->m[4] * in->m[2] * in->m[9] +
              in->m[8] * in->m[1] * in->m[6] -
              in->m[8] * in->m[2] * in->m[5];

    det = in->m[0] * inv[0] + in->m[1] * inv[4] + in->m[2] * inv[8] + in->m[3] * inv[12];

    if (det == 0)
    {
        printf("oh oh!");
        return -1;
    }

    det = 1.0 / det;

    for (i = 0; i < 16; i++)
        out->m[i] = inv[i] * det;

    return 0;
}

void mat4transpose(mat4* out, const mat4* in)
{
    out->m[0]  = in->m[0];
    out->m[1]  = in->m[4];
    out->m[2]  = in->m[8];
    out->m[3]  = in->m[12];

    out->m[4]  = in->m[1];
    out->m[5]  = in->m[5];
    out->m[6]  = in->m[9];
    out->m[7]  = in->m[13];

    out->m[8]  = in->m[2];
    out->m[9]  = in->m[6];
    out->m[10] = in->m[10];
    out->m[11] = in->m[14];

    out->m[12]  = in->m[3];
    out->m[13]  = in->m[7];
    out->m[14] = in->m[11];
    out->m[15] = in->m[15];
}

Is this the proper memory layout to send the data over to opengl without needing to do the transpose?

OpenGL’s “default” layout (i.e. with the transpose parameter set to GL_FALSE) is column-major, i.e.


 0  4  8 12
 1  5  9 13
 2  6 10 14
 3  7 11 15

It’s impossible to say for sure which order your functions are using because there isn’t any documentation for them. And it isn’t possible to infer their interface from their implementation because, for any two matrices A and B (including 1xN or Nx1 matrices, i.e. vectors),

(A . B)T = (BT . AT)

I.e. any function which multiplies row-major matrices will also multiply column-major matrices. The only difference is the order of the arguments.

If mat4mul(out, a, b) calculates out = a . b, your layout is transposed. If it calculates out = b . a, your order is correct.

Similarly, if mat4mulvec3(out, v, a) calculates out = a . v (where v is a column vector), your layout is transposed. If it calculates out = v . a (where v is a row vector), your layout is correct.

IOW, most of your functions produce meaningful results for either row-major or column-major matrices. The only difference is how the arguments are interpreted.

If your functions are supposed to mimic the usual mathematical conventions (i.e. transforming a vector by a matrix has the matrix on the left and a column vector on the right), your layout is transposed. If they’re supposed to mimic DirectX convention (with a row vector on the left and the matrix on the right), your layout is correct.

Given that you’re posting this on an OpenGL forum, and the OpenGL documentation uses the normal mathematical convention, I’d be tempted to just say that your order is transposed. However, the fact that your matrix-vector multiply has the prototype


void mat4mulvec3(vec3f* out, const vec3f* v, const mat4* a)

leads me to wonder whether you might be using the DirectX convention (v.A) rather than mathematical convention (A.v).