object translation and rotation around local coordinates

Hi all,

I’ve looked at about 100 or more forums about translations and rotations and a lot of them say the same thing that i’ve been doing in my code. However, I’m still unable to do what i want.

What I have is a large plane that is to be my area where my object is able to move. I then have a cube that is drawn and starts off at a certain position on the plain. It then has the ability to rotate 360 degrees about its local Y-axis and then translate up and down its local Z-axis.

I am able to draw my cube on its own local coordinate system. Before i command it to move, it rotates correctly about its own Y-axis. Once i command it to translate down the positive Z-axis, that is also correct. The problem is once I try to rotate my cube again, it rotates relative to the Y-axis at the initial position Z=0. Basically, I want the coordinate system to follow the cube where ever it goes, so that it can rotate and move about the plane freely.

Relevant code is posted below. If you need more information, please let me know!!! ANY HELP IS APPRECIATED!! THANKS!

CODE FOR DRAWING AND MOVING THE CUBE
cube.cpp

 
void cube::drawCube(GLfloat x, GLfloat y, GLfloat z, int spin){
	glRotatef((GLfloat) _spin, 0.0, 1.0, 0.0);
	glTranslatef(_x, _y, _z);
	if (!_texLoaded)
		setupTexture();
	glEnable(GL_TEXTURE_2D);
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
	glBindTexture(GL_TEXTURE_2D, _robotTex);
	glBegin(GL_QUADS);
		// front
		glTexCoord2f(0.0f, 0.0f); glVertex3f(-8.0f, 0.0f, 8.0f);
		glTexCoord2f(1.0f, 0.0f); glVertex3f(8.0f, 0.0f, 8.0f);
		glTexCoord2f(1.0f, 1.0f); glVertex3f(8.0f, 16.0f, 8.0f);
		glTexCoord2f(0.0f, 1.0f); glVertex3f(-8.0f, 16.0f, 8.0f);
	glEnd();
	glBindTexture(GL_TEXTURE_2D, _robot2Tex);
	glBegin(GL_QUADS);
		// left side
		glTexCoord2f(0.0f, 0.0f); glVertex3f(-8.0f, 0.0f, -8.0f);
		glTexCoord2f(1.0f, 0.0f); glVertex3f(-8.0f, 0.0f, 8.0f);
		glTexCoord2f(1.0f, 1.0f); glVertex3f(-8.0f, 16.0f, 8.0f);
		glTexCoord2f(0.0f, 1.0f); glVertex3f(-8.0f, 16.0f, -8.0f);
		// backside
		glTexCoord2f(0.0f, 0.0f); glVertex3f(8.0f, 0.0f, -8.0f);
		glTexCoord2f(1.0f, 0.0f); glVertex3f(-8.0f, 0.0f, -8.0f);
		glTexCoord2f(1.0f, 1.0f); glVertex3f(-8.0f, 16.0f, -8.0f);
		glTexCoord2f(0.0f, 1.0f); glVertex3f(8.0f, 16.0f, -8.0f);
		// right side
		glTexCoord2f(0.0f, 0.0f); glVertex3f(8.0f, 0.0f, 8.0f);
		glTexCoord2f(1.0f, 0.0f); glVertex3f(8.0f, 0.0f, -8.0f);
		glTexCoord2f(1.0f, 1.0f); glVertex3f(8.0f, 16.0f, -8.0f);
		glTexCoord2f(0.0f, 1.0f); glVertex3f(8.0f, 16.0f, 8.0f);
		// top
		glTexCoord2f(0.0f, 0.0f); glVertex3f(-8.0f, 16.0f, 8.0f);
		glTexCoord2f(1.0f, 0.0f); glVertex3f(8.0f, 16.0f, 8.0f);
		glTexCoord2f(1.0f, 1.0f); glVertex3f(8.0f, 16.0f, -8.0f);
		glTexCoord2f(0.0f, 1.0f); glVertex3f(-8.0f, 16.0f, -8.0f);
	glEnd();
	glDisable(GL_TEXTURE_2D);
}

void cube::moveForward(){
	_z += 3.5f;
}

void cube::moveBack(){
	_z -= 3.5f;
}

void cube::rotateLeft(){
	_spin = (_spin + 20) % 360;
}

void cube::rotateRight(){
	_spin = (_spin - 20) % 360;
} 

CODE FOR DRAWING THE PLANE
plane.cpp

 void plane::drawPlane(){
	if (!_texLoaded)
		setupTextures();
	glEnable(GL_TEXTURE_2D);
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
	glBindTexture(GL_TEXTURE_2D, _terrainTex);
	// plain
	GLfloat cellZ = 0.0f;
	GLfloat cellX = 0.0f;
	 
	for (int row = 1; row <= 19; row++){
		cellX = 0.0f;
		if (row != 19){
			for (int cell = 0; cell < 25; cell++){
				glBegin(GL_QUADS);	
					glTexCoord2f(0.0f, 0.0f);	glVertex3f(cellX, 0.0f, cellZ+32.0f);
					glTexCoord2f(1.0f, 0.0f);	glVertex3f(cellX+32.0f, 0.0f, cellZ+32.0f);
					glTexCoord2f(1.0f, 1.0f);	glVertex3f(cellX+32.0f, 0.0f, cellZ);
					glTexCoord2f(0.0f, 1.0f);	glVertex3f(cellX, 0.0f, cellZ);
				glEnd();
				cellX += 32.0f;
			}
		}	
		else{
			for (int cell = 0; cell < 25; cell++){
				glBegin(GL_QUADS);	
					glTexCoord2f(0.0f, 0.0f);	glVertex3f(cellX, 0.0f, cellZ+24.0f);
					glTexCoord2f(1.0f, 0.0f);	glVertex3f(cellX+32.0f, 0.0f, cellZ+24.0f);
					glTexCoord2f(1.0f, 1.0f);	glVertex3f(cellX+32.0f, 0.0f, cellZ);
					glTexCoord2f(0.0f, 1.0f);	glVertex3f(cellX, 0.0f, cellZ);
				glEnd();
				cellX += 32.0f;
			}
		}
		cellZ += 32.0f;
	}
	glDisable(GL_TEXTURE_2D);
} 

CODE FOR DRAWING THE SCENE
main.cpp

 
void drawScene(){
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(90.0f, 800.0f/ 600.0f, 1.0f, 750.0f);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(400.0f, cameraY, cameraZ, 400.0f, 0.0f, 300.0f, 0.0f, 1.0f, 0.0f);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glPushMatrix();
		_plane.drawPlane();	
	glPopMatrix();
	glTranslatef(400.0f, 0.0f, 300.0f);
	glPushMatrix();
		_cube.drawCube(_cube.getX(), _cube.getY(), _cube.getZ(), _cube.getSpin());
	glPopMatrix();
	glTranslatef(_cube.getX(), 0.0f, _cube.getZ());

	glFlush();
	glutSwapBuffers();
	
} 

Firstly, you need to make sure you understand how the gl deals with transformations. When you multiply a matrix by the current matrix stack you have
GL_Matrix = GL_Matirx * YourModelMatrix, where the “GL_Matrix” is the concatenation (combination) of all transformations since the last LoadIdentity() call. Your transformed point is then Point’ = (M1 * (M2 * (M3 … (Mn * Point)))), where each M is a successive transformation. As you can see, the last matrix is applied first, this is important to remember!
So, say you first want to orient your cube on the plane. First you need to convert the plane into a matrix that you can use as a transformation. For example, if your plane normal is (0,0,1), then one of the columns in your matrix is this vector. You simply need to find the other 2 “basis” vectors to complete the matrix. There are a number of ways to do this. A common approach is to use the major axis of this plane normal:

switch( majorAxis(plane) ) {
case 0: // fall through
case 1: temp = Vector(0,0,1); break;
case 2: temp = Vector(1,0,0); break;
}
tangentVector = cross(temp,plane);
binormalVector = cross(plane,tangentVector);

where majorAxis() returns 0,1, or 2, depending on the component with the largest unsigned magnitude, cross() is the standard vector outer product. From the above example (0,0,1), our other 2 vector would be (1,0,0), (0,1,0), giving us the identity matrix, but this will work with any plane normal.
Now we have the columns of our matrix.
M = ( planeT, tangentVectorT, binormalVectorT ), where the T indicates transposition. We still need to deal with the distance of the plane for translation, the 4th column of the matrix, but this is just the distance of any point on the plane along each of the vector we just created.
4th column = ( dot(point,plane),dot(point,trangentVector), dot(point,binormalVector), 1), where point is any point on the plane.

Now that you have a matrix, the rest is easy, just push it onto the stack, the draw your box as usual. Any additional transforms you can push onto the stack to orient the cube and move it as you see fit. The trick is using glPushMatrix() to perform incremental rotations and translations, rather that starting from scratch each time.

Hi,

Thanks for your reply. I am trying to follow your explanation but I don’t quite follow. My experience with matrices is pretty limited and im very new to openGL. is there any way you could explain this again?

your time and help is very appreciated!!

Hi moogoo, let me suggest that you bone up on matrix algebra. Take my word for it, if you don’t, you will be in agony woking with complex articulated systems later on. But please don’t be put off, it is really quite fun. Grab any book on linear algebra. It will pay fotr itself 100000000000000 fold! It would simply take me too long to get into the whole thing here. Let me say that your first question was not a simple one at all!

I would like to add this thought though:
Every rotation matrix is simply a coordinate system transposed. That is the colums of every rotation matrix form the x, y, and z axes of that system. If you stick the world axes x=(1,0,0), y=(0,1,0), and z=(0,0,1) into the columns of a matrix, you get the identity matrix.
Just try to imagine the vectors you need for a particular setup, make sure they are mutually orthogonal (perpendicular) and normalized (unit length), put them into a matrix, and multiply that matrix onto the stack. I hope this clarifies things a little.