MVP functions

Hi,
I’m very new to OpenGL and for various reasons I’m rolling my own functions to calculate the various parts of my MVP matrix (described here). Anyway the code based off of them doesn’t work (naturally…) and i would greatly appreciate if someone with a few minutes could take a look at them and check whether they’re at fault, since I can’t find a good way to isolate them from the rest of the code, so can’t tell where the error is.
Thanks in advance
Angus

void perspective(float fovy, float aspect, float zNear, float zFar, float result[4][4])
{
	float f=1/tanf(fovy/2);
	
	result[0][0]=f/aspect;
	result[0][1]=0;
	result[0][2]=0;
	result[0][3]=0;
	result[1][0]=0;
	result[1][1]=f;
	result[1][2]=0;
	result[1][3]=0;
	result[2][0]=0;
	result[2][1]=0;
	result[2][2]=(zFar+zNear)/(zNear-zFar);
	result[2][3]=(2*zNear*zFar)/(zNear-zFar);
	result[3][0]=0;
	result[3][1]=0;
	result[3][2]=-1;
	result[3][3]=0;
}

void crossproduct(float a[4], float b[4], float result[4]);

void lookat(float eyeX, float eyeY, float eyeZ, float centreX, float centreY, float centreZ, float upX, float upY, float upZ, float result[4][4])
{
	float f[4] = {centreX-eyeX, centreY-eyeY, centreZ-eyeZ, 1};
	float u[4] = {upX, upY, upZ, 1};
	float s[4];
	float temp[4][4];
	float temp2[4][4];
	normalise(f);
	normalise(u);

	crossproduct(f, u, s);
	crossproduct(s, f, u);

	temp[0][0]=s[0];
	temp[0][1]=s[1];
	temp[0][2]=s[2];
	temp[0][3]=0;
	temp[1][0]=u[0];
	temp[1][1]=u[1];
	temp[1][2]=u[2];
	temp[1][3]=0;
	temp[2][0]=-f[0];
	temp[2][1]=-f[1];
	temp[2][2]=-f[2];
	temp[2][3]=0;
	temp[3][0]=0;
	temp[3][1]=0;
	temp[3][2]=0;
	temp[3][3]=1;

	translate(temp2, -eyeX, -eyeY, -eyeZ);
	multmatrix(temp, temp2, result);
}

void crossproduct(float a[4], float b[4], float result[4])
{
	result[0] = (a[1]*b[2]) - (a[2] * b[1]);
	result[1] = (a[2]*b[0]) - (a[0] * b[2]);
	result[2] = (a[0]*b[1]) - (a[1] * b[0]);
	result[3] = 1;
}

void multmatrix(float a[4][4], float b[4][4], float result[4][4])
{
	int i;
	int j;
	int k;
	float total;

	for(i=0;i<4;i++)
		for(j=0;j<4;j++)
		{
			total=0;
			for(k=0;k<4;k++)
				total+=a[i][k]*b[k][j];
			result[i][j]=total;
		}

}

void translate(float result[4][4], float x, float y, float z)
{
	result[0][0]=1;
	result[0][1]=0;
	result[0][2]=0;
	result[0][3]=x;
	result[1][0]=0;
	result[1][1]=1;
	result[1][2]=0;
	result[1][3]=y;
	result[2][0]=0;
	result[2][1]=0;
	result[2][2]=1;
	result[2][3]=z;
	result[3][0]=0;
	result[3][1]=0;
	result[3][2]=0;
	result[3][3]=1;
}

I’ve only looked at your translate.
OpenGL expects matrices stored in float arrays to be column major, i.e. the first index is the column and the second is the row - your translate matrix are transposed.

Thanks, don’t know why I didn’t think to check which way matrices should be. Will change that and see if it at all improves things.
Angus

Yeah, an easy way to deal with this given that C/C++ is row-major order and OpenGL is column-major is: Just do row-major like C/C++ wants, and flip the transform order.

That is, instead of doing “PVM * v_obj = v_clip” like OpenGL wants, do this “v_obj * MVP = v_clip”. In practice this means you use the transposes of the matrices, and you composite them in the opposite order. Then you can blissfully care less that OpenGL uses column major order.

There’s also glLoadTransposeMatrix and glMultTransposeMatrix.

Tried flipping round the order of the transformations just now which didn’t help. I’ll do a full proper check of the entire piece of code tomorrow/over the next week, mainly worried about these bits of code as I’ve no real way to check they’re working.
Just as a matter of interest, is there any good reason to keep the C code using row major matrices? I just did it because it seems intuitive and I hadn’t checked properly but you seem to imply there’s a good reason one might want to do that.

is there any good reason to keep the C code using row major matrices?

No, not really. It’s all purely conventional; just put the columns where you put the rows now. Your matrix multiplication functions will all work exactly the same.

Yes. Then you never have to do transposes to pass matrices to OpenGL. Further, you end up with matrix translate and basis vectors being in rows of the matrix rather than column which is more efficient to access. And you can just forget the whole column major issue on the C/C++ side.

Further, you end up with matrix translate and basis vectors being in rows of the matrix rather than column which is more efficient to access.

I think you’re conflating certain terms, which is why we give conflicting advice that is in fact the same. The major storage order of a matrix does not change how the basis vectors are stored. It simply changes the order of storage of the data.

What you’re talking about is combing row-major storage with row vectors. That is, you’re transposing the matrix into a row-based matrix, and then storing that row-based matrix in a row-major array.

This results in identical data to storing a column-based matrix in a column-major array. That’s why transposing isn’t necessary; because you’ve transposed the data at storage time.

A more detailed discussion of the issue can be found on this Stack Exchange answer.

Exactly. You’re right. We’re talking about the same thing. Thanks for clarifying that.

Thanks for all the help everyone, changed the matrices to be column major which didn’t help at first, then found the real bug which made me feel like a total idiot. My program is filled with code to generate the MVP matrix but was missing the single line of code needed to load it into the shader!
Once I added that in it finally drew something and a couple of tweaks to undo a bit of damage I’d unintentionally done while debugging it finally drew what I wanted it to.
Thanks again
Angus