Open GL Super Bible
Displaying the ResultsOnce we have determined the viewing, lighting, and material parameters, all that remains is to render the scene. Listing 103 shows the code outline used to display our bolt and bolt pieces. The SomeFunc() line is just a placeholder for function calls to render the head, shaft, and threads individually. We save the matrix state, perform any rotations (defined by the keyboard activity, as in all this book’s previous examples), and call a function that renders some specific object or part of an object. Listing 103 Rendering the object, allowing for rotated views // Called to draw scene void RenderScene(void) { // Clear the window with current clearing color glClear(GL_COLOR_BUFFER_BIT  GL_DEPTH_BUFFER_BIT); // Save the matrix state glMatrixMode(GL_MODELVIEW); glPushMatrix(); // Rotate about x and y axes glRotatef(xRot, 1.0f, 0.0f, 0.0f); glRotatef(yRot, 0.0f, 1.0f, 0.0f); // Specific code to draw the object … … … SomeFunc(); // Place Holder glPopMatrix(); // Flush drawing commands glFlush(); } Constructing a Model, One Piece at a TimeAny given programming task can be separated into smaller, more manageable tasks. This makes the smaller pieces easier to handle and code, and introduces some reusability into our code base, as well. Threedimensional modeling is no exception, you will create large complex systems out of many smaller and more manageable pieces. We have decided to break the bolt down into three pieces: head, shaft, and thread. Certainly this makes it much simpler for us to consider each section graphically, but it also give us three objects that we can reuse. In more complex modeling applications, this reusability is of crucial importance. In a CADtype application, for example, you would probably have many different bolts to model—with various lengths, thickness, and thread density. Instead of the RenderHead() function that draws the head of the bolt in this example, you might want to write a function that takes parameters specifying the number of sides, thickness, and diameter of the bolt head. Another thing we will do is model each piece of our bolt in coordinates that are most convenient for describing the object. Most often, objects are modeled around the origin and then translated and rotated into place. Later, when composing the final object, we can translate the components, rotate them, and even scale them if necessary to assemble our composite object. The HeadThe head of our bolt has six smooth sides and is smooth on top and bottom, as well. We can construct this solid object with two hexagons that represent the top and bottom of the head, and a series of quadrilaterals around the edges to represent the sides. We could use GL_QUAD and GL_POLYGON to draw this with a minimum number of vertices; however, as we’ve mentioned previously, you should always use triangles whenever possible. For any accelerated OpenGL hardware (and even some software routines), it may actually be faster to draw two triangles arranged together rather than a single quadrilateral. Figure 102 illustrates how the bolt head will be constructed with triangles. We use a triangle fan with six triangles for the top and bottom sections of the head. Then each face of the side of the bolt is composed of two triangles.
A total of 24 triangles are used to draw the head of the bolt: 6 each on the top and bottom, and 12 more to compose the sides of the bolt head. Listing 104 is the function that renders the head of the bolt. Figure 103 shows the output of this program, HEAD, in this chapter’s subdirectory on the CD. Notice that this code contains no functions that we haven’t yet covered, but it’s more substantial than any of the simpler chapter examples.
Listing 104 Rendering the head of the bolt // Creates the head of the bolt void RenderHead(void) { float x,y,angle; // Calculated positions float height = 25.0f; // Thickness of the head float diameter = 30.0f; // Diameter of the head float normal[3],corners[4][3]; // Storage of vertices and normals float step = (3.1415f/3.0f); // step = 1/6th of a circle = hexagon // Set material color for head of bolt glColor3f(0.0f, 0.0f, 0.7f); // Clockwise polygons face out, set for fans glFrontFace(GL_CW); // Begin a new triangle fan to cover the top glBegin(GL_TRIANGLE_FAN); // All the normals for the top of the bolt point straight up // the z axis. glNormal3f(0.0f, 0.0f, 1.0f); // Center of fan is at the origin glVertex3f(0.0f, 0.0f, 0.0f); // Divide the circle up into 6 sections and start dropping // points to specify the fan for(angle = 0.0f; angle < (2.0f*3.1415f); angle += step) { // Calculate x and y position of the next vertex x = diameter*(float)sin(angle); y = diameter*(float)cos(angle); // Specify the next vertex for the triangle fan glVertex3f(x, y, 0.0f); } // Last vertex closes the fan glVertex3f(0.0f, diameter, 0.0f); // Done drawing the fan that covers the bottom glEnd(); // Now draw the bottom of the bolt head. Switch to // clockwise polygons facing out. glFrontFace(GL_CCW); // Begin a new triangle fan to cover the bottom glBegin(GL_TRIANGLE_FAN); // Normal for bottom points straight down the negative z axis glNormal3f(0.0f, 0.0f, 1.0f); // Center of fan is at the origin glVertex3f(0.0f, 0.0f, height); // Divide the circle up into 6 sections and start dropping // points to specify the fan for(angle = 0.0f; angle < (2.0f*3.1415f); angle += step) { // Calculate x and y position of the next vertex x = diameter*(float)sin(angle); y = diameter*(float)cos(angle); // Specify the next vertex for the triangle fan glVertex3f(x, y, height); } // Last vertex, used to close the fan glVertex3f(0.0f, diameter, height); // Done drawing the fan that covers the bottom glEnd(); // Build the sides out of triangles (two each). Each face // will consist of two triangles arranged to form a // quadrilateral glBegin(GL_TRIANGLES); // Go around and draw the sides for(angle = 0.0f; angle < (2.0f*3.1415f); angle += step) { // Calculate x and y position of the next hex point x = diameter*(float)sin(angle); y = diameter*(float)cos(angle); // start at bottom of head corners[0][0] = x; corners[0][1] = y; corners[0][2] = height; // extrude to top of head corners[1][0] = x; corners[1][1] = y; corners[1][2] = 0.0f; // Calculate the next hex point x = diameter*(float)sin(angle+step); y = diameter*(float)cos(angle+step); // Make sure we aren't done before proceeding if(angle+step < 3.1415*2.0) { // If we are done, just close the fan at a // known coordinate. corners[2][0] = x; corners[2][1] = y; corners[2][2] = 0.0f; corners[3][0] = x; corners[3][1] = y; corners[3][2] = height; } else { // We aren't done, the points at the top and bottom // of the head. corners[2][0] = 0.0f; corners[2][1] = diameter; corners[2][2] = 0.0f; corners[3][0] = 0.0f; corners[3][1] = diameter; corners[3][2] = height; } // The normal vectors for the entire face will // all point the same direction calcNormal(corners, normal); glNormal3fv(normal); // Specify each triangle separately to lie next // to each other. glVertex3fv(corners[0]); glVertex3fv(corners[1]); glVertex3fv(corners[2]); glVertex3fv(corners[0]); glVertex3fv(corners[2]); glVertex3fv(corners[3]); } glEnd(); }
