Hello, everyone!

I'm starting to learn OpenGL and I have a problem with linear transformations.

The program is very basic: I want to draw 4 squares and make them move individually (by doing translations and rotation). I understand that I need to stock all the transformation in a matrix, so I can multiply it to the square information to get the good translation and rotation parameters. Unfortunately, the red square (the one missing) doesn't want to print on the screen. I have searched for a long time and I don't know what causes the problem: maybe a bad mode initialize or something else... Can someone help me? I have posted the code below.

N.B: You may need to add some includes for the code to work. My confguration of OpenGL only needs <gl/glut.h>

#include <gl/glut.h>
#include <stdio.h>

/**
	This program draws 4 squares and do moves on them (translations, rotations, ...)
*/

GLint windowX;
GLint windowY;

GLdouble redTransformationMatrix[16];

void reshape(GLint x, GLint y)
{
	windowX = x;
	windowY = y;

	glViewport(0,0,windowX,windowY);						// Reset The Current Viewport

	glMatrixMode(GL_PROJECTION);						// Select The Projection Matrix
	glLoadIdentity();									// Reset The Projection Matrix

	glMatrixMode(GL_MODELVIEW);							// Select The Modelview Matrix
	glLoadIdentity();									// Reset The Modelview Matrix
}

/**
	\brief	Initializes transformations.
*/
void init()
{
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glGetDoublev(GL_MODELVIEW_MATRIX, redTransformationMatrix);		//Sets the red square transformation matrix with an identity matrix.
}

/**
	\brief Function that is called when a key is pressed on the keyboard.
	\param key		The ASCII value of the key.
	\param x		The x value of the mouse position at the event.
	\param y		The y value of the mouse position at the event.
*/
void keyboardPressed(unsigned char key, int x, int y)
{
	switch (key)
	{
		case 'D':
		case 'd':
		{
			//Translation of the red square.
			glMatrixMode(GL_MODELVIEW);
			glLoadMatrixd(redTransformationMatrix);
			glTranslated(25.0, 0.0, 0.0);
			glGetDoublev(GL_MODELVIEW, redTransformationMatrix);
			glutPostRedisplay();
			break;
		}

	}
}

/**
	\brief Function that draws a square, knowing the middle point and the length of a side.
	\param middleX		The X coordinate of the middle point.
	\param middleY		The Y coordinate of the middle point.
	\param sideLength	The length of a side of the square.
*/
void DrawSquare(GLdouble middleX, GLdouble middleY, GLdouble sideLength)
{
	glBegin(GL_QUADS);
	glVertex2d(middleX - (sideLength / 2), middleY + (sideLength / 2));
	glVertex2d(middleX + (sideLength / 2), middleY + (sideLength / 2));
	glVertex2d(middleX + (sideLength / 2), middleY - (sideLength / 2));
	glVertex2d(middleX - (sideLength / 2), middleY - (sideLength / 2));
	glEnd();
}

/**
	\brief Function that is called for every display on the screen.
*/
void display()
{
	glMatrixMode(GL_MODELVIEW);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    // Reset the current modelview matrix
    glLoadIdentity();		//Makes sure that no affine transformation applies here.

	//We need to set the viewport and the orthonormal bounds.
	glViewport(0, 0, windowX, windowY);
	glOrtho(0.0, windowX, 0.0, windowY, -1.0, 1.0);

	//Let's draw the squares.
	//Red square -- trying here to apply the transformation matrix on the red square.
	glPushMatrix();
	glLoadMatrixd(redTransformationMatrix);
	glColor3d(1.0, 0.0, 0.0);
	DrawSquare(200.0, 600.0, 100.0);
	glPopMatrix();

	//Green square
	glColor3d(0.0, 1.0, 0.0);
	DrawSquare(600.0, 600.0, 100.0);
	//Blue square
	glColor3d(0.0, 0.0, 1.0);
	DrawSquare(600.0, 200.0, 100.0);
	//Yellow square
	glColor3d(1.0, 1.0, 0.0);
	DrawSquare(200.0, 200.0, 100.0);

	glutSwapBuffers();		//Very important, because we are using double buffers and we want to print.
}

int main(int argc, char** argv)
{
	glutInit(&argc, argv);

	glClearColor(0.0, 0.0, 0.0, 0.0);		//Black background color.

	windowX = 800;
	windowY = 800;

	glutInitWindowPosition(0, 0);
	glutInitWindowSize(windowX, windowY);

	glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);

	glutCreateWindow("FourSquares");

	//Basic initialize...
	init();
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glutKeyboardFunc(keyboardPressed);
	glutReshapeFunc(reshape);
	glutDisplayFunc(display);

	//The events may start.
	glutMainLoop();

	return 0;
}

You setup an orthographic projection matrix, and then in the pushmatrix/popmatrix block, you load a matrix in model space, this 'undoes' your projection until the matrix is popped...

You could either replace this:

glPushMatrix();
glLoadMatrixd(redTransformationMatrix);
glColor3d(1.0, 0.0, 0.0);
DrawSquare(200.0, 600.0, 100.0);
glPopMatrix();

with this:

glPushMatrix();
[b]glMultMatrixd[/b](redTransformationMatrix);
glColor3d(1.0, 0.0, 0.0);
DrawSquare(200.0, 600.0, 100.0);
glPopMatrix();

Which will work in this case, or much better, use the projection matrix aswell.

The projection matrix is automatically multiplied onto the modelview matrix during vertex transform, and that's typically where glOrtho/glFrustum/etc are used. Eg:

//  VIEWPORT AND BUFFER CLEAR FIRST, BECAUSE THEY ARE MATRIX-MODE INDEPENDANT

glViewport(0, 0, windowX, windowY);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

// SETUP ORTHOGRAPHIC PROJECTION

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, windowX, 0.0, windowY, -1.0, 1.0);

// NOW RESET THE MODELVIEW

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

// ... ETC...

Thanks for the help! My main error was the use of the GL_MODELVIEW flag instead of the GL_MODELVIEW_MATRIX flag.

Now I would like to do rotations. I know how to use glRotate*(), but I don't want to provide a vector expressed by the origin. For example, I would like to put a vector at the center of a square and make this square rotate around this vector. Any ideas to guide me?

It's easy enough to arbitrarily set the rotation point, e.g. this will work:

case 'E':
case 'e':
{
	//Rotation of the red square.
	glMatrixMode(GL_MODELVIEW_MATRIX);
	glLoadMatrixd(redTransformationMatrix);
	glTranslated(200.0, 600.0, 0.0);
	glRotated(1.0,0.0, 0.0, 1.0);
	glTranslated(-200.0, -600.0, 0.0);
	glGetDoublev(GL_MODELVIEW_MATRIX, redTransformationMatrix);
	glutPostRedisplay();
	break;
}

The general rule is, translate to the origin, rotate, and then translate back again.

However, you are making your life abit more difficult by offsetting the square outside of the matrix.. that is, you are doing matrix transforms, and then offseting by 200, 600 in that space.. you'll find it easier to initialize your matrix to that 200, 600 offset, and then always draw the square at 0,0 in the space of your matrix transform.

That is, if you do this:

void init()
{
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	[b]glTranslated (200.0, 600.0, 100.0);[/b]
	glGetDoublev(GL_MODELVIEW_MATRIX, redTransformationMatrix);
}
...
void display()
{
...
	glPushMatrix();
	glLoadMatrixd(redTransformationMatrix);
	glColor3d(1.0, 0.0, 0.0);
	[b]DrawSquare(0.0, 0.0, 0.0);[/b]
	glPopMatrix();
...
}

Then, you can just do this:

case 'E':
case 'e':
{
	//Rotation of the red square.
	glMatrixMode(GL_MODELVIEW_MATRIX);
	glLoadMatrixd(redTransformationMatrix);
	glRotated(1.0,0.0, 0.0, 1.0);
	glGetDoublev(GL_MODELVIEW_MATRIX, redTransformationMatrix);
	glutPostRedisplay();
	break;
}

You will find, when you do get it rotating properly, that any subsequent translation will be in the local space of the previous transform. That is, what you consider to be moving in 'x' will actually be moving in the rotated x direction. If that's what you want, great, otherwise, you can keep transforming in global 'x' by reversing the translate multiplication order, like this:

case 'D':
case 'd':
{
	//Translation of the red square.
	glMatrixMode(GL_MODELVIEW_MATRIX);
	glLoadIdentity ( );
	glTranslated(25.0, 0.0, 0.0);
	glMultMatrixd(redTransformationMatrix);
	glGetDoublev(GL_MODELVIEW_MATRIX, redTransformationMatrix);
	glutPostRedisplay();
	break;
}
commented: Your knowledge of 3d-development is somewhat frightening ;) +17

Thank you for all your tips. Now I understand how to do rotations and translations properly.

Comment the line like below
//glLoadMatrixd(redTransformationMatrix);

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.