Vertex Arrays (State Management and Drawing Geometric Objects) (OpenGL Programming) Part 2

Dereferencing a List of Array Elements

glArrayElement() is good for randomly "hopping around" your data arrays. Similar routines, glDrawElements(), glMultiDrawElements(), and glDrawRangeElements(), are good for hopping around your data arrays in a more orderly manner.

void glDrawElements(GLenum mode, GLsizei count, GLenum type, void * indices);

Defines a sequence of geometric primitives using count number of ele- ■ ments, whose indices are stored in the array indices, type must be one of GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, or GL_UNSIGNED_INT, indicating the data type of the indices array, mode specifies what kind of primitives are constructed and is one of the same values that is accepted by glBegin(); for example, GL_POLYGON, GL_LINE .LOOP, GL_LINES, GL_POINTS, and so on.

The effect of glDrawElements() is almost the same as this command sequence:

tmp5324-99_thumb_thumb

glDrawElements() additionally checks to make sure mode, count, and type are valid. Also, unlike the preceding sequence, executing glDrawElements() leaves several states indeterminate. After execution of glDrawElements(), current RGB color, secondary color, color index, normal coordinates, fog coordinates, texture coordinates, and edge flag are indeterminate if the corresponding array has been enabled.


With glDrawElementsO, the vertices for each face of the cube can be placed in an array of indices. Example 2-11 shows two ways to use glDrawElementsO to render the cube. Figure 2-15 shows the numbering of the vertices used in Example 2-11.

Cube with Numbered Vertices

Figure 2-15 Cube with Numbered Vertices

Example 2-11 Using glDrawElementsO to Dereference Several Array Elements

Using glDrawElementsO to Dereference Several Array Elements

Note: It is an error to encapsulate glDrawElementsO between a glBegin()/glEnd() pair.

With several primitive types (such as GL_QUADS, GL_TRIANGLES, and GL_ LINES), you may be able to compact several lists of indices together into a single array. Since the GL_QUADS primitive interprets each group of four vertices as a single polygon, you may compact all the indices used in Example 2-11 into a single array, as shown in Example 2-12:

Example 2-12 Compacting Several glDrawElementsQ Calls into One

tmp5324-102_thumb[2]

For other primitive types, compacting indices from several arrays into a single array renders a different result. In Example 2-13, two calls to glDrawElements() with the primitive GL_LINE_STRIP render two line strips. You cannot simply combine these two arrays and use a single call to glDrawElements() without concatenating the lines into a single strip that would connect vertices #6 and #7. (Note that vertex #1 is being used in both line strips just to show that this is legal.)

Example 2-13 Two glDrawElementsQ Calls That Render Two Line Strips

Two glDrawElementsQ Calls That Render Two Line Strips

The routine glMultiDrawElements() was introduced in OpenGL Version 1.4 to enable combining the effects of several glDrawElements() calls into a single call.

void glMultiDrawElements(GLenum mode, GLsizei * count,

GLenum type, void **indices, GLsizei primcount); ..

Calls a sequence of primcount (a number of) glDrawElements() commands. indices is an array of pointers to lists of array elements, count is an array of how many vertices are found in each respective array element list, mode (primitive type) and type (data type) are the same as they are in glDrawElements 0 ·

The effect of glMultiDrawElementsQ is the same as

tmp5324-104_thumb[2]

The calls to glDrawElements() in Example 2-13 can be combined into a single call of glMultiDrawElementsQ, as shown in Example 2-14:

Example 2-14 Use of glMultiDrawElements(): mvarray.c

Use of glMultiDrawElements(): mvarray.c

Like glDrawElements() or glMultiDrawElements(), glDrawRangeElements() is also good for hopping around data arrays and rendering their contents. glDrawRangeElements() also introduces the added restriction of a range of legal values for its indices, which may increase program performance. For optimal performance, some OpenGL implementations may be able to prefetch (obtain prior to rendering) a limited amount of vertex array data. glDrawRangeElements() allows you to specify the range of vertices to be prefetched.

void glDrawRangeElements(GLenum mode, GLuint start, GLuint end,

GLsizei count, GLenum type, void *indices);

Creates; a sequence of geometric primitives that is similar to, but more restricted than, the sequence created by glDrawF,lements(). Several parameters of glDrawRangeElementsO are the same as counterparts m glDrawElements(), including mode (kind of primitives), count (number of elements}, type (data type), and indices (array locations of vertex data),. glDrawRangeElementsO introduces two new parameters: start and end, which specify a range of acceptable values for indices.. To be valid, values in the array indices must lie between start and end, inclusive.

It is a mistake for vertices in the array indices to reference outside the range [start, end]. However, OpenGL implementations are not required to find or report this mistake. Therefore, illegal index values may or may not generate an OpenGL error condition, and it is entirely up to the implementation to decide what to do.

You can use glGetIntegerv() with GL_MAX_ELEMENTS_VERTICES and GL_MAX_ELEMENTS_INDICES to find out, respectively, the recommended maximum number of vertices to be prefetched and the maximum number of indices (indicating the number of vertices to be rendered) to be referenced. If end – start + 1 is greater than the recommended maximum of prefetched vertices, or if count is greater than the recommended maximum of indices, glDrawRangeElementsO should still render correctly, but performance may be reduced.

Not all vertices in the range [start, end] have to be referenced. However, on some implementations, if you specify a sparsely used range, you may unnecessarily process many vertices that go unused.

With glArrayElementO, glDrawElements(), glMultiDrawElementsO, and glDrawRangeElementsO, it is possible that your OpenGL implementation caches recently processed (meaning transformed, lit) vertices, allowing your application to "reuse" them by not sending them down the transformation pipeline additional times. Take the aforementioned cube, for example, which has six faces (polygons) but only eight vertices. Each vertex is used by exactly three faces. Without gl*Elements(), rendering all six faces would require processing 24 vertices, even though 16 vertices are redundant. Your implementation of OpenGL may be able to minimize redundancy and process as few as eight vertices. (Reuse of vertices may be limited to all vertices within a single glDrawElementsQ or glDrawRangeElementsO call, a single index array for glMultiDrawElementsO, or, for glArrayElementO, within one glBegin()/glEnd() pair.)

Dereferencing a Sequence of Array Elements

While glArrayElementO, glDrawElements(), and glDrawRangeElementsO

"hop around" your data arrays, glDrawArrays() plows straight through them.

void glDrawArrays(GLenum mode, GLint first, GLsizei count);

Constructs a sequence of geometric primitives using array elements starting at first and ending at frrst + count – 1 of each enabled array, mode specifies what kinds of primitives are constructed and is one of the same values accepted by glBegin(); for example, GL_POLYGON, GL_LINE_ LOOP, GLJUNES,’gL_POINTS, and so on.

The effect of glDrawArrays() is almost the same as this command sequence:

tmp5324-106_thumb[2]

As is the case with glDrawElements(), glDrawArrays() also performs error checking on its parameter values and leaves the current RGB color, secondary color, color index, normal coordinates, fog coordinates, texture coordinates, and edge flag with indeterminate values if the corresponding array has been enabled.

Try This

Change the icosahedron drawing routine in Example 2-17 on page 96 to use vertex arrays.

Similar to glMultiDrawElementsO, the routine glMultiDrawArraysO was introduced in OpenGL Version 1.4 to combine several glDrawArraysO calls into a single call.

■Void glMultiDrawArrays( GLenum mode, GLint * first, GLsizei * count

GLsizei primcount); 

Calls a sequence of primcount (a number of) glDrawArray() commands. mode specifies the primitive type with the same values as accepted by glBeginO· first and count contain lists of array locations indicating where to process each list of array elements. Therefore, for tne ;’th list of array elements, a geometric primitive is constructed starting at firstfi] and ending at firstfi] + countfi] – 1.

tmp5324-107_thumb[2]

Interleaved Arrays

Advanced

Earlier in this topic (see "Stride" on page 71), the special case of interleaved arrays was examined. In that section, the array intertwined, which interleaves RGB color and 3D vertex coordinates, was accessed by calls to glColorPointer() and glVertexPointer(). Careful use of stride helped properly specify the arrays:

tmp5324-108_thumb[2]

 

tmp5324-109_thumb[2]

There is also a behemoth routine, glInterleavedArrays(), that can specify several vertex arrays at once. glInterleavedArrays() also enables and disables the appropriate arrays (so it combines "Step 1: Enabling Arrays" on page 67 and "Step 2: Specifying Data for the Arrays" on page 68). The array intertwined exactly fits one of the 14 data-interleaving configurations supported by glInterleavedArrays(). Therefore, to specify the contents of the array intertwined into the RGB color and vertex arrays and enable both arrays, call

tmp5324-110_thumb[2]

This call to glInterleavedArrays() enables GL_COLOR_ARRAY and GL_VERTEX_ARRAY. It disables GL_SECONDARY_COLOR_ARRAY, GL_INDEX_ARRAY, GL_NORMAL_ARRAY, GL_FOG_COORDINATE_ ARRAY, GL_TEXTURE_COORD_ARRAY, and GL_EDGE_FLAG_ARRAY.

This call also has the same effect as calling glColorPointer() and glVertexPointerO to specify the values for six vertices in each array. Now you are ready for Step 3: calling glArrayElementO, glDrawElements(), glDrawRangeElementsO, or glDrawArrays() to dereference array elements.

void glInttrleavedArrays(GI,enum format, GLsizei stride, void *pointer)

Initializes all eight arrays, disabling arrays that are not specified in format, and enabling the arrays that are specified, format is one of 14 symbolic constants, which represent 14 data configurations; Table 2-5 displays format values, stride specifies the byte offset between consecutive vertices. If stride is 0, the vertices are understood to be tightly packed in the array. pointer is the memory address of the first coordinate of the first vertex in the array.

If multitexturing is enabled, gllnterleavedArraysO affects only the active texture unit. See "Multitexturing" on page 438 for details.

Note that gllnterleavedArraysO does not support edge flags.

The mechanics of gllnterleavedArraysO are intricate and require reference to Example 2-15 and Table 2-5. In that example and table, you’ll seetmp5324-111_thumb[2] andtmp5324-112_thumb[2]which are the Boolean values for the enabled or disabled texture coordinate, color, and normal arrays; and you’ll seetmp5324-113_thumb[2]which are the sizes (numbers of components) for the texture coordinate, color, and vertex arrays. tc is the data type for RGBA color, which is the only array that can have nonfloating-point interleaved values. pc, pn, and pv are the calculated strides for jumping into individual color, normal, and vertex values; and s is the stride (if one is not specified by the user) to jump from one array element to the next.

The effect of glInterleavedArrays() is the same as calling the command sequence in Example 2-15 with many values defined in Table 2-5. All pointer arithmetic is performed in units of sizeof(GLubyte).

Example 2-15 Effect of glInterleavedArrays(format, stride, pointer)

Effect of glInterleavedArrays(format, stride, pointer)

 

 

Effect of glInterleavedArrays(format, stride, pointer)

In Table 2-5, T and F are True and False, f is sizeof(GLfloat). c is 4 times sizeof(GLubvte), rounded up to the nearest multiple of f.

Format

et

ec

en

st

Sc

sv

Pc

Pn

Pv

s

GL_V2F

F

F

F

2

0

2f

GL_V3F

F

F

F

3

0

3f

GL_C4UB_V2F

F

T

F

4

2

GL_UNSIGNED_BYTE

0

c

c+2f

GL_C4UB_V3F

F

T

F

4

3

GL_UNSIGNED_BYTE

0

c

c+3f

GL_C3F_V3F

F

T

F

3

3

GL_FLOAT

0

3f

6f

GL_N3F_V3F

F

F

T

3

0

3f

6f

GL_C4F_N3F_V3F

F

T

T

4

3

GL_FLOAT

0

4f

7f

lOf

GL_T2F_V3F

T

F

F

2

3

2f

5f

GL_T4F_V4F

T

F

F

4

4

4f

8f

GL_T2F_C4UB_V3F

T

T

F

2

4

3

GL_UNSIGNED_BYTE

2f

c+2f

c+5f

GL_T2F_C3F_V3F

T

T

F

2

3

3

GL_FLOAT

2f

5f

8f

GL_T2F_N3F_V3F

T

F

T

2

3

2f

5f

8f

GL_T2F_C4F_N3F_V3F

T

T

T

2

4

3

GL_FLOAT

2f

6f

9f

12f

GL_T4F_C4F_N3F_V4F

T

T

T

4

4

4

GL_FLOAT

4f

8f

Ilf

15f

Table 2-5 Variables That Direct glInterleavedArrays()

Start by learning the simpler formats, GL_V2F, GL_V3F, and GL_C3F_V3F. If you use any of the formats with C4UB, you may have to use a struct data type or do some delicate type casting and pointer math to pack four unsigned bytes into a single 32-bit word.

For some OpenGL implementations, use of interleaved arrays may increase application performance. With an interleaved array, the exact layout of your data is known. You know your data is tightly packed and may be accessed in one chunk. If interleaved arrays are not used, the stride and size information has to be examined to detect whether data is tightly packed.

Note: glInterleavedArrays() only enables and disables vertex arrays and specifies values for the vertex-array data. It does not render anything. You must still complete "Step 3: Dereferencing and Rendering" on page 71 and call glArrayElement(), glDrawElementsO, glDrawRangeElementsO, or glDrawArraysO to dereference the pointers and render graphics.

Next post:

Previous post: