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:

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

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

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 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

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 indexth 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 ORed 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, PF_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 ANDed 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 ORed 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.