Chapter 3. Specifying the Appearance of Geometries

The geometry and appearance of a shape are independent of one another. The appearance of a shape is the two-dimensional texture, such as the rind of an orange, that is mapped onto a geometry.

This chapter describes how to specify the appearance of a geometry in the following sections::

csContext Overview

A csContext object maintains the OpenGL graphics state for a scene graph and therefore contains all the default appearance values necessary to render a shape.

Appearance values, such as material and texture, can be specified per shape using csAppearance. If csAppearance fields are not set, the shape inherits the default appearance values set in csContext. For optimal performance, set as few csAppearance object fields as possible by setting the global defaults in csContext to values that satisfy the majority of geometries in a scene graph. This practice minimizes state changes while rendering.

There is an inheritance mask in each csAppearance that specifies which appearance values are inherited by csAppearance from csContext. csAppearance values automatically override csContext default values on a per-shape basis, regardless of the bit values in the inheritance mask.

State Machine

csContext maintains and manages OpenGL graphics state for the purpose of efficient graphics pipeline state control. OpenGL is a state machine: you put it into various states (or modes) that then remain in effect until you change them. For example, the current color is a state variable. The Cosmo 3D context maintains two notions of state:

  • Default state—is the global graphics state defined on a per-context basis and maintained separately from the current state.

  • Current state—represents the accumulation of the default state and the state set when csAppearance nodes are encountered during a traversal.

State that is not explicitly set in a csAppearance via the appropriate csAppearance set() methods is inherited from the default state. An inheritance mask, however, specifies which csContext fields a shape inherits by default.

The effect of a set() method is immediate, as if you made the OpenGL calls directly. The set() methods affect the current state but have no effect on the default state.

Inheritance Mask

Inheritance masks specify which csContext fields are inherited by csAppearance. Each bit in the bitmask corresponds to a specific csAppearance field. All fields are inherited by default.

Figure 3-1 demonstrates how the inheritance mask works as a result of the code in Example 3-1.

Example 3-1. Inheritance Mask


csContext *ctx = new cscontext;

ctx->setCullFace(BACK);
ctx->setLightEnable(TRUE);
.
.
.
csAppearance *app = new csAppearance;
app->setMaterial(mtl);
shape->setAppearance(app);

Figure 3-1. Inheritance Mask

Figure 3-1 Inheritance Mask

All 0 bits indicate that the default value is used.

When you set a csAppearance value, its corresponding bit in the inheritance mask is set.

Accessing States

Applications can access the current state using the various get() methods. The primary use of the get() method is to ask the system about its current state. Cosmo 3D makes this state available so that a user callback can know the current state of the system and make OpenGL calls appropriately.


Warning: It is critical that such a callback not alter the OpenGL state.

You can avoid altering the OpenGL state either by using csContext::set() calls or by saving and restoring OpenGL state explicitly upon entry and exit of the callback.

What Modifies the Graphics State?

As a csDrawAction traverses the scene graph, the current state is modified when:

  • Appearances a their draw methods are invoked.

  • Calls users make to various set() methods in csContext invoke pre- and post-node callbacks.

Traversal Order

The actual appearance of a csGeometry being drawn is independent of traversal order. Only that csShape's appearance and the default state affect the actual appearance. At no time does Cosmo 3D depend on the traversal order of the draw action. No guarantees are made about traversal order because the traversal order of the draw is subject to change. Because applications cannot depend on draw traversal order to imply the state of the context, methods to query the context for its current state are available.

csContext in Multi-threaded Programs

csContext can be used in a multi-threaded program. When makeCurrent() is called within a given thread, that context is attached to the thread. Binding a thread to a context allows multi-pipe or multi-window rendering in parallel using a single, shared copy of the scene graph.

For more information about multi-threaded implementation, see Chapter 13, “Multiprocessing.”

Overriding Appearances and Geometry Properties with csContext

In general, csAppearance settings override csContext default values. You can, however, override csAppearance settings using csContext::pushOverrideAppearance(). Only one override appearance per context can be in place at a time.

You can override some properties that are not in csAppearance and are geometry-specific through the use of pushOverrideGeoProp(). Currently, csCullFace, csLineWidth, and csPointSize are the only geometry-specific properties that can be overridden.

Making the Screen One Color

To change the screen to a specified color, use one of the following csContext methods:

static void clear(int which);
static void clear(int which, float r, float g, float b, float a);

where which is a bitmask specifying whether to clear the color planes, depth planes, or both.

The first clear() method clears the screen to black. The second version allows you to set a uniform color and transparency.

Changing the Context

You can create multiple csContext objects but only one can be active at a time. In this way, in addition to changing field values in a context object, you can change the entire context all at once using makeCurrent(). This method replaces the current context with the csContext object specified in the argument, for example:

csContext* context1 = new csContext;
csContext* context2 = new csContext;

context1->makeCurrent(display, window);
context2->makeCurrent(display, window);

In this example, the second context replaces the first.

  • display is a pointer to the X window display.

  • window is the GLXDrawable in which the scene is displayed.

getCurrent() returns the context object on top of the context stack.

csWindow has a context and calls makeCurrent() automatically.

Using csAppearance

csAppearance fields define the appearance of a csGeometry object, for example, its texture, material, or color. All of the fields in csAppearance are replicated in csContext.

Inheriting Appearance Values

To specify the appearance of a csGeometry, you can either

  • Set all of the appearance fields in a csAppearance object.

  • Use the inherited, global, default values from the current context, csContext.

  • Use a combination of the first two options.

If you set all of the fields of an appearance object, the appearance object becomes the full graphic context of the csShape. The more appearance fields you set, however, the slower the application's performance because you are triggering lots of state changes.

For maximum performance, set the appearance values in csContext to satisfy the maximum number of shapes so that the fewest number of csAppearance fields are set on a per-shape basis.

Setting Appearance Fields Locally

The only fields that you should set locally are those that change often, such as the field values for material and texture. Changing a field value locally overrides any value inherited from csContext.

The csAppearance class includes a series of set...() methods to define the appearance characteristics of a geometry. A series of corresponding get...() methods provide access to those values. The following set...() methods are described in greater detail in the rest of this chapter:

void setTexture(csTexture* texture);
void setTexEnable(csBool texEnable);
void setTexMode(csContext::TexModeEnum texMode);
void setTexBlendColor(const csVec4f& texBlendColor);
void setTexBlendColor(csFloat v0, csFloat v1, csFloat v2, csFloat v3);
void setTexEnv(csContext::TexEnvEnum texEnv);
void setTexGen(csTexGen* texGen);
void setTexGenEnable(csBool texGenEnable);

void setMaterial(csMaterial* material);
void setLightEnable(csBool lightEnable);
void setShadeModel(csContext::ShadeModelEnum shadeModel);
void setTranspEnable(csBool transpEnable);
void setTranspMode(csContext::TranspModeEnum transpMode);
void setAlphaFunc(csContext::AlphaFuncEnum alphaFunc);
void setAlphaRef(csFloat alphaRef);
void setBlendColor(const csVec4f& blendColor); 
void setBlendColor(csFloat v0, csFloat v1, csFloat v2, csFloat v3); 
void setSrcBlendFunc(csContext::SrcBlendFuncEnum srcBlendFunc);
void setDstBlendFunc(csContext::DstBlendFuncEnum dstBlendFunc);
void setColorMask(const csVec4ub &colorMask); 
void setColorMask(csUByte v0, csUByte v1, csUByte v2, csUByte v3); 
void setDepthFunc(csContext::DepthFuncEnum depthFunc);
void setDepthMask(csUInt depthMask);
void setFogEnable(csBool fogEnable);
void setPolyMode(csContext::PolyModeEnum polyMode);

These method are separated into two groups:

  • Methods containing the string “tex” modify textures.

  • The remaining methods modify the appearance of geometries.

Lazy Updating of Appearance Values

csAppearance values are updated in a lazy way: a value is changed only when it is used. For example, if a ball is currently displayed and you change its color using setColor(), the ball would change color immediately on the screen. If, however, the ball is out of view of the camera, the color of the ball would not be updated until it is seen by the camera.

Applying Textures to Geometries

One way to affect the appearance of a geometry is to apply a texture to it. A texture is a rectangular 2D image, for example, a 2D map of the world. This rectangular texture is scaled or repeated to fit on the surface of a 3D object, such as a sphere. The clamping and repetition of a texture over a surface is programmatically controllable.

To create the image of an orange, for example, you first create the orange, pitted texture of orange rind and then apply it to a sphere. The difference between using and not using a texture, in this example, is the difference between rendering a generic sphere and a realistic-looking orange.

Figure 3-2. Applying a Texture to a Geometry

Figure 3-2 Applying a Texture to a Geometry

Texture Map Coordinates

A texture map is always defined by the coordinates s, for the horizontal component, and t, for the vertical component, each of which range in values from 0.0 to 1.0, as shown in Figure 3-3.

Figure 3-3. Texture Coordinates

Figure 3-3 Texture Coordinates

Texture coordinates are assigned to each vertex of a geometry either by you or by Cosmo 3D.

Applying a Texture

To apply a texture to a geometry, set the argument of the csAppearance::setTexEnable() method to ON. If you do not want to apply a texture to a geometry, set the argument of setTexEnable() to OFF.

Texture rendering uses the texture values specified in csContext by default. To set the texture values locally, however, use the following methods in csAppearance:

setTexture()  

to specify the image used as the texture.

setTexMode()  

to specify the speed and quality of the rendered texture.

setTexBlendColor() 


to specify the color to use in “blend” mode.

setTexEnv() 

to specify how texture colors are blended with the colors of a geometry.

setTexGen(), setTexGenEnable() 


to generate, if enabled, texture coordinates automatically instead of using the csGeoSet's TexCoordSet.

The following sections describe these methods.

Specifying a Texture Image

To apply a texture to a geometry, supply a csTexture object in the argument of setTexture(). csTexture is a class consisting of the following fields and default values:

csSFString filename “noName”
csMFRef  imageLevel[] 
csSFEnum format 0
csSFEnum repeat_S REPEAT
csSFEnum repeat_T REPEAT
csSFEnum minFilter FAST
csSFEnum magFilter FAST
csSFEnum source 0

imageLevel is an array of MIPmap levels for this texture of type csImages. If this field is not set or has all NULL values, the texture is loaded from csSFString fileName instead.

format is the pixel format of the image. For more information about pixel format, see “Color Components”.

repeat_S and repeat_T specify whether or not the texture is repeated in the s and t directions on the geometry, respectively. If the texture is not repeated, it is clamped.

minFilter specifies what to do with texels that project smaller than a screen pixel. Possible values include NEAREST_MIN, LINEAR_MIN, or MIPMAP.

magFilter specifies what to do with texels that project larger than a screen pixel. Possible values include NEAREST_MAX or LINEAR_MAX.

Texture Mode Settings

The texture mode method allows you to specify texture rendering speed, quality, and perspective where speed and quality, and speed and perspective are trade-offs.

You specify the mode of the texture rendering using setTexMode() with one of the following arguments from csContext::TexModeEnum:

FAST_TEX 

for a low quality, more quickly-rendered texture.

NICE_TEX 

for a high quality, more slowly-rendered texture.

NON_PERSP_TEX 


for a non-perspectively-correct, more quickly-rendered texture.

PERSP_TEX 

for a more slowly-rendered texture in perspective.

When you choose the NON_PERSP_TEX mode, Cosmo 3D applies the texture to a geometry without proper perspective. For example, if you apply a texture to a plane extending into the Z dimension, the pattern should not distort but just appear to recede into the distance. In NON_PERSP_TEX mode, however, the pattern is distorted, as shown in Figure 3-4.

Figure 3-4. Non-Perspective and Perspective Modes

Figure 3-4 Non-Perspective and Perspective Modes

If you enable texture rendering but do not set the texture mode in a csContext or csAppearance object, the texture rendering mode is defined by the csTexture object in the argument of csAppearance::setTexture() or csContext::setTexture(). A csTexture object can specify one of the values in TexModeEnum.

Texture Environment Settings

Texture environment variables specify how texture colors are blended with the colors of a geometry; the texture color can replace, blend with, or subtract from the colors already on the geometry.

To specify how texture colors are blended with the colors of a geometry, use the setTexEnv() method with one of the following csContext::TexEnvEnum values as an argument:

MODULATE_TENV 


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. MODULATE is the default value.

BLEND_TENV 


uses the texture color to blend together the blend color and the underlying geometry's color. In the above example, the lettering would be a mixture of green, white, and black.

REPLACE_TENV 


replaces the underlying geometry's color with the texture color. If the texture has an alpha component, the alpha value specifies the texture's transparency, allowing the geometry's color to show through the texture. In the above example, the lettering would be white and black.

ADD_TENV 

adds the underlying geometry's color with the texture color.

DECAL_TENV 

replaces the underlying geometry's color with the color of the texture. When this token is used with RGBA values, the alpha value determines the blending between the shape's and texture's color: when the alpha value is 1.0, the color is only the texture's; when the value is 0.0, the color is only that of the shape's.


Tip: If you use MODULATE, consider surrounding your texture images with a one-pixel border of white pixels and set csTexture::setRepeatS() and csTexture::setRepeatT() to CLAMP so the geometry's color is used where the texture runs out.


Color Components

A texture image can have up to four components per texture element:

  • A one-component image consists of a luminance value, Lt. One-component textures are often referred to as intensity maps. For example, an image of a statue could use polygons of different intensities to shade and provide detail.

  • A two-component image consists of luminance, Lt, and transparency, At. For example, you could create an architect's diagram of a house using polygons of different intensities to give detail to the building materials and then vary the transparency of the polygons to see through the building materials.

  • A three-component image consists of a set of RGB values, referred to as a color triplet, Ct. For example, any color image is at least a three-component image.

  • A four-component image consists of an RGB (or Ct) set of values, and transparency, At. The “t” subscript denotes the transparency or the color of the texture. For example, you could create an architect's diagram of a house using a variety of colors and transparencies.

The color components work with the texture environments in the following way:

  • MODULATE works with any texture file.

  • BLEND works with one- to four-component textures.

  • REPLACE works with three- or four-component textures.

  • ADD works with three- or four-component textures.

  • DECAL works with three- or four-component textures.


Tip: MODULATE works best with bright materials because the texture intensity is reduced by the factor of the geometry's intensity.


Specifying Texture Coordinates

There are two ways to specify how a texture is applied to a geometry:

  • Use the default. Cosmo 3D applies textures to geometries according to the geometry.

  • Use the texture coordinate function, setTexGen().

Using the Default

Cosmo 3D applies textures to geometries according to the geometry. For all geometries subclassed from csGeometry, Cosmo 3D

  • Computes the bounding box.

  • Turns the texture so its longest side is in the horizontal (s) direction.

The horizontal (s) value ranges from 0.0 to 1.0 and the vertical component ranges from 0.0 to n, where n equals the ratio of the t dimension to the s dimension; this ratio maintains the texture without distorting it.

Using the Texture Coordinate Function

The setTexGen() method generates texture coordinates by, in effect, projecting a texture plane onto a geometry, as shown in Figure 3-5.

Figure 3-5. Texture Coordinate Function

Figure 3-5 Texture Coordinate Function

The setTexGen() method specifies

  • Whether or not the texture plane is repeated across the geometry.

  • Whether the texture plane is stationary or moves in concert with the motion of the geometry.

The setTexGen() method takes a csTexGen object for an argument. In a csTexGen object, you set the

  • Repetition of the texture image in three dimensions, s, t, and r.

  • Mode of the texture in each of the dimensions.

For example, csTexGen::setPlaneS(2.5, 0, 0, 0) repeats the texture two-and-a-half times in the s dimension.

Figure 3-6 shows how a texture plane is repeated across a geometry.

Figure 3-6. Repeated Texture on a Geometry

Figure 3-6 Repeated Texture on a Geometry

The default values of both s plane equations are (1,0,0,0), both t plane equations are (0,1,0,0), and all r and q plane equations are (0,0,0,0).

Setting the csTexGen Mode

If you think of the texture plane as being projected onto the surface of a geometry rather than being on the surface of a geometry, it is easy to understand how the mode settings in csTexGen work. Either the plane is stationary and the geometry moves “under” it or the plane moves in concert with the geometry. In the second case, the colors of the plane appear to be part of the geometry when it moves; in the first case, the colors of the plane appear to ride over the geometry when it moves.

You set the mode of each plane in csTexGen to one of the following values:

OFF 

Turns off the texture.

EYE_LINEAR 

Lets the geometry turn independently of the texture plane. In this case, the colors of the plane appear to ride over the geometry when it moves. This value is the default.

OBJECT_LINEAR 


Lets the texture move in coordination with the geometry. In this case, the texture appears to be on the surface of the geometry.

SPHERE_MAP 


Lets the texture pattern remain stationary as the geometry moves thus producing a mirror-like, circular reflection.

EYE_LINEAR_IDENT 


Loads the identity matrix onto the modelview stack before loading the plane equation.

Enabling Texture Generation

The setTexGen() function is enabled or disabled using setTexGenEnable() with an argument of ON or OFF, respectively. Enabling the generation is, in effect, like turning on the light which shines through the plane and onto a geometry. Disabling the generation turns off the light.

The remaining sections apply to the appearance of the geometry itself.

Material Settings

The material field in csAppearance defines the surface qualities of a geometry, such as how well it reflects light, what color it reflects, and what color it emits. The material field is of type csMaterial, which has the following set...() methods:

void setAmbientIntensity(csFloat ambientintensity);
void setAmbientColor (const csVec3f& ambientColor);
void setDiffuseColor(const csVec3f& diffuseColor);
void setDiffuseColor(float v0, float v1, float v2);
void setSpecularColor(const csVec3f& specularColor);
void setSpecularColor(float v0, float v1, float v2);
void setEmissiveColor(const csVec3f& emissiveColor);
void setEmissiveColor(float v0, float v1, float v2);
void setAmbientIndex(csShort ambientIndex);
void setDiffuseIndex(csShort diffuseIndex);
void setSpecularIndex(csShort specularIndex);
void setShininess(csFloat shininess);
void setTransparency(csFloat transparency);

csMaterial also has a corresponding set of get...() methods.

Ambient color is the color of the light reflected from an object when lit by another ambient object in the scene. The default value is [0.2, 0.2, 0.2]. Ambient intensity refers to the strength of the reflection—a value between 0.0 and 1.0 where 1.0 is a strong reflection. Ambient index refers to a color lookup table in which each ambient color is paired with an index number for easy look-ups.

Diffuse color is an object's base color. The default value is [0.8, 0.8, 0.8]. Diffuse index refers to a color lookup table in which each diffuse color is paired with an index number for easy look-ups.

Specular color is the reflected color of an object's highlights. Specular intensity refers to the strength of the reflection. The default value is [0.0, 0.0, 0.0]. Specular index refers to a color lookup table in which each specular color is paired with an index number for easy look ups.

Emissive color is the color emitted by an object. A lamp shade for example, might have a base color of yellow. When the lamp is turned on, however, the emissive color might be white. The default value is [0.0, 0.0, 0.0].

Shininess describes how much of the surroundings are reflected by an object, for example, a mirror would have a large shininess value so that surrounding objects would be seen in it. Values range from 0.0, for a very dull surface, to 1.0, for a highly polished surface. The default value is 0.2.

Transparency describes how opaque or clear an object is, for example, water might be more clear than opaque. Values range from 0.0, for opaque, to 1.0, for complete transparency. The default value is 0.0.

Material Example

The following example shows the material settings for gold:

csMaterial *gold = new csMaterial;

gold->setAmbientColor(.3, .1, .1);
gold->setDiffuseColor(.8, .7, .2);
gold->setSpecularColor(.4, .3, .1);
gold->setShininess(.4);

Since gold is opaque, the default value, 0.0, for transparency suffices.

Filling Geometries

The setPolyMode() method specifies how to render the elementary polygons that compose a geometry. The following values for the method are valid:

POINT_PMODE 


The polygon is rendered as points.

LINE_PMODE 


The polygon is rendered as a line. This option is equivalent to rendering the geometry as a wireframe.

FILL_PMODE 


The polygon is rendered as filled.

For example, if you use POINT_PMODE, a triangle would appear as three points. LINE_PMODE would render a triangle as a set of three lines; FILL_PMODE would render the triangle as filled.

Shade Model Settings

You set the shading model using the setShadeModel() method with one of the following csContext::ShadeModelEnum values as its argument:

FLAT_SHADE 

Each primitive, geometric polygon that comprises a geometry has the same shade value. This option has the effect of making the primitive geometric polygons visible.

SMOOTH_SHADE 


Shade values are interpolated across primitive geometric polygons. This option makes the primitive polygons look more like a curved surface.

Transparency Settings

To specify the transparency of a geometry locally, enable the transparency mode by setting setTranspEnable() to ON and then specifying the transparency mode in the argument of setTranspMode(). The possible transparency values include

FAST_TRANSP 

Produces as quickly-rendered, lower-quality transparent geometry.

NICE_TRANSP 

Produces a more slowly-rendered, higher-quality transparent geometry.

BLEND_TRANSP 


Produces a smooth transparency between foreground and background images.

SCREEN_DOOR_TRANSP 


Produces a mottled transparency, as though every other pixel is transparent.

Commonly, you use the setTranspMode() twice: you specify

  • FAST_TRANSP or NICE_TRANSP

  • BLEND_TRANSP or SCREEN_DOOR_TRANSP

Producing Transparency Without Blending

The setAlphaFunc() method sets the requirements for whether or not a pixel is rendered. Not rendering some of the pixels in a geometry has the effect of making the geometry partially transparent.

To use the setAlphaFunc() method, you first set the reference value against which you measure the alpha value of the pixel to be drawn using setAlphaRef(), for example

setAlphaRef(10);

Then you supply, as an argument to setAlphaFunc(), one of the values of csContext::AlphaFuncEnum:

  • NEVER_AFUNC

  • LESS_AFUNC

  • EQUAL_AFUNC

  • LEQUAL_AFUNC

  • GREATER_AFUNC

  • NOTEQUAL_AFUNC

  • GEQUAL_AFUNC

  • ALWAYS_AFUNC

For example, the following code

setAlphaRef(0.5);
setAlphaFunc(LESS_AFUNC);

is similar to the following lines of code:

if(Alpha < 0.5) {
    //draw the pixel };

where Alpha is the alpha values of pixels to be drawn in a geometry.

The values have the following effects:

NEVER_AFUNC 


The incoming pixel is never rendered. This function has the effect of creating a totally transparent geometry.

LESS_AFUNC 

The incoming pixel is rendered only if its alpha value is less than the reference value.

ALWAYS_AFUNC 


The incoming pixel is always displayed. This function has the effect of creating an opaque geometry.