Matrix multiplication

Started by
13 comments, last by Zakwayda 18 years, 7 months ago
Okay, what am I doing wrong?

util::CMatrix m;
m.setTranslation(0.0f, 0.0f, -5.0f);
util::CMatrix r1;
util::CMatrix r2;
util::CMatrix r3;

// create a transformation node, with a translation matrix
scene::CTransformationNode* node = new scene::CTransformationNode(m, engine->getSceneManager() );

r1.setRotationXDegrees( 0.05f );
r2.setRotationYDegrees( 0.05f );
r3.setRotationZDegrees( 0.05f );

float temp2 = 0.0f;
	
while( engine->run() ){
		
	driver->beginScene();	// clears the window with glClear
	
	glLoadIdentity();

	/*
	glTranslatef(0.0f, 0.0f, -5.0f);
	temp2 += 0.05f;
	glRotatef(temp2, 1.0f, 0.0f, 0.0f);
	glRotatef(temp2, 0.0f, 1.0f, 0.0f);
	glRotatef(temp2, 0.0f, 0.0f, 1.0f);
	*/

	node->multMatrix(r1);	// rotate by 0.05 degrees on X
	node->multMatrix(r2);	// rotate by 0.05 degrees on Y
	node->multMatrix(r3);	// rotate by 0.05 degrees on Z
		
	engine->getSceneManager()->render();

	driver->endScene();	// actually renders the window
}



The matrix multiplication is the problem. Am I able to multiply like this? Because the commented-out gl functions and my multMatrix don't act the same as my multMatrix functions. Are rotation matricies cumulative like this? Even if the matrix starts out as a translation matrix and then get multiplied by rotation matrices? Because I was doing the same thing in 2d (but only one axis), and I was getting the proper result. Edit:: So, if I had a rotationX matrix rotated by 10 degrees, and I multiplied that by another rotationX matrix rotated by 15 degrees, would the final matrix be rotated around the X axis by 25 degrees?
[size="2"][size=2]Mort, Duke of Sto Helit: NON TIMETIS MESSOR -- Don't Fear The Reaper
Advertisement
Your multMatrixmight be multiplying in the opposite order of what glRotate would do.
All gl transformation functions work in local space.

Applying a local space transformation T to your current transformation M would be:

if row major:
M = T * M

if column major:
M = M * T


Hope that helps
c2_0 may be right about multiplication order, but there are some other things about your example that are a little unclear. For example, the gl code increases the angle each frame, but the code you're using appears to always rotate by a (possibly unnoticable) .05 degrees. Unless, perhaps node->multMatrix() accumulates the rotation? In any case, you might post multMatrix(), as it's not clear from the context what it does.
Quote:if row major:
M = T * M

if column major:
M = M * T
Minor detail, but multiplication order is actually determined by whether you're using row or column vectors, not whether the matrices are row or column major.
Quote:Original post by Endar
Okay, what am I doing wrong?

It would be easier to tell if you post your matrix code. c2_0 might be right, but there could be other problems with it (for example the rotation matrix construction functions)...

Tom

EDIT: wow, did it really take me 9 minutes two write 2 lines? Anyway, what jyk said :)
Some of the relevant code:

Note:: The matrix is row-major, but is then transposed, immediately before multiplying by the glMultMatrixf (or whatever it's called) function.

class CTransformationNode	:	public ISceneGraphNode{protected:	util::CMatrix m_matrix;	///< The 4x4 matrix to hold the transformation for the nodepublic:	/**	 * Constructor	 */	CTransformationNode(const util::CMatrix& m, scene::ISceneManager* smgr)		:	ISceneGraphNode(smgr), m_matrix(m)	{	}	/**	 * Copy constructor	 */	CTransformationNode(const CTransformationNode& n)		:	 ISceneGraphNode(n), m_matrix(n.m_matrix)	{	}	/**	 * Apply the transformation of the node.	 */	virtual void render()	{		// FIXME - 9-09-05 - re-write this function and video driver to eliminate the gl func calls here		glMatrixMode(GL_MODELVIEW_MATRIX);		glPushMatrix();	// push a matrix onto the top of the model matrix stack		// apply the node's transform to the model matrix		m_sceneManager->getVideoDriver()->transform(video::MODEL_MATRIX, m_matrix);		ISceneGraphNode::render();	// render all the child nodes				glPopMatrix();	// pop matrix from the stack	}	/**	 * Multiply a matrix by the transformation matrix of the node.	 * \param m A const reference to a CMatrix object.	 */	void multMatrix(const util::CMatrix& m)	{		m_matrix *= m;	}};


/** * Perform a transform on a matrix. * \param m The matrix you want to transform * \param mat A CMatrix object which you want to transform the selected matrix by, */void COpenGLDriver::transform(MatrixType m, const util::CMatrix& mat){	float mtx[16];	mat.convertToOpenGL(mtx);	switch(m){	case MODEL_MATRIX:		glMatrixMode(GL_MODELVIEW);		glMultMatrixf(mtx);		break;	default:		break;	}	// FIXME: finish learning about the necessary matrices 	// and transforms and finish writing - 6/09/05}


CMatrix functions:
class CMatrix{private:	float matrix[4][4];};/** * Copies a transpose of this matrix into the array * pointed to by 'mtx'. * \param mtx An array of floats, at least 16 (floats) in size. */void CMatrix::convertToOpenGL(float* mtx) const{	CMatrix mat(*this);	mat.transpose();	memcpy(mtx, mat.matrix, sizeof(float)*16);// copy transposed matrix into 'mtx'}/** * Sets 'this' matrix to the translation matrix, given 3 floats. * \param x The x component of the translation matrix. * \param y The y component of the translation matrix. * \param z The z component of the translation matrix. */void CMatrix::setTranslation(float x, float y, float z){	setIdentity();		matrix[0][3] = x;	matrix[1][3] = y;	matrix[2][3] = z;}/** * Sets 'this' matrix to a rotation matrix. The rotation is about the Z axis. * \param deg The radian value to rotate the matrix about the Z axis. */void CMatrix::setRotationZ(float rad){	setIdentity();	matrix[0][0] = (float)cos(rad);	matrix[0][1] = (float)-(sin(rad));	matrix[1][0] = (float)sin(rad);	matrix[1][1] = (float)cos(rad);}/** * Converts the degrees parameter to a radian value and then calls CMatrix::setRotationZ * to set the rotation matrix. * \param deg The degrees value to set the rotation matrix around the Z axis */void CMatrix::setRotationZDegrees(float deg){	setRotationZ( (float)(deg*PI) / 180.0f );}/** * Sets 'this' matrix to a rotation matrix. The rotation is about the X axis. * \param deg The radian value to rotate the matrix about the X axis. */void CMatrix::setRotationX(float rad){	setIdentity();	matrix[1][1] = (float)cos(rad);	matrix[1][2] = (float)-(sin(rad));	matrix[2][1] = (float)sin(rad);	matrix[2][2] = (float)cos(rad);}/** * Converts the degrees parameter to a radian value and then calls CMatrix::setRotationX * to set the rotation matrix. * \param deg The degrees value to set the rotation matrix around the X axis */void CMatrix::setRotationXDegrees(float deg){	setRotationX( (float)(deg*PI) / 180.0f );}/** * Sets 'this' matrix to a rotation matrix. The rotation is about the Y axis. * \param deg The radian value to rotate the matrix about the Y axis. */void CMatrix::setRotationY(float rad){	setIdentity();	matrix[0][0] = (float)cos(rad);		matrix[0][2] = (float)sin(rad);	matrix[2][0] = (float)-(sin(rad));	matrix[2][2] = (float)cos(rad);}/** * Converts the degrees parameter to a radian value and then calls CMatrix::setRotationY * to set the rotation matrix. * \param deg The degrees value to set the rotation matrix around the Y axis */void CMatrix::setRotationYDegrees(float deg){	setRotationY( (float)(deg*PI) / 180.0f );}/** * Overloaded *= operator. * \param o A const reference to a CMatrix object. * \return A reference to 'this' CMatrix object. */CMatrix& CMatrix::operator*=(const CMatrix& o){	// Temp vars	CMatrix& t = *this;	// Create a new matrix as a copy of this one	CMatrix m(*this);	// Multiply matricies - each row of first by each column of second	// First row	t(0,0)= m(0,0)*o(0,0) + m(0,1)*o(1,0) + m(0,2)*o(2,0) + m(0,3)*o(3,0);	t(0,1)= m(0,0)*o(0,1) + m(0,1)*o(1,1) + m(0,2)*o(2,1) + m(0,3)*o(3,1);	t(0,2)= m(0,0)*o(0,2) + m(0,1)*o(1,2) + m(0,2)*o(2,2) + m(0,3)*o(3,2);	t(0,3)= m(0,0)*o(0,3) + m(0,1)*o(1,3) + m(0,2)*o(2,3) + m(0,3)*o(3,3);	// Second row	t(1,0)= m(1,0)*o(0,0) + m(1,1)*o(1,0) + m(1,2)*o(2,0) + m(1,3)*o(3,0);	t(1,1)= m(1,0)*o(0,1) + m(1,1)*o(1,1) + m(1,2)*o(2,1) + m(1,3)*o(3,1);	t(1,2)= m(1,0)*o(0,2) + m(1,1)*o(1,2) + m(1,2)*o(2,2) + m(1,3)*o(3,2);	t(1,3)= m(1,0)*o(0,3) + m(1,1)*o(1,3) + m(1,2)*o(2,3) + m(1,3)*o(3,3);	// Third row	t(2,0)= m(2,0)*o(0,0) + m(2,1)*o(1,0) + m(2,2)*o(2,0) + m(2,3)*o(3,0);	t(2,1)= m(2,0)*o(0,1) + m(2,1)*o(1,1) + m(2,2)*o(2,1) + m(2,3)*o(3,1);	t(2,2)= m(2,0)*o(0,2) + m(2,1)*o(1,2) + m(2,2)*o(2,2) + m(2,3)*o(3,2);	t(2,3)= m(2,0)*o(0,3) + m(2,1)*o(1,3) + m(2,2)*o(2,3) + m(2,3)*o(3,3);	// Fourth row	t(3,0)= m(3,0)*o(0,0) + m(3,1)*o(1,0) + m(3,2)*o(2,0) + m(3,3)*o(3,0);	t(3,1)= m(3,0)*o(0,1) + m(3,1)*o(1,1) + m(3,2)*o(2,1) + m(3,3)*o(3,1);	t(3,2)= m(3,0)*o(0,2) + m(3,1)*o(1,2) + m(3,2)*o(2,2) + m(3,3)*o(3,2);	t(3,3)= m(3,0)*o(0,3) + m(3,1)*o(1,3) + m(3,2)*o(2,3) + m(3,3)*o(3,3);return *this;}



I think that should be all of the relevant code.


[size="2"][size=2]Mort, Duke of Sto Helit: NON TIMETIS MESSOR -- Don't Fear The Reaper
First, from your setTranslation function it looks like your matrix is column major, not row major.

Also, if I remember correctly, OpenGL uses row major layout of matrices in memory, it's only the documentation which uses column major notation. I might be wrong on this, since I don't usually use OpenGL.

Lastly, you'd probably want to concatinate the matrices in the opposite order to perform local transformations.
Quote:First, from your setTranslation function it looks like your matrix is column major, not row major.

Also, if I remember correctly, OpenGL uses row major layout of matrices in memory, it's only the documentation which uses column major notation. I might be wrong on this, since I don't usually use OpenGL.
Just a couple of random points and clarifications...

The OP's setTranslation() function indicates that he's using column vectors, not that the matrix is column major. In fact, he's using 2d indexing, which clouds the issue of majorness somewhat. It happens that 2d c++ arrays are stored in row-major format, a fact the OP relies on in his memcpy() call. To ensure portability of code and consistency from platform to platform, it's a good idea to represent your matrix internally using a 1d rather than 2d array. This gives you complete control over the majorness and any related issues.

Confusing row/column vectors and row/column-major is a common mistake, one which I also made when I was first learning this material. Getting these two concepts clear in your mind early on will save you trouble down the line.

Also, OpenGL usus column-major rather than row-major format. It also uses column vectors, so it happens that the layout in memory is identical to that of a DirectX matrix. However, the code for multiplying OpenGL and DirectX matrices, as well as the order of operations, is different - the similarity in layout is really just incidental and shouldn't be invested with too much meaning.

I didn't look over all the code, but given the variables of majorness and vector convention, there are a lot of ways to get things wrong. Worse yet, two mistakes will often cancel each other out, so it's possible to have working code that is still incorrect :-)
Okay, well, I'm now re-writing my matrix class for a 1D array. Is there anything specific way I have to write it to insure that it is row-major or column-major?

And I'm going to keep the vectors as rows, instead of columns, and do a simple transpose when I need to interact with OGL.
[size="2"][size=2]Mort, Duke of Sto Helit: NON TIMETIS MESSOR -- Don't Fear The Reaper
Quote:Okay, well, I'm now re-writing my matrix class for a 1D array. Is there anything specific way I have to write it to insure that it is row-major or column-major?
Just remember how the matrix is laid out in memory. Here's a matrix with 2d indexing:

[00 01]
[10 11]

The row-major form would be:

[0 1]
[2 3]

Column-major:

[0 2]
[1 3]

In code it's generally more intuitive to work with 2d indexing. One way to do this is to write an inline function that converts from 2d to 1d, something like:
private:    float    m_data[16];    float& m_(unsigned int i, unsigned int j) {        return m_data[i*4+j];    }
That example would be for row major. For column major, you'd use i+j*4.
Quote:And I'm going to keep the vectors as rows, instead of columns, and do a simple transpose when I need to interact with OGL.
Are you going to be using this class primarily with OpenGL? If so, why not just simplify things and use column vectors for consistency?

Let me know if you need more info.
Thanks, jyk.

I might be PM'ing you soon.
[size="2"][size=2]Mort, Duke of Sto Helit: NON TIMETIS MESSOR -- Don't Fear The Reaper

This topic is closed to new replies.

Advertisement