# 4D matrix math library for use with OpenGL

I am trying to create a simple library for C to handle OpenGL matrix operations.

You can see the vec3fscalar here.

#ifndef __MATH_MAT4FSCALAR_H__
#define __MATH_MAT4FSCALAR_H__

#include "vec3fScalar.h"

typedef struct mat4
{
double m;
}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 mat4inv(mat4* out, const mat4* in);

void mat4print(const char* tag, const mat4* m4);

#endif /* __MATH_AT4FSCALAR_H__ */


mat4.c

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "mat4fScalar.h"

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

static mat4* mMat(mat4* out, const double* data)
{
out->m  = data;
out->m  = data;
out->m  = data;
out->m = data;

out->m  = data;
out->m  = data;
out->m  = data;
out->m = data;

out->m  = data;
out->m  = data;
out->m = data;
out->m = data;

out->m  = data;
out->m  = data;
out->m = data;
out->m = data;
return out;

}

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

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

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

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

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

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

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

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

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

void mat4mul(mat4* out, const mat4* a, const mat4* b)
{
out->m  = ( a->m  *  b->m) + (a->m  * b->m) + (a->m  *  b->m) + (a->m  * b->m );
out->m  = ( a->m  *  b->m) + (a->m  * b->m) + (a->m  *  b->m) + (a->m  * b->m );
out->m  = ( a->m  *  b->m) + (a->m  * b->m) + (a->m *  b->m) + (a->m * b->m );
out->m = ( a->m *  b->m) + (a->m * b->m) + (a->m *  b->m) + (a->m * b->m );

out->m  = ( a->m  *  b->m) + (a->m  * b->m) + (a->m  * b->m)  + (a->m  * b->m );
out->m  = ( a->m  *  b->m) + (a->m  * b->m) + (a->m  * b->m)  + (a->m  * b->m );
out->m  = ( a->m  *  b->m) + (a->m  * b->m) + (a->m * b->m)  + (a->m * b->m );
out->m = ( a->m *  b->m) + (a->m * b->m) + (a->m * b->m)  + (a->m * b->m );

out->m  = ( a->m  *  b->m) + (a->m  * b->m) + (a->m  * b->m) + (a->m  * b->m );
out->m  = ( a->m  *  b->m) + (a->m  * b->m) + (a->m  * b->m) + (a->m  * b->m );
out->m = ( a->m  *  b->m) + (a->m  * b->m) + (a->m * b->m) + (a->m * b->m );
out->m = ( a->m *  b->m) + (a->m * b->m) + (a->m * b->m) + (a->m * b->m );

out->m  = ( a->m  *  b->m) + (a->m  * b->m) + (a->m  * b->m) + (a->m  * b->m );
out->m  = ( a->m  *  b->m) + (a->m  * b->m) + (a->m  * b->m) + (a->m  * b->m );
out->m = ( a->m  *  b->m) + (a->m  * b->m) + (a->m * b->m) + (a->m * b->m );
out->m = ( a->m *  b->m) + (a->m * b->m) + (a->m * b->m) + (a->m * b->m );
}

void mat4mulvec3(vec3f* out, const vec3f* v, const mat4* a)
{
out->p = a->m * v->p + a->m * v->p + a->m  * v->p;
out->p = a->m * v->p + a->m * v->p + a->m  * v->p;
out->p = a->m * v->p + a->m * v->p + a->m * v->p;
}

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

void mat4transv(mat4* out, const vec3f* v)
{
out->m  += v->p;
out->m  += v->p;
out->m += v->p;
}

void mat4rotate(mat4*  out, const double angle, const double x, const double y, const double z)
{
out->m   = cos(y) * cos(z * mAng);
out->m   = cos(y) * sin(z * mAng);
out->m   = sin(y);

out->m   = ((cos(y) * sin(z)) + (sin(x) * sin(y) * cos(z)));
out->m   = ((cos(y) * cos(z)) - (sin(x) * sin(y) * sin(z)));
out->m   = -sin(x)  * cos(y);

out->m   = ((sin(x) * sin(z)) - (cos(x) * sin(y) * cos(z)));
out->m   = ((sin(x) * sin(z)) + (cos(x) * sin(y) * sin(z)));
out->m  = (cos(x)  * cos(y));
}

void mat4rotatev(mat4* out, const double angle, const vec3f* v)
{
double x, y, z;
x = v->p;
y = v->p;
z = v->p;

out->m   = cos(y) * cos(z * mAng);
out->m   = cos(y) * sin(z * mAng);
out->m   = sin(y);

out->m   = ((cos(y) * sin(z)) + (sin(x) * sin(y) * cos(z)));
out->m   = ((cos(y) * cos(z)) - (sin(x) * sin(y) * sin(z)));
out->m   = -sin(x)  * cos(y);

out->m   = ((sin(x) * sin(z)) - (cos(x) * sin(y) * cos(z)));
out->m   = ((sin(x) * sin(z)) + (cos(x) * sin(y) * sin(z)));
out->m  = (cos(x)  * cos(y));
}

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

out->m  = sin(mAng);
out->m = cos(mAng);
}

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

out->m  = -sin(mAng);
out->m = cos(mAng);
}

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

out->m = -sin(mAng);
out->m = cos(mAng);
}

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

void mat4scalev(mat4* out, const vec3f* v)
{
out->m   *= v->p;
out->m   *= v->p;
out->m  *= v->p;
}

void mat4inv(mat4* out, const mat4* in)
{
double inv, det;
int i;

inv =  in->m  * in->m * in->m -
in->m  * in->m * in->m -
in->m  * in->m  * in->m +
in->m  * in->m  * in->m +
in->m * in->m  * in->m -
in->m * in->m  * in->m;

inv = -in->m  * in->m * in->m +
in->m  * in->m * in->m +
in->m  * in->m  * in->m -
in->m  * in->m  * in->m -
in->m * in->m  * in->m +
in->m * in->m  * in->m;

inv =  in->m  * in->m  * in->m -
in->m  * in->m * in->m -
in->m  * in->m  * in->m +
in->m  * in->m  * in->m +
in->m * in->m  * in->m -
in->m * in->m  * in->m;

inv = -in->m  * in->m * in->m +
in->m  * in->m * in->m +
in->m  * in->m * in->m -
in->m  * in->m * in->m -
in->m * in->m * in->m +
in->m * in->m * in->m;

inv =  -in->m  * in->m * in->m +
in->m  * in->m * in->m +
in->m  * in->m * in->m -
in->m  * in->m * in->m -
in->m * in->m * in->m +
in->m * in->m * in->m;

inv =  in->m  * in->m * in->m -
in->m  * in->m * in->m -
in->m  * in->m * in->m +
in->m  * in->m * in->m +
in->m * in->m * in->m -
in->m * in->m * in->m;

inv =  -in->m  * in->m * in->m +
in->m  * in->m * in->m +
in->m  * in->m * in->m -
in->m  * in->m * in->m -
in->m * in->m * in->m +
in->m * in->m * in->m;

inv =  in->m  * in->m * in->m -
in->m  * in->m * in->m -
in->m  * in->m * in->m +
in->m  * in->m * in->m +
in->m * in->m * in->m -
in->m * in->m * in->m;

inv =  in->m  * in->m * in->m -
in->m  * in->m * in->m -
in->m  * in->m * in->m +
in->m  * in->m * in->m +
in->m * in->m * in->m -
in->m * in->m * in->m;

inv =  -in->m  * in->m * in->m +
in->m  * in->m * in->m +
in->m  * in->m * in->m -
in->m  * in->m * in->m -
in->m * in->m * in->m +
in->m * in->m * in->m;

inv =  in->m  * in->m * in->m -
in->m  * in->m * in->m -
in->m  * in->m * in->m +
in->m  * in->m * in->m +
in->m * in->m * in->m -
in->m * in->m * in->m;

inv =  -in->m  * in->m * in->m +
in->m  * in->m * in->m +
in->m  * in->m * in->m -
in->m  * in->m * in->m -
in->m * in->m * in->m +
in->m * in->m * in->m;

inv =  -in->m * in->m * in->m +
in->m * in->m * in->m +
in->m * in->m * in->m -
in->m * in->m * in->m -
in->m * in->m * in->m +
in->m * in->m * in->m;

inv =  in->m * in->m * in->m -
in->m * in->m * in->m -
in->m * in->m * in->m +
in->m * in->m * in->m +
in->m * in->m * in->m -
in->m * in->m * in->m;

inv = -in->m * in->m * in->m +
in->m * in->m * in->m +
in->m * in->m * in->m -
in->m * in->m * in->m -
in->m * in->m * in->m +
in->m * in->m * in->m;

inv = in->m * in->m * in->m -
in->m * in->m * in->m -
in->m * in->m * in->m +
in->m * in->m * in->m +
in->m * in->m * in->m -
in->m * in->m * in->m;

det = in->m * inv + in->m * inv + in->m * inv + in->m * inv;

if (det == 0)
printf("oh oh!");

det = 1.0 / det;

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

void mat4print(const char* tag, const mat4* m4)
{
printf("\n\t%s\t\n[\t%f\t%f\t%f\t%f\t]\n[\t%f\t%f\t%f\t%f\t]\n"
"[\t%f\t%f\t%f\t%f\t]\n[\t%f\t%f\t%f\t%f\t]\n",
tag,
m4->m,  m4->m,  m4->m,  m4->m,
m4->m,  m4->m,  m4->m,  m4->m,
m4->m,  m4->m,  m4->m, m4->m,
m4->m, m4->m, m4->m, m4->m);
}


I didn't write my own inverse function; I got it from this SO question.

I really hope that I can get some good help with this because matrix math is difficult enough w/o translating it to code. Are there any functions that I am missing? I would like to avoid adding functions just to have them there unless they'll be useful for rendering pipeline which fits nicely in the 4x4 square matrix.

A few things you might want to look into:

• Prefer to avoid names starting with double underscore (__MATH_MAT4FSCALAR_H__). This notation is usually reserved for compiler extensions and Standard Library internals. Steer clear from them to avoid a name collision with your own code. In this case, simple MATH_MAT4FSCALAR_H would be just fine.

• I don't see any reason for not making iData a mat4 as well. Then also take two matrices in mMat(). By the way, mMat tells me nothing. copyMatrix would be the logical name for that function if it took two matrices and copied b over a. If you leave the second param as double array, then perhaps setMatrix will make more sense.

• Avoid casting the return value of malloc, unless you need to compile the code as C++. In C the cast from void* to whatever is implicit, so that only adds boilerplate and maintenance overhead.

• You might also consider using the variable instance in the sizeof on mallocs to avoid repeating the types on the left and right, E.g.:

mat4* v = malloc(sizeof *v); // The ( ) around sizeof are not even mandatory!

• Descent compilers are capable of consolidating those redundant sin/cos calls on the same value during the common subexpression elimination pass, but... I don't know, call me paranoid, but I'd rather not rely on that. Specially if you target some more exotic systems with much simpler compilers. I'd manually consolidate the repeated calls into local variables and use the locals instead.

• Hehe, okay, suppose that does happen:

if (det == 0)
printf("oh oh!");


What is your course of action? Allow the division by zero to take place, returning NaN/Inf? Better would be to make the function return a boolean and let the caller know about the error.

• In mat4print(), you might consider accepting a third parameter with the FILE* to print to. That makes you code reusable if you want for instance write to a log file on disk. For console dumps, just pass stdout.

Apart from the above, it looks good to me. I commend you for making consistent use of const. I'm a "const nazi" myself. Good job!

• thanks for the tips, i renamed the mMat func to copymatrix and also you gave me a real gem to save the sin cos values and use those instead of constantly calling sin, cos. I also return a value from the determinant funcction as well and printing to a file. All great tips, thanks a lot. – user1610950 Aug 3 '15 at 8:49