Open GL Super Bible

Previous Table of Contents Next


Displaying the Results

Once we have determined the viewing, lighting, and material parameters, all that remains is to render the scene. Listing 10-3 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 10-3 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 Time

Any 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. Three-dimensional 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 CAD-type 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 Head

The 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 10-2 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.


Figure 10-2  Triangle outline of bolt head

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 10-4 is the function that renders the head of the bolt. Figure 10-3 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.


Figure 10-3  Output from the HEAD program

Listing 10-4 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();

}


Previous Table of Contents Next