Chapter 10. Curves and Surfaces

by Richard S. Wright, Jr.

WHAT YOU'LL LEARN IN THIS CHAPTER:

How To

Functions You'll Use

Draw spheres, cylinders, and disks

gluSphere/gluCylinder/gluDisk

Use maps to render Bézier curves and surfaces

glMap/glEvalCoord

Use evaluators to simplify surface mapping

glMapGrid/glEvalMesh

Create NURBS surfaces

gluNewNurbsRenderer/gluBeginSurface/ gluNurbsSurface/ gluEndSurface/ gluDeleteNurbsRendererf10

Create trimming curves

gluBeginTrim/gluPwlCurve/gluEndTrim

Tessellate concave and convex polygons

gluTessBeginPolygon/gluTessEndPolygon

The practice of 3D graphics is little more than a computerized version of connect-the-dots. Vertices are laid out in 3D space and connected by flat primitives. Smooth curves and surfaces are approximated using flat polygons and shading tricks. The more polygons used, usually the more smooth and curved a surface may appear. OpenGL, of course, supports smooth curves and surfaces implicitly because you can specify as many vertices as you want and set any desired or calculated values for normals and color values.

OpenGL does provide some additional support, however, that makes the task of constructing more complex surfaces a bit easier. The easiest to use are some GLU functions that render spheres, cylinders, cones (special types of cylinders, as you will see), and flat, round disks, optionally with holes in them. OpenGL also provides top-notch support for complex surfaces that may be difficult to model with a simple mathematical equation: Bézier and NURB curves and surfaces. Finally, OpenGL can take large, irregular, and concave polygons and break them up into smaller, more manageable pieces.

Built-in Surfaces

The OpenGL Utility Library (GLU) that accompanies OpenGL contains a number of functions that render three quadratic surfaces. These quadric functions render spheres, cylinders, and disks. You can specify the radius of both ends of a cylinder. Setting one end's radius to 0 produces a cone. Disks, likewise, provide enough flexibility for you to specify a hole in the center (producing a washer-like surface). You can see these basic shapes illustrated graphically in Figure 10.1.

Possible quadric shapes.

Figure 10.1. Possible quadric shapes.

These quadric objects can be arranged to create more complex models. For example, you could create a 3D molecular modeling program using just spheres and cylinders. Figure 10.2 shows the 3D unit axes drawn with a sphere, three cylinders, three cones, and three disks. This model can be included into any of your own programs using the following glTools function:

void gltDrawUnitAxes(void)
The x,y,z-axes drawn with quadrics.

Figure 10.2. The x,y,z-axes drawn with quadrics.

Setting Quadric States

The quadric surfaces can be drawn with some flexibility as to whether normals, texture coordinates, and so on are specified. Putting all these options into parameters to a sphere drawing function, for example, would create a function with an exceedingly long list of parameters that must be specified each time. Instead, the quadric functions use an object-oriented model. Essentially, you create a quadric object and set its rendering state with one or more state setting functions. Then you specify this object when drawing one of the surfaces, and its state determines how the surface is rendered. The following code segment shows how to create an empty quadric object and later delete it:

GLUquadricObj *pObj;
// . . .
pObj = gluNewQuadric();   // Create and initialize Quadric
// Set Quadric rendering Parameters
// Draw Quadric surfaces
// . . .
gluDeleteQuadric(pObj);   // Free Quadric object

Note that you create a pointer to the GLUQuadricObj data type, not an instance of the data structure itself. The reason is that the gluNewQuadric function not only allocates space for it, but also initializes the structure members to reasonable default values.

There are four functions that you can use to modify the drawing state of the GLUQuadricObj object and, correspondingly, to any surfaces drawn with it. The first function sets the quadric draw style:

void gluQuadricDrawStyle(GLUquadricObj *obj, GLenum drawStyle);

The first parameter is the pointer to the quadric object to be set, and the drawStyle parameter is one of the values in Table 10.1.

Table 10.1. Quadric Draw Styles

Constant

Description:

GLU_FILL

Quadric objects are drawn as solid objects.

GLU_LINE

Quadric objects are drawn as wireframe objects.

GLU_POINT

Quadric objects are drawn as a set of vertex points.

GLU_SILHOUETTE

This is similar to a wireframe, except adjoining faces of polygons are not drawn.

The next function specifies whether the quadric surface geometry would be generated with surface normals:

void gluQuadricNormals(GLUquadricObj *pbj, GLenum normals);

Quadrics may be drawn without normals (GLU_NONE), with smooth normals (GLU_SMOOTH), or flat normals (GLU_FLAT). The primary difference between smooth and flat normals is that for smooth normals, one normal is specified for each vertex of the surface, giving a smoothed-out appearance. For flat normals, one normal is used for all the vertices of any given facet (triangle) of the surface.

You can also specify whether the normals point out of the surface or inward. For example, looking at a lit sphere, you would want normals pointing outward from the surface of the sphere. However, if you were drawing the inside of a sphere--say, as part of a vaulted ceiling--you would want the normals and lighting to be applied to the inside of the sphere. The following function sets this parameter:

void gluQuadricOrientation(GLUquadricObj *obj, GLenum orientation);

Here, orientation can be either GLU_OUTSIDE or GLU_INSIDE. By default, quadric surfaces are wound counterclockwise, with the front faces facing the outsides of the surfaces. The outside of the surface is intuitive for spheres and cylinders; for disks, it is simply the side facing the positive z-axis.

Finally, you can request that texture coordinates be generated for quadric surfaces with the following function:

void gluQuadricTexture(GLUquadricObj *obj, GLenum textureCoords);

Here, the textureCoords parameter can be either GL_TRUE or GL_FALSE. When texture coordinates are generated for quadric surfaces, they are wrapped around spheres and cylinders evenly; they are applied to disks using the center of the texture for the center of the disk, and the edges of the texture lining up with the edges of the disk.

Drawing Quadrics

After the quadric object state has been set satisfactorily, each surface is drawn with a single function call. For example, to draw a sphere, you simply call the following function:

void gluSphere(GLUQuadricObj *obj, GLdouble radius, GLint slices, GLint stacks);

The first parameter, obj, is just the pointer to the quadric object that was previously set up for the desired rendering state. The radius parameter is then the radius of the sphere, followed by the number of slices and stacks. Spheres are drawn with rings of triangle strips (or quad strips, depending on whose GLU library you're using) stacked from the bottom to the top, as shown in Figure 10.3. The number of slices specifies how many triangle sets (or quads) are used to go all the way around the sphere. You could also think of this as the number of lines of latitude and longitude around a globe.

A quadric sphere's stacks and slices.

Figure 10.3. A quadric sphere's stacks and slices.

The quadric spheres are drawn on their sides with the positive z-axis pointing out the top of the spheres. Figure 10.4 shows a wireframe quadric sphere drawn around the unit axes.

A quadric sphere's orientation.

Figure 10.4. A quadric sphere's orientation.

Cylinders are also drawn along the positive z-axis and are composed of a number of stacked strips. The following function, which is similar to the gluSphere function, draws a cylinder:

void gluCylinder(GLUquadricObj *obj, GLdouble baseRadius,
                        GLdouble topRadius, GLdouble height,
                        GLint slices, GLint stacks);

With this function, you can specify both the base radius (near the origin) and the top radius (out along positive z-axis). The height parameter is simply the length of the cylinder. The orientation of the cylinder is shown in Figure 10.5. Figure 10.6 shows the same cylinder, but with the topRadius parameter set to 0, making a cone.

A quadric cylinder's orientation.

Figure 10.5. A quadric cylinder's orientation.

A quadric cone made from a cylinder.

Figure 10.6. A quadric cone made from a cylinder.

The final quadric surface is the disk. Disks are drawn with loops of quads or triangle strips, divided into some number of slices. You use the following function to render a disk:

void gluDisk(GLUquadricObj *obj, GLdouble innerRadius, 
                         GLdouble outerRadius,GLint slices, GLint loops);

To draw a disk, you specify both an inner radius and an outer radius. If the inner radius is 0, you get a solid disk like the one shown in Figure 10.7. A nonzero radius gives you a disk with a hole in it, as shown in Figure 10.8. The disk is drawn in the xy plane.

A quadric disk showing loops and slices.

Figure 10.7. A quadric disk showing loops and slices.

A quadric disk with a hole in the center.

Figure 10.8. A quadric disk with a hole in the center.

Modeling with Quadrics

In the sample program SNOWMAN, all the quadric objects are used to piece together a crude model of a snowman. White spheres make up the three sections of the body. Two small black spheres make up the eyes, and an orange cone is drawn for the carrot nose. A cylinder is used for the body of a black top hat, and two disks, one closed and one open, provide the top and the rim of the hat. The output from SNOWMAN is shown in Figure 10.9. Listing 10.1 shows the rendering code that draws the snowman by simply transforming the various quadric surfaces into their respective positions.

Example 10.1. Rendering Code for the SNOWMAN Example

void RenderScene(void)
    {
    GLUquadricObj *pObj;    // Quadric Object

    // Clear the window with current clearing color
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // Save the matrix state and do the rotations
    glPushMatrix();
        // Move object back and do in place rotation
        glTranslatef(0.0f, -1.0f, -5.0f);
        glRotatef(xRot, 1.0f, 0.0f, 0.0f);
        glRotatef(yRot, 0.0f, 1.0f, 0.0f);

        // Draw something
        pObj = gluNewQuadric();
        gluQuadricNormals(pObj, GLU_SMOOTH);

        // Main Body
        glPushMatrix();
            glColor3f(1.0f, 1.0f, 1.0f);
            gluSphere(pObj, .40f, 26, 13);  // Bottom

            glTranslatef(0.0f, .550f, 0.0f); // Mid section
            gluSphere(pObj, .3f, 26, 13);

            glTranslatef(0.0f, 0.45f, 0.0f); // Head
            gluSphere(pObj, 0.24f, 26, 13);

            // Eyes
            glColor3f(0.0f, 0.0f, 0.0f);
            glTranslatef(0.1f, 0.1f, 0.21f);
            gluSphere(pObj, 0.02f, 26, 13);

            glTranslatef(-0.2f, 0.0f, 0.0f);
            gluSphere(pObj, 0.02f, 26, 13);

            // Nose
            glColor3f(1.0f, 0.3f, 0.3f);
            glTranslatef(0.1f, -0.12f, 0.0f);
            gluCylinder(pObj, 0.04f, 0.0f, 0.3f, 26, 13);
        glPopMatrix();

        // Hat
        glPushMatrix();
            glColor3f(0.0f, 0.0f, 0.0f);
            glTranslatef(0.0f, 1.17f, 0.0f);
            glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);
            gluCylinder(pObj, 0.17f, 0.17f, 0.4f, 26, 13);

            // Hat brim
            glDisable(GL_CULL_FACE);
            gluDisk(pObj, 0.17f, 0.28f, 26, 13);
            glEnable(GL_CULL_FACE);

            glTranslatef(0.0f, 0.0f, 0.40f);
            gluDisk(pObj, 0.0f, 0.17f, 26, 13);
        glPopMatrix();

    // Restore the matrix state
    glPopMatrix();

    // Buffer swap
    glutSwapBuffers();
    }
A snowman rendered from quadric objects.

Figure 10.9. A snowman rendered from quadric objects.

Bézier Curves and Surfaces

Quadrics provide built-in support for some very simple surfaces easily modeled with algebraic equations. Suppose, however, you want to create a curve or surface, and you don't have an algebraic equation to start with. It's far from a trivial task to figure out your surface in reverse, starting from what you visualize as the result and working down to a second- or third-order polynomial. Taking a rigorous mathematical approach is time consuming and error prone, even with the aid of a computer. You can also forget about trying to do it in your head.

Recognizing this fundamental need in the art of computer-generated graphics, Pierre Bézier, an automobile designer for Renault in the 1970s, created a set of mathematical models that could represent curves and surfaces by specifying only a small set of control points. In addition to simplifying the representation of curved surfaces, the models facilitated interactive adjustments to the shape of the curve or surface.

Other types of curves and surfaces and indeed a whole new vocabulary for computer-generated surfaces soon evolved. The mathematics behind this magic show are no more complex than the matrix manipulations in Chapter 4, “Geometric Transformation: The Pipeline,” and an intuitive understanding of these curves is easy to grasp. As we did in Chapter 4, we take the approach that you can do a lot with these functions without a deep understanding of their mathematics.

Parametric Representation

A curve has a single starting point, a length, and an endpoint. It's really just a line that squiggles about in 3D space. A surface, on the other hand, has width and length and thus a surface area. We begin by showing you how to draw some smooth curves in 3D space and then extend this concept to surfaces. First, let's establish some common vocabulary and math fundamentals.

When you think of straight lines, you might think of this famous equation:

  • y = mx + b

Here, m equals the slope of the line, and b is the y intercept of the line (the place where the line crosses the y-axis). This discussion might take you back to your eighth-grade algebra class, where you also learned about the equations for parabolas, hyperbolas, exponential curves, and so on. All these equations expressed y (or x) in terms of some function of x (or y).

Another way of expressing the equation for a curve or line is as a parametric equation. A parametric equation expresses both x and y in terms of another variable that varies across some predefined range of values that is not explicitly a part of the geometry of the curve. Sometimes in physics, for example, the x, y, and z coordinates of a particle might be in terms of some functions of time, where time is expressed in seconds. In the following, f(), g(), and h() are unique functions that vary with time (t):

  • x = f(t)

  • y = g(t)

  • z = h(t)

When we define a curve in OpenGL, we also define it as a parametric equation. The parametric parameter of the curve, which we call u, and its range of values is the domain of that curve. Surfaces are described using two parametric parameters: u and v. Figure 10.10 shows both a curve and a surface defined in terms of u and v domains. The important point to realize here is that the parametric parameters (u and v) represent the extents of the equations that describe the curve; they do not reflect actual coordinate values.

Parametric representation of curves and surfaces.

Figure 10.10. Parametric representation of curves and surfaces.

Control Points

A curve is represented by a number of control points that influence the shape of the curve. For a Bézier curve, the first and last control points are actually part of the curve. The other control points act as magnets, pulling the curve toward them. Figure 10.11 shows some examples of this concept, with varying numbers of control points.

How control points affect curve shape.

Figure 10.11. How control points affect curve shape.

The order of the curve is represented by the number of control points used to describe its shape. The degree is one less than the order of the curve. The mathematical meaning of these terms pertains to the parametric equations that exactly describe the curve, with the order being the number of coefficients and the degree being the highest exponent of the parametric parameter. If you want to read more about the mathematical basis of Bézier curves, see Appendix A, “Further Reading.”

The curve in Figure 10.11(b) is called a quadratic curve (degree 2), and Figure 10.11(c) is called a cubic (degree 3). Cubic curves are the most typical. Theoretically, you could define a curve of any order, but higher order curves start to oscillate uncontrollably and can vary wildly with the slightest change to the control points.

Continuity

If two curves placed side by side share an endpoint (called the breakpoint), they together form a piecewise curve. The continuity of these curves at this breakpoint describes how smooth the transition is between them. The four categories of continuity are none, positional (C0), tangential (C1), and curvature (C2).

As you can see in Figure 10.12, no continuity occurs when the two curves don't meet at all. Positional continuity is achieved when the curves at least meet and share a common endpoint. Tangential continuity occurs when the two curves have the same tangent at the breakpoint. Finally, curvature continuity means the two curves' tangents also have the same rate of change at the breakpoint (thus an even smoother transition).

Continuity of piecewise curves.

Figure 10.12. Continuity of piecewise curves.

When assembling complex surfaces or curves from many pieces, you usually strive for tangential or curvature continuity. You'll see later that some parameters for curve and surface generation can be chosen to produce the desired continuity.

Evaluators

OpenGL contains several functions that make it easy to draw Bézier curves and surfaces. To draw them, you specify the control points and the range for the parametric u and v parameters. Then, by calling the appropriate evaluation function (the evaluator), OpenGL generates the points that make up the curve or surface. We start with a 2D example of a Bézier curve and then extend it to three dimensions to create a Bézier surface.

A 2D Curve

The best way to start is to go through an example, explaining it line by line. Listing 10.2 shows some code from the sample program BEZIER in this chapter's subdirectory on the CD. This program specifies four control points for a Bézier curve and then renders the curve using an evaluator. The output from Listing 10.2 is shown in Figure 10.13.

Example 10.2. Code from BEZIER That Draws a Bézier Curve with Four Control Points

// The number of control points for this curve
GLint nNumPoints = 4;

GLfloat ctrlPoints[4][3]= {{  -4.0f, 0.0f, 0.0f},   // End Point
                           { -6.0f, 4.0f, 0.0f},    // Control Point
                           {  6.0f, -4.0f, 0.0f},   // Control Point
                           {  4.0f, 0.0f, 0.0f }};  // End Point

// This function is used to superimpose the control points over the curve
void DrawPoints(void)
    {
    int i;    // Counting variable

    // Set point size larger to make more visible
    glPointSize(5.0f);

    // Loop through all control points for this example
    glBegin(GL_POINTS);
        for(i = 0; i < nNumPoints; i++)
           glVertex2fv(ctrlPoints[i]);
    glEnd();
    }


// Called to draw scene
void RenderScene(void)
    {
    int i;

    // Clear the window with current clearing color
    glClear(GL_COLOR_BUFFER_BIT);

    // Sets up the bezier
    // This actually only needs to be called once and could go in
    // the setup function
    glMap1f(GL_MAP1_VERTEX_3, // Type of data generated
    0.0f,                     // Lower u range
    100.0f,                   // Upper u range
    3,                        // Distance between points in the data
    nNumPoints,               // number of control points
    &ctrlPoints[0][0]);       // array of control points

    // Enable the evaluator
    glEnable(GL_MAP1_VERTEX_3);

    // Use a line strip to "connect-the-dots"
    glBegin(GL_LINE_STRIP);
        for(i = 0; i <= 100; i++)
        {
        // Evaluate the curve at this point
        glEvalCoord1f((GLfloat) i);
        }
    glEnd();

// Draw the Control Points
    DrawPoints();

    // Flush drawing commands
    glutSwapBuffers();
    }

// This function does any needed initialization on the rendering
// context.
void SetupRC()
    {
    // Clear Window to white
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f );

    // Draw in Blue
    glColor3f(0.0f, 0.0f, 1.0f);
    }

///////////////////////////////////////
// Set 2D Projection
void ChangeSize(int w, int h)
    {
    // Prevent a divide by zero
    if(h == 0)
        h = 1;

    // Set Viewport to window dimensions
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    gluOrtho2D(-10.0f, 10.0f, -10.0f, 10.0f);

    // Modelview matrix reset
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    }
Output from the BEZIER sample program.

Figure 10.13. Output from the BEZIER sample program.

The first thing we do in Listing 10.2 is define the control points for our curve:

// The number of control points for this curve
GLint nNumPoints = 4;

GLfloat ctrlPoints[4][3]= {{  -4.0f, 0.0f, 0.0f},    // Endpoint
                           { -6.0f, 4.0f, 0.0f},     // Control point
                           {  6.0f, -4.0f, 0.0f},    // Control point
                           {  4.0f, 0.0f, 0.0f }};   // Endpoint

We defined global variables for the number of control points and the array of control points. To experiment, you can change them by adding more control points or just modifying the position of these points.

The DrawPoints function is reasonably straightforward. We call this function from our rendering code to display the control points along with the curve. This capability also is useful when you're experimenting with control-point placement. Our standard ChangeSize function establishes a 2D orthographic projection that spans from –10 to +10 in the x and y directions.

Finally, we get to the rendering code. The RenderScene function first calls glMap1f (after clearing the screen) to create a mapping for our curve:

// Called to draw scene
void RenderScene(void)
    {
    int i;

    // Clear the window with current clearing color
    glClear(GL_COLOR_BUFFER_BIT);

    // Sets up the Bezier
    // This actually only needs to be called once and could go in
    // the setup function
    glMap1f(GL_MAP1_VERTEX_3,   // Type of data generated
    0.0f,                       // Lower u range
    100.0f,                     // Upper u range
    3,                          // Distance between points in the data
    nNumPoints,                 // Number of control points
    &ctrlPoints[0][0]);         // Array of control points
    ...
    ...

The first parameter to glMap1f, GL_MAP1_VERTEX_3, sets up the evaluator to generate vertex coordinate triplets (x, y, and z). You can also have the evaluator generate other values, such as texture coordinates and color information. See the reference section at the end of this chapter for details.

The next two parameters specify the lower and upper bounds of the parametric u value for this curve. The lower value specifies the first point on the curve, and the upper value specifies the last point on the curve. All the values in between correspond to the other points along the curve. Here, we set the range to 0–100.

The fourth parameter to glMap1f specifies the number of floating-point values between the vertices in the array of control points. Each vertex consists of three floating-point values (for x, y, and z), so we set this value to 3. This flexibility allows the control points to be placed in an arbitrary data structure, as long as they occur at regular intervals.

The last parameter is a pointer to a buffer containing the control points used to define the curve. Here, we pass a pointer to the first element of the array. After creating the mapping for the curve, we enable the evaluator to make use of this mapping. This capability is maintained through a state variable, and the following function call is all that is needed to enable the evaluator to produce points along the curve:

// Enable the evaluator
glEnable(GL_MAP1_VERTEX_3);

The glEvalCoord1f function takes a single argument: a parametric value along the curve. This function then evaluates the curve at this value and calls glVertex internally for that point. By looping through the domain of the curve and calling glEvalCoord to produce vertices, we can draw the curve with a simple line strip:

// Use a line strip to "connect the dots"
glBegin(GL_LINE_STRIP);
    for(i = 0; i <= 100; i++)
        {
        // Evaluate the curve at this point
        glEvalCoord1f((GLfloat) i);
        }
glEnd();

Finally, we want to display the control points themselves:

// Draw the control points
DrawPoints();

Evaluating a Curve

OpenGL can make things even easier than what we've done so far. We set up a grid with the glMapGrid function, which tells OpenGL to create an evenly spaced grid of points over the u domain (the parametric argument of the curve). Then we call glEvalMesh to “connect the dots” using the primitive specified (GL_LINE or GL_POINTS). The two function calls

// Use higher level functions to map to a grid, then evaluate the
// entire thing.

// Map a grid of 100 points from 0 to 100
glMapGrid1d(100,0.0,100.0);

// Evaluate the grid, using lines
glEvalMesh1(GL_LINE,0,100);

completely replace the code

// Use a line strip to "connect-the-dots"
glBegin(GL_LINE_STRIP);
    for(i = 0; i <= 100; i++)
        {
        // Evaluate the curve at this point
        glEvalCoord1f((GLfloat) i);
        }
glEnd();

As you can see, this approach is more compact and efficient, but its real benefit comes when evaluating surfaces rather than curves.

A 3D Surface

Creating a 3D Bézier surface is much like creating the 2D version. In addition to defining points along the u domain, we must define them along the v domain. Listing 10.3 contains code from our next sample program, BEZ3D, and displays a wire mesh of a 3D Bézier surface. The first change from the preceding example is that we have defined three more sets of control points for the surface along the v domain. To keep this surface simple, we've kept the same control points except for the z value. This way, we create a uniform surface, as if we simply extruded a 2D Bézier along the z-axis.

Example 10.3. BEZ3D Code to Create a Bézier Surface

// The number of control points for this curve
GLint nNumPoints = 3;

GLfloat ctrlPoints[3][3][3]= {{{  -4.0f, 0.0f, 4.0f},
                               { -2.0f, 4.0f, 4.0f},
                               { 4.0f, 0.0f, 4.0f }},

                             {{  -4.0f, 0.0f, 0.0f},
                              { -2.0f, 4.0f, 0.0f},
                              {  4.0f, 0.0f, 0.0f }},

                             {{  -4.0f, 0.0f, -4.0f},
                              { -2.0f, 4.0f, -4.0f},
                              {  4.0f, 0.0f, -4.0f }}};


// This function is used to superimpose the control points over the curve
void DrawPoints(void)
    {
    int i,j;    // Counting variables

    // Set point size larger to make more visible
    glPointSize(5.0f);

    // Loop through all control points for this example
    glBegin(GL_POINTS);
    for(i = 0; i < nNumPoints; i++)
        for(j = 0; j < 3; j++)
            glVertex3fv(ctrlPoints[i][j]);
    glEnd();
    }

// Called to draw scene
void RenderScene(void)
    {
    // Clear the window with current clearing color
    glClear(GL_COLOR_BUFFER_BIT);

    // Save the modelview matrix stack
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();

    // Rotate the mesh around to make it easier to see
    glRotatef(45.0f, 0.0f, 1.0f, 0.0f);
    glRotatef(60.0f, 1.0f, 0.0f, 0.0f);

    // Sets up the Bezier
    // This actually only needs to be called once and could go in
    // the setup function
    glMap2f(GL_MAP2_VERTEX_3,     // Type of data generated
    0.0f,                         // Lower u range
    10.0f,                        // Upper u range
    3,                            // Distance between points in the data
    3,                            // Dimension in u direction (order)
    0.0f,                         // Lover v range
    10.0f,                        // Upper v range
    9,                            // Distance between points in the data
    3,                            // Dimension in v direction (order)
    &ctrlPoints[0][0][0]);        // array of control points

    // Enable the evaluator
    glEnable(GL_MAP2_VERTEX_3);

    // Use higher level functions to map to a grid, then evaluate the
    // entire thing.

    // Map a grid of 10 points from 0 to 10
    glMapGrid2f(10,0.0f,10.0f,10,0.0f,10.0f);

    // Evaluate the grid, using lines
    glEvalMesh2(GL_LINE,0,10,0,10);

    // Draw the Control Points
    DrawPoints();

    // Restore the modelview matrix
    glPopMatrix();

    // Display the image
    glutSwapBuffers();
    }

Our rendering code is different now, too. In addition to rotating the figure for a better visual effect, we call glMap2f instead of glMap1f. This call specifies control points along two domains (u and v) instead of just one (u):

// Sets up the Bezier
// This actually only needs to be called once and could go in
// the setup function
glMap2f(GL_MAP2_VERTEX_3,  // Type of data generated
0.0f,                      // Lower u range
10.0f,                     // Upper u range
3,                         // Distance between points in the data
3,                         // Dimension in u direction (order)
0.0f,                      // Lower v range
10.0f,                     // Upper v range
9,                         // Distance between points in the data
3,                         // Dimension in v direction (order)
&ctrlPoints[0][0][0]);     // Array of control points

We must still specify the lower and upper range for u, and the distance between points in the u domain is still 3. Now, however, we must also specify the lower and upper range in the v domain. The distance between points in the v domain is now nine values because we have a three-dimensional array of control points, with each span in the u domain being three points of three values each (3 × 3 = 9). Then we tell glMap2f how many points in the v direction are specified for each u division, followed by a pointer to the control points themselves.

The two-dimensional evaluator is enabled just like the one-dimensional version, and we call glMapGrid2f with the number of divisions in the u and v direction:

// Enable the evaluator
glEnable(GL_MAP2_VERTEX_3);

// Use higher level functions to map to a grid, then evaluate the
// entire thing.

// Map a grid of 10 points from 0 to 10
glMapGrid2f(10,0.0f,10.0f,10,0.0f,10.0f);

After the evaluator is set up, we can call the two-dimensional (meaning u and v) version of glEvalMesh to evaluate our surface grid. Here, we evaluate using lines and specify the u and v domains' values to range from 0 to 10:

// Evaluate the grid, using lines
glEvalMesh2(GL_LINE,0,10,0,10);

The result is shown in Figure 10.14.

Output from the BEZ3D program.

Figure 10.14. Output from the BEZ3D program.

Lighting and Normal Vectors

Another valuable feature of evaluators is the automatic generation of surface normals. By simply changing this code

// Evaluate the grid, using lines
glEvalMesh2(GL_LINE,0,10,0,10);

to this

// Evaluate the grid, using lines
glEvalMesh2(GL_FILL,0,10,0,10);

and then calling

glEnable(GL_AUTO_NORMAL);

in our initialization code, we enable easy lighting of surfaces generated by evaluators. Figure 10.15 shows the same surface as Figure 10.14, but with lighting enabled and automatic normalization turned on. The code for this program appears in the BEZLIT sample in the CD subdirectory for this chapter. The program is only slightly modified from BEZ3D.

Output from the BEZLIT program.

Figure 10.15. Output from the BEZLIT program.

NURBS

You can use evaluators to your heart's content to evaluate Bézier surfaces of any degree, but for more complex curves, you have to assemble your Béziers piecewise. As you add more control points, creating a curve that has good continuity becomes difficult. A higher level of control is available through the GLU library's NURBS functions. NURBS stands for non-uniform rational B-spline. Mathematicians out there might know immediately that this is just a more generalized form of curves and surfaces that can produce Bézier curves and surfaces, as well as some other kinds (mathematically speaking). These functions allow you to tweak the influence of the control points you specified for the evaluators to produce smoother curves and surfaces with larger numbers of control points.

From Bézier to B-Splines

A Bézier curve is defined by two points that act as endpoints and any number of other control points that influence the shape of the curve. The three Bézier curves in Figure 10.16 have three, four, and five control points specified. The curve is tangent to a line that connects the endpoints with their adjacent control points. For quadratic (three points) and cubic (four points) curves, the resulting Béziers are quite smooth, usually with a continuity of C2 (curvature). For higher numbers of control points, however, the smoothness begins to break down as the additional control points pull and tug on the curve.

Bézier continuity as the order of the curve increases.

Figure 10.16. Bézier continuity as the order of the curve increases.

B-splines (bi-cubic splines), on the other hand, work much as the Bézier curves do, but the curve is broken down into segments. The shape of any given segment is influenced only by the nearest four control points, producing a piecewise assemblage of a curve with each segment exhibiting characteristics much like a fourth-order Bézier curve. A long curve with many control points is inherently smoother, with the junction between each segment exhibiting C2 continuity. It also means that the curve does not necessarily have to pass through any of the control points.

Knots

The real power of NURBS is that you can tweak the influence of the four control points for any given segment of a curve to produce the smoothness needed. This control is handled via a sequence of values called knots. Two knot values are defined for every control point. The range of values for the knots matches the u or v parametric domain and must be nondescending. The knot values determine the influence of the control points that fall within that range in u/v space. Figure 10.17 shows a curve demonstrating the influence of control points over a curve having four units in the u parametric domain. Points in the middle of the u domain have a greater pull on the curve, and only points between 0 and 3 have any effect on the shape of the curve.

Control point influence along the u parameter.

Figure 10.17. Control point influence along the u parameter.

The key here is that one of these influence curves exists at each control point along the u/v parametric domain. The knot sequence then defines the strength of the influence of points within this domain. If a knot value is repeated, points near this parametric value have even greater influence. The repeating of knot values is called knot multiplicity. Higher knot multiplicity decreases the curvature of the curve or surface within that region.

Creating a NURBS Surface

The GLU NURBS functions provide a useful high-level facility for rendering surfaces. You don't have to explicitly call the evaluators or establish the mappings or grids. To render a NURBS, you first create a NURBS object that you reference whenever you call the NURBS-related functions to modify the appearance of the surface or curve.

The gluNewNurbsRenderer function creates a renderer for the NURB, and gluDeleteNurbsRenderer destroys it. The following code fragments demonstrate these functions in use:

// NURBS object pointer
GLUnurbsObj *pNurb = NULL;
...
...

// Setup the NURBS object
    pNurb = gluNewNurbsRenderer();

...
// Do your NURBS things...
...
...

// Delete the NURBS object if it was created
if(pNurb)
    gluDeleteNurbsRenderer(pNurb);

NURBS Properties

After you have created a NURBS renderer, you can set various high-level NURBS properties for the NURB:

// Set sampling tolerance
gluNurbsProperty(pNurb, GLU_SAMPLING_TOLERANCE, 25.0f);

// Fill to make a solid surface (use GLU_OUTLINE_POLYGON to create a
// polygon mesh)
gluNurbsProperty(pNurb, GLU_DISPLAY_MODE, (GLfloat)GLU_FILL);

You typically call these functions in your setup routine rather than repeatedly in your rendering code. In this example, GLU_SAMPLING_TOLERANCE defines the fineness of the mesh that defines the surface, and GLU_FILL tells OpenGL to fill in the mesh instead of generating a wireframe.

Defining the Surface

The surface definition is passed as arrays of control points and knot sequences to the gluNurbsSurface function. As shown here, this function is also bracketed by calls to gluBeginSurface and gluEndSurface:

// Render the NURB
// Begin the NURB definition
gluBeginSurface(pNurb);

// Evaluate the surface
gluNurbsSurface(pNurb,  // Pointer to NURBS renderer
   8, Knots,            // No. of knots and knot array u direction
   8, Knots,            // No. of knots and knot array v direction
   4 * 3,               // Distance between control points in u dir.
   3,                   // Distance between control points in v dir.
   &ctrlPoints[0][0][0],// Control points
   4, 4,                // u and v order of surface
   GL_MAP2_VERTEX_3);   // Type of surface

// Done with surface
gluEndSurface(pNurb);

You can make more calls to gluNurbsSurface to create any number of NURBS surfaces, but the properties you set for the NURBS renderer are still in effect. Often, this is desired; you rarely want two surfaces (perhaps joined) to have different fill styles (one filled and one a wire mesh).

Using the control points and knot values shown in the next code segment, we produced the NURBS surface shown in Figure 10.18. You can find this NURBS program in this chapter's subdirectory on the CD:

// Mesh extends four units -6 to +6 along x and y axis
// Lies in Z plane
//                 u  v  (x,y,z)
GLfloat ctrlPoints[4][4][3]= {{{  -6.0f, -6.0f, 0.0f},    // u = 0,   v = 0
                               {  -6.0f, -2.0f, 0.0f},    //        v = 1
                               {   -6.0f,  2.0f, 0.0f},   //        v = 2
                               {   -6.0f,  6.0f, 0.0f}},  //        v = 3

                              {{  -2.0f, -6.0f, 0.0f},    // u = 1    v = 0
                               {   -2.0f, -2.0f, 8.0f},   //        v = 1
                               {   -2.0f,  2.0f, 8.0f},   //        v = 2
                               {   -2.0f,  6.0f, 0.0f}},  //        v = 3

                              {{   2.0f, -6.0f, 0.0f },   // u =2     v = 0
                               {    2.0f, -2.0f, 8.0f },  //        v = 1
                               {    2.0f,  2.0f, 8.0f },  //        v = 2
                               {    2.0f,  6.0f, 0.0f }}, //        v = 3

                              {{   6.0f, -6.0f, 0.0f},    // u = 3    v = 0
                               {    6.0f, -2.0f, 0.0f},   //        v = 1
                               {    6.0f,  2.0f, 0.0f},   //        v = 2
                               {    6.0f,  6.0f, 0.0f}}}; //        v = 3
// Knot sequence for the NURB
GLfloat Knots[8] = {0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f};
Output from the NURBS program.

Figure 10.18. Output from the NURBS program.

Trimming

Trimming means creating cutout sections from NURBS surfaces. This capability is often used for literally trimming sharp edges of a NURBS surface. You can also create holes in your surface just as easily. The output from the NURBT program is shown in Figure 10.19. This is the same NURBS surface used in the preceding sample (without the control points shown), with a triangular region removed. This program, too, is on the CD.

Output from the NURBT program.

Figure 10.19. Output from the NURBT program.

Listing 10.4 shows the code added to the NURBS sample program to produce this trimming effect. Within the gluBeginSurface/gluEndSurface delimiters, we call gluBeginTrim, specify a trimming curve with gluPwlCurve, and finish the trimming curve with gluEndTrim.

Example 10.4. Modifications to NURBS to Produce Trimming

// Outside trimming points to include entire surface
GLfloat outsidePts[5][2] = /* counterclockwise */
     {{0.0f, 0.0f}, {1.0f, 0.0f}, {1.0f, 1.0f}, {0.0f, 1.0f}, {0.0f, 0.0f}};

// Inside trimming points to create triangle shaped hole in surface
GLfloat insidePts[4][2] = /* clockwise */
     {{0.25f, 0.25f}, {0.5f, 0.5f}, {0.75f, 0.25f}, { 0.25f, 0.25f}};
...
...
...

// Render the NURB
// Begin the NURB definition
gluBeginSurface(pNurb);

// Evaluate the surface
gluNurbsSurface(pNurb,  // Pointer to NURBS renderer
   8, Knots,            // No. of knots and knot array u direction
   8, Knots,            // No. of knots and knot array v direction
   4 * 3,               // Distance between control points in u dir.
   3,                   // Distance between control points in v dir.
   &ctrlPoints[0][0][0],// Control points
   4, 4,                // u and v order of surface
   GL_MAP2_VERTEX_3);   // Type of surface

// Outer area, include entire curve
gluBeginTrim (pNurb);
gluPwlCurve (pNurb, 5, &outsidePts[0][0], 2, GLU_MAP1_TRIM_2);
gluEndTrim (pNurb);

// Inner triangular area
gluBeginTrim (pNurb);
gluPwlCurve (pNurb, 4, &insidePts[0][0], 2, GLU_MAP1_TRIM_2);
gluEndTrim (pNurb);

// Done with surface
gluEndSurface(pNurb);

Within the gluBeginTrim/gluEndTrim delimiters, you can specify any number of curves as long as they form a closed loop in a piecewise fashion. You can also use gluNurbsCurve to define a trimming region or part of a trimming region. These trimming curves must, however, be in terms of the unit parametric u and v space. This means the entire u/v domain is scaled from 0.0 to 1.0.

gluPwlCurve defines a piecewise linear curve--nothing more than a list of points connected end to end. In this scenario, the inner trimming curve forms a triangle, but with many points, you could create an approximation of any curve needed.

Trimming a curve trims away surface area that is to the right of the curve's winding. Thus, a clockwise-wound trimming curve discards its interior. Typically, an outer trimming curve is specified, which encloses the entire NURBS parameter space. Then smaller trimming regions are specified within this region with clockwise winding. Figure 10.20 illustrates this relationship.

An area inside clockwise-wound curves is trimmed away.

Figure 10.20. An area inside clockwise-wound curves is trimmed away.

NURBS Curves

Just as you can have Bézier surfaces and curves, you can also have NURBS surfaces and curves. You can even use gluNurbsCurve to do NURBS surface trimming. By this point, we hope you have the basics down well enough to try trimming surfaces on your own. However, another sample, NURBC, is included on the CD if you want a starting point to play with.

Tessellation

To keep OpenGL as fast as possible, all geometric primitives must be convex. We made this point in Chapter 3, “Drawing in Space: Geometric Primitives and Buffers.” However, many times we have vertex data for a concave or more complex shape that we want to render with OpenGL. These shapes fall into two basic categories, as shown in Figure 10.21. A simple concave polygon is shown on the left, and a more complex polygon with a hole in it is shown on the right. For the shape on the left, you might be tempted to try using GL_POLYGON as the primitive type, but the rendering would fail because OpenGL algorithms are optimized for convex polygons. As for the figure on the right…well, there is little hope for that shape at all!

Some nonconvex polygons.

Figure 10.21. Some nonconvex polygons.

The intuitive solution to both of these problems is to break down the shape into smaller convex polygons or triangles that can be fit together to create the final overall shape. Figure 10.22 shows one possible solution to breaking the shapes in Figure 10.21 into more manageable triangles.

Complex shapes broken down into triangles.

Figure 10.22. Complex shapes broken down into triangles.

Breaking down the shapes by hand is tedious at best and possibly error prone. Fortunately, the OpenGL Utility Library contains functions to help you break concave and complex polygons into smaller, valid OpenGL primitives. The process of breaking down these polygons is called tessellation.

The Tessellator

Tessellation works through a tessellator object that must be created and destroyed much in the same way that we did for quadric state objects:

GLUtesselator *pTess;
pTess = gluNewTes();
. . .
// Do some tessellation
. . .
gluDeleteDess(pTess);

All the tessellation functions use the tessellator object as the first parameter. This allows you to have more than one tessellation object active at a time or interact with libraries or other code that also uses tessellation. The tessellation functions change the tessellator's state and behavior, and this allows you to make sure your changes affect only the object you are currently working with. Alas, yes, GLUtesselator has only one l and is thus misspelled!

The tessellator breaks up a polygon and renders it appropriately when you perform the following steps:

  1. Create the tessellator object.

  2. Set tessellator state and callbacks.

  3. Start a polygon.

  4. Start a contour.

  5. Feed the tessellator the vertices that specify the contour.

  6. End the contour.

  7. Go back to step 4 if there are more contours.

  8. End the polygon.

Each polygon consists of one or more contours. The polygon to the left in Figure 10.21 contains one contour, simply the path around the outside of the polygon. The polygon on the right, however, has two contours: the outside edge and the edge around the inner hole. Polygons may contain any number of contours (several holes) or even nested contours (holes within holes). The actual work of tessellating the polygon does not occur until step 8. This task can sometimes be very time consuming, and if the geometry is static, it may be best to store these function calls in a display list (the next chapter discusses display lists).

Tessellator Callbacks

During tessellation, the tessellator calls a number of callback functions that you must provide. You use these callbacks to actually specify the vertex information and begin and end the primitives. The following function registers the callback functions:

void gluTessCallback(GLUTesselator *tobj, GLenum which, void (*fn)());

The first parameter is the tessellation object. The second specifies the type of callback being registered, and the last is the pointer to the callback function itself. You can specify various callbacks, which are listed in Table 10.3 in the reference section. As an example, examine the following lines of code:

// Just call glBegin at beginning of triangle batch
gluTessCallback(pTess, GLU_TESS_BEGIN, glBegin);

// Just call glEnd at end of triangle batch
gluTessCallback(pTess, GLU_TESS_END, glEnd);

// Just call glVertex3dv for each  vertex
gluTessCallback(pTess, GLU_TESS_VERTEX, glVertex3dv);

The GLU_TESS_BEGIN callback specifies the function to call at the beginning of each new primitive. Specifying glBegin simply tells the tessellator to call glBegin to begin a primitive batch. This may seem pointless, but you can also specify your own function here to do additional processing whenever a new primitive begins. For example, suppose you want to find out how many triangles are used in the final tessellated polygon.

The GLU_TESS_END callback, again, simply tells the tessellator to call glEnd and that you have no other specific code you want to inject into the process. Finally, the GLU_TESS_VERTEX call drops in a call to glVertex3dv to specify the tessellated vertex data. Tessellation requires that vertex data be specified as double precision, and always uses three component vertices. Again, you could substitute your own function here to do some additional processing (such as adding color, normal, or texture coordinate information).

For an example of specifying your own callback (instead of cheating and just using existing OpenGL functions), the following code shows the registration of a function to report any errors that may occur during tessellation:

////////////////////////////////////////////////////////////////////
// Tessellation error callback
void tessError(GLenum error)
    {
    // Get error message string
    const char *szError = gluErrorString(error);

    // Set error message as window caption
    glutSetWindowTitle(szError);
    }

. . .
. . .

// Register error callback
gluTessCallback(pTess, GLU_TESS_ERROR, tessError);

Specifying Vertex Data

To begin a polygon (this corresponds to step 3 shown earlier), you call the following function:

void gluTessBeginPolygon(GLUTesselator *tobj, void *data);

You first pass in the tessellator object and then a pointer to any user-defined data that you want associated with this tessellation. This data can be sent back to you during tessellation using the callback functions listed in Table 10.3. Often, this is just NULL. To finish the polygon (step 8) and begin tessellation, call this function:

void gluTessEndPolygon(GLUTesselator *tobj);

Nested within the beginning and ending of the polygon, you specify one or more contours using the following pair of functions (steps 4 and 6):

void gluTessBeginContour(GLUTesselator *tobj);
void gluTessEndContour(GLUTesselator *tobj);

Finally, within the contour, you must add the vertices that make up that contour (step 5). The following function feeds the vertices, one at a time, to the tessellator:

void gluTessVertex(GLUTesselator *tobj, GLdouble v[3], void *data);

The v parameter contains the actual vertex data used for tessellator calculations. The data parameter is a pointer to the vertex data passed to the callback function specified by GLU_VERTEX. Why two different arguments to specify the same thing? Because the pointer to the vertex data may also point to additional information about the vertex (color, normals, and so on). If you specify your own function for GLU_VERTEX (instead of our cheat), you can access this additional vertex data in the callback routine.

Putting It All Together

Now let's look at an example that takes a complex polygon and performs tessellation to render a solid shape. The sample program FLORIDA contains the vertex information to draw the crude, but recognizable, shape of the state of Florida. The program has three modes of rendering, accessible via the context menu: Line Loops, Concave Polygon, and Complex Polygon. The basic shape with Line Loops is shown in Figure 10.23.

Basic outline of Florida.

Figure 10.23. Basic outline of Florida.

Listing 10.5 shows the vertex data and the rendering code that draws the outlines for the state and Lake Okeechobee.

Example 10.5. Vertex Data and Drawing Code for State Outline

// Coast Line Data
#define COAST_POINTS 24
GLdouble vCoast[COAST_POINTS][3] = {{-70.0, 30.0, 0.0 },
                                    {-50.0, 30.0, 0.0 },
                                    {-50.0, 27.0, 0.0 },
                                    { -5.0, 27.0, 0.0 },
                                    {  0.0, 20.0, 0.0 },
                                    {  8.0, 10.0, 0.0 },
                                    { 12.0,  5.0, 0.0 },
                                    { 10.0,  0.0, 0.0 },
                                    { 15.0,-10.0, 0.0 },
                                    { 20.0,-20.0, 0.0 },
                                    { 20.0,-35.0, 0.0 },
                                    { 10.0,-40.0, 0.0 },
                                    {  0.0,-30.0, 0.0 },
                                    { -5.0,-20.0, 0.0 },
                                    {-12.0,-10.0, 0.0 },
                                    {-13.0, -5.0, 0.0 },
                                    {-12.0,  5.0, 0.0 },
                                    {-20.0, 10.0, 0.0 },
                                    {-30.0, 20.0, 0.0 },
                                    {-40.0, 15.0, 0.0 },
                                    {-50.0, 15.0, 0.0 },
                                    {-55.0, 20.0, 0.0 },
                                    {-60.0, 25.0, 0.0 },
                                    {-70.0, 25.0, 0.0 }};

// Lake Okeechobee
#define LAKE_POINTS 4
GLdouble vLake[LAKE_POINTS][3] = {{ 10.0, -20.0, 0.0 },
                                  { 15.0, -25.0, 0.0 },
                                  { 10.0, -30.0, 0.0 },
                                  {  5.0, -25.0, 0.0 }};

. . .
. . .

case DRAW_LOOPS:                    // Draw line loops
    {
    glColor3f(0.0f, 0.0f, 0.0f);    // Just black outline

    // Line loop with coastline shape
    glBegin(GL_LINE_LOOP);
    for(i = 0; i < COAST_POINTS; i++)
        glVertex3dv(vCoast[i]);
    glEnd();

    // Line loop with shape of interior lake
    glBegin(GL_LINE_LOOP);
    for(i = 0; i < LAKE_POINTS; i++)
        glVertex3dv(vLake[i]);
    glEnd();
    }
    break;

For the Concave Polygon rendering mode, only the outside contour is drawn. This results in a solid filled shape, despite the fact that the polygon is clearly concave. This result is shown in Figure 10.24, and the tessellation code is shown in Listing 10.6.

Example 10.6. Drawing a Convex Polygon

case DRAW_CONCAVE:              // Tessellate concave polygon
    {
    // Tessellator object
    GLUtesselator *pTess;

    // Green polygon
    glColor3f(0.0f, 1.0f, 0.0f);

    // Create the tessellator object
    pTess = gluNewTess();

    // Set callback functions
    // Just call glBegin at beginning of triangle batch
    gluTessCallback(pTess, GLU_TESS_BEGIN, glBegin);

    // Just call glEnd at end of triangle batch
    gluTessCallback(pTess, GLU_TESS_END, glEnd);

    // Just call glVertex3dv for each  vertex
    gluTessCallback(pTess, GLU_TESS_VERTEX, glVertex3dv);

    // Register error callback
    gluTessCallback(pTess, GLU_TESS_ERROR, tessError);

    // Begin the polygon
    gluTessBeginPolygon(pTess, NULL);

    // Begin the one and only contour
    gluTessBeginContour(pTess);

    // Feed in the list of vertices
    for(i = 0; i < COAST_POINTS; i++)
        gluTessVertex(pTess, vCoast[i], vCoast[i]); // Can't be NULL

    // Close contour and polygon
    gluTessEndContour(pTess);
    gluTessEndPolygon(pTess);

    // All done with tessellator object
    gluDeleteTess(pTess);
    }
    break;
A solid convex polygon.

Figure 10.24. A solid convex polygon.

Finally, we present a more complex polygon, one with a hole in it. The Complex Polygon drawing mode draws the solid state, but with a whole representing Lake Okeechobee (a large lake in south Florida, typically shown on maps). The output is shown in Figure 10.25, and the relevant code is presented in Listing 10.7.

Example 10.7. Tessellating a Complex Polygon with Multiple Contours

case DRAW_COMPLEX:          // Tessellate, but with hole cut out
    {
    // Tessellator object
    GLUtesselator *pTess;

    // Green polygon
    glColor3f(0.0f, 1.0f, 0.0f);

    // Create the tessellator object
    pTess = gluNewTess();

    // Set callback functions
    // Just call glBegin at beginning of triangle batch
    gluTessCallback(pTess, GLU_TESS_BEGIN, glBegin);

    // Just call glEnd at end of triangle batch
    gluTessCallback(pTess, GLU_TESS_END, glEnd);

    // Just call glVertex3dv for each  vertex
    gluTessCallback(pTess, GLU_TESS_VERTEX, glVertex3dv);

    // Register error callback
    gluTessCallback(pTess, GLU_TESS_ERROR, tessError);

    // How to count filled and open areas
    gluTessProperty(pTess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD);

    // Begin the polygon
    gluTessBeginPolygon(pTess, NULL); // No user data

    // First contour, outline of state
    gluTessBeginContour(pTess);
    for(i = 0; i < COAST_POINTS; i++)
        gluTessVertex(pTess, vCoast[i], vCoast[i]);
    gluTessEndContour(pTess);

    // Second contour, outline of lake
    gluTessBeginContour(pTess);
    for(i = 0; i < LAKE_POINTS; i++)
        gluTessVertex(pTess, vLake[i], vLake[i]);
    gluTessEndContour(pTess);

    // All done with polygon
    gluTessEndPolygon(pTess);

    // No longer need tessellator object
    gluDeleteTess(pTess);
    }
    break;
The solid polygon, but with a hole.

Figure 10.25. The solid polygon, but with a hole.

This code contained a new function call:

// How to count filled and open areas
gluTessProperty(pTess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD);

This call tells the tessellator how to decide what areas to fill in and which areas to leave empty when there are multiple contours. The value GLU_TESS_WINDING_ODD is actually the default, and we could have skipped this function. However, you should understand how the tessellator handles nested contours. By specifying ODD, we are saying that any given point inside the polygon is filled in if it is enclosed in an odd number of contours. The area inside the lake (inner contour) is surrounded by two (an even number) contours and is left unfilled. Points outside the lake but inside the state boundary are enclosed by only one contour (an odd number) and are drawn filled.

Summary

The quadrics library makes creating a few simple surfaces (spheres, cylinders, disks, and cones) child's play. Expanding on this concept into more advanced curves and surfaces could have made this chapter the most intimidating in the entire book. As you have seen, however, the concepts behind these curves and surfaces are not very difficult to understand. Appendix A suggests further reading if you want in-depth mathematical information or tips on creating NURBS-based models.

Other examples from this chapter give you a good starting point for experimenting with NURBS. Adjust the control points and knot sequences to create warped or rumpled surfaces. Also, try some quadratic surfaces and some with higher order than the cubic surfaces. Watch out: One pitfall to avoid as you play with these curves is trying too hard to create one complex surface out of a single NURB. You can find greater power and flexibility if you compose complex surfaces out of several smaller and easy-to-handle NURBS or Bézier surfaces.

Finally, in this chapter, we saw OpenGL's powerful support for automatic polygon tessellation. You learned that you can draw complex surfaces, shapes, and patterns with only a few points that specify the boundaries. You also learned that concave regions and even regions with holes can be broken down into simpler convex primitives using the GLU library's tessellator object.

Reference

glEvalCoord

Purpose:

Evaluates 1D and 2D maps that have been previously enabled.

Include File:

<gl.h>

Variations:

void glEvalCoord1d(GLdouble u);
void glEvalCoord1f(GLfloat u);
void glEvalCoord2d(GLdouble u, GLdouble v);
void glEvalCoord2f(GLfloat u, GLfloat v);
void glEvalCoord1dv(const GLdouble *u);
void glEvalCoord1fv(const GLfloat *u);
void glEvalCoord2dv(const GLdouble *u);
void glEvalCoord2fv(const GLfloat *u);

Description:

This function uses a previously enabled evaluator (set up with glMap) to produce vertex, color, normal, or texture values based on the parametric u/v values. The types of data and function calls simulated are specified by the glMap1 and glMap2 functions.

Parameters:

u, v

These parameters specify the u and v parametric value that is to be evaluated along the curve or surface.

Returns:

None.

See Also:

glEvalMesh, glEvalPoint, glMap1, glMap2, glMapGrid

glEvalMesh

Purpose:

Computes a 1D or 2D grid of points or lines.

Include File:

<gl.h>

Variations:

void glEvalMesh1(GLenum mode, GLint i1, GLint i2);
void glEvalMesh2(GLenum mode, GLint i1, GLint i2, 
ReferenceGLint j1, GLint j2);

Description:

You use this function with glMapGrid to efficiently create a mesh of evenly spaced u and v domain values. glEvalMesh actually evaluates the mesh and produces the points, line segments, or filled polygons.

Parameters:

mode

GLdoubleSpecifies whether the mesh should be computed as points (GL_POINT), lines (GL_LINE), or filled meshes for surfaces (GL_FILL).

i1, i2

GLintSpecifies the first and last integer values for the u domain.

j1, j2

GLintSpecifies the first and last integer values for the v domain.

Returns:

None.

See Also:

glBegin, glEvalCoord, glEvalPoint, glMap1, glMap2, glMapGrid

glEvalPoint

Purpose:

Generates and evaluates a single point in a mesh.

Include File:

<gl.h>

Variations:

void glEvalPoint1(GLint i);
void glEvalPoint2(GLint i, GLint j);

Description:

You can use this function in place of glEvalMesh to evaluate a domain at a single point. The evaluation produces a single primitive, GL_POINTS. The first variation (glEvalPoint1) is used for curves, and the second (glEvalPoint2) is for surfaces.

Parameters:

i, j

GLintSpecifies the u and v domain parametric values.

Returns:

None.

See Also:

glEvalCoord, glEvalMesh, glMap1, glMap2, glMapGrid

glGetMap

Purpose:

Returns evaluator parameters.

Include File:

<gl.h>

Variations:

void glGetMapdv(GLenum target, GLenum query, 
GLint:GLdouble *v);
void glGetMapfv(GLenum target, GLenum query, 
GLint:GLfloat *v);
void glGetMapiv(GLenum target, GLenum query, GLint
GLint: *v);

Description:

This function retrieves map settings that were set by the glMap functions. See glMap1 and glMap2 in this section for explanations of the types of maps.

Parameters:

target

GLenumThe name of the map; the following maps are defined: GL_MAP1_COLOR_4, GL_MAP1_INDEX, GL_MAP1_NORMAL, GL_MAP1_TEXTURE_COORD_1, GL_MAP1_TEXTURE_COORD_2, GL_MAP1_TEXTURE_COORD_3, GL_MAP1_TEXTURE_COORD_4, GL_MAP1_VERTEX_3, GL_MAP1_VERTEX_4, GL_MAP2_COLOR_4, GL_MAP2_INDEX, GL_MAP2_NORMAL, GL_MAP2_TEXTURE_COORD_1, GL_MAP2_TEXTURE_COORD_2, GL_MAP2_TEXTURE_COORD_3, GL_MAP2_TEXTURE_COORD_4, GL_MAP2_VERTEX_3, and GL_MAP2_VERTEX_4. See glMap in this section for an explanation of these map types.

query

GLenumSpecifies which map parameter to return in *v. It may be one of the following values:

GL_COEFFReturns an array containing the control points for the map. Coordinates are returned in row-major order. 1D maps return order control points, and 2D maps return u-order times the v-order control points.

GL_ORDERReturns the order of the evaluator function. For 1D maps, this is a single value. For 2D maps, two values are returned (an array) that contain first the u-order and then the v-order.

GL_DOMAINReturns the linear parametric mapping parameters. For 1D evaluators, this is the lower and upper u value. For 2D maps, it's the lower and upper u followed by the lower and upper v.

*v

Pointer to storage that will contain the requested parameter. The data type of this storage should match the function used (double, float, or integer).

Returns:

None.

See Also:

glEvalCoord, glMap1, glMap2

glMap

Purpose:

Defines a 1D or 2D evaluator.

Include File:

<gl.h>

Variations:

void glMap1d(GLenum target, GLdouble u1, GLdouble u2,
                                            GLint
GL_DOMAIN: ustride, GLint uorder,
                                            const
GL_DOMAIN: GLdouble *points);
void glMap1f(GLenum target, GLfloat u1, GLfloat u2,
                                            GLint
GL_DOMAIN: ustride, GLint uorder,
                                            const
GL_DOMAIN: GLfloat *points);
void glMap2d(GLenum target, GLdouble u1, GLdouble u2,
                                           GLint
GL_DOMAIN: ustride, GLint uorder,
                                          
GL_DOMAIN: GLdouble v1, GLdouble v2,
                                           GLint
GL_DOMAIN: vstride, GLint vorder,
                                           const
GL_DOMAIN: GLdouble *points);
void glMap2f(GLenum target, GLfloat u1, GLfloat u2,
                                           GLint
GL_DOMAIN: ustride, GLint uorder,
                                           GLfloat
GL_DOMAIN: v1, GLfloat v2,
                                           GLint
GL_DOMAIN: vstride, GLint vorder,
                                           const
GL_DOMAIN: GLfloat *points);

Description:

These functions define 1D or 2D evaluators. The glMap1x functions are used for 1D evaluators (curves), and the glMap2x functions are used for 2D evaluators (surfaces). Evaluators produce vertex or other information (see the target parameter) evaluated along one or two dimensions of a parametric range (u and v).

Parameters:

target

GLenumSpecifies what kinds of values are produced by the evaluator. Valid values for 1D and 2D evaluators are as follows:

GL_MAP1_VERTEX_3 (or GL_MAP2_VERTEX_3)Control points are three floats that represent x, y, and z coordinate values. glVertex3 commands are generated internally when the map is evaluated.

GL_MAP1_VERTEX_4 (or GL_MAP2_VERTEX_4)Control points are four floats that represent x, y, z, and w coordinate values. glVertex4 commands are generated internally when the map is evaluated.

GL_MAP1_INDEX (or GL_MAP2_INDEX)The generated control points are single floats that represent a color index value. glIndex commands are generated internally when the map is evaluated. Note: The current color index is not changed as it would be if glIndex were called directly.

GL_MAP1_COLOR_4 (or GL_MAP2_COLOR_4)The generated control points are four floats that represent red, green, blue, and alpha components. glColor4 commands are generated internally when the map is evaluated. Note: The current color is not changed as it would be if glColor4f were called directly.

GL_MAP1_NORMAL (or GL_MAP2_NORMAL)The generated control points are three floats that represent the x, y, and z components of a normal vector. glNormal commands are generated internally when the map is evaluated. Note: The current normal is not changed as it would be if glNormal were called directly.

GL_MAP1_TEXTURE_COORD_1 (or GL_MAP2_TEXTURE_COORD_1)The generated control points are single floats that represent the s texture coordinate. glTexCoord1 commands are generated internally when the map is evaluated. Note: The current texture coordinates are not changed as they would be if glTexCoord were called directly.

GL_MAP1_TEXTURE_COORD_2 (or GL_MAP2_TEXTURE_COORD_2)The generated control points are two floats that represent the s and t texture coordinates. glTexCoord2 commands are generated internally when the map is evaluated. Note: The current texture coordinates are not changed as they would be if glTexCoord were called directly.

GL_MAP1_TEXTURE_COORD_3 (or GL_MAP2_TEXTURE_COORD_3)The generated control points are three floats that represent the s, t, and r texture coordinates. glTexCoord3 commands are generated internally when the map is evaluated. Note: The current texture coordinates are not changed as they would be if glTexCoord were called directly.

GL_MAP1_TEXTURE_COORD_4 (or GL_MAP2_TEXTURE_COORD_4)The generated control points are four floats that represent the s, t, r, and q texture coordinates. glTexCoord4 commands are generated internally when the map is evaluated. Note: The current texture coordinates are not changed as they would be if glTexCoord were called directly.

u1, u2

Specifies the linear mapping of the parametric u parameter.

v1, v2

Specifies the linear mapping of the parametric v parameter. This parameter is used only for 2D maps.

ustride, vstride

Specifies the number of floats or doubles between control points in the *points data structure. The coordinates for each point occupy consecutive memory locations, but this parameter allows the points to be spaced as needed to let the data come from an arbitrary data structure.

uorder, vorder

Specifies the number of control points in the u and v direction.

*points

A memory pointer that points to the control points. It can be a 2D or 3D array or any arbitrary data structure.

Returns:

None.

See Also:

glBegin, glColor, glEnable, glEvalCoord, glEvalMesh, glEvalPoint, glMapGrid, glNormal, glTexCoord, glVertex

glMapGrid

Purpose:

Defines a 1D or 2D mapping grid.

Include File:

<gl.h>

Variations:

void glMapGrid1d(GLint un, GLdouble u1, GLdouble u2);
void glMapGrid1f(GLint un, GLfloat u1, GLfloat u2);
void glMapGrid2d(GLint un, GLdouble u1, GLdouble u2,
                        GLint vn, GLdouble v1, 
GL_MAP1_TEXTURE_COORD_4 (or GL_MAP2_TEXTURE_COORD_4):GLdouble v2);
void glMapGrid2f(GLint un, GLfloat u1, GLfloat u2,
GL_MAP1_TEXTURE_COORD_4 (or GL_MAP2_TEXTURE_COORD_4): GLint vn,
                        GLfloat v1, GLfloat v2);

Description:

This function establishes a 1D or 2D mapping grid. It is used with glMap and glEvalMesh to efficiently evaluate a mapping and create a mesh of coordinates.

Parameters:

un, vn

GLintSpecifies the number of grid subdivisions in the u or v direction.

u1, u2

Specifies the lower and upper grid domain values in the u direction.

v1, v2

Specifies the lower and upper grid domain values in the v direction.

Returns:

None.

See Also:

glEvalCoord, glEvalMesh, glEvalPoint, glMap1, glMap2

gluBeginCurve

Purpose:

Begins a NURBS curve definition.

Include File:

<glu.h>

Syntax:

void gluBeginCurve(GLUnurbsObj *nObj);

Description:

You use this function with gluEndCurve to delimit a NURBS curve definition.

Parameters:

nObj

GLUnurbsObj *Specifies the NURBS object.

Returns:

None.

See Also:

gluEndCurve

gluBeginSurface

Purpose:

Begins a NURBS surface definition.

Include File:

<glu.h>

Syntax:

void gluBeginSurface(GLUnurbsObj *nObj);

Description:

You use this function with gluEndSurface to delimit a NURBS surface definition.

Parameters:

nObj

GLUnurbsObj *Specifies the NURBS object.

Returns:

None.

See Also:

gluEndSurface

gluBeginTrim

Purpose:

Begins a NURBS trimming loop definition.

Include File:

<glu.h>

Syntax:

void gluBeginTrim(GLUnurbsObj *nObj);

Description:

You use this function with gluEndTrim to delimit a trimming curve definition. A trimming curve is a curve or set of joined curves defined with gluNurbsCurve or gluPwlCurve. The gluBeginTrim and gluEndTrim functions must reside inside the gluBeginSurface/gluEndSurface delimiters. When you use trimming, the direction of the curve specifies which portions of the surface are trimmed. Surface area to the left of the traveling direction of the trimming curve is left untrimmed. Thus, clockwise-wound trimming curves eliminate the area inside them, and counterclockwise-wound trimming curves eliminate the area outside them.

Parameters:

nObj

GLUnurbsObj *Specifies the NURBS object.

Returns:

None.

See Also:

gluEndTrim

gluCylinder

Purpose:

Draws a quadric cylinder.

Include File:

<glu.h>

Syntax:

void gluCylinder(GLUquadricObj *obj, GLdouble 
GLUnurbsObj *:baseRadius,
                       GLdouble topRadius, 
GLUnurbsObj *:GLdouble height,
                       GLint slices, GLint stacks);

Description:

This function draws a hollow cylinder with no ends along the z-axis. If topRadius or bottomRadius is 0, a cone is drawn instead. The cylinder is projected height units along the positive z-axis. The slices argument controls the number of sides along the cylinder. The stacks argument controls the number of segments generated along the z-axis across the cylinder.

Parameters:

obj

GLUquadricObj *The quadric state information to use for rendering.

baseRadius

GLdoubleThe radius of the base (z = 0) of the cylinder.

topRadius

GLdoubleThe radius of the top (z = height) of the cylinder.

height

GLdoubleThe height or length of the cylinder along the z-axis.

slices

GLintThe number of sides on the cylinder.

stacks

GLintThe number of segments in the cylinder along the z-axis.

Returns:

None.

See Also:

gluDeleteQuadric, gluNewQuadric, gluQuadricCallback, gluQuadricDrawStyle, gluQuadricNormals, gluQuadricOrientation, gluQuadricTexture

gluDeleteNurbsRenderer

Purpose:

Destroys a NURBS object.

Include File:

<glu.h>

Syntax:

void gluDeleteNurbsRenderer(GLUnurbsObj *nobj);

Description:

This function deletes the NURBS object specified and frees any memory associated with it.

Parameters:

nObj

GLUnurbsObj *Specifies the NURBS object to delete.

Returns:

None.

See Also:

gluNewNurbsRenderer

gluDeleteQuadric

Purpose:

Deletes a quadric state object.

Include File:

<glu.h>

Syntax:

void gluDeleteQuadric(GLUquadricObj *obj);

Description:

This function deletes a quadric state object. After an object has been deleted, it cannot be used for drawing again.

Parameters:

obj

GLUquadricObj *The quadric state object to delete.

Returns:

None.

See Also:

gluNewQuadric, gluQuadricCallback, gluQuadricDrawStyle, gluQuadricNormals, gluQuadricOrientation, gluQuadricTexture

gluDeleteTess

Purpose:

Deletes a tessellator object.

Include File:

<glu.h>

Syntax:

void gluDeleteTess(GLUtesselator *tobj);

Description:

This function frees all memory associated with a tessellator object.

Parameters:

tobj

GLUtesselator *The tessellator object to delete.

Returns:

None.

See Also:

gluNewTess

gluDisk

Purpose:

Draws a quadric disk.

Include File:

<glu.h>

Syntax:

void gluDisk(GLUquadricObj *obj, GLdouble innerRadius,
                             GLdouble outerRadius,
GLUtesselator *: GLint slices, GLint loops);

Description:

This function draws a disk perpendicular to the z-axis. If innerRadius is 0, a solid (filled) circle is drawn instead of a washer. The slices argument controls the number of sides on the disk. The loops argument controls the number of rings generated out from the z-axis.

Parameters:

obj

GLUquadricObj *The quadric state information to use for rendering.

innerRadius

GLdoubleThe inside radius of the disk.

outerRadius

GLdoubleThe outside radius of the disk.

slices

GLintThe number of sides on the cylinder.

loops

GLintThe number of rings out from the z-axis.

Returns:

None.

See Also:

gluDeleteQuadric, gluNewQuadric, gluQuadricCallback, gluQuadricDrawStyle, gluQuadricNormals, gluQuadricOrientation, gluQuadricTexture

gluEndCurve

Purpose:

Ends a NURBS curve definition.

Include File:

<glu.h>

Syntax:

void gluEndCurve(GLUnurbsObj *nobj);

Description:

You use this function with gluBeginCurve to delimit a NURBS curve definition.

Parameters:

nObj

GLUnurbsObj *Specifies the NURBS object.

Returns:

None.

See Also:

gluBeginCurve

gluEndSurface

Purpose:

Ends a NURBS surface definition.

Include File:

<glu.h>

Syntax:

void gluEndSurface(GLUnurbsObj *nObj);

Description:

You use this function with gluBeginSurface to delimit a NURBS surface definition.

Parameters:

nObj

GLUnurbsObj *Specifies the NURBS object.

Returns:

None.

See Also:

gluBeginSurface

gluEndTrim

Purpose:

Ends a NURBS trimming loop definition.

Include File:

<glu.h>

Syntax:

void gluEndTrim(GLUnurbsObj *nObj);

Description:

You use this function with gluBeginTrim to mark the end of a NURBS trimming loop. See gluBeginTrim for more information on trimming loops.

Parameters:

nObj

GLUnurbsObj *Specifies the NURBS object.

Returns:

None.

See Also:

gluBeginTrim

gluGetNurbsProperty

Purpose:

Retrieves a NURBS property.

Include File:

<gl.h>

Syntax:

void gluGetNurbsProperty(GLUnurbsObj *nObj, GLenum
GLUnurbsObj *:  property, GLfloat *value);

Description:

This function retrieves the NURBS property specified for a particular NURBS object. See gluNurbsProperty for an explanation of the various properties.

Parameters:

nObj

GLUnurbsObj *Specifies the NURBS object.

property

GLenum *The NURBS property to be retrieved. Valid properties are GLU_SAMPLING_TOLERANCE, GLU_DISPLAY_MODE, GLU_CULLING, GLU_AUTO_LOAD_MATRIX, GLU_PARAMETRIC_TOLERANCE, GLU_SAMPLING_METHOD, GLU_U_STEP, and GLU_V_STEP. See the gluNurbsProperty function for details on these properties.

value

GLfloat *A pointer to the location into which the value of the named property is to be copied.

Returns:

None.

See Also:

gluNewNurbsRenderer, gluNurbsProperty

gluLoadSamplingMatrices

Purpose:

Loads NURBS sampling and culling matrices.

Include File:

<gl.h>

Syntax:

void gluLoadSamplingMatrices(GLUnurbsObj *nObj, 
GLfloat *:const GLfloat modelMatrix[16],
                                const GLfloat
GLfloat *: projMatrix[16],
                                const GLint
GLfloat *: viewport[4]);

Description:

You use this function to recompute the sampling and culling matrices for a NURBS surface. The sampling matrix enables you to determine how finely the surface must be tessellated to satisfy the sampling tolerance. The culling matrix enables you to determine whether the surface should be culled before rendering. Usually, this function does not need to be called, unless the GLU_AUTO_LOAD_MATRIX property is turned off. This might be the case when using selection and feedback modes.

Parameters:

nObj

GLUnurbsObj *Specifies the NURBS object.

modelMatrix

GLfloat[16]Specifies the modelview matrix.

projMatrix

GLfloat[16]Specifies the projection matrix.

viewport

GLint[4]Specifies a viewport.

Returns:

None.

See Also:

gluNewNurbsRenderer, gluNurbsProperty

gluNewNurbsRenderer

Purpose:

Creates a NURBS object.

Include File:

<glu.h>

Syntax:

GLUnurbsObj* gluNewNurbsRenderer(void);

Description:

This function creates a NURBS rendering object. This object is used to control the behavior and characteristics of NURBS curves and surfaces. The functions that allow the NURBS properties to be set all require this pointer. You must delete this object with gluDeleteNurbsRenderer when you are finished rendering your NURBS.

Returns:

A pointer to a new NURBS object. This object is used when you call the rendering and control functions.

See Also:

gluDeleteNurbsRenderer

gluNewQuadric

Purpose:

Creates a new quadric state object.

Include File:

<glu.h>

Syntax:

GLUquadricObj *gluNewQuadric(void);

Description:

This function creates a new opaque quadric state object to be used for drawing. The quadric state object contains specifications that determine how subsequent images will be drawn.

Parameters:

None.

Returns:

GLUquadricObj *NULL if no memory is available; otherwise, a valid quadric state object pointer.

See Also:

gluDeleteQuadric, gluQuadricCallback, gluQuadricDrawStyle, gluQuadricNormals, gluQuadricOrientation, gluQuadricTexture

gluNewTess

Purpose:

Creates a tessellator object.

Include File:

<glu.h>

Syntax:

GLUtriangulatorObj *gluNewTess(void);

Description:

This function creates a tessellator object.

Parameters:

None.

Returns:

GLUtriangulatorObj *: The new tessellator object.

See Also:

gluDeleteTess

gluNurbsCallback

Purpose:

Defines a callback for a NURBS function.

Include File:

<glu.h>

Syntax:

void gluNurbsCallback(GLUnurbsObj *nObj, GLenum 
GLUquadricObj *:which, void(*fn)( ));

Description:

This function sets a NURBS callback function. The only supported callback prior to GLU version 1.3 is GL_ERROR. When an error occurs, this function is called with an argument of type GLenum. One of 37 NURBS errors can be specified by the constants GLU_NURBS_ERROR1 through GLU_NURBS_ERROR37. You can retrieve a character string definition of the error with the function gluErrorString. These error codes are listed in Table 10.2. For GLU version 1.3 and later, GLU_ERROR has been superceded by GLU_NURBS_ERROR and a number of other callbacks listed under “Parameters.”

Parameters:

nObj

GLUnurbsObj *Specifies the NURBS object.

which

GLenumSpecifies the callback being defined. Prior to GLU version 1.3, the only valid value was GLU_ERROR. For GLU version 1.3 and later (currently, only MacOS X) GLU_ERROR has been superceded by GLU_NURBS_ERROR and any of the following other callbacks: GLU_NURBS_BEGIN, GLU_NURBS_VERTEX, GLU_NURBS_NORMAL, GLU_NURBS_COLOR, GLU_NURBS_TEXTURE_COORD, GLU_NURBS_END, GLU_NURBS_BEGIN_DATA, GLU_NURBS_VERTEX_DATA, GLU_NURBS_NORMAL_DATA, GLU_NURBS_COLOR_DATA, GLU_NURBS_TEXTURE_COORD_DATA, and GLU_NURBS_END_DATA.

fn

void *()Specifies the function that should be called for the callback. The following prototypes are used for the different callbacks:

GLU_NURBS_BEGIN: void *(GLenum type);
GLU_NURBS_BEGIN_DATA: void *(GLenum type, void 
void *():*userData)
GLU_NURBS_VERTEX: void *(GLfloat *vertex);
GLU_NURBS_VERTEX_DATA: void (GLfloat *vertex, void
void *(): *userData)
GLU_NURBS_NORMAL: void *(GLfloat *normal);
GLU_NURBS_NORMAL_DATA: void *(GLfloat *normal, 
void *():void *userData);
GLU_NURBS_COLOR: void *(GLfloat *color);
GLU_NURBS_COLOR_DATA: void *(GLfloat *color, void 
void *():*userData);
GLU_NURBS_TEXTURE_COORD: void *(GLfloat *texCoord);
GLU_NURBS_TEXTURE_COORD_DATA: void *(GLfloat
void *(): *texCoord, void *userData);
GLU_NURBS_END: void *(void);
GLU_NURBS_END_DATA: void *(void userData);
GLU_NURBS_ERROR: void *(GLenum error);

Returns:

None.

See Also:

gluErrorString

Table 10.2. NURBS Error Codes

Error Code

Definition

GLU_NURBS_ERROR1

Spline order unsupported.

GLU_NURBS_ERROR2

Too few knots.

GLU_NURBS_ERROR3

Valid knot range is empty.

GLU_NURBS_ERROR4

Decreasing knot sequence knot.

GLU_NURBS_ERROR5

Knot multiplicity greater than order of spline.

GLU_NURBS_ERROR6

endcurve must follow bgncurve.

GLU_NURBS_ERROR7

bgncurve must precede endcurve.

GLU_NURBS_ERROR8

Missing or extra geometric data.

GLU_NURBS_ERROR9

Can't draw pwlcurves.

GLU_NURBS_ERROR10

Missing or extra domain data.

GLU_NURBS_ERROR11

Missing or extra domain data.

GLU_NURBS_ERROR12

endtrim must precede endsurface.

GLU_NURBS_ERROR13

bgnsurface must precede endsurface.

GLU_NURBS_ERROR14

Curve of improper type passed as trim curve.

GLU_NURBS_ERROR15

bgnsurface must precede bgntrim.

GLU_NURBS_ERROR16

endtrim must follow bgntrim.

GLU_NURBS_ERROR17

bgntrim must precede endtrim.

GLU_NURBS_ERROR18

Invalid or missing trim curve.

GLU_NURBS_ERROR19

bgntrim must precede pwlcurve.

GLU_NURBS_ERROR20

pwlcurve referenced twice.

GLU_NURBS_ERROR21

pwlcurve and nurbscurve mixed.

GLU_NURBS_ERROR22

Improper usage of trim data type.

GLU_NURBS_ERROR23

nurbscurve referenced twice.

GLU_NURBS_ERROR24

nurbscurve and pwlcurve mixed.

GLU_NURBS_ERROR25

nurbssurface referenced twice.

GLU_NURBS_ERROR26

Invalid property.

GLU_NURBS_ERROR27

endsurface must follow bgnsurface.

GLU_NURBS_ERROR28

Intersecting or misoriented trim curves.

GLU_NURBS_ERROR29

Intersecting trim curves.

GLU_NURBS_ERROR30

Unused.

GLU_NURBS_ERROR31

Unconnected trim curves.

GLU_NURBS_ERROR32

Unknown knot error.

GLU_NURBS_ERROR33

Negative vertex count encountered.

GLU_NURBS_ERROR34

Negative byte-stride encountered.

GLU_NURBS_ERROR35

Unknown type descriptor.

GLU_NURBS_ERROR36

Null control point reference.

GLU_NURBS_ERROR37

Duplicate point on pwlcurve.

gluNurbsCurve

Purpose:

Defines the shape of a NURBS curve.

Include File:

<glu.h>

Syntax:

void gluNurbsCurve(GLUnurbsObj *nObj, GLint nknots
NURBS Error Codes, GLfloat *knot,
                   GLint stride, GLfloat *ctlArray
NURBS Error Codes, GLint order, GLenum type);

Description:

This function defines the shape of a NURBS curve. The definition of this curve must be delimited by gluBeginCurve and gluEndCurve.

Parameters:

nObj

GLUnurbsObj *A pointer to the NURBS object (created with gluNewNurbsRenderer).

nknots

GLintThe number of knots in *knots. This is the number of control points plus order.

knot

GLfloat *An array of knot values in nondescending order.

stride

GLintThe offset, as a number of single-precision floating-point values, between control points.

ctlArray

GLfloat *A pointer to an array or data structure containing the control points for the NURBS surface.

order

GLintThe order of the NURBS surface. The order is 1 more than the degree.

type

GLenumThe type of surface. It can be any of the two-dimensional evaluator types: GL_MAP2_VERTEX_3, GL_MAP2_VERTEX_4, GL_MAP2_INDEX, GL_MAP2_COLOR_4, GL_MAP2_NORMAL, GL_MAP2_TEXTURE_COORD_1, GL_MAP2_TEXTURE_COORD_2, GL_MAP2_TEXTURE_COORD_3, and GL_MAP2_TEXTURE_COORD_4.

Returns:

None.

See Also:

gluBeginCurve, gluEndCurve, gluNurbsSurface

gluNurbsProperty

Purpose:

Sets a NURBS property.

Include File:

<glu.h>

Syntax:

void gluNurbsProperty(GLUnurbsObj *nObj, GLenum 
GLenum:property, GLfloat value);

Description:

This function sets the properties of the NURBS object. Valid properties are as follows:

GLU_SAMPLING_TOLERANCESets the maximum length in pixels to use when using the GLU_PATH_LENGTH sampling method. The default is 50.0.

GLU_DISPLAY_MODEDefines how the NURBS surface is rendered. The value parameter can be GLU_FILL to use filled and shaded polygons, GLU_OUTLINE_POLYGON to draw just the outlines of the polygons (after tessellation), or GLU_OUTLINE_PATCH to draw just the outlines of user-defined patches and trim curves. The default is GLU_FILL.

GLU_CULLINGInterprets the value parameter as a Boolean value that indicates whether the NURBS curve should be discarded if its control points are outside the viewport.

GLU_PARAMETRIC_TOLERANCESets the maximum pixel distance used when the sampling method is set to GLU_PARAMETRIC_ERROR. The default is 0.5. This property was introduced in GLU version 1.1.

GLU_SAMPLING_METHODSpecifies how to tessellate the NURBS surface. This property was introduced in GLU version 1.1. The following values are valid:

  • GLU_PATH_LENGTH specifies that surfaces rendered with the maximum pixel length of the edges of the tessellation polygons are not greater than the value specified by GLU_SAMPLING_TOLERANCE. GLU_PARAMETRIC_ERROR specifies that the surface is rendered with the value of GLU_PARAMETRIC_TOLERANCE designating the maximum distance, in pixels, between the tessellation polygons and the surfaces they approximate. GLU_DOMAIN_DISTANCE specifies, in parametric coordinates, how many sample points per unit length to take in the u and v dimensions. The default is GLU_PATH_LENGTH.

GLU_U_STEPSets the number of sample points per unit length taken along the u dimension in parametric coordinates. This value is used when GLU_SAMPLING_METHOD is set to GLU_DOMAIN_DISTANCE. The default is 100. This property was introduced in GLU version 1.1.

GLU_V_STEPSets the number of sample points per unit length taken along the v dimension in parametric coordinates. This value is used when GLU_SAMPLING_METHOD is set to GLU_DOMAIN_DISTANCE. The default is 100. This property was introduced in GLU version 1.1.

GLU_AUTO_LOAD_MATRIXInterprets the value parameter as a Boolean value. When it is set to GL_TRUE, it causes the NURBS code to download the projection matrix, the modelview matrix, and the viewport from the OpenGL server to compute sampling and culling matrices for each NURBS curve. Sampling and culling matrices are required to determine the tessellation of a NURBS surface into line segments or polygons and to cull a NURBS surface if it lies outside the viewport. If this mode is set to GL_FALSE, the user needs to provide these matrices and a viewport for the NURBS renderer to use in constructing sampling and culling matrices. This can be done with the gluLoadSamplingMatrices function. The default value for this mode is GL_TRUE. Changing this mode does not affect the sampling and culling matrices until gluLoadSamplingMatrices is called.

Parameters:

nObj

GLUnurbsObj *The NURB object that is having a property modified. (It is created by calling glNewNurbsRenderer.)

property

GLenumThe property to be set or modified. It may be any of the following values: GLU_SAMPLING_TOLERANCE, GLU_DISPLAY_MODE, GLU_CULLING, GLU_AUTO_LOAD_MATRIX, GLU_PARAMETRIC_TOLERANCE, GLU_SAMPLING_METHOD, GLU_U_STEP, and GLU_V_STEP.

value

GLfloatThe value to which the indicated property is being set.

Returns:

None.

See Also:

gluGetNurbsProperty, gluGetString, gluLoadSamplingMatrices, gluNewNurbsRenderer, gluNewNurbsRenderer, gluNurbsCurve, gluPwlCurve

gluNurbsSurface

Purpose:

Defines the shape of a NURBS surface.

Include File:

<glu.h>

Syntax:

void gluNurbsSurface(GLUnurbsObj *nObj, GLint 
GLfloat:uknotCount, GLfloat *uknot,
                     GLint vknotCount, GLfloat 
GLfloat:*vknot, GLint uStride,
                     GLint vStride, GLfloat 
GLfloat:*ctlArray, GLint uorder,
                     GLint vorder, GLenum type);

Description:

This function defines the shape of a NURBS surface. It must be delimited by gluBeginSurface and gluEndSurface. The shape of the surface is defined before any trimming takes place. You can trim a NURBS surface by using gluBeginTrim/gluEndTrim and gluNurbsCurve or gluPwlCurve to do the trimming.

Parameters:

nObj

GLUnurbsObj *A pointer to the NURBS object. (It is created with gluNewNurbsRenderer.)

uknotCount

GLintThe number of knots in the parametric u direction.

uknot

GLfloat *An array of knot values that represent the knots in the u direction. These values must be nondescending. The length of the array is specified in uknotCount.

vknotCount

GLintThe number of knots in the parametric v direction.

vknot

GLfloat*An array of knot values that represent the knots in the v direction. These values must be nondescending. The length of the array is specified in vknotCount.

uStride

GLintThe offset, as a number of single-precision, floating-point values, between successive control points in the parametric u direction in ctlArray.

vStride

GLintThe offset, as a number of single-precision, floating-point values, between successive control points in the parametric v direction in ctlArray.

ctlArray

GLfloat *A pointer to an array containing the control points for the NURBS surface. The offsets between successive control points in the parametric u and v directions are given by uStride and vStride.

uorder

GLintThe order of the NURBS surface in the parametric u direction. The order is 1 more than the degree; hence, a surface that is cubic in u has a u order of 4.

vorder

GLintThe order of the NURBS surface in the parametric v direction. The order is 1 more than the degree; hence, a surface that is cubic in v has a v order of 4.

type

GLenumThe type of surface. It can be any of the 2D evaluator types: GL_MAP2_VERTEX_3, GL_MAP2_VERTEX_4, GL_MAP2_INDEX, GL_MAP2_COLOR_4, GL_MAP2_NORMAL, GL_MAP2_TEXTURE_COORD_1, GL_MAP2_TEXTURE_COORD_2, GL_MAP2_TEXTURE_COORD_3, and GL_MAP2_TEXTURE_COORD_4.

Returns:

None.

See Also:

gluBeginSurface, gluBeginTrim

gluPartialDisk

Purpose:

Draws a partial quadric disk.

Include File:

<glu.h>

Syntax:

void gluPartialDisk(GLUquadricObj *obj, GLdouble 
GLenum:innerRadius,
                             GLdouble outerRadius,
GLenum: GLint slices, GLint loops,
                             GLdouble startAngle, 
GLenum:GLdouble sweepAngle);

Description:

This function draws a partial disk perpendicular to the z-axis. If innerRadius is 0, a solid (filled) circle is drawn instead of a washer. The slices argument controls the number of sides on the disk. The loops argument controls the number of rings generated out from the z-axis. The startAngle argument specifies the starting angle of the disk with 0° at the top of the disk and 90° at the right of the disk. The sweepAngle argument specifies the portion of the disk in degrees.

Parameters:

obj

GLUquadricObj *The quadric state information to use for rendering.

innerRadius

GLdoubleThe inside radius of the disk.

outerRadius

GLdoubleThe outside radius of the disk.

slices

GLintThe number of sides on the cylinder.

loops

GLintThe number of rings out from the z-axis.

startAngle

GLdoubleThe start angle of the partial disk.

sweepAngle

GLdoubleThe angular size of the partial disk.

Returns:

None.

See Also:

gluDeleteQuadric, gluNewQuadric, gluQuadricCallback, gluQuadricDrawStyle, gluQuadricNormals, gluQuadricOrientation, gluQuadricTexture

gluPwlCurve

Purpose:

Specifies a piecewise NURBS trimming curve.

Include File:

<glu.h>

Syntax:

void gluPwlCurve(GLUnurbsObj *nObj, GLint count, 
GLdouble:GLfloat *array, GLint stride, GLenum type);

Description:

This function defines a piecewise linear trimming curve for a NURBS surface. The array of points is in terms of the parametric u and v coordinate space. This space is a unit square exactly 1 unit in length along both axes. Clockwise-wound trimming curves eliminate the enclosed area; counterclockwise trimming curves discard the exterior area. Typically, a trimming region is first established around the entire surface area that trims away all points not on the surface. Then smaller trimming areas wound clockwise are placed within it to cut away sections of the curve. Trimming curves can be piecewise. This means one or more calls to gluPwlCurve or gluNurbsCurve can be called to define a trimming region as long as they share endpoints and define a close region in u/v space.

Parameters:

nObj

GLUnurbsObj *Specifies the NURBS object being trimmed.

count

GLintSpecifies the number of points on the curve listed in *array.

array

GLfloat *Specifies the array of boundary points for this curve.

stride

GLintSpecifies the offset between points on the curve.

type

GLenumSpecifies the type of curve. It can be GLU_MAP1_TRIM_2, used when the trimming curve is specified in terms of u and v coordinates, or GLU_MAP1_TRIM_3, used when a w (scaling) coordinate is also specified.

Returns:

None.

gluQuadricCallback

Purpose:

Defines a quadric callback function.

Include File:

<glu.h>

Syntax:

void gluQuadricCallback(GLUquadricObj *obj, GLenum
GLenum: which, void (*fn)());

Description:

This function defines callback functions to be used when drawing quadric shapes. At present, the only defined callback function is GLU_ERROR, which is called whenever an OpenGL or GLU error occurs.

Parameters:

obj

GLUquadricObj *The quadric state information to use for rendering.

which

GLenumThe callback function to define. It must be GLU_ERROR.

fn

void (*)()The callback function (receives one GLenum containing the error).

Returns:

None

See Also:

gluDeleteQuadric, gluNewQuadric, gluQuadricDrawStyle, gluQuadricNormals, gluQuadricOrientation, gluQuadricTexture

gluQuadricDrawStyle

Purpose:

Sets the drawing style of a quadric state object.

Include File:

<glu.h>

Syntax:

void gluQuadricDrawStyle(GLUquadricObj *obj, 
void (*)():GLenum drawStyle);

Description:

This function selects a drawing style for all quadric shapes.

Parameters:

obj

GLUquadricObj *The quadric state information to use for rendering.

drawStyle

GLenumThe drawing style. Valid styles are as follows:

GLU_FILLQuadrics are drawn filled, using polygon and strip primitives.

GLU_LINEQuadrics are drawn “wireframe,” using line primitives.

GLU_SILHOUETTEQuadrics are drawn using line primitives; only the outside edges are drawn.

GLU_POINTQuadrics are drawn using point primitives.

Returns:

None.

See Also:

gluDeleteQuadric, gluNewQuadric, gluQuadricCallback, gluQuadricNormals, gluQuadricOrientation, gluQuadricTexture

gluQuadricNormals

Purpose:

Sets the type of lighting normals used for quadric objects.

Include File:

<glu.h>

Syntax:

void gluQuadricNormals(GLUquadricObj *obj, GLenum 
GLU_POINT:normals);

Description:

This function sets the type of lighting normals that are generated when drawing shapes using the specified quadric state object.

Parameters:

obj

GLUquadricObj *The quadric state information to use for rendering.

normals

GLenumThe type of normal to generate. Valid types are as follows:

GLU_NONENo lighting normals are generated.

GLU_FLATLighting normals are generated for each polygon to generate a faceted appearance.

GLU_SMOOTHLighting normals are generated for each vertex to generate a smooth appearance.

Returns:

None.

See Also:

gluDeleteQuadric, gluNewQuadric, gluQuadricCallback, gluQuadricDrawStyle, gluQuadricOrientation, gluQuadricTexture

gluQuadricOrientation

Purpose:

Sets the orientation of lighting normals for quadric objects.

Include File:

<glu.h>

Syntax:

void gluQuadricOrientation(GLUquadricObj *obj, 
GLU_SMOOTH:GLenum orientation);

Description:

This function sets the direction of lighting normals for hollow objects. The orientation parameter can be GLU_OUTSIDE to point lighting normals outward or GLU_INSIDE to point them inward.

Parameters:

obj

GLUquadricObj *The quadric state information to use for rendering.

orientation

GLenumThe orientation of lighting normals, GLU_OUTSIDE or GLU_INSIDE. The default is GLU_OUTSIDE.

Returns:

None.

See Also:

gluDeleteQuadric, gluNewQuadric, gluQuadricCallback, gluQuadricDrawStyle, gluQuadricNormals, gluQuadricTexture

gluQuadricTexture

Purpose:

Enables or disables texture coordinate generation for texture-mapping images onto quadrics.

Include File:

<glu.h>

Syntax:

void gluQuadricTexture(GLUquadricObj *obj, 
GLenum:GLboolean textureCoords);

Description:

This function controls whether texture coordinates are generated for quadric shapes.

Parameters:

obj

GLUquadricObj *The quadric state information to use for rendering.

textureCoords

GLbooleanGL_TRUE if texture coordinates should be generated; GL_FALSE otherwise.

Returns:

None.

See Also:

gluDeleteQuadric, gluNewQuadric, gluQuadricCallback, gluQuadricDrawStyle, gluQuadricNormals, gluQuadricOrientation

gluSphere

Purpose:

Draws a quadric sphere.

Include File:

<glu.h>

Syntax:

void gluSphere(GLUquadricObj *obj, GLdouble radius
GLboolean:, GLint slices, GLint stacks);

Description:

This function draws a hollow sphere centered at the origin. The slices argument controls the number of lines of longitude on the sphere. The stacks argument controls the number of lines of latitude on the sphere.

Parameters:

obj

GLUquadricObj *The quadric state information to use for rendering.

radius

GLdoubleThe radius of the sphere.

slices

GLintThe number of lines of longitude on the sphere.

stacks

GLintThe number of lines of latitude on the sphere.

Returns:

None.

See Also:

gluDeleteQuadric, gluNewQuadric, gluQuadricCallback, gluQuadricDrawStyle, gluQuadricNormals, gluQuadricOrientation, gluQuadricTexture

gluTessBeginContour

Purpose:

Specifies a new contour or hole in a complex polygon.

Include File:

<glu.h>

Syntax:

void gluTessBeginContour(GLUtesselator *tobj);

Description:

This function specifies a new contour or hole in a complex polygon.

Parameters:

tobj

GLUtesselator *The tessellator object to use for the polygon.

Returns:

None.

See Also:

gluTessBeginPolygon, gluTessEndPolygon, gluTessEndContour, gluTessVertex

gluTessBeginPolygon

Purpose:

Starts tessellation of a complex polygon.

Include File:

<glu.h>

Syntax:

void gluTessBeginPolygon(GLUtesselator *tobj, 
GLUtesselator *:GLvoid *data);

Description:

This function starts tessellation of a complex polygon.

Parameters:

tobj

GLUtesselator *The tessellator object to use for the polygon.

data

GLvoid *The data that is passed to GLU_TESS_*_DATA callbacks.

Returns:

None.

See Also:

gluTessEndPolygon, gluTessBeginContour, gluTessEndContour, gluTessVertex

gluTessCallback

Purpose:

Specifies a callback function for tessellation.

Include File:

<glu.h>

Syntax:

void gluTessCallback(GLUtesselator *tobj, GLenum 
GLvoid *:which, void (*fn)());

Description:

This function specifies a callback function for various tessellation functions. Callback functions do not replace or change the tessellator performance. Rather, they provide the means to add information to the tessellated output (such as color or texture coordinates).

Parameters:

tobj

GLUtesselator *The tessellator object to use for the polygon.

which

GLenumThe callback function to define. Valid functions appear in Table 10.3

fn

void (*)()The function to call.

Returns:

None.

Table 10.3. Tessellator Callback Identifiers

Constant

Description

GLU_TESS_BEGIN

Specifies a function that is called to begin a GL_TRIANGLES, GL_TRIANGLE_STRIP, or GL_TRIANGLE_FAN primitive. The function must accept a single GLenum parameter that specifies the primitive to be rendered and is usually set to glBegin.

GLU_TESS_BEGIN_DATA

Like GLU_TESS_BEGIN, specifies a function [GLU 1.2] that is called to begin a GL_TRIANGLES, GL_TRIANGLE_STRIP, or GL_TRIANGLE_FAN primitive. The function must accept a Glenum parameter that specifies the primitive to be rendered and a GLvoid pointer from the call to gluTessBeginPolygon.

GLU_TESS_COMBINE

Specifies a function that is called when [GLU 1.2] vertices in the polygon are coincident; that is, they are equal.

GLU_TESS_COMBINE_DATA

Like GLU_TESS_COMBINE, specifies a function [GLU 1.2] that is called when vertices in the polygon are coincident. The function also receives a pointer to the user data from gluTessBeginPolygon.

GLU_TESS_EDGE_FLAG

Specifies a function that marks whether succeeding GLU_TESS_VERTEX callbacks refer to original or generated vertices. The function must accept a single GLboolean argument that is GL_TRUE for original and GL_FALSE for generated vertices.

GLU_TESS_EDGE_FLAG_DATA

Specifies a function similar to the GLU_TESS_EDGE_FLAG, with the exception that a void pointer to user data is also accepted.

GLU_TESS_END

Specifies a function that marks the end of a drawing primitive, usually glEnd. It takes no arguments.

GLU_TESS_END_DATA

Specifies a function similar to GLU_TESS_END, with the addition of a void pointer to user data.

GLU_TESS_ERROR

Specifies a function that is called when an error occurs. It must take a single argument of type GLenum.

GLU_TESS_VERTEX

Specifies a function that is called before every vertex is sent, usually with glVertex3dv. The function receives a copy of the third argument to gluTessVertex.

GLU_TESS_VERTEX_DATA

Like GLU_TESS_VERTEX, specifies a function [GLU 1.2] that is called before every vertex is sent. The function also receives a copy of the second argument to gluTessBeginPolygon.

gluTessEndContour

Purpose:

Ends a contour in a complex polygon.

Include File:

<glu.h>

Syntax:

void gluTessEndContour(GLUtesselator *tobj);

Description:

This function ends the current polygon contour.

Parameters:

tobj

GLUtesselator *The tessellator object to use for the polygon.

Returns:

None.

See Also:

gluTessBeginPolygon, gluTessBeginContour, gluTessEndPolygon, gluTessVertex

gluTessEndPolygon

Purpose:

Ends tessellation of a complex polygon and renders it.

Include File:

<glu.h>

Syntax:

void gluTessEndPolygon(GLUtesselator *tobj);

Description:

This function ends tessellation of a complex polygon and renders the final result.

Parameters:

tobj

GLUtesselator *The tessellator object to use for the polygon.

Returns:

None.

See Also:

gluTessBeginPolygon, gluTessBeginContour, gluTessEndContour, gluTessVertex

gluTessProperty

Purpose:

Sets a tessellator property value.

Include File:

<glu.h>

Syntax:

void gluTessProperty(GLUtesselator *tobj, GLenum 
GLUtesselator *:which, GLdouble value);

Description:

This function sets a tessellator property value.

Parameters:

tobj

GLUtesselator *The tessellator object to change.

which

GLenumThe property to change: GLU_TESS_BOUNDARY_ONLY, GLU_TESS_TOLERANCE, or GLU_TESS_WINDING_RULE.

value

GLdoubleThe value for the property.

For GLU_TESS_BOUNDARY_ONLY, the value can be GL_TRUE or GL_FALSE. If GL_TRUE, only the boundary of the polygon is displayed (no holes).

For GLU_TESS_TOLERANCE, the value is the coordinate tolerance for vertices in the polygon.

For GLU_TESS_WINDING_RULE, the value is one of GLU_TESS_WINDING_NONZERO, GLU_TESS_WINDING_POSITIVE, GLU_TESS_WINDING_NEGATIVE, GLU_TESS_WINDING_ABS_GEQ_TWO, or GLU_TESS_WINDING_ODD.

Returns:

None.

See Also:

gluTessBeginPolygon, gluTessEndPolygon, gluTessBeginContour, gluTessEndContour, gluTessCallback, gluTessVertex, gluNewTess, gluDeleteTess

gluTessVertex

Purpose:

Adds a vertex to the current polygon path.

Include File:

<glu.h>

Syntax:

void gluTessVertex(GLUtesselator *tobj, GLdouble 
GLdouble:v[3], void *data);

Description:

This function adds a vertex to the current tessellator path. The data argument is passed through to the GL_VERTEX callback function.

Parameters:

tobj

GLUtesselator *The tessellator object to use for the polygon.

v

GLdouble[3]The 3D vertex.

data

void *A data pointer to be passed to the GL_VERTEX callback function.

Returns:

None.

See Also:

gluTessBeginPolygon, gluTessEndPolygon, gluTessBeginContour, gluTessEndContour

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset