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.
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.
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 |
|---|---|---|
Points | numPrims | |
Independent line segments | 2 * numPrims | |
Strips of connected lines | Sum of lengths array | |
Strips of flat-shaded lines | Sum of lengths array | |
Independent triangles | 3 * numPrims | |
Strips of connected triangles | Sum of lengths array | |
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 |
Independent quadrilaterals | 4 * numPrims | |
Independent polygons | Sum of lengths array |
The parameters in the last column denote the following:
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.
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 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.
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. |
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.
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.
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.
Binding Token | Color | Normal | Texture Coordinate | Coordinate |
|---|---|---|---|---|
Yes | Yes | No | No | |
Yes | Yes | No | No | |
Yes | Yes | Yes | Yes | |
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.
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.
Indexing can save system memory, but rendering performance is often lost.
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.
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.
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.
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.
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:
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.
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:
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:
In case of textures, you can specify the texture stage 0,1,... . Generic attributes use the set function glVertexAttribPointerARB() and mask 0x10.
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.
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.
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:
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);
|
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.
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.
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.
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.
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.
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
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 |
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.
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.
Example 8-1. Loading Characters into a pfFont
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.