Chapter 8. Geometry
Prev |  | Next

## Chapter 8. Geometry

All `libpr` geometry is defined by modular units of prmitives that employ a flexible specification method. The following two classes allow you to group these basic primitives:

- “pfGeoSet (Geometry Set)”

- “pfGeoArray (Geometry Array)”

This chapter describes these two classes. In addition, the last section, “3D Text”, describes how you can render 3D characters.

## pfGeoSet (Geometry Set)

A pfGeoSet is a collection of geometry that shares certain characteristics. All items in a pfGeoSet must be of the same primitive type (whether they are points, lines, or triangles) and share the same set of attribute bindings (you cannot specify colors-per-vertex for some items and colors-per-primitive for others in the same pfGeoSet). A pfGeoSet forms primitives out of lists of attributes that may be either indexed or nonindexed. An indexed pfGeoSet uses a list of unsigned short integers to index an attribute list. (See “Attributes” for information about attributes and bindings.)

Indexing provides a more general mechanism for specifying geometry than hard-wired attribute lists and also has the potential for substantial memory savings as a result of shared attributes. Nonindexed pfGeoSets are sometimes easier to construct, usually a bit faster to render, and may save memory (since no extra space is needed for index lists) in situations where vertex sharing is not possible. A pfGeoSet must be either completely indexed or completely nonindexed; it is not valid to have some attributes indexed and others nonindexed.

| Note: `libpf` applications can include pfGeoSets in the scene graph with the pfGeode (Geometry Node).

Table 8-1 lists a subset of the routines that manipulate pfGeoSets.

Table 8-1. pfGeoSet Routines

Routine | Description
--- | ---
pfNewGSet() | Create a new pfGeoSet.
pfDelete() | Delete a pfGeoSet.
pfCopy() | Copy a pfGeoSet.
pfGSetGState() | Specify the pfGeoState to be used.
pfGSetGStateIndex() | Specify the pfGeoState index to be used.
pfGSetNumPrims() | Specify the number of primitive items.
pfGSetPrimType() | Specify the type of primitive.
pfGSetPrimLengths() | Set the lengths array for strip primitives.
pfGetGSetPrimLength() | Get the length for the specified strip primitive.
pfGSetAttr() | Set the attribute bindings.
pfGSetMultiAttr() | Set multi-value attributes (for example, multi-texture coordinates).
pfGSetDrawMode() | Specify draw mode (for example, flat shading or wireframe).
pfGSetLineWidth() | Set the line width for line primitives.
pfGSetPntSize() | Set the point size for point primitives.
pfGSetHlight() | Specify highlighting type for drawing.
pfDrawGSet() | Draw a pfGeoSet.
pfGSetBBox() | Specify a bounding box for the geometry.
pfGSetIsectMask() | Specify an intersection mask for pfGSetIsectSegs().
pfGSetIsectSegs() | Intersect line segments with pfGeoSet geometry.
pfQueryGSet() | Determine the number of triangles or vertices.
pfPrint() | Print the pfGeoSet contents.

### Primitive Types

All primitives within a given pfGeoSet must be of the same type. To set the type of all primitives in a pfGeoSet named gset, call pfGSetPrimType(gset, type). Table 8-2 lists the primitive type tokens, the primitive types that they represent, and the number of vertices in a coordinate list for that type of primitive.

Table 8-2. Geometry Primitives

Token | Primitive Type | Number of Vertices
--- | --- | ---
PFGS_POINTS | Points | numPrims
PFGS_LINES | Independent line segments | 2 * numPrims
PFGS_LINESTRIPS | Strips of connected lines | Sum of lengths array
PFGS_FLAT_LINESTRIPS | Strips of flat-shaded lines | Sum of lengths array
PFGS_TRIS | Independent triangles | 3 * numPrims
PFGS_TRISTRIPS | Strips of connected triangles | Sum of lengths array
PFGS_FLAT_TRISTRIPS | Strips of flat-shaded triangles | Sum of lengths array
PFGS_TRIFANS | Fan of conected triangles | Sum of lengths array
PFGS_FLAT_TRIFANS | Fan of flat-shaded triangles | Sum of lengths array
PFGS_QUADS | Independent quadrilaterals | 4 * numPrims
PFGS_POLYS | Independent polygons | Sum of lengths array

The parameters in the last column denote the following:

numPrims |  | The number of primitive items in the pfGeoSet, as set by pfGSetNumPrims().
lengths |  | The array of strip lengths in the pfGeoSet, as set by pfGSetPrimLengths() (note that length is measured here in terms of number of vertices).

Connected primitive types (line strips, triangle strips, and polygons) require a separate array that specifies the number of vertices in each primitive. Length is defined as the number of vertices in a strip for STRIP primitives and is the number of vertices in a polygon for the POLYS primitive type. The number of line segments in a line strip is numVerts – 1, while the number of triangles in a triangle strip and polygon is numVerts – 2. Use pfGSetPrimLengths() to set the length array for strip primitives.

The number of primitives in a pfGeoSet is specified by pfGSetNumPrims(gset, num). For strip and polygon primitives, num is the number of strips or polygons in gset.

### pfGeoSet Draw Mode

In addition to the primitive type, pfGSetDrawMode() further defines how a primitive is drawn. Triangles, triangle strips, quadrilaterals, and polygons can be specified as either filled or as wireframe, where only the outline of the primitive is drawn. Use the PFGS_WIREFRAME argument to enable or disable wireframe mode. Another argument, PFGS_FLATSHADE, specifies that primitives should be shaded. If flat shading is enabled, each primitive or element in a strip is shaded with a single color.

PFGS_COMPILE_GL
| At the next draw for each pfState, compile gset's geometry into a GL display list and subsequently render the display list.
PFGS_DRAW_GLOBJ
| Select the rendering of an already created display list but do not force a recompile.
PFGS_PACKED_ATTRS
| Use the gset's packed attribute arrays, set with the PFGS_PACKED_ATTRS to pfGSetAttr, to render geometry with GL vertex arrays.

The pfGeoSets are normally processed in immediate mode, which means that pfDrawGSet() sends attributes from the user-supplied attribute arrays to the Graphics Pipeline for rendering. However, this kind of processing is subject to some overhead, particularly if the pfGeoSet contains few primitives. In some cases it may help to use GL display lists (this is different from the `libpr` display list type pfDispList) or compiled mode. In compiled mode, pfGeoSet attributes are copied from the attribute lists into a special data structure called a display list during a compilation stage. This data structure is highly optimized for efficient transfer to the graphics hardware. However, compiled mode has some major disadvantages:

- Compilation is usually costly.

- A GL display list must be recompiled whenever its pfGeoSet's attributes change.

- The GL display list uses a significant amount of extra host memory.

In general, immediate mode will offer excellent performance with minimal memory usage and no restrictions on attribute volatility, which is a key aspect in may advanced applications. Despite this, experimentation may show databases or machines where compiled mode offers a performance benefit.

To enable or disable compiled mode, call pfGSetDrawMode() with the PFGS_COMPILE_GL token. When enabled, compilation is delayed until the next time the pfGeoSet is drawn with pfDrawGSet(). Subsequent calls to pfDrawGSet() will then send the compiled pfGeoSet to the graphics hardware.

To select a display list to render, without recompiling it, use pfGSetDrawMode() with the token PFGS_DRAW_GLOBJ.

#### Packed Attributes

Packed attributes is an optimized way of sending formatted data to the graphics pipeline under OpenGL that does not incur the same memory overead or recompilation burden as GL display lists. To render geometry with packed attributes, use the pfGSetDrawMode( PFGS_PACKED_ATTRS) method when using OpenGL. This pfGSetAttr list includes the currently bound PER_VERTEX vertex attribute data packed into a single nonindexed array. When specifying a packed attribute array, the optional vertex attributes, colors, normals, and texture coordinates, can be NULL. This array, like the other attribute arrays, is then shared betweenOpenGL Performer, the GL, and accessible by the user. Optionally, you can put your vertex coordinates in this packed array but in this case the vertices must be duplicated in the normal coordinate array because vertex coordinate data is used internally for other nondrawing operations such as intersections and computation of bounding geometry. Packed attribute arrays also allow OpenGL Performer to extend the vertex attribute types accepted by pfGeoSets. There are several base formats that expect all currently bound attributes of specified data type (unsigned byte, short, or float) to be in the attribute array. Attributes specified by the format but not bound to vertices are assumed to not be present and the present data is packed with the data for each vertex starting on a 32-bit word-aligned boundary. Then, there are several derived formats that let you put some attribute data in the packed array while leaving the rest in the normal individual coordinate attribute arrays. Table 8-3 shows the different base formats supported.

Table 8-3. pfGeoSet PACKED_ATTR Formats

Format | Description
--- | ---
PFGS_PA_C4UBN3ST2FV3F | Accepts all currently bound coordinate attributes; colors are unsigned bytes; normals are shorts. Vertices are duplicated in the packed attribute array.
PFGS_PA_C4UBN3ST2F | Vertices are in the normal coordinate array.
PFGS_PA_C4UBT2F | Normals and vertices are in the normal coordinate array.
PFGS_PA_C4UBN3ST2SV3F | All bound coordinate attributes are in the packed attribute array. Colors are unsigned bytes, normals are shorts, and texture coordinates are unsigned shorts.
PFGS_PA_C4UBN3ST3FV3F | Texture coordinates are 3D floats.
PFGS_PA_C4UBN3ST3SV3F | Texture coordinates are 2D shorts.

To create packed attributes, you can use the utility pfuTravCreatePackedAttrs(), which traverses a scene graph to create packed attributes for pfGeoSets and, optionally, pfDelete redundant attribute arrays. This utility packs the pfGeoSet attributes using pfuFillGSetPackedAttrs(). Examples of packed attribute usage can be seen in `/usr/share/Performer/src/pguide/libpr/C/packedattrs.c` and `/usr/share/Performer/src/sample/C/perfly.c` and `/usr/share/Performer/src/sample/C++/perfly.C` for IRIX and Linux and in `%PFROOT%\Src\pguide\libpr\C\packedattrs.c`, `%PFROOT%\Src\sample\C\perfly.c`, and `%PFROOT%\Src\sample\C++\perfly.C` for Microsoft Windows.

### Primitive Connectivity

A pfGeoSet requires a coordinate array that specifies the world coordinate positions of primitive vertices. This array is either indexed or not, depending on whether a coordinate index list is supplied. If the index list is supplied, it is used to index the coordinate array; if not, the coordinate array is interpreted in a sequential order.

A pfGeoSet's primitive type dictates the connectivity from vertex to vertex to define geometry. Figure 8-1 shows a coordinate array consisting of four coordinates, A, B, C, and D, and the geometry resulting from different primitive types. This example uses index lists that index the coordinate array.

| Note: Flat-shaded line strip and flat-shaded triangle strip primitives have the vertices listed in the same order as for the smooth-shaded varieties.

Figure 8-1. Primitives and Connectivity

### Attributes

The definition of a primitive is not complete without attributes. In addition to a primitive type and count, a pfGeoSet references four attribute arrays (see Figure 8-2):

- Colors (red, green, blue, alpha)

- Normals (Nx, Ny, Nz)

- Texture coordinates (S, T)—multiple arrays for multitexture.

- Vertex coordinates (X, Y, Z)

(A pfGeoState is also associated with each pfGeoSet; see Chapter 12, “Graphics State” for details.) The four components listed above can be specified with pfGSetAttr(). Multivalue attributes (texture coordinates) can be specified using pfGSetMultiAttr() or pfGSetAttr(). Using zero as the index parameter for pfGSetMultiAttr() is equivalent to calling pfGSetAttr(). Attributes may be set in two ways: by indexed specification—using a pointer to an array of components and a pointer to an array of indices; or by direct specification—providing a NULL pointer for the indices, which indicates that the indices are sequential from the initial value of zero. The choice of indexed or direct components applies to an entire pfGeoSet; that is, all of the supplied components within one pfGeoSet must use the same method. However, you can emulate partially indexed pfGeoSets by using indexed specification and making each nonindexed attribute's index list be a singly shared “identity mapping” index array whose elements are 0, 1, 2, 3,…, N–1, where N is the largest number of attributes in any referencing pfGeoSet. (You can share the same array for all such emulated pfGeoSets.) The direct method avoids one level of indirection and may have a performance advantage compared with indexed specification for some combinations of CPUs and graphics subsystems.

| Note: Use pfMalloc() to allocate your arrays of attribute data. This allows OpenGL Performer to reference-count the arrays and delete them when appropriate. It also allows you to easily put your attribute data into shared memory for multiprocessing by specifying an arena such as pfGetSharedArena() to pfMalloc(). While perhaps convenient, it is very dangerous to specify pointers to static data for pfGeoSet attributes. Early versions of OpenGL Performer permitted this but it is strongly discouraged and may have undefined and unfortunate consequences.

Attribute arrays can be created through pfFlux to support the multiprocessed generation of the vertex data for a dynamic object, such as ocean waves, or morphing geometry. pfFlux will automatically keep separate copies of data for separate proceses so that one process can generate data while another draws it. The pfFluxed buffer can be handed directly to pfGSetAttr() or pfGSetMultiAttr(). In fact, the entire pfGeoSet can be contained in a pfFlux. Index lists cannot be pfFluxed. See Chapter 19, “Dynamic Data”, for more information on pfFlux.

Figure 8-2. pfGeoSet Structure

| Note: When using multiple texture-coordinate arrays, pfGeoSet recognizes texture-coordinate arrays starting at the first array (index of 0) and ending immediately before the first index with a NULL array. In other words, specifying texture-coordinate arrays using pfGSetMultiAttr() for indices 0, 1, and 3 is equivalent to specifying texture-coordinate arrays for only indices 0 and 1. When using pfTexGen to automatically generate texture coordinates for some texture units, the application should not interleave texture units with texture coordinates and texture units with pfTexGen. Texture units with texture coordinates should come before texture units with pfTexGen. This is an implementation limitation and may be removed in future releases.

### Attribute Bindings

Attribute bindings specify where in the definition of a primitive an attribute has effect. You can leave a given attribute unspecified; otherwise, its binding location is one of the following:

- Overall (one value for the entire pfGeoSet)

- Per primitive

- Per vertex

Only certain binding types are supported for some attribute types.

Table 8-4 shows the attribute bindings that are valid for each type of attribute.

Table 8-4. Attribute Bindings

Binding Token | Color | Normal | Texture Coordinate | Coordinate
--- | --- | --- | --- | ---
PFGS_OVERALL | Yes | Yes | No | No
PFGS_PER_PRIM | Yes | Yes | No | No
PFGS_PER_VERTEX | Yes | Yes | Yes | Yes
PFGS_OFF | Yes | Yes | Yes | No

Attribute lists, index lists, and binding types are all set by pfGSetAttr().

For FLAT primitives ( PFGS_FLAT_TRISTRIPS, PFGS_FLAT_TRIFANS, PFGS_FLAT_LINESTRIPS), the PFGS_PER_VERTEX binding for normals and colors has slightly different meaning. In these cases, per-vertex colors and normals should not be specified for the first vertex in each line strip or for the first two vertices in each triangle strip since FLAT primitives use the last vertex of each line segment or triangle to compute shading.

### Indexed Arrays

A cube has six sides; together those sides have 24 vertices. In a vertex array, you could specify the primitives in the cube using 24 vertices. However, most of those vertices overlap. If more than one primitive can refer to the same vertex, the number of vertices can be streamlined to 8. The way to get more than one primitive to refer to the same vertex is to use an index; three vertices of three primitives use the same index which points to the same vertex information. Adding the index array adds an extra step in the determination of the attribute, as shown in Figure 8-3.

Figure 8-3. Indexing Arrays

Indexing can save system memory, but rendering performance is often lost.

#### When to Index Attributes

The choice of using indexed or sequential attributes applies to all of the primitives in a pfGeoSet; that is, all of the primitives within one pfGeoSet must be referenced sequentially or by index; you cannot mix the two.

The governing principle for whether to index attributes is how many vertices in a geometry are shared. Consider the following two examples in Figure 8-4, where each dot marks a vertex.

Figure 8-4. Deciding Whether to Index Attributes

In the triangle strip, each vertex is shared by two adjoining triangles. In the square, the same vertex is shared by eight triangles. Consider the task that is required to move these vertices when, for example, morphing the object. If the vertices were not indexed, in the square, the application would have to look up and alter eight triangles to change one vertex.

In the case of the square, it is much more efficient to index the attributes. On the other hand, if the attributes in the triangle strip were indexed, since each vertex is shared by only two triangles, the index look-up time would exceed the time it would take to simply update the vertices sequentially. In the case of the triangle strip, rendering is improved by handling the attributes sequentially.

The deciding factor governing whether to index attributes relates to the number of primitives that share the same attribute: if attributes are shared by many primitives, the attributes should be indexed; if attributes are not shared by many primitives, the attributes should be handled sequentially.

### pfGeoSet Operations

There are many operations you can perform on pfGeoSets. pfDrawGSet() “draws “ the indicated pfGeoSet by sending commands and data to the Geometry Pipeline, unless OpenGL Performer's display-list mode is in effect. In display-list mode, rather than sending the data to the pipeline, the current pfDispList “captures” the pfDrawGSet() command. The given pfGeoSet is then drawn along with the rest of the pfDispList with the pfDrawDList() command.

When the PFGS_COMPILE_GL mode of a pfGeoSet is not active ( pfGSetDrawMode()), pfDrawGSet() uses rendering loops tuned for each primitive type and attribute binding combination to reduce CPU overhead in transferring the geometry data to the hardware pipeline. Otherwise, pfDrawGSet() sends a special, compiled data structure.

Table 8-1 lists other operations that you can perform on pfGeoSets. pfCopy() does a shallow copy, copying the source pfGeoSet's attribute arrays by reference and incrementing their reference counts. pfDelete() frees the memory of a pfGeoSet and its attribute arrays (if those arrays were allocated with pfMalloc() and provided their reference counts reach zero). pfPrint() is strictly a debugging utility and will print a pfGeoSet's contents to a specified destination. pfGSetIsectSegs() allows intersection testing of line segments against the geometry in a pfGeoSet; see “Intersecting with pfGeoSets” in Chapter 22 for more information on that function.

## pfGeoArray (Geometry Array)

The pfGeoArray is a new OpenGL Performer data structure aimed at replacing the class pfGeoSet. Conceptually, pfGeoArrays are very similar to pfGeoSets, but they allow you to define new sets of attributes in addition to the standard vertex coordinates, normals, texture coordinates, and colors. These attributes can be used by vertex or fragment programs applied to the primitives (see “Using OpenGL Performer with GPUs” in Chapter 14). Also, pfGeoArrays are rendered using vertex arrays and vertex objects, making the rendering much more efficient. pfGeoArrays can be up to 10 times faster than pfGeoSets on Onyx4 systems.

Each pfGeoArray is a collection of geometry with one primitive type, such as points, lines, or triangles. Vertex coordinates, normals, colors, texture coordinates, and user-defined attributes are used to specify the primitives. There are two ways to specify the attributes. First, each attribute is specified per vertex, there is no concept of an attribute per primitive or an overall attribute. Second, you can use a single list of unsigned integers to index all attributes of a pfGeoArray.

Indexing provides a more general mechanism for specifying geometry than hardwired attribute lists and, in many cases, provides substantial memory savings due to shared attributes. Nonindexed pfGeoArrays are sometimes easier to construct and may exhibit better caching behavior. Indexing is often a desirable approach especially when your primitives are sharing many attributes (such as having the same normal for each face). Also, if you have a primitive with many triangle strips, it is better to create a single pfGeoArray containing indexed triangles than to have a set of short pfGeoArrays, each containing one triangle strip.

The function pfNewGArray() creates and returns a handle to a pfGeoArray. The parameter `arena` specifies a malloc() arena out of which the pfGeoArray is allocated or `NULL` for allocation off the process heap. pfGeoArrays can be deleted with pfDelete().

The call new(arena) allocates a pfGeoArray from the specified memory arena, or from the process heap if arena is `NULL`. The new() call allocates a pfGeoArray from the default memory arena (see the man page for pfGetSharedArena). Like other pfObjects, pfGeoArrays cannot be automatically created statically on the stack or in arrays. Delete pfGeoArrays with pfDelete() rather than with the delete operator.

### pfGeoArray Attributes

The function pfGArrayAddAttr() adds a new attribute to the list of attributes of a pfGeoArray. This list is initially empty. An attribute is specified by its attribute type and the following parameters:

`size` |  | Specifies the number of coordinates per vertex; It must be 2, 3, or 4.
`type` |  | Specifies the type of each component in the attribute data. It is one of `GL_DOUBLE`, `GL_FLOAT`, `GL_INT`, `GL_UNSIGNED_INT`, `GL_UNSIGNED_SHORT`, `GL_SHORT`, `GL_UNSIGNED_BYTE`, and `GL_BYTE`.
`stride` |  | Specifies the byte offset between consecuitive vertex data. It is usually `0`.
`pointer` |  | Specifies a pointer to the attribute data.

According to the attribute type, pfGeoArray attributes may include vertex coordinates, color specification, normal vector, texture coordinates, or a user-defined generic attribute. See the following section “pfGeoArray Attribute Types”.

You can modify the size, the data type, the stride, and the data pointer for any existing attribute using the following functions:

- pfGArrayAttrSize()

- pfGArrayAttrType()

- pfGArrayAttrStride()

- pfGArrayAttrPtr()

You can also remove an attribute using the function pfGArrayRemoveAttr(). In all these functions, the attribute is identified by its attribute type and an index identifying which attribute of the given attribute type to use. Any attribute type can be used multiple times in a single pfGeoArray, but this is rarely a case.

Multitexturing is supported by adding a texture stage index to the attribute type specifying the texture. To do so, use the function pfGArrayAddTextureAttrType(). Similarly, the generic parameters, specified by pfGArrayAddGenericAttrType() can be indexed.

Note that since the pfGeoArray attributes are rendered in the order they were added, it is possible to interleave the attributes with your own callbacks. To do so, create a special "callback" type with a function mask `0` (no callback data) or a function mask `0x1` (callback data is used).

It is possible to index the attributes, although (in contrast to pfGeoSets), a single index list is used for all attributes. The optional attribute index list is a list of unsigned short integers. The index list is specified using the function pfGArrayIndexArray().

If attribute and index lists are allocated from the pfMalloc routines, pfGArrayAddAttr() and pfGArrayAttrPtr() will correctly update the reference counts of the lists. Specifically, they will decrement the reference counts of the old lists and increment the reference counts of the new lists. It will not free any lists whose reference counts reach 0. When a pfGeoArray is deleted with pfDelete(), all pfMalloc'ed lists will have their reference counts decremented by one and will be freed if their count reaches 0.

When pfGeoArrays are copied with pfCopy(), all pfMalloc'ed lists of the source pfGeoArray will have their reference counts incremented by one and those pfMalloc'ed lists of the destination pfGeoArray will have their reference counts decremented by one. The function pfCopy() copies lists only by reference (only the pointer is copied) and will not free any lists whose reference counts reach 0.

Attribute data may be any of the following types of memory:

- Data allocated with pfMalloc

  This is the usual and recommended memory type for pfGeoArray index and attribute arrays.

- Static, malloc(), amalloc(), usmalloc(), and similar data (non-pfMalloc'ed data)

  This type of memory is not generally recommended since it does not support reference counting or other features provided by pfMalloc. In particular, do not use static data because it may result in segmentation violations.

- pfFlux memory

  In a pipelined, multiprocessing environment, a pfFlux provides multiple data buffers, which allow frame-accurate data modifications to pfGeoArray attribute arrays like coordinates (facial animation) and texture coordinates (ocean waves, surf). The functions pfGArrayAddAttr() and pfGArrayAttrPtr() will accept a pfFlux* or pfFluxMemory* for the attribute list (index lists do not support pfFlux) and the pfGeoArray will select the appropriate buffer when rendered or intersected. See the man page for pfFlux for more details.

  Since pfGeoArrays are cached using vertex array objects, if you want to animate some attributes, you need to either disable caching using the function pfGArrayAllowCache() or call the function pfGArrayUpdateData() each time you change any of the attribute data.

- pfCycleBuffer and pfCycleMemory

  Note that pfCycleBuffer has been obsoleted by pfFlux. See the man page for pfCycleBuffer for more details.

Performer allows mixing pfMalloc'ed, pfFlux, and pfCycleBuffer attributes on a single pfGeoArray.

### pfGeoArray Attribute Types

When a new pfGeoArray is created it has no default attribute. When adding a new attribute, you must specify the type of the attribute— that is, whether it specifies one of the following:

- Vertex coordinates

- A normal vector

- color

- Texture coordinates

- Generic user-defined attributes

Attribute types are identified by their name (a string). By default, the following four basic types are defined:

"`vertex`" |  | Specifies vertex coordinates. The set function is glVertexPointer(), the OpenGL name is `GL_VERTEX_ARRAY`, and the mask is `0x0f`.
"`normal`" |  | Specifies normals. The set function is glNormalPointer(), the OpenGL name is `GL_NORMAL_ARRAY`, and the mask is `0x07`.
"`color`" |  | Specifies colors. The set function is glColorPointer(), the OpenGL name is `GL_COLOR_ARRAY`, and the mask is `0x0f`.
"`texture coord`" |  | Specifies texture coordinates. The set function is glTexCoordPointer(), the OpenGL name is `GL_TEXTURE_COORD_ARRAY`, and the mask is `0x0f`.

These default attribute types and any other types you add are shared among all pfGeoArray classes.

A new attribute type can be added using the function pfGArrayAddAttrType(). The parameter `func` is the function used to set the attribute, the parameter `ecs` is the OpenGL name of the attribute, and the parameter `mask` is a mask specifying what parameters are sent to the function `func`. The function `func` can have up to four parameters: `size`, `type`, `stride`, and `pointer` (see the previous section for an explanation of these parameters). The parameter mask determines which parameters need to be sent. For example, the function glNormalPointer() does not take size as a parameter. Therefore, the corresponding mask would be 0x07. A special mask of `0x10` is used for the function glVertexAttribPointerARB(), which takes six parameters: index, size, type, normalized, stride, and data.

For convenience, you can also use the following functions to define the attribute types:

pfGArrayAddVertexAttrType() |  | Defines the vertex coordinates attribute type.
pfGArrayAddNormalAttrType() |  | Defines the normals attribute type.
pfGArrayAddColorAttrType() |  | Defines the colors attribute type.
pfGArrayAddTextureAttrType() |  | Defines the texture coordinates attribute type.
pfGArrayAddGenericAttrType() |  | Defines a generic attribute type.

In case of textures, you can specify the texture stage 0,1,... . Generic attributes use the set function glVertexAttribPointerARB() and mask `0x10`.

### pfGeoSet Primitive Types

A primitive is a single point, line segment, line strip, triangle, triangle strip, quad, or polygon depending on the primitive type. The primitive type dictates how the coordinate and coordinate index lists are interpreted to form geometry.

#### Specifying Primitive Types

The function pfGSetPrimType() specifies the type of primitives found in a pfGeoArray. The type is one of the following symbolic tokens:

- `PFGS_POINTS`

- `PFGS_LINES`

- `PFGS_LINESTRIPS`

- `PFGS_TRIS`

- `PFGS_QUADS`

- `PFGS_TRISTRIPS`

- `PFGS_TRIFANS`

- `PFGS_POLYS`

See the following subsection describes the primitive types. The function pfGetGSetPrimType() returns the primitive type of the pfGeoArray. The functions pfGSetNumPrims() and pfGetGSetNumPrims() set and get the number of primitives in the pfGeoArray, respectively.

#### Primitive Type Descriptions

A single line strip, triangle strip, or polygon is considered to be a primitive. Therefore, a pfGeoArray may contain multiple strips of differing lengths or multiple polygons with differing number of sides. Therefore, for strip primitives and `PFGS_POLYS`, a separate array is necessary to specify the number of vertices in each strip or polygon. This array is set by pfGSetPrimLengths(). The parameter lengths is an array of vertex counts where the array index is defined as follows:

    lengths[0] = number of vertices in strip/polygon 0
    lengths[1] = number of vertices in strip/polygon 1
       .
       .
       .
    lengths[n-1] = number of vertices in strip/polygon n–1

The value n is the number of primitives set by pfGSetNumPrims(). The negative of the proper length may be specified for an individual length of a strip primitive in the lengths array to indicate that this pfGeoArray attribute should be "hidden" from drawing.

You can use the following functions to manage the length of the strip:

pfGetGSetPrimLengths() |  | Returns a pointer to the lengths array of `geoa`, the pfGeoArray.
pfHideGSetStripPrim() |  | Forces the length of the ith primitive of a stripped pfGeoArray to be the negative of its proper length.
pfUnhideGSetStripPrim() |  | Sets the primitive length to be the positive value.
pfisGSetStripPrimHidden() |  | Returns `1` if the ith primitive of the pfGeoArray is negative and `0`, otherwise.
pfGetGSetPrimLength() |  | Always returns a positive length for the ith primitive of the pfGeoArray and can be used wherever the lengths of pfGeoArrays of an unknown source must be inspected to protect against negative values in the lengths array.

In case of line strips, triangle strips, and polygons, use only one primitive in the pfGeoArray. It is possible to define more than one strip or polygon but then, internally, one vertex array is used for each strip or polygon, often reducing the performance. It is better to split the strips and polygon into triangles and render it as a set of indexed triangles.

Assume the the following:

- The index list is an array `V` indexed by i.

- `num` is the number of primitives.

- `lengths` is the array of strip or polygon lengths.

- `Nv` the size of the coordinate index list.

Given these assumptions, the primitive type interprets `V` in the following ways:

Primitive Type |  | Array Interpretation
`PFGS_POINTS` |  | The pfGeoArray is a set of `num` points. Each `V`[i] is a point where i = 0, 1, 2, ..., `num`–1. `Nv` = `num`.
`PFGS_LINES` |  | The pfGeoArray is a set of `num` disconnected line segments. Each line segment is drawn from `V`[i] to `V`[i+1] where i = 0, 2, ..., 2*(`num`–1). `Nv` = 2 * `num`.
`PFGS_LINESTRIPS` |  | The pfGeoArray is a collection of num vertex arrays, each containing one line strip (also known as a polyline). Linestrip[i] is drawn between `V`[p+j], j = 0, 1, ..., `lengths`[i]–1 where p is sum of all `lengths`[k], 0 <= k < i. `Nv` = sum of all `lengths`[k] where k = 0, 1, ..., `num`–1. Note that all `lengths`[i] values should be greater than or equal to 2.
`PFGS_TRIS` |  | The pfGeoArray is a set of num independent triangles. Each triangle is V[i], V[i+1], V[i+2] where i = 0, 3, 6, ..., 3*(num-1). Nv = 3 * num.
`PFGS_QUADS` |  | The pfGeoArray is a set of `num` independent quads. Each quad is `V`[i], `V`[i+1], `V`[i+2], `V`[i+3] where i = 0, 4, 8, ..., 4*(`num`–1). `Nv` = 4 * `num`.
`PFGS_TRISTRIPS` |  | The pfGeoArray is a collection of `num` vertex arrays, each containing one triangle strip. `Tristrip`[i] is drawn between `V`[p+j], j = 0, 1, ..., `lengths`[i]–1, where p is sum of all `lengths`[k] where 0 <= k < i. `Nv` = sum of all `lengths`[k] where k = 0, 1, ..., num–1. Note that all `lengths`[i] values should be greater than or equal to 3.
`PFGS_TRIFANS` |  | The pfGeoArray is a collection of `num` vertex arrays, each containing one triangle fan. Each `trifan`[i] is a set of `lengths`[i] triangles. The fan starts with the center vertex `V`[0], with the remaining `lengths`[i]–1 vertices specifying the outside points.
`PFGS_POLYS` |  | The pfGeoArray is a collection of `num` vertex arrays, each containing one polygon. `Polygon`[i] is the convex hull of the vertices `V`[p+j] where j = 0, 1, ..., `lengths`[i]–1 where p is sum of all `lengths`[k] where 0 <= k < i. `Nv` = sum of all `lengths`[k] where k = 0, 1, ..., `num`–1. Note that all `lengths`[i] should be greater or equal to 3.

The primitive types `PFGS_TRIS`, `PFGS_QUADS`, `PFGS_TRISTRIPS`, and `PFGS_POLYS` are rendered as filled polygons but will be rendered in wireframe according to the following rules:

Always render in wireframe mode if `PFEN_WIREFRAME` mode is enabled through pfGSetDrawMode().

Use the wireframe mode set by the attached pfGeoState, if any, as described in the following section “pfGeoArrays (Geometry) and pfGeoStates (Appearance)”.

Use the wireframe mode set by pfEnable() or pfDisable() with the `PFEN_WIREFRAME` argument.

The following example creates a nonindexed tristrip pfGeoArray.

    /* Set up a non-indexed, TRISTRIP pfGeoArray */

    /* get the default attribute types */
    vattr = pfGArrayQueryAttrType("vertex");
    cattr = pfGArrayQueryAttrType("color");

    garray = pfNewGArray(NULL);
    pfGSetPrimType(garray, PFGS_TRISTRIPS);
    pfGSetNumPrims(garray, 1);
    lengths[0] = 4;
    pfGSetPrimLengths(gset, lengths);

    coords = (pfVec3*) pfMalloc(sizeof(pfVec3) * 4, NULL);
    colors = (pfVec4*) pfMalloc(sizeof(pfVec4) * 4, NULL);

    pfGArrayAddAttr(garray, vattr, 3, GL_FLOAT, 0, coords);
    pfGArrayAddAttr(garray, cattr, 4, GL_FLOAT, 0, colors);

### pfGeoArray Special Rendering Characteristics

The functions pfGSetPntSize() and pfGSetLineWidth() set the point size and line width, respectively, of the pfGeoArray. Point size has effect only when the primitive type is `PFGS_POINTS` and line width is used only for primitive types `PFGS_LINES` and `PFGS_LINESTRIPS` and for all primitives drawn in wireframe mode. A pfGeoArray sets the point size and line width immediately before rendering only if the size or width is greater than zero. Otherwise, it will inherit size or width through the graphics library.

The functions pfGetGSetPntSize() and pfGetGSetLineWidth() return the point size and line width of the `garray`, respectively.

For pfGeoArrays, there is no function equivalent to pfGSetDrawMode() for pfGeosets. Since pfGeoArrays are directly using vertex arrays and vertex objects, the rendering into display lists is not relevant anyway.

### pfGeoArrays (Geometry) and pfGeoStates (Appearance)

A pfGeoState is an encapsulation of `libpr` graphics modes and attributes (see Chapter 12, “Graphics State”). For example, a pfGeoState representing a glass surface may reference a shiny pfMaterial and enable transparency. A pfGeoState does not inherit state from other pfGeoStates. Consequently, when attached to a pfGeoArray using pfGSetGState(), the pfGeoArray will always be rendered with the state encapsulated by `gstate`, regardless of the order in which pfGeoArray/pfGeoState pairs are rendered. This behavior greatly eases the burden of managing graphics state in the graphics library. A pfGeoArray may directly reference or indirectly index a pfGeoState through a global table.

The function pfGSetGState() "attaches" `gstate` to the pfGeoArray so that the pfGeoArray may be drawn with a certain graphics state. When drawn, a pfGeoArray will apply its pfGeoState (if it has one) with pfApplyGState() and the graphics library will be initialized to the proper state for drawing the pfGeoArray. A `gstate` value of `NULL` will remove any previous pfGeoState and cause the pfGeoArray to inherit whatever graphics state is around at the time of rendering.

The function pfGSetGStateIndex() allows a pfGeoArray to index its pfGeoState. Indexing is useful for efficiently managing a single database with multiple appearances—for example, a normal compared to an infrared view of a scene would utilize two pfGeoState tables, each referencing a different set of pfGeoStates.

Indexed pfGeoStates use a global table of `pfGeoState*` specified by pfApplyGStateTable(). When indexing a pfGeoState during the rendering of a pfGeoArray, pfApplyGState() is called with the `index`th entry of this table if the index can be properly resolved. Otherwise, no pfGeoState is applied. The function pfGetGSetGStateIndex() returns the pfGeoState index of the pfGeoArray or –`1` if the pfGeoArray directly references its pfGeoState.

The function pfGSetGState() increments the reference count of the new pfGeoState by one and decrements the reference count of the previous pfGeoState by one but does not delete the previous pfGeoState if its reference count reaches zero. The function pfGSetGStateIndex() does not affect pfGeoState reference counts.

| Note: Any pfGeoArray without an associated pfGeoState will not be rendered with the global, default state but will be drawn with the current state. To inherit the global state, a pfGeoState that inherits all state elements should be attached to the pfGeoArray. For economy (space and rendering time), pfGeoArrays should share like pfGeoStates. For more details, see the man page for pfGeoState.

The function pfGetGSetGState() returns the pfGeoState associated with the pfGeoArray or `NULL` if there is none. If the pfGeoArry indexes its pfGeoState, pfGetGSetGState() will look up the pfGeoState index in the global pfGeoState table and return the result or `NULL` if it cannot resolve the reference.

The function pfGSetDecalPlane() sets `plane` to be the reference plane used for the the pfGeoArray when the current decal mode has `PFDECAL_PLANE` selected. Setting a decal plane on a pfGeoArray (as opposed to a pfGeoState) may add a small amount of overhead to the drawing of the pfGeoArray that may be visible for small pfGeoArrays. However, this may be preferable to breaking up pfGeoStates for handling many different reference planes. Reference planes are only supported under OpenGL operation and require the `reference_plane` SGI extension to OpenGL. The `PFQFTR_DECAL_PLANE` to `pfQueryFeature` can be used for run-time queries for the support of decal reference planes. See the man page for pfDecal for more information.

The function pfGSetHlight() sets `hlight` to be the highlighting structure used for the pfGeoArray. When this flag is not `PFHL_OFF`, this pfGeoArray will be drawn as highlighted unless highlighting has been overridden as off with pfOverride. See the man page for pfHighlight for information on creating and configuring a highlighting state structure. The function pfGetGSetHlight() returns the current pfGeoSet highlight definition.

The function pfDrawHlightedGSet() is a convenience routine for drawing only the highlighting stage of `gset`, the pfGeoSet, according to the currently active highlighting structure. pfGeoArrays do not support the highlight mode with highlighted normals.

### Drawing pfGeoArrays

The behavior of pfDrawGSet() is affected by the presence of a display list. If a pfDispList has been opened by pfOpenDList(), pfDrawGSet() will not have immediate effect but will be captured by the pfDispList and will only have effect when that pfDispList is later drawn with pfDrawDList().

If `grray` has an attached pfGeoState, then pfDrawGSet() first calls pfApplyGState() before rendering the pfGeoArray geometry, as shown in following examples. Note that the pfGeoArray inherits the draw function from the parent pfGeoSet class.

Example 1:

    /* Make sure 'garray' has not attached pfGeoState */
    pfGSetGState(garray, NULL);

    /* Apply graphics state encapsulated by 'gstate' */
    pfApplyGState(gstate);

    /* Draw 'garray' with graphics state encapsulated by 'gstate' */
    pfDrawGSet(garray);

Example 2:

    /* Attach 'gstate' to 'garray' */
    pfGSetGState(garray, gstate);

    /* Draw 'garray' with graphics state encapsulated by 'gstate' */
    pfDrawGSet(garray);

Example 3:

    /* Use indexed pfGeoState */
    pfGSetGStateIndex(garray, 2);

    /* Set up and apply pfGeoState table */
    pfSet(list, 2, gstate);
    pfApplyGStateTable(list);

    /* Draw 'garray' with graphics state encapsulated by 'gstate' */
    pfDrawGSet(garray);

Examples 1, 2, and 3 are equivalent methods for drawing the same thing. Example 2 is recommended though since the pfGeoState and pfGeoArray pair can be set up at database initialization time.

The function pfGSetDrawBin() sets the pfGeoArray's draw bin identifier to `bin`. The parameter `bin` identifies a drawing bin to which the pfGeoArray belongs and is used for controlling the rendering order of a database. The pfGeoArray draw bin is currently used only by `libpf` applications (see the description of pfChanBinOrder() in section “Sorting the Scene” in Chapter 4) and is ignored by`libpr`-only applications. The default pfGeoArray draw bin identifier is –`1`. The function pfGetGSetDrawBin() returns the draw bin identifier of `garray`.

The function pfGSetDrawOrder() sets the pfGeoArray's `order` in the bin if the bin sort order is `PFSORT_DRAW_ORDER`. It is necessary to set the draw order of each pfGeoArray that contains calligraphic light points (see the man pages for pfCalligraphic). Do not set the draw bin for those pfGeoArrays, as they will automatically go in the light point display list if a LPoint process has been started.

### Intersecting with pfGeoArrays

The function pfGSetIsectMask() enables intersections and sets the intersection mask for the pfGeoArray. The parameter `mask` is a 32-bit mask used to determine whether a particular pfGeoArray should be examined during a particular intersection request. A non-zero bit-wise `AND` of the pfGeoArray's mask with the mask of the intersection request pfGSetIsectSegs() indicates that the pfGeoArray should be tested. The default mask is all one—that is, `0xffffffff`. The function pfGetGSetIsectMask() returns the intersection mask of the specified pfGeoArray.

Intersections for geometry whose vertex coordinates do not change are more efficient when information is cached for each pfGeoArray subjected for intersection. When setting the mask or changing caching, `PFTRAV_SELF` should always be part of `setMode`. If you `OR` `PFTRAV_IS_CACHE` into `setMode`, it causes the creation or update of the cache. Because creating the cache requires a moderate amount of computation, it is best done at setup time.

For objects whose geometry changes only occasionally, additional calls to pfGSetIsectMask() with `PFTRAV_IS_CACHE` `OR`ed into `setMode` will recompute the cached information. Alternately, if you `OR` `PFTRAV_IS_UNCACHE` into `setMode`, it will disable caching.

The `bitOp` argument is one of `PF_AND`, P`F_OR`, or `PF_SET` and indicates, respectively, whether the new mask is derived from an `AND` operation with the old mask, an `OR` operation with the old mask, or whether it is simply set.

The function pfGSetBBox() sets the bounding volume of the pfGeoArray. Each pfGeoArray has an associated bounding volume used for culling and intersection testing and a bounding mode, either static or dynamic. By definition, the bounding volume of a node encloses all the geometry parented by node. This means that the node and all its children fit within the node's bounding volume.

The `mode` argument to pfGSetBBox() specifies whether or not the bounding volume for `node` should be recomputed when an attribute of the pfGeoArray is changed. If the mode is `PFBOUND_STATIC`, OpenGL Performer will not modify the bound once it is set or computed. If the mode is `PFBOUND_DYNAMIC`, OpenGL Performer will recompute the bound if the number of primitives, the primitive lengths array, or the vertex coordinate arrays are changed. Note that OpenGL Performer does not know if the contents of these arrays changes, only when the pointer itself is set. Recomputation of the bounding box can be forced by calling pfGSetBBox() with a `bbox` that is `NULL`. Also note that a pfGeoArray cannot update the parent pfGeode with this new bounding volume information. As a result, a pfGeoArray will not propagate new bounding information up the tree. Call pfNodeBSphere() on the parent pfGeode to recompute the bounding volume.

The function pfGetGSetBBox() copies the bounding box of the pfGeoArray into `bbox` and returns the current bounding mode.

The functions pfGSetBBoxFlux() and pfGetGSetBBoxFlux() set and get a pfFlux that can be used to contain the bounding box of a pfGeoArray.

The function pfGSetIsectSegs() tests for intersections among the pfGeoArray and the group of line segments specified in `segSet`. The resulting intersections (if any) are returned in `hits`. The return value of pfGSetIsectSegs() is the number of segments that intersected the pfGeoArray.

You must supply an empty array `hits` through which results are returned. The array must have an entry for each segment in `segSet`. Upon return, `hits`[i][0] is a `pfHit*` that gives the intersection result for the ith segment in `segSet`. The pfHit objects come from an internally maintained pool and are reused on subsequent requests. Hence, the contents are only valid until the next invocation of pfGSetIsectSegs() in the current process. They should not be freed by the application.

The argument `segSet` is a pfSegSet public structure specifying the intersection request. In the structure, `segs` is an array of line segments to be intersected against the pfGeoArray. The argument `activeMask` is a bit vector specifying which segments in the pfSegSet are to be active for the current request. If the ith bit is set to `1`, it indicates the corresponding segment in the `segs` array is active.

The bit vector `mode` specifies the behavior of the intersection process and is a bit-wise `OR` of the following:

`PFTRAV_IS_PRIM` |  | Intersect with primitives (quads or triangles).
`PFTRAV_IS_GSET` |  | Intersect with pfGeoArray bounding boxes.
`PFTRAV_IS_NORM` |  | Return a normal in the pfHit structure.
`PFTRAV_IS_CULL_BACK` |  | Ignore backfacing polygons.
`PFTRAV_IS_CULL_FRONT` |  | Ignore front-facing polygons.

The bit fields `PFTRAV_IS_PRIM` and `PFTRAV_IS_GSET` indicate the level at which intersections should be evaluated and discriminator callbacks, if any, invoked. Note that if neither of these level selectors are specified, no intersection testing is done at all. In the pfSegSet, `isectMask` is another bit vector. It is bit-wise `AND`ed with the intersection mask of the pfGeoArray. If the result is zero, no intersection testing is done.

The `bound` field in a pfSegSet is an optional user-specified bounding volume around the set of segments. The only supported volume is a cylinder. To use a bounding cylinder, bit-wise `OR` `PFTRAV_IS_BCYL` into the `mode` field of the pfSegSet and assign the pointer to the bounding volume to the bound field. The function pfCylAroundSegs() will construct a cylinder around the segments.

When a bounding volume is supplied, the bounding volume is tested against the pfGeoArray bounding box before examining the individual segments. The largest improvement is for groups of at least several segments which are closely grouped segments. Placing a bounding cylinder around small groups or widely dispersed segments can decrease performance.

The `userData` pointer allows an application to associate other data with the pfSegSet. Upon its return in discriminator callbacks, the pfSegSet's `userData` pointer can be obtained from the returned pfHit with pfGetUserData(), the virtual function pfObject::getUserData().

The field `discFunc` is a user-supplied callback function which provides a more powerful means for controlling intersections than the simple mask test. The function acts as a discriminating function which examines information about candidate intersections and judges their validity. When a candidate intersection occurs, the `discFunc` callback is invoked with a pfHit structure containing information about the intersection.

The callback may then return a value that indicates whether and how the intersection should continue. This value is composed of the following major action specifications with additional modifiers bit-wise `OR`ed as explained in the following list:

`PFTRAV_CONT` |  | Indicates that the process should continue traversing the primitive list.
`PFTRAV_PRUNE` |  | Stops further testing of the line segment against the current pfGeoArray.
`PFTRAV_TERM` |  | Stops further testing of the line segment completely.

To have `PFTRAV_TERM` or `PFTRAV_PRUNE` apply to all segments, `PFTRAV_IS_ALL_SEGS` can be ORed into the discriminator return value. This causes the entire traversal to be terminated or pruned.

The callback may `OR` into the status return value any of the following:

`PFTRAV_IS_IGNORE` |  | Indicates that the current intersection should be ignored. Otherwise, the intersection is taken as valid.
`PFTRAV_IS_CLIP_START` |  | Indicates that for pruned and continued traversals that, before proceeding, the segment should be clipped to start at the current intersection point.
`PFTRAV_IS_CLIP_END` |  | Indicates that for pruned and continued traversals that, before proceeding, the segment should be clipped to end at the current intersection point.

If the field `discFunc` is `NULL`, the behavior is the same as if the discriminator returned (`PFTRAV_CONT` | `PFTRAV_IS_CLIP_END`) so that the intersection nearest the start of the segment will be returned.

A pfHit object also conveys information, if any, to the discriminator callback. Table 8-5 lists the information which can be obtained from a pfHit.

Table 8-5. Information Obtained from a pfHit

Query | Type | Contents
--- | --- | ---
`PFQHIT_FLAGS` | `int` | Status flags
`PFQHIT_SEGNUM` | `int` | Index of segment in pfSegSet
`PFQHIT_SEG` | `pfSeg` | Segment, as clipped
`PFQHIT_POINT` | `pfVec3` | Intersection point
`PFQHIT_NORM` | `pfVec3` | Normal at intersection point
`PFQHIT_VERTS` | `pfVec3[3]` | Vertices of intersected triangle
`PFQHIT_TRI` | `int` | Index of triangle in pfGeoArray primitive
`PFQHIT_PRIM` | `int` | Index of primitive in pfGeoArray
`PFQHIT_GSET` | `pfGeoArray *` | Pointer to intersected pfGeoArray
`PFQHIT_NODE` | `pfNode *` | Pointer to pfGeode
`PFQHIT_NAME` | `char *` | Name of pfGeode
`PFQHIT_XFORM` | `pfMatrix` | Transformation matrix
`PFQHIT_PATH` | `pfPath *` | Path within scene graph

The function pfQueryGSet() is a convenience routine for determining the values of implicit pfGeoArray parameters. The `which` argument is a token which selects the parameter from the set `PFQGSET_NUM_TRIS` and `PFQGSET_NUM_VERTS`. The result is written to the address indicated by `dst`. The number of bytes written to `dst` is returned as the value of pfQueryGSet(). The function pfMQueryGSet() is similar but copies a series of items sequentially into the buffer specified by `dst`. The items and their order are defined by a `NULL`-terminated array of query tokens pointed to by `which`. For both functions, the return value is the number of bytes written to the destination buffer.

The function pfGetGSetClassType() returns the `pfType*` for the pfGeoArray class. The `pfType*` returned by pfGetGSetClassType() is the same as the `pfType*` returned by invoking pfGetType(), the virtual function getType(), on any instance of the pfGeoArray class. Because OpenGL Performer allows subclassing of built-in types, when decisions are made based on the type of an object, it is usually better to use pfIsOfType(), the member function isOfType(), to test if an object is of a type derived from OpenGL Performer type rather than to test for strict equality of the types.

### Clip Texture Emulation Support

The function pfGSetUpdateCteRefs() determines whether a pfGeoArray is to be rendered with an emulated clip texture by looking at the texture attr of the pfGeoState associated with the pfGeoArray. If so, texture coordinate bounds are computed and stored. If not, then any previously allocated texture bounds data is freed. This should be called after changing the texture `attr` (or the texture enable flag) on a pfGeoState that has already been assigned to a pfGeoArray. Note that pfGSetGState() automatically calls pfGSetUpdateCteRefs().

The function pfGSetCalcTexBBox() forces the recomputation of a pfGeoArray's texture coordinate bounds, used for cliptexture emulation. This should be called after changing texture coordinate values for a pfGeoArray if this is to be rendered with an emulated clip texture. Note that this has no effect on pfGeoArrays that have not been identified as using an emulated clip texture by a previous call to pfGSetUpdateCteRefs(). Note also that pfGSetUpdateCteRefs() automatically calls pfGSetCalcTexBBox().

The functions pfGSetTexBBox_i(uint,uint,uint,uint) and pfGSetTexBBox_f(float,float,float,float) allow you to explicitly set texture coordinate bounds for a pfGeoArray. Texture coordinate bounds may be set as four floats, indicating minimum and maximum texture coordinate values for `s` and `t`, or as four integers, as follows for a clip texture with virtual size `V`, and texture coordinate bounding box of `minS`, `maxS`, `minT`, `maxT`:

    centerS = V*(minS+maxS)/2;
    centerT = V*(minT+maxT)/2;
    halfwidth = V*(maxS-minS)/2;
    halfheight = V*(maxT-minT)/2;

Note that this has no effect on pfGeoArrays that have not been identified as using an emulated clip texture by a previous call to pfGSetUpdateCteRefs(). Note also that pfGeoArray's texture coordinate bounds are internally stored in the integer representation. Therefore, using the integer method is faster.

The functions pfGSetTexBBox_i(uint*,uint*,uint*,uint*) and pfGSetTexBBox_f(float*,float*,float*,float*) may be used to retrieve the texture coordinate bounds for a pfGeoArray that has been identified as using an emulated clip texture by a previous call to pfGSetUpdateCteRefs(). Note that both functions return `FALSE` if pfGeoArray has not been identified as using an emulated clip texture or `TRUE`, otherwise.

### Quick Manipulation of Temporary pfGeoArrays

For more information about temporary pfGeoArrays and about using the "quick methods/functions", see the man page for pfDispListOptimizer.

The function pfGSetOptimize() controls whether a pfGeoArray is optimized by a pfDispListOptimizer (from within a `CULL_SIDEKICK` process). If `_state` is `0`, a pfDispListOptimizer skips this pfGeoArray entirely. The function pfGetGSetOptimize() returns the current optimization state of a pfGeoArray. The default optimization state of a pfGeoArray is `1`.

The following example shows one way to create a pfGeoArray defining a hexahedron (cube).

    static pfVec3 coords[] =
    {
        {-1.0, -1.0,  1.0}, /* front */
        { 1.0, -1.0,  1.0},
        { 1.0,  1.0,  1.0},
        {-1.0,  1.0,  1.0},

        {-1.0, -1.0,  1.0}, /* left */
        {-1.0,  1.0,  1.0},
        {-1.0,  1.0, -1.0},
        {-1.0, -1.0, -1.0},

        {-1.0, -1.0, -1.0}, /* back */
        {-1.0,  1.0, -1.0},
        { 1.0,  1.0, -1.0},
        { 1.0, -1.0, -1.0},

        { 1.0, -1.0,  1.0}, /* right */
        { 1.0, -1.0, -1.0},
        { 1.0,  1.0, -1.0},
        { 1.0,  1.0,  1.0},

        {-1.0,  1.0,  1.0}, /* top */
        { 1.0,  1.0,  1.0},
        { 1.0,  1.0, -1.0},
        {-1.0,  1.0, -1.0},

        {-1.0, -1.0,  1.0}, /* bottom */
        {-1.0, -1.0, -1.0},
        { 1.0, -1.0, -1.0},
        { 1.0, -1.0,  1.0}
    };

    static pfVec3 norms[] =
    {
        { 0.0,  0.0,  1.0},
        { 0.0,  0.0,  1.0},
        { 0.0,  0.0,  1.0},
        { 0.0,  0.0,  1.0},

        {-1.0,  0.0,  0.0},
        {-1.0,  0.0,  0.0},
        {-1.0,  0.0,  0.0},
        {-1.0,  0.0,  0.0},

        { 0.0,  0.0, -1.0},
        { 0.0,  0.0, -1.0},
        { 0.0,  0.0, -1.0},
        { 0.0,  0.0, -1.0},

        { 1.0,  0.0,  0.0},
        { 1.0,  0.0,  0.0},
        { 1.0,  0.0,  0.0},
        { 1.0,  0.0,  0.0},

        { 0.0,  1.0,  0.0},
        { 0.0,  1.0,  0.0},
        { 0.0,  1.0,  0.0},
        { 0.0,  1.0,  0.0},

        { 0.0, -1.0,  0.0},
        { 0.0, -1.0,  0.0},
        { 0.0, -1.0,  0.0},
        { 0.0, -1.0,  0.0}
    };

    /* Convert static data to pfMalloc'ed data */
    static void*
    memdup(void *mem, size_t bytes, void *arena)
    {
        void *data = pfMalloc(bytes, arena);
        memcpy(data, mem, bytes);
        return data;
    }

    /* Set up a PFGS_QUADS pfGeoArray */
    vattr = pfGArrayQueryAttrType("vertex");
    nattr = pfGArrayQueryAttrType("normal");

    garray = pfNewGArray(NULL);
    pfGSetPrimType(garray, PFGS_QUADS);
    pfGSetNumPrims(garray, 6);

    pfGArrayAddAttr(garray, vattr, 3, GL_FLOAT, 0,
    		memdup(coords, sizeof(coords), NULL));
    pfGArrayAddAttr(garray, nattr, 3, GL_FLOAT, 0,
    		memdup(norms, sizeof(norms), NULL));

    static pfVec3 coords[] =
    {
        {-1.0, -1.0,  1.0}, /* front */
        { 1.0, -1.0,  1.0},
        { 1.0,  1.0,  1.0},
        {-1.0,  1.0,  1.0},

        {-1.0, -1.0,  1.0}, /* left */
        {-1.0,  1.0,  1.0},
        {-1.0,  1.0, -1.0},
        {-1.0, -1.0, -1.0},

        {-1.0, -1.0, -1.0}, /* back */
        {-1.0,  1.0, -1.0},
        { 1.0,  1.0, -1.0},
        { 1.0, -1.0, -1.0},

        { 1.0, -1.0,  1.0}, /* right */
        { 1.0, -1.0, -1.0},
        { 1.0,  1.0, -1.0},
        { 1.0,  1.0,  1.0},

        {-1.0,  1.0,  1.0}, /* top */
        { 1.0,  1.0,  1.0},
        { 1.0,  1.0, -1.0},
        {-1.0,  1.0, -1.0},

        {-1.0, -1.0,  1.0}, /* bottom */
        {-1.0, -1.0, -1.0},
        { 1.0, -1.0, -1.0},
        { 1.0, -1.0,  1.0}
    };

    static pfVec3 norms[] =
    {
        { 0.0,  0.0,  1.0},
        { 0.0,  0.0,  1.0},
        { 0.0,  0.0,  1.0},
        { 0.0,  0.0,  1.0},

        {-1.0,  0.0,  0.0},
        {-1.0,  0.0,  0.0},
        {-1.0,  0.0,  0.0},
        {-1.0,  0.0,  0.0},

        { 0.0,  0.0, -1.0},
        { 0.0,  0.0, -1.0},
        { 0.0,  0.0, -1.0},
        { 0.0,  0.0, -1.0},

        { 1.0,  0.0,  0.0},
        { 1.0,  0.0,  0.0},
        { 1.0,  0.0,  0.0},
        { 1.0,  0.0,  0.0},

        { 0.0,  1.0,  0.0},
        { 0.0,  1.0,  0.0},
        { 0.0,  1.0,  0.0},
        { 0.0,  1.0,  0.0},

        { 0.0, -1.0,  0.0},
        { 0.0, -1.0,  0.0},
        { 0.0, -1.0,  0.0},
        { 0.0, -1.0,  0.0}
    };

    // Convert static data to pfMalloc'ed data
    static void*
    memdup(void *mem, size_t bytes, void *arena)
    {
        void *data = pfMalloc(bytes, arena);
        memcpy(data, mem, bytes);
        return data;
    }

    /* Set up a PFGS_QUADS pfGeoArray */
    vattr = pfGeoArray::queryAttrType("vertex");
    nattr = pfGeoArray::queryAttrType("normal");

    garray = new pfGeoArray;
    garray->setPrimType(PFGS_QUADS);
    garray->setNumPrims(6);

    garray->addAttr(vattr,  3, GL_FLOAT, 0,
    		memdup(coords, sizeof(coords), NULL));
    garray->addAttr(cattr,  3, GL_FLOAT, 0,
    		memdup(norms, sizeof(norms), NULL));

With pfGeoArrays, unlike with pfGeoSets, you cannot index vertex coordinates and normals separately. This results in bigger memory requirements. The extra storage is worth the reduced rendering times, though.

Other examples of creating pfGeoArrays can be found in following files:

(IRIX and Linux) `/usr/share/Performer/src/pguide/libpfdu/pfdConvertToGeoArrays.C
/usr/share/Performer/src/pguide/libpr/C++/geoArray.C
` (Microsoft Windows)
`%PFROOT%\Src\pguide\libpfdu\pfdConvertToGeoArrays.cxx
%PFROOT%\Src\pguide\libpr\C++\geoArray.cxx`

### Converting pfGeoSets to pfGeoArrays

Since using pfGeoArrays can be much faster on some platforms, such as Onyx4 systems, you can convert your geometry from pfGeoSets to pfGeoArrays using two functions: pfdConvertGeoSetToGeoArray() and pfdConvertNodeGeoSetsToGeoArrays(). The first function converts an individial pfGeoSet into a pfGeoArray. The second function traverses a pfNode and replaces all its pfGeoSets with pfGeoArrays.

Also, it is possible to use the pseudo loader `libpfgeoa` to convert the geometry from pfGeoSets to pfGeoArrays during loading. The pseudo loader is used as follows:

    perfly file.ext.geoa

## 3D Text

In addition to the pfGeoSet and pfGeoArray, `libpr` offers two other primitives which together are useful for rendering a specific type of geometry— 3D characters. See Chapter 3, “Nodes and Node Types” and the description for pfText nodes for an example of how to set up the 3D text within the context of `libpf`.

### pfFont

The basic primitive supporting text rendering is the `libpr` pfFont primitive. A pfFont is essentially a collection of pfGeoSets in which each pfGeoSet represents one character of a particular font. pfFont also contain metric data, such as a per-character spacing, the 3D escapement offset used to increment a text `cursor' after the character has been drawn. Thus, pfFont maintains all of the information that is necessary to draw any and all valid characters of a font. However, note that pfFonts are passive and have little functionality on their own; for example, you cannot draw a pfFont—it simply provides the character set for the next higher-level text data object, the pfString.

Table 8-6 lists some routines that are used with a pfFont.

Table 8-6. pfFont Routines

Routine | Description
--- | ---
pfNewFont() | Create a new pfFont.
pfDelete() | Delete a pfFont.
pfFontCharGSet() | Set the pfGeoSet to be used for a specific character of this pfFont.
pfFontCharSpacing() | Set the 3D spacing to be used to update a text cursor after this character has been rendered.
pfFontMode() | Specify a particular mode for this pfFont. Valid modes: PFFONT_CHAR_SPACING—Specify whether to use fixed or variable spacings for all characters of a pfFont. Possible values are PFFONT_CHAR_SPACING_FIXED and PFFONT_CHAR_SPACING_VARIABLE, the latter being the default. PFFONT_NUM_CHARS—Specify how many characters are in this font. PFFONT_RETURN_CHAR—Specify the index of the character that is considered a `return' character and thus relevant to line justification.
pfFontAttr() | Specify a particular attribute of this pfFont. Valid attributes: PFFONT_NAME—Name of this font. PFFONT_GSTATE—pfGeoState to be used when rendering this font. PFFONT_BBOX—Bounding box that bounds each individual character. PFFONT_SPACING—Set the overall character spacing if this is a fixed width font (also the spacing used if one has not been set for a particular character).

Example 8-1. Loading Characters into a pfFont

    /* Setting up a pfFont */
    pfFont *ReadFont(void)
    {
        pfFont *fnt = pfNewFont(pfGetSharedArena());
        for(i=0;i<numCharacters;i++)
        {
            pfGeoSet* gset = getCharGSet(i);
            pfVec3* spacing = getCharSpacing(i);

            pfFontCharGSet(fnt, i, gset);
            pfFontCharSpacing(fnt, i, spacing);
        }
    }

### pfString

Simple rendering of 3D text can be done using a pfString. A pfString is an array of font indices stored as 8-bit bytes, 16-bit shorts, or 32-bit integers. Each element of the array contains an index to a particular character of a pfFont structure. A pfString can not be drawn until it has been associated with a pfFont object with a call to pfStringFont(). To render a pfString once it references a pfFont, call the function pfDrawString().

The pfString class supports the notion of `flattening' to trade off memory for faster processing time. This causes individual, noninstanced geometry to be used for each character, eliminating the cost of translating the text cursor between each character when drawing the pfString.

Example 8-2 illustrates how to set up and draw a pfString.

Example 8-2. Setting Up and Drawing a pfString

    /* Create a string a rotate it for 2.5 seconds */
    void
    LoadAndDrawString(const char *text)
    {
        pfFont *myfont = ReadMyFont();
        pfString *str = pfNewString(NULL);
        pfMatrix mat;
        float start,t;

        /* Use myfont as the 3-d font for this string */
        pfStringFont(str, fnt);

        /* Center String */
        pfStringMode(str, PFSTR_JUSTIFY, PFSTR_MIDDLE);

        /* Color String is Red */
        pfStringColor(str, 1.0f, 0.0f, 0.0f, 1.0f);

        /* Set the text of the string */
        pfStringString(str, text);

        /* Obtain a transform matrix to place this string */
        GetTheMatrixToPlaceTheString(mat);
        pfStringMat(str, &mat);

        /* optimize for draw time by flattening the transforms */
        pfFlattenString(str);

        /* Twirl text for 2.5 seconds */
        start = pfGetTime();
        do
        {
            pfVec4 clr;
            pfSetVec4(clr, 0.0f, 0.0f, 0.0f, 1.0f);

            /* Clear the screen to black */
            pfClear(PFCL_COLOR|PFCL_DEPTH, clr);

            t = (pfGetTime() - start)/2.5f;
            t = PF_MIN2(t, 1.0f);

            pfMakeRotMat(mat, t * 315.0f, 1.0f, 0.0f, 0.0f);
            pfPostRotMat(mat, mat, t * 720.0f, 0.0f, 1.0f, 0.0f);

            t *= t;
            pfPostTransMat(mat, mat, 0.0f,
                150.0f * t + (1.0f - t) * 800.0f, 0.0f);

            pfPushMatrix();
            pfMultMatrix(mat);

            /* DRAW THE INPUT STRING */
            pfDrawString(str);

            pfPopMatrix();

            pfSwapWinBuffers(pfGetCurWin());
        } while(t < 2.5f);
    }

Table 8-7 lists the key routines used to manage pfStrings.

Table 8-7. pfString Routines

Routine | Description
--- | ---
pfNewString() | Create a new pfString.
pfDelete() | Delete a pfString.
pfStringFont() | Set the pfFont to use when drawing this pfString.
pfStringString() | Set the character array that this pfString will represent or render.
pfDrawString() | Draw this pfString.
pfFlattenString() | Flatten all positional translations and the current specification matrix into individual pfGeoSets so that more memory is used, but no matrix transforms or translates have to be done between each character of the pfString.
pfStringColor() | Set the color of the pfString.
pfStringMode() | Specify a particular mode for this pfString. Valid modes: PFSTR_JUSTIFY — Sets the line justification and has the following possible values: PFSTR_FIRST or PFSTR_LEFT, PFSTR_MIDDLE or PFSTR_CENTER, and PFSTR_LAST or PFSTR_RIGHT. PFSTR_CHAR_SIZE — Sets the number of bytes per character in the input string and has the following possible values: PFSTR_CHAR, PFSTR_SHORT, PFSTR_INT.
pfStringMat() | Specify a transform matrix that affects the entire character string when the pfString is drawn.
pfStringSpacingScale() | Specify a scale factor for the escapement translations that happen after each character is drawn. This routine is useful for changing the spacing between characters and even between lines.

Prev | Table of Contents | Next
Chapter 7. Importing Databases |  | Chapter 9. Higher-Order Geometric Primitives