Chapter 8. Specifying the Appearance of Geometry with pfState and pfGeoState
Prev | Part II. Programming with OpenGL Performer | Next

## Chapter 8. Specifying the Appearance of Geometry with pfState and pfGeoState

A pfState holds the global graphic's state description. A pfGeoState encapsulates the graphics state elements, such as lighting, transparency, and texture that define the appearance of a pfGeoSet. Every pfGeoSet must reference a pfGeoState. State definitions for the pfGeoSet come either from its pfGeoState, or from the global, default settings in the global pfState.

This chapter describes how to define the appearance of geometries in the following sections:

- “Setting the Graphics State”

- “Using Textures”

- “Specifying the Material”

- “Specifying Lighting”

## Setting the Graphics State

Graphics state elements can be directly set in immediate mode through pfApply*() routines. For example, use pfApplyMtl() to set a current material; use pfEnable() to enable a specific mode, such as lighting, or use pfApplyGState() to set a complete collection of graphics state elements. When these calls are made, the current graphics state is recorded by OpenGL Performer in a pfState. This provides functionality, as well as optimizations, to prevent redundant graphics state changes.

### Global State

pfState contains all global graphics state information and all of the information necessary to define the appearance of a geometry. You must have a current pfState to create any other OpenGL Performer state objects or to do any graphics operations; however, pfWindows by default automatically creates and selects its own pfState when it is opened. You can create and select a pfState object with pfInitState(). There is also a state stack that can be pushed and popped in pfState with pfPushState() and pfPopState(). You can lock state settings can be locked by using the pfState function pfOverride() to prevent future changes. pfState graphic state values become the default appearance values for all pfGeoSets.

pfGeoStates state values found in pfState and are primarily used for specifying the appearance of geometry. pfGeoStates can specify the following, among other things:

- Material properties with the pfMaterial state attribute object

- Textures with the pfTexture state attribute object

- Transparency with the transparency mode

When a pfGeoState is created, it is configured to inherit all appearance values from the current pfState when it is applied. Those values can be changed using methods in pfGeoState: pfGStateAttr(), pfGStateMode(), and pfGStateVal(). pfGeoStates settings can be applied directly to the current global state with pfLoadGState(). pfGeoStates referenced by pfGeoSets only affect the pfGeoSets referencing them. pfApplyGState() will set state values for subsequent pfGeoSets, but those state values will revert back to the previous pfState values for the next call to pfApplyGState(). A pfGeoState can be directly loaded into the current pfState to set inherited values by future pfGeoStates with pfLoadGState().

### Defining a pfGeoState

To define a pfGeoState, follow these steps:

1. Create a pfGeoState object using pfNewGState().

2. Associate the pfGeoState appearance values with a geometry using pfGSetGState().

3. Specify the modal graphic states, such as enables, you want to change using pfGStateMode().

4. Specify the attribute graphic states you want to change, such as textures and materials, using pfGStateAttr().

For example, to enable lighting and antialiasing and to set the material of the geometry to metal, use code similar to the following:

    pfMaterial *mtl = pfNewMtl(arena);

    pfGeoState *gstate = pfNewGState(arena);
    pfGStateMode(gstate, PFSTATE_ENLIGHTING, PF_ON);
    pfGStateMode(gstate, PFSTATE_ANTIALIAS, PFAA_ON);
    pfGStateAttr(gstate, PFSTATE_FRONTMTL, mtl);

#### Setting pfGeoState Values for a Scene

Generally, pfGeoState values alter global, pfState appearance values for specific pfGeoSets. You can also use a pfGeoState to set global state values by attaching a pfGeoState object to a scene node using pfSceneGState(), as follows:

    void pfSceneGState(pfScene *scene, pfGeoState *gstate);

OpenGL Performer does a pfPushState() and a pfLoadGState() of the pfScene pfGeoState before rendering the scene graph.

#### pfGeoStates and pfGeoSets

pfState contains the default, global state values, many of which define the appearance of the geometric objects in the scene. When pfGeoSets are drawn with pfDrawGSet(), they automatically call pfApplyGState() on their pfGeoState. If a pfGeoState is not defined for a geometry, the appearance values are undefined. To inherit all values from the global pfState, a pfGeoSet should have a pfGeoState with all values set to inherit, which is the default. A state value defined for a specific pfGeoSet takes precedence over the corresponding global state value.

For an example of a pfGeoState used globally, see “Computing the Optimal, Global Graphics State” in Chapter 15.

#### Optimizing Graphics State Changes

Changing the graphics context from the global value to a value defined for a specific geometry impacts the performance of an application. For that reason, it is important to set the global appearance values to satisfy most geometries, thus changing the local appearance values as little as possible.

For more information about optimizing graphic state changes, see “Optimizing Graphics State Changes” in Chapter 15.

### Setting Modal pfGeoState Values

Many pfGeoState graphic states, such as transparency, are specified with a token. These graphic states are set using pfGStateMode().

Table 8-1 shows the modal graphic states you can specify along with their possible values and defaults.

Table 8-1. Graphic States

Graphic State | Possible Values | Default Value
--- | --- | ---
PFSTATE_TRANSPARENCY | See “pfTransparency” | PFTR_OFF
PFSTATE_ANTIALIAS | PFAA_OFF, PFAA_ON | PFAA_OFF
PFSTATE_DECAL | See “pfDecal” | PFDECAL_OFF
PFSTATE_ALPHAFUNC | See “pfAlphaFunc” | PFAF_ALWAYS
PFSTATE_ALPHAREF | Float between 0.0 and 1.0 | 0.0
PFSTATE_ENLIGHTING | PF_OFF, PF_ON | PF_OFF
PFSTATE_ENTEXTURE | PF_OFF, PF_ON | PF_OFF
PFSTATE_ENFOG | PF_OFF, PF_ON | PF_OFF
PFSTATE_CULLFACE | PFCF_OFF, PFCF_BACK, PFCF_FRONT, PFCF_BOTH | PFCF_OFF
PFSTATE_ENWIREFRAME | PF_OFF, PF_ON | PF_OFF
PFSTATE_ENCOLORTABLE | PF_OFF, PF_ON | PF_OFF
PFSTATE_ENHIGHLIGHTING | PF_OFF, PF_ON | PF_OFF
PFSTATE_ENLPOINTSTATE | PF_OFF, PF_ON | PF_OFF
PFSTATE_ENTEXGEN | PF_OFF, PFTG_OBJECT_LINEAR, PFTG_EYE_LINEAR, PFTG_EYE_LINEAR_IDENT | PF_OFF
PFSTATE_ENTEXLOD | PF_OFF, PF_ON | PF_OFF
PFSTATE_ENTEXMAT | PF_OFF, PF_ON | PF_OFF

#### pfTransparency

pfTransparency sets the type of transparency computation used for rendering transparency effects. The different types of transparency computations define how the geometry's color and the framebuffer color are blended. Transparency can have different performance and image-quality characteristics on different graphics subsystems. For this reason, it is better to provide OpenGL Performer with a hint, such as PFTR_HIGH_QUALITY, rather than specifying a method that does not work on all platforms; OpenGL Performer interprets the hint, PFTR_HIGH_QUALITY, for all platforms.

Transparency modes include:

- PFTR_OFF—the default, draws transparent objects as opaque.

- PFTR_ON—allows OpenGL Performer to choose the default mode based on speed and quality.

- PFTR_HIGH_QUALITY—uses methods for highest image quality.

- PFTR_FAST—uses methods for fastest rendering.

- PFTR_BLEND_ALPHA—OpenGL glBlendFunc(3g) method.

- PFTR_MS_ALPHA—OpenGL glEnable(GL_SAMPLE_ALPHA_TO_ONE_SGIS) method when multisampling is available and enabled.

- PFTR_MS_ALPHA_MASK—OpenGL glEnable(GL_SAMPLE_ALPHA_TO_MASK_SGIS) when multisampling is enabled.

#### pfDecal

Dec aled geometry can be thought of as a stack, where each layer has visual priority over the geometry beneath it in the stack. As with transparencies, different hardware platforms offer different methods with different performance and image quality characteristics. For this reason, OpenGL Performer allows and recommends that unless you have specific motivation, use a hint rather than a specific method, which might not work on all platforms.

pfDecal modes include:

- PFDECAL_OFF

- PFDECAL_BASE

- PFDECAL_LAYER

- PFDECAL_BASE_FAST, PFDECAL_LAYER_FAST

- PFDECAL_BASE_HIGH_QUALITY, PFDECAL_LAYER_HIGH_QUALITY

- PFDECAL_BASE_DISPLACE, PFDECAL_LAYER_DISPLACE

- PFDECAL_BASE_DISPLACE, PFDECAL_LAYER_DISPLACE_AWAY

- PFDECAL_BASE_STENCIL, PFDECAL_LAYER_STENCIL

- PFDECAL_PLANE

See the `pfDecal` man page for the definition of the decal mode values. PFDECAL_OFF is the default.

#### pfAlphaFunc

pfAlphaFunc sets the alpha function mode. The alpha function mode specifies whether or not a given pixel is rendered according to its alpha value. For example, if you set pfAlphaFunc to PFAF_GREATER, only pixels with alpha values greater than a reference value are rendered.

You specify the reference value using PFSTATE_ALPHAREF. For example, to render only those pixels with alpha values greater than 0.5, use the following code:

    pfGStateMode(gstate, PFSTATE_ALPHAFUNC, PFAF_GREATER);
    pfGStateValue(gstate, PFSTATE_ALPHAREF, 0.5);

#### Alpha Func Modes

PFSTATE_ALPHAFUNC is the function you use to compare a reference alpha value with the alpha value of a geometry. PFSTATE_ALPHAFUNC must be set to one of the following modes:

- PFAF_ALWAYS

- PFAF_EQUAL

- PFAF_GEQUAL

- PFAF_GREATER

- PFAF_LEQUAL

- PFAF_LESS

- PFAF_NEVER

- PFAF_NOTEQUAL

- PFAF_OFF

See the pfAlphaFunc man page for the definition of the PFSTATE_ALPHAFUNC mode values.

### Setting pfGeoState Attributes

Many pfGeoState graphic states are specified using an object, such as pfMaterial. These graphic states are set using pfGStateAttr(). To use an object as the definition for an attribute, you must create the object and define it before calling pfGStateAttr().

Table 8-2 shows the attribute pfGeoState values and the objects that define them.

Table 8-2. Attribute pfGeoState Values

Attribute | Object
--- | ---
PFSTATE_FRONTMTL | pfMaterial
PFSTATE_BACKMTL | pfMaterial
PFSTATE_TEXTURE | pfTexture
PFSTATE_TEXENV | pfTexEnv
PFSTATE_FOG | pfFog
PFSTATE_LIGHTMODEL | pfLightModel
PFSTATE_LIGHTS | pfLight
PFSTATE_COLORTABLE | pfColortable
PFSTATE_HIGHLIGHT | pfHighlight
PFSTATE_LPOINTSTATE | pfLPointState
PFSTATE_TEXGEN | pfTexGen
PFSTATE_TEXLOD | pfTexLOD
PFSTATE_TEXMAT | pfMatrix
PFSTATE_DECALPLANE | pfPlane

All attributes default to NULL, which means that OpenGL default values are used.

For more information about any of the attributes, see the man pages of the objects associated with them.

## Using Textures

T extures are images that are applied to the surface of a geometry, as shown in Figure 8-1.

Figure 8-1. Applying Textures to Geometries

Textures can add tremendous realism to the rendered scene because they can be real photographs. An image of the pitted rind of an orange applied to a sphere, for example, creates a realistic-looking orange.

To use a texture, follow these steps:

1. Enable texture mapping.

2. Create a pfTexture.

3. Load or create the texture image and assign it to the pfTexture.

4. Optionally set the texture environment using pfTexEnv.

5. Set the texture coordinates on the pfGeoSet using pfGSetAttr, or else use a pfTexGen in the pfGeoState to automatically generate texture coordinates.

### Enabling Texture Mapping

Texture mapping is expensive; consequently, by default, it is turned off. Enable texture mapping only for those objects that are textured.

To enable texture mapping for a geometry, use the following pfGeoState methods:

    pfGStateMode(gstate, PFSTATE_ENTEXTURE, PF_ON);
    pfGSetGState(gset, gstate);

The first line enables texture mapping, and the second line selects the pfGeoSet that is to be texture mapped.

### Creating a Texture Object

To create a pfTexture, use the following method:

    pfTexture *pfNewTex(void *arena)

The arena is that part of memory shared by all OpenGL Performer processes.

### Loading an Image as a Texture

The easy way to set up a pfTexture is to load an image file. To load an image and make it a texture, use pfLoadTexFile():

    int pfLoadTexFile(pfTexture *tex, char *fname)

The texture must be in either the SGI format or the fast-loading OpenGL Performer PFI format. The texture is created with reasonable defaults for the specified image for the various control modes discussed further in this section.

You can set the paths that OpenGL Performer uses to find the image file fname using pfFilePathv(), for example:

    pfFilePathv(“/usr/demos/data/textures",
               "/usr/demos/data/images",
               "/usr/share/Performer/data”,
               NULL);

OpenGL Performer searches through the directories in the order of their specification.

#### Preloading Textures

Downloading texture images from disk to the arena is time consuming. You can improve the performance of your application if you download all of the textures that your application needs one time.

Two tools help you preload textures:

    pfList *pfuMakeSceneTexList(pfScene *scene)
    void pfuDownloadTexList(pfList *list, int mode)

pfuMakeSceneTexList() traverses the scene graph and builds a list of all the textures used. pfuDownloadTexList() downloads the textures specified in the list to the GL and hardware texture memory and must be called from the DRAW process.

### Specifying Texture Attribute

The texture image can be loaded or generated by some other utility besides pfLoadTexFile(), and in this case you must fully specify the texture image details with pfTexImage().

    void pfTexImage(pfTexture* tex, uint* image,
    int comp, int sx, int sy, int sz);

T extures can have as many as four components. The following are example uses:

- One component—consisting of intensity (I) or luminance (L) only, useful geometries that repeat but vary in contrast, such as grass and sand.

- Two components—consisting of intensity and transparency (IA), useful for geometries that repeat but vary in contrast and transparency, such as clouds.

- Three components—consisting of red, green, and blue (RGB).

- Four components—consisting of red, green, blue, and alpha (RGBA), useful for full-color textures.

A texture object also contains information about the handling of the image data, including

- Image data formats: host memory external format, internal hardware format, and the type of image data (RGB, Luminance, Intensity, etc.), set with pfTexFormat().

- Minification or magnification filters, which specify whether the image is reduced or magnified before being applied to the surface of a geometry, set with pfTexFilter().

  Texture wrap options, which specifies what happens when the texture is too small to completely cover a geometry, set with pfTexRepeat(). Options include repeating the texture until the geometry is covered or expanding the texture so that it covers the geometry.

The prototypes for these basic configuration routines are:

    void pfTexFormat(pfTexture *tex, int format, int type);
    void pfTexFilter(pfTexture *tex, int filt, int type);
    void pfTexRepeat(pfTexture *tex, int wrap, int type);

### Specifying Texture Formats

The format in which an image is stored in texture memory is defined with pfTexFormat():

    void pfTexFormat(pfTexture *tex, int format, int type)

format specifies which format to set. Valid formats and their basic types include:

- PFTEX_INTERNAL_FORMAT— specifies how many bits per component are to be used in internal hardware texture memory storage. The default is 16-bits per full texel and is based on the number of components and external format.

- PFTEX_IMAGE_FORMAT— describes the type of image data and must match the number of components, such as PFTEX_LUMINANCE, PFTEX_LUMINANCE_ALPHA, PFTEX_RGB, and PFTEX_RGBA. The default is the token in this list that matches the number of components. Other OpenGL selections can be specified with the GL token.

- PFTEX_EXTERNAL_FORMAT—specifies the format of the data in the pfTexImage array. The default is packed with 8-bits per component. There are special fast-loading hardware ready formats, such as PFTEX_UNSIGNED_SHORT_5_5_5_1.

- PFTEX_SUBLOAD_FORMAT—a boolean to specify if the texture will be a sub-loadable paging texture. Default is FALSE.

In general, you just need to specify the number of components in pfTexImage(). You may want to specify a fast-loading hardware-ready external format, such as PFTEX_UNSIGNED_SHORT_5_5_5_1, in which case OpenGL Performer will automatically choose a matching internal format. See the `pfTexFormat(3pf)` man page for more information on texture configuration details.

### Setting the Texture Environment

The environment specifies how the potentially lit colors of the geometry and the texture image interact. This is described with a pfTexEnv object. The mode of interaction is set with pfTEnvMode(), and valid modes include:

PFTE_MODULATE—gray scale of the geometry is mixed with the color of the texture (the default). This option multiplies the shaded color of the geometry by the texture color. If the texture has an alpha component, the alpha value modulates the geometry's transparency. For example, if a black and white texture, such as text, is applied to a green polygon, the polygon remains green and the writing appears as dark green lettering.

- PFTE_DECAL—texture alpha component acts as a selector between 1.0 for the texture color, and 0.0 for the base color, to decal an image onto geometry.

- PFTE_BLEND—alpha acts as a selector between 0.0 for the base color and 1.0 for the texture color modulated by a constant texture blend color specified with pfTEnvBlendColor(). The alpha/intensity components are multiplied.

- PFTE_ADD—RGB components of the base color are added to the product of the texture color modulated by the current texture environment blend color. The alpha/intensity components are multiplied.

### Setting the Texture Coordinates

The texture coordinates specify how the coordinates of the texture map to the coordinates of the geometry, as shown in Figure 8-2.

Figure 8-2. Texture Coordinates

The pfGeoSet method pfGSetAttr() specifies the texture coordinates for mapping each vertex of a pfGeoSet into texture space:

    void pfGSetAttr(pfGeoSet *gset, PFGS_TEXCOORD2, PFGS_PER_VERTEX,
        void *alist, ushort *llist)

PFGS_TEXCOORD2 specifies pfVec2 texture coordinates.

PFGS_PER_VERTEX means the attribute is specified once per vertex.

alist is a pointer to the array of pfVec2 texture coordinates.

ilist is an optional pointer to the indices in the texture coordinate array.

Texture coordinates can also be automatically generated by various functions specified by a pfTexGen object. See Chapter 9, “Graphics State ,” in the OpenGL Performer Programmer's Guide, and the `pfTGenMode(3pf)` man page for more information on this object.

## Specifying the Material

A material specifies the color of a geometry under different lighting conditions and opacity. There are five lighting conditions:

- Specular—highlights, such as shiny glints, (0.0, 0.0, 0.0), by default.

- Diffuse—directly illuminated portions of the geometry outside the specular region (0.8, 0.8, 0.8), by default.

- Ambient—those portions of the geometry illuminated by background lighting (0.2, 0.2, 0.2), by default.

- Emissive—color of the light emanating from the shape (0.0, 0.0, 0.0), by default.

- Alpha—transparency of the texture; the default, 1.0, is completely opaque.

Figure 8-3 shows three of these lighting conditions on a sphere illuminated from above.

Figure 8-3. Light Characteristics

### Specifying the Color and Shininess

To create a material and specify its color and shininess, use the following methods:

    pfMaterial *   pfNewMtl(void *arena);
    void pfMtlColor(pfMaterial *mtl, int color, float r, float g, float b);
    void pfMtlShininess(pfMaterial *mtl, float shininess);

arena is memory allocated from the shared memory arena.

color specifies one of the lighting conditions:

- PFMTL_AMBIENT

- PFMTL_DIFFUSE

- PFMTL_SPECULAR

- PFMTL_EMISSION

To define more than one of these lighting conditions, use the method repeatedly with a different token for color each time.

shininess is a float between 0.0 and 128.0 where 0.0 is very dull and 128.0 is very shiny.

#### Color Mode

Loading material information is computationally intensive. In some situations, you can take a shortcut. For example, consider the case where you have three differently colored but otherwise identical balls. Rather than reload a new material for each ball, you can change the color of the material of each ball through the object colors. pfMtlColorMode() specifies the particular material attribute that can be set through object or vertex colors. Changing a material color this way is much faster than switching to a different material; it allows for sharing of materials and shading control.

The default is PFMTL_CMODE_AD, which sets the material's ambient and diffuse colors with the pfGeoSet colors. To turn this default functionality off, set the color mode to PFMTL_CMODE_COLOR, so that geometry colors will only set the current GL color and will not affect the material state.

#### Material Side

With the method pfMtlSide(), you can specify whether to apply the material on the side facing the viewer ( PFMTL_FRONT), the side not facing the viewer ( PFMTL_BACK), or both (PFMTL_BOTH). Back-sided lighting only takes effect if a two-sided lighting model is active. Two-sided lighting typically has a significant performance cost.

Object materials only have effects when lighting is active.

## Specifying Lighting

Lighting requires a specified lighting model, an active light, and the enabling of graphics lighting operations. As lighting is typically applied to an entire scene, you probably want to enable lighting in your global state with pfEnable(PFEN_LIGHTING), or in the scene pfGeoState:

    pfGeoState *gstate = pfNewGState(arena);
    pfScene *sceneNode = pfNewScene(void);

    pfGStateMode(gstate, PFSTATE_ENLIGHTING, PF_ON);
    pfSceneGState(sceneNode, gstate);

The lighting model, specified with the pfLightModel state attribute object, describes the type of lighting operations to be considered, including local lighting, two-sided lighting, and light attenuation. The fastest light model is infinite single-sided lighting. A light model also allows you to specify ambient light for the scene, such as might come from the sun, with pfLModelAmbient().

You create pfLights by calling pfNewLight(). Lights have color and position. The light colors are specified with pfLightColor():

    void pfLightColor(pfLightSource* lsource, int which, float r,
       float g, float b);

which specifies one of three light regions:

- PFLT_AMBIENT

- PFLT_DIFFUSE

- PFLT_SPECULAR

r, g, and b specify the color components of the specified light color.

To position the light source using pfLightPos():

    void pfLightPos(pfLight* light, float x, float y,
        float z, float w);

w is the distance between the location in the scene defined by (x, y, z) and the light source, lsource. If w equals zero, lsource is infinitely far away and (x, y, z) defines a vector pointing from the origin in the direction of lsource. If w equals one, lsource is located at the position, (x, y, z). The default position is (0, 0, 1, 0): directly overhead, infinitely far away.

pfLights are attached to a pfGeoState through the PFSTATE_LIGHTS attribute.

For moving lights in a `libpf` scene, you can use a pfLightSource node. pfLightSource defines a pfLight with light color and position. They take effect when lighting is active.

pfLightSource nodes are nodes that can be placed in the scene graph and have their position transformed by pfSCS and other transform nodes. pfLightSource nodes are active for the rendering of the entire scene. pfLightSource nodes are not pfLights and cannot be attached to pfGeoStates, and visa vera.

pfLights cannot be attached directly to the scene graph and must be attached to a pfGeoState.

Prev | Table of Contents | Next
Chapter 7. Creating Geometry with pfGeoSet |  | Chapter 9. Placing Geometry in a Scene