Lesson 3 

Lists and Models

In this lesson we take what we learned in lesson 2 and build on it. We will learn about triangle lists and modeling.  You can use other planar polygons such as squares but when using them you must make sure that the points are all on the same plane. With triangles we don't have the same worries. Any three points in space when connected form a planar triangle. If you do use squares and accidentally use four points not in the same plane you will get unpredictable erroneous results.

You can continue with the lesson 1 project you've already created and modified in lesson 2. For continuity and to allow you to download the lessons as separate projects I will be showing the new code in a new project called Lesson3. 

If you didn't do lesson 2 you can get it here.

Up until now we have been putting all our drawing code in the display function. We could continue to do this but you can imagine its going to get pretty cumbersome once we have a few hundred things on the screen; not to mention all the repetitive cut and paste code blocks we would have. Lets start by encapsulating the code for drawing an object.

Here's what we need:

1. A class to hold our OpenGL object code.

2. A draw function for that object.

3. A public member variable to hold the position of the object in 3 space

4. A public member variable to hold the current rotation of the object on 3 axis.

5. A public member variable to hold the rate of rotation of the object on 3 axis.

Notice that we will be storing sets of 3 numbers for location, rotation and rate of rotation.

6. Create a class that holds 3 floats. We can use instances of this class to hold the location, rotation, and rate of rotation for our object.

7. A function for our object to do one animation frame.

Click on the class tab and right click on the project and choose add, and add class. Create a generic C++ class named glObject. Right click on the glObject class and choose add, and add function. Create a function named draw with a void return and no parameters. Create  a public member function called tick with with a void return and no parameters.

Click on the class tab and right click on the project and choose add, and add class. Create a generic C++ class named glPoint3D. Right click on the glPoint3D class and choose add and add variable. Add 3 public float variables to the glPoint3D class named x, y and z.

Right click on the glObject class and choose add and add variable. Add 3 public variables to the glObject class of type glPoint3D named loc, rotation and rateRotate. In the add class variable wizard you will need to type in the variable type of glPoint3D since its not a standard type and therefore is not on the combobox drop down list.

Expand the glObject class and double click on the default constructor glObject(void) you will notice that the 3  glPoint3D variables are listed here with a constructor input of (0). This code was automatically generated by the IDE when we used the wizard to add variables to the class. However there is not a constructor for the glPoint3D class that takes an int as a parameter. To correct this just erase the 0 in each of these lines so we will be calling the default constructor for the glPoint3D class. The glObject constructor should look like this now.

Now we are ready to put code in our the glObject draw function. Most of this code you have seen before in the display function for lesson 2. The notable changes are that we will use a list of triangles to draw our cube on the screen instead of using the glut pre-made cube function. It takes 3 points to make each triangle and since there are 6 side to a cube we will have 12 triangles. At first you might think we need 36 points (3 * 12) for the triangles but a lot of the points are shared in common with neighboring triangles so we really only have 8 unique points in a cube (the corners). We also need to set the color for each of the triangles. We will pick different colors for each triangle so we can see the cube well. We will set up some arrays holding our floats so we can iterate through them when drawing the triangle list. Expand the glObject class and double click on the draw function. Put the following code in the draw function:

glPushMatrix();// save matrix prior to transform
glTranslatef(loc.x, loc.y, loc.z);
glRotatef( rotation.x, 1.0, 0.0, 0.0 );//rotate about the X axis
glRotatef( rotation.y, 0.0, 1.0, 0.0 );//rotate about the Y axis
glRotatef( rotation.z, 0.0, 0.0, 1.0 );//rotate about the Z axis
// 8 unique points in a cube
float vert_xyz[8][3] =
{
{ -10.0, -10.0, -10.0 }, // point 0
{ -10.0, -10.0, 10.0 }, // point 1
{ -10.0, 10.0, -10.0 }, // point 2
{ -10.0, 10.0, 10.0 }, // point 3
{ 10.0, -10.0, -10.0 }, // point 4
{ 10.0, -10.0, 10.0 }, // point 5
{ 10.0, 10.0, -10.0 }, // point 6
{ 10.0, 10.0, 10.0 } // point 7
};
int tri_abc[12][3] =
{
{0,2,4}, {4,2,6}, // Back
{0,4,1}, {1,4,5}, // Bottom
{0,1,2}, {2,1,3}, // Left
{4,6,5}, {5,6,7}, // Right
{2,3,6}, {6,3,7}, // Top
{1,5,3}, {3,5,7} // Front
};
//the colors of each of the triangles
float colors_rgb[12][3] =
{
{0.5f,0.1f,0.1f }, {1.0f,0.1f,0.1f }, // Red
{0.5f,0.5f,0.1f }, {1.0f,1.0f,0.1f }, // Yellow
{0.1f,0.5f,0.1f }, {0.1f,1.0f,0.1f }, // Green
{0.1f,0.5f,0.5f }, {0.1f,1.0f,1.0f }, // Cyan
{0.1f,0.1f,0.5f }, {0.1f,0.1f,1.0f }, // Blue
{0.5f,0.1f,0.5f }, {1.0f,0.1f,1.0f } // Magenta
};

// heres where we iterate through the triangle list
int iTriTotal = 12;
int iTriIndex = 0;
glBegin(GL_TRIANGLES); // this tells OpenGL to change the state to drawing triangles
for ( iTriIndex = 0; iTriIndex < iTriTotal; iTriIndex++ )
{
glColor3fv( colors_rgb[iTriIndex] );//set the color of the vertex
glVertex3fv( vert_xyz[tri_abc[iTriIndex][0]] );//set the A vertex of the triangle
glColor3fv( colors_rgb[iTriIndex] );//set the color of the vertex
glVertex3fv( vert_xyz[tri_abc[iTriIndex][1]] );//set the B vertex of the triangle
glColor3fv( colors_rgb[iTriIndex] );//set the color of the vertex
glVertex3fv( vert_xyz[tri_abc[iTriIndex][2]] );//set the C vertex of the triangle
}
glEnd();// take OpenGL out of the draw list state
glPopMatrix();// restore matrix after transform

Here's a screen capture of the function with indentations::


Notice we used arrays in the draw function. The reason we did this is that glVertex3fv expects arrays as inputs. Even though this can be error prone with larger models we will use simple arrays  for these simple examples.  You may to use this array method of defining triangle lists in your larger projects if you like, but we will show you a more robust method later so you can choose how you would like to implement your models.

Now that our glObject has some display code lets add an instance of it to our project so we can see it in the scene.

Click on the class tab and right click on the MFCopenGL class and choose add and add variable. Create a private class variable of type glObject named cube to the MFCopenGL class.

In the MFCopenGL class display function add "cube.draw();" just before the "glutPostRedisplay();" call.

Compile and run and press play. You should see:

The spinning cube from lesson 2 is in our upper left but now we have a triangle list cube in the default location in the center of the scene.

In order to animate our new glObject we will need to put code in the glObject tick() function and call that function from our main program.

Click on the class tab and expand the glObject class. Double click on the tick(void) member function. Put the following code in the tick() fucntion:

rotation.x = rotation.x + rateRotate.x; //Increment the rotation about the X-axis
rotation.y = rotation.y + rateRotate.y; //Increment the rotation about the Y-axis
rotation.z = rotation.z + rateRotate.z; //Increment the rotation about the Z-axis
if(rotation.x >= 360) rotation.x = 0;// this keeps out rotation in the range of 0 to 360 degrees
if(rotation.y >= 360) rotation.y = 0;// this keeps out rotation in the range of 0 to 360 degrees
if(rotation.z >= 360) rotation.z = 0;// this keeps out rotation in the range of 0 to 360 degrees
if(rotation.x < 0) rotation.x = 359;// this keeps out rotation in the range of 0 to 360 degrees
if(rotation.y < 0) rotation.y = 359;// this keeps out rotation in the range of 0 to 360 degrees
if(rotation.z < 0) rotation.z = 359;// this keeps out rotation in the range of 0 to 360 degrees

I bet this code looks remarkably familiar doesn't it. That's a modified version of our animation code we used in lesson 2. What we are doing is adding the rate of rotation value to the rotation storage variable on each animation tick. Your tick function should look like this:

We will need to set the rate of rotation for our cube. In the default constructor for the MFCopenGL class add

cube.rateRotate.x = 1;
cube.rateRotate.y = 2;
cube.rateRotate.z = 4;

The constructor should look like this:

In order to make the animation happen we will need to call it in our loop. In our display function put "cube.tick();" just before "cube.draw();"

Compile and run and press play. You should see the frame cube from lesson 2 and the spinning multicolored cube in the center.

Remember how we moved and shrank the frame cube? See if you can move the new cube to the lower right and shrink it to half size so it looks like this:

In order to move our cube you may have realized you could set the loc variable for cube in the MFCopenGL constructor just like we set the rotation.

You may be wondering where we set the scale for our cube. Some of you may have just hard coded a "glScalef(0.5,0.5,0.5);" in the glObjects draw function while others may have been more clever and added another variable to our glObject class. We will go with the latter method. Add a public member variable of type glPoint3D to the glObject class.

Fix the default constructor by removing the 0 in (0).

Set the cube scale in the MFCopenGL default constructor.

Add a call to glScalef to the glObject draw function.


Now your scene should show:

Remove the code left over from lesson 2 that draws the frame cube and replace it with another instance of our glObject.

Did you get you scene to look like this?

If not here's the code:

The MFCopenGL with another glObject added.

The MFCopenGL default constructor.

The display function.

The display function is much easier to read now. Lets add two more cubes in the other corners, but put all 4 cubes in an array instead of instancing them separately. Use for loops to iterate through your object array and vary the speed of rotation based on the position in the array. This will give you visual feed back to indicate which cube is which on the screen. 

The array declaration.

The constructor.

The display function.

That's it for this lesson. On your own try making models for other common shapes like pyramid or sphere. Modeling software maybe needed for more complex shapes but you should be able to do some simple ones by using graph paper and hand coding them.

You can download the source code for this lesson here.