OpenGL Performer provides pfGeoSets for holding low-level geometric descriptions of objects. A pfGeoSet is a collection of like-geometric primitives, such as points, line segments, triangle strips, triangles, or triangle fans. The primitives in a pfGeoSet share a state description for texture, material, and other surface attributes in a pfGeoState.
By combining multiple pfGeoSets you can create a complex object, such as a house, car, or terrain. By manipulating the vertices of pfGeoSet elements, you can create a dynamic object, such as ocean waves.
This chapter describes how to create geometric surfaces and place them in the scene graph in the following sections:
For more information about pfGeoState, see Chapter 8, “Specifying the Appearance of Geometry with pfState and pfGeoState”.
![]() | Note: OpenGL Performer provides a richer alternative to the pfGeoSet class: the pfGeoArray class. To find out how it is used, see the OpenGL Performer Programmer's Guide. |
A pfGeoSet is a collection of one or more like primitives, such as lines or triangles. These primitives are arranged in a way that forms a geometric surface.
A defined primitive type set with pfGSetPrimType().
The function pfGSetNumPrims() specifies the number of primitives in the pfGeoSet.
For stripped primitives, such as triangle strips, the number of vertices in each strip is set with a lengths array using pfGSetPrimLengths().
Vertex coordinate attribute lists, with optional corresponding index lists set with pfGSetAttr().
The kind of attribute binding, also specified in pfGSetAttr(), determines whether attributes are specified as follows:
A reference to a pfGeoState specified with pfGSetGState(), which specifies the surface appearance (lighting material, texture, transparency, etc.) of the geometry.
A simple example of pfGeoSet creation and rendering demonstrating the concepts in this chapter can be found at /usr/share/Performer/src/pguide/libpr/gset.c on IRIX and Linux systems and at %PFROOT%\Src\pguide\libpr\gset.c on Windows systems.
For information about optimizing pfGeoSet performance, see “Optimizing Geometry Performance” in Chapter 15.
The following sections describe how to create a pfGeoSet.
To create a pfGeoSet from the shared memory arena, use the following line of code:
pfGeoSet *pfNewGSet(void *arena) |
Use the following pfGeoSet method to specify the type of primitive in the pfGeoSet:
void pfGSetPrimType(pfGeoSet *gset, int type); |
type is one of the primitives provided by OpenGL Performer:
PFGS_FLAT_* primitives are flat-shaded. PFGS_POLYS draws polygons of arbitrary vertex lengths.
Figure 7-1 shows some of these primitives.
The numbers in Figure 7-1 show the order in which the vertex attributes should appear in the attribute array.
Use the following pfGeoSet method to specify the number of primitives in the pfGeoSet:
void pfGSetNumPrims(pfGeoSet *gset, int num); |
num is the number of primitives.
When using one of the following pfGeoSet primitives, which have an arbitrary number of vertices, you must define the number of vertices of each primitive in the pfGeoSet:
PFGS_LINESTRIPS
PFGS_FLAT_LINESTRIPS
PFGS_TRISTRIPS
PFGS_FLAT_TRISTRIPS
PFGS_TRIFANS
PFGS_FLAT_TRIFANS
PFGS_POLYS
Use pfGSetPrimLengths() to specify the number of vertices of each primitive in the pfGeoSet:
void pfGSetPrimLengths(pfGeoSet* gset, int *lengths); |
lengths is an array of the number of strips in a pfGeoSet. Each element of the lengths array is the number of vertices in a corresponding strip. For example:
lengths[0] = 8; lengths[1] = 5; |
These lines of code mean that the number 0 primitive has 8 vertices, and the number 1 primitive has 5 vertices. Use pfGetGSetPrimLength() to return the length of an individual primitive from the lengths array.
![]() | Note: pfGetGSetPrimLength() checks for NULL or negative lengths. |
The vertex information of the primitives in a pfGeoSet are described by attribute lists. Each element in an attribute list contains information for a single vertex. The vertex attributes for all vertices of all primitives of a pfGeoSet are stored in separate arrays, according to the following attribute types:
Vertices—pfVec3 coordinates (required)
Colors—pfVec4 colors
Normals—pfVec3 normals
Texture coordinates—pfVec2 texture coordinates
A pfGeoSet has at least one array pfVec3 of vertex coordinates. Optional attributes include colors, normals, and texture coordinates. Arrays holding these attributes for the vertices of the pfGeoSet are specified for the pfGeoSet using pfGSetAttr(). Figure 7-2 shows the arrays of attributes.
Indexes for indexed attributes are in separate index arrays. For more information about indexed attributes, see “Indexed Arr ays”.
You can also put all vertex attributes of a pfGeoSet in a single-packed attribute array. You may use packed attribute arrays for performance reasons or for specifying specialized custom formats of data. For more information about packed attribute arrays, see “Packed Attributes”.
To set the attributes of a pfGeoSet, use pfGSetAttr() as follows:
void setAttr(int attr, int bind, void *alist, ushort *ilist); |
attr specifies the attribute array to set. The tokens for the different attribute lists are:
PFGS_COLOR4
PFGS_NORMAL3
PFGS_TEXCOORD2
PFGS_COORD3
PFGS_PACKED_ATTRS
bind is the binding type. The tokens are listed in Table 7-1.
alist is a pointer to an attribute array for appropriate, corresponding data.
ilist is a pointer to an index array for used to access the attribute array.
A ttribute bindings specify whether attributes are as follows:
Per vertex— PFGS_PER_VERTEX
Per primitive—PFGS_PER_PRIM
For all the primitives in the pfGeoSet—PFGS_OVERALL
Unspecified—PFGS_OFF
For example, you can specify the following:
A unique color for each vertex (PFGS_PER_VERTEX).
A unique color for each primitive (PFGS_PER_PRIM).
One color for all primitives in the pfGeoSet (PFGS_OVERALL).
An unspecified color (PFGS_OFF).
Table 7-1 shows the possible bindings per attribute type.
Table 7-1. Possible Bindings Per Attribute Type
|
|
| Texture Coordinates |
|
---|---|---|---|---|
PFGS_OFF | Yes | Yes | Yes | No |
PFGS_OVERALL | Yes | Yes | No | No |
PFGS_PER_PRIM | Yes | Yes | No | No |
PFGS_PER_VERTEX | Yes | Yes | Yes | Yes |
A cube has 6 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 1 primitive can refer to the same vertex, the number of vertices can be streamlined to 8. To get more than 1 primitive to refer to the same vertex, use an index; 3 vertices of 3 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 7-3.
Indexing can save system memory, but rendering performance is often lost.
Whether or not attributes should be indexed depends on how many vertices in a geometry are shared:
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.
Consider the following two examples in Figure 7-4, in which each dot marks a vertex.
In the triangle strip, each vertex is shared by 2 adjoining triangles. In the square, the same vertex is shared by 8 triangles. Consider moving these vertices when, for example, morphing the object. If the vertices were not indexed in the square, the application would have to search for and alter 8 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, because each vertex is shared by only 2 triangles, the index search time would exceed the time required to simply update the vertices sequentially. In the case of the triangle strip, rendering is improved by handling the attributes sequentially.
The choice of using indexed or sequential attributes applies to all of the primitives in a pfGeoSet. All of the primitives within one pfGeoSet must be referenced sequentially or by index; you cannot mix the two.
Using p acked attributes is an optimized way of sending formatted data to the graphics pipeline under OpenGL operation. Using packed attributes can help host traversal performance because they remove subroutine call overhead. Packed attributes can also reduce memory usage because they allow for the format specification of attributes such as normals, texture coordinates as floats, and colors as unsigned bytes. Some small additional overhead might be incurred by the geometry subsystem of the graphics pipeline, which has to unpack the data.
The packed attribute array holds the currently bound per-vertex attribute data packed into a single non-indexed array and is specified with the matching format of the data with pfGSetAttr() as follows:
pfGSetAttr(gset, PFGS_PACKED_ATTRS, PFGS_PA_C4UBN3ST2F /*the format*/); |
V ertex coordinate attributes can be placed in this array and do not need to be duplicated in their regular arrays. Specify NULL for the attribute list to pfGSetAttr(). Vertex coordinates themselves must always be provided in the normal vertex coordinate list. They can, based on the packed format, be duplicated in the packed array.
To create packed attributes, you can use the utility pfuTravCreatePackedAttrs(), which traverses a scene graph to create packed attributes according to the specified format for pfGeoSets and, optionally, pfDelete redundant attribute arrays. This utility packs the pfGeoSet attributes using pfuFillGSetPackedAttrs(). To then render geometry with packed attributes, use the pfGSetDrawMode(PFGS_PACKED_ATTRS) method when using OpenGL.
For more information on packed arrays on IRIX and Linux systems, see the following examples:
/usr/share/Performer/src/pguide/libpr/C/packedattrs.c
/usr/share/Performer/src/sample/C/perfly.c
/usr/share/Performer/src/sample/C++/perfly/perfly.C
For more information on packed arrays on Windows systems, see following the examples:
%PFROOT%\Src\pguide\libpr\C\packedattrs.c
%PFROOT%\Src\sample\C\perfly.c
%PFROOT%\Src\sample\C++\perfly\perfly.C
Also, see Chapter 8, “Geometry ,” in the OpenGL Performer Programmer's Guide.
pfGeoSets are the lowest-level OpenGL Performer object that can be rendered. To directly draw a pfGeoSet, use pfDrawGset().
pfGeoSets are pfObjects, so they can have their contents printed for debugging with different levels of verbosity via the pfObject routine, pfPrint().You can also use other pfObject methods, such as pfCopy() and pfDelete(), can also be used with pfGeoSets.
You incorporate pfGeoSets into scene graphs using a pfGeode leaf node. A single pfGeode node can have multiple pfGeoSets associated with it if you use the pfAddGSet() method.
To place geometry in a scene graph, follow these steps:
The result is shown in Figure 7-5.
To create a pfGeode node, associate two pfGeoSets with it, and attach the node to the scene graph using code similar to the following:
pfGeode *geode = pfNewGeode(); pfAddSet( geode, gSet1 ); pfAddSet( geode, gSet2 ); pfAddChild( GeodesParentNode, geode ); |
The structure of a scene graph impacts the performance of your application. For more information, see “Arrangement of Nodes” in Chapter 6.
libpfdu provides routines to generate pfGeoSets for common shapes, including
When creating these shapes, you can specify the number of triangles that comprise them. For example, the following method sets the number of triangles comprising the sphere to 200:
pfGeoSet *sphere = pfdNewSphere(200, arena); |
This method returns the number of vertices and normals in the shape.
The more triangles, the smoother the curves but the slower the rendering; fewer triangles allow faster rendering but produce more jagged curves.
Table 7-2 shows the libpf utilities that generate pfGeoSets of l arger geometric shapes.
Table 7-2. Common Geometric Objects
Geometry | Properties | Utilities That Create the Geometry |
---|---|---|
Sphere | Unit | extern pfGeoSet * pfdNewSphere(int ntris, void *arena) |
| Radius=1, | extern pfGeoSet * pfdNewCylinder(int ntris, void *arena) |
| Radius=1, | extern pfGeoSet * pfdNewCone(int ntris, void *arena) |
Cube | Unit | extern pfGeoSet * pfdNewCube(void *arena) |
Pyramid | Unit square base, | extern pfGeoSet * pfdNewPyramid (void *arena) |
| Z=0 to Z=1 | extern pfGeoSet * pfdNewArrow (int ntris, void *arena) |
| Z=-1 to Z=1 | extern pfGeoSet * pfdNewDoubleArrow (int ntris, void
*arena) |
Cylinder | Without end caps | extern pfGeoSet * pfdNewPipe (float botRadius, float topRadius, int ntris, void *arena) |
Circle | Unit circle facing +Z,
filled | extern pfGeoSet * pfdNewCircle (int ntris, void *arena) |
| Unit circle in Z=0
plane, lines | extern pfGeoSet * pfdNewRing (int ntris, void *arena); |