Chapter 4. Sample OpenGL Volumizer Application

This chapter presents a sample OpenGL Volumizer application. It employs a simple trackball user interface and displays a textured cube.

The sample application is divided into two parts:

This chapter explains in detail the steps you take to create an OpenGL Volumizer application.

glwSimpleVolume.cxx

glwSimpleVolume.cxx sets the geometry and appearance parameters and displays the shape.

The protgramatic structure of glwSimpleVolume.cxx is characteristic of OpenGL Volumizer applications:

  1. Inclusions and declarations

  2. Set the appearance parameters

  3. Allocate memory for geometry

  4. Allocate memory for bricks

  5. Read in the brick data

  6. Create the geometry

  7. Set drawing parameters

  8. Polygonize the shape

  9. Draw the shape

  10. Clean up

Inclusions and Declarations

 

 

OpenGL Volumizer and standard C++ includes.

 

 

#include <stdio.h>

#include <stdlib.h>

#include < vo /GeometryActions.h>

#include < vo /AppearanceActions.h>

Method declaration that returns data about the geometry's file.

The reader must provide this.

 

 

extern int myGetVolumeSizesIfl(

char *fileName, int &xSize, int &ySize,

int &zSize,

voExternalFormatType & diskDataFormat,

voDataType & dataType);

Method declaration that returns texture and geometry values.

The reader must provide this.

 

 

extern int myReadBrickIfl(

char *fileName, void *data,

int xBrickOrigin, int yBrickOrigin,

int zBrickOrigin,

int xBrickSize, int yBrickSize,

int zBrickSize,

int xVolumeSize, int yVolumeSize,

int zVolumeSize);

Declaration of scaling factor, one value for each dimension.

 

 

extern float modelScale[3];

The number of bricks in the largest copy of the volume.

 

 

int maxBrickCount = 0;

Determines maximum number of sampling planes.

 

 

int maxSamplesNumber = 256;

Value determines that every voxel is sampled.

 

 

float samplingRate = 1.0;

Vertex format; here only vertex coordinates are specified, not textures or colors.

 

 

voInterleavedArrayType interleavedArrayFormat = voInterleavedArrayTypeScope ::V3F;

Storage for transient polygons.

 

 

voVertexData *allVertexData;

voIndexedFaceSet ***aPolygonSetArray;

 

Set the Appearance Parameters

 

 

Method for displaying an error string. This also resets the error condition state.

 

 

void

my_ExceptionHandler(void)

{

fprintf(stderr,

"OpenGL Volumizer error %d: %s\n",

voError ::getErrorNumber(),

voError ::getErrorString());

}

Method for handling appearance, which is a collection of bricksets.

 

 

 

voBrickSetCollection *

my_InitAppearance(int argc, char **argv)

{

The size of the geometry and texture.

 

 

int xVolumeSize, yVolumeSize, zVolumeSize;

int xBrickSize, yBrickSize, zBrickSize;

Data formats to use externally and internally.

 

 

voExternalFormatType diskDataFormat;

voExternalFormatType externalFormat;

voInternalFormatType internalFormat;

Describes what format the voxels are in.

 

 

voDataType dataType;

Specifies whether the texture is sampled using bilinear or trilinear interpolation.

 

 

  voInterpolationType interpolationType = voInterpolationTypeScope ::DEFAULT;

Sets rendering mode to monochrome.

 

 

voRenderingMode renderingMode = voRenderingModeScope ::MONOCHROME;

float loValue = -1.0, hiValue = -1.0;

Prompts user if filename of geometry was not included on the command line.

 

 

 if (argc < 2) {

 fprintf(stderr, “Usage: %s inFileName\n”,argv[0]);

 exit(1);

}

Retrieves the file name of the volume in the first argument of the command.

 

 

char *dataFileName = argv[1];

Get volume sizes and voxel format and exits.

 

 

 

if (myGetVolumeSizesIfl(dataFileName,

 xVolumeSize, yVolumeSize, zVolumeSize,

 diskDataFormat, dataType))

 exit(1);

Sets the texture size to the geometry size.

 

 

xBrickSize = xVolumeSize;

yBrickSize = yVolumeSize;

zBrickSize = zVolumeSize;

Returns, in the last three arguments, the optimal size for bricks based on the current machine's hardware.

 

 

voAppearanceActions ::getBestParameters(

interpolationType, renderingMode, dataType,

diskDataFormat, internalFormat, externalFormat,

xBrickSize, yBrickSize, zBrickSize);

Defines the brick collection. This method does not read or allocate any data, it only computes bricks' origins and sizes given the volume dimensions.

 

 

voBrickSetCollection *aVolume =

new voBrickSetCollection (

 xVolumeSize, yVolumeSize, zVolumeSize,

 xBrickSize, yBrickSize, zBrickSize,

  voPartialBrickTypeScope ::TRUNCATE,

 1,

 internalFormat,

 externalFormat,

 dataType,

 interpolationType);

Handles errors.

 

 

if ( voError ::getErrorNumber() !=
voErrorTypeScope ::NO_ERROR) {

 fprintf(stderr, “%s”, voError ::getErrorString());

 exit(1);

}

 

Allocate Memory for Bricks

 

 

Uses aVolume as the brick set collection to iterate through.

 

voBrickSetCollectionIterator  collectionIter(aVolume);

Iterates over all of the brick sets within the collection of brick sets.

 

 

for ( voBrickSet * brickSet; brickSet =

collectionIter();) {

Iterates over all of the bricks within each brick set.

 

 

 

voBrickSetIterator brickSetIter(brickSet);

for ( voBrick * brick;brick = brickSetIter();)

Inside the double for loop, this line allocates memory for all of the bricks in all of the bricksets.

 

 

voAppearanceActions ::dataAlloc(brick);

}

Get the brick set of aVolume and then iterate over all the bricks in the brick set. brick is a pointer to a brickset.

 

aVolume->setCurrentBrickSet(

voPlaneOrientationScope ::XY);

voBrickSetIterator brickSetIter(aVolume->getCurrentBrickSet());

for (voBrick * brick; brick = brickSetIter();) {

int xBrickOrigin, yBrickOrigin, zBrickOrigin;

int xBrickSize, yBrickSize, zBrickSize;

void *vdata = brick->getDataPtr();

Gets brick sizes and locations; the brick sizes might be different from those requested.

 

 

brick->getBrickSizes(

xBrickOrigin, yBrickOrigin, zBrickOrigin,

xBrickSize, yBrickSize, zBrickSize);

 

Load the Brick Data

 

 

Reads in the brick data from disk. It is okay to use the brick's data area as a temporary buffer.

 

 

myReadBrickIfl(dataFileName, vdata,

xBrickOrigin, yBrickOrigin, zBrickOrigin,

xBrickSize, yBrickSize, zBrickSize,

xVolumeSize, yVolumeSize, zVolumeSize);

Replicate to the desired external format

 

 

voAppearanceActions ::dataConvert(

brick, vdata, diskDataFormat);

Converts geometry to the desired external format. Scales the voxel values within a brick from a specified dynamic range to the following standard ranges: (<0,255> for ubyte, <0,65535> for ushort, and <0.0,1.0> for floats).

 

 

voAppearanceActions ::dataScaleRange(brick, loValue, hiValue);

}

 

If the texture is sampled using bilinear interpolation, transpose the volume.

 

 

if (aVolume->getInterpolationType() == voInterpolationTypeScope ::_2D)

  voAppearanceActions ::volumeMakeTransposed

 (aVolume);

Determines the largest number of bricks in a brick set, in the brick set collection. This number is used to allocate the buffers.

 

 

voBrickSetCollectionIterator aCollectionIterator

(aVolume);

voBrickSet *aBrickSet;

for (int j1 = 0; aBrickSet = aCollectionIterator();

j1++)

Revise the value for maxBrickCount if the number of bricks in the brickset exceeds the current value for maxBrickCount, which was initialized as 256.

 

if (j1 == 0 || aBrickSet->getBrickCount() > maxBrickCount)

maxBrickCount = aBrickSet->getBrickCount();

Set the maximum number of sampling surfaces to twice the largest dimension.

 

 

maxSamplesNumber = xVolumeSize;

if (maxSamplesNumber < yVolumeSize)

maxSamplesNumber = yVolumeSize;

if (maxSamplesNumber < zVolumeSize)

maxSamplesNumber = zVolumeSize;

maxSamplesNumber *= 2;

return aVolume;

}

 

Create the Containers to Hold the Faces

 

 

Procedure to create a geometry.

 

 

voIndexedTetraSet *

my_InitGeometry(
voBrickSetCollection * aVolume)

{

voError::setErrorHandler(my_ExceptionHandler);

int xVolumeSize, yVolumeSize, zVolumeSize;

aVolume->getCurrentBrickSet()->getVolumeSizes(

xVolumeSize, yVolumeSize, zVolumeSize);

Set vertex coordinates. Texgen will be used to generate tex coords.

 

 

 

static float vtxData[8][3] = {

0, 0, 0,

xVolumeSize, 0, 0,

xVolumeSize, yVolumeSize, 0,

0, yVolumeSize, 0,

0, 0, zVolumeSize,

xVolumeSize, 0, zVolumeSize,

xVolumeSize, yVolumeSize, zVolumeSize,

0, yVolumeSize, zVolumeSize,

};

Defines the geometry to be drawn. Each row defines one tetrahedron, five of which define a cube. Values index into vtxData[].

Constructs the tetra set.

 

 

 

static int cubeIndices[] =

 {

0, 2, 5, 7,

3, 2, 0, 7,

1, 2, 5, 0,

2, 7, 6, 5,

5, 4, 0, 7,

 };

int tetraCount =

sizeof(cubeIndices) / (VO_TETRA_VERTICES * sizeof(cubeIndices[0]));

int valuesPerVtx = sizeof(vtxData[0]) / sizeof(float);

Allocates storage for transient polygons; note that all the polygons share vertex data area allVertexData[] to minimize fragmentation boundFaceCount() determines the maximum number of indices required to store a polygon (11).

Sizeof refers to the number of vertices.

 

 

 

  voIndexedTetraSet *aTetraSet = new voIndexedTetraSet (

(float *) vtxData,

8,

valuesPerVtx,

cubeIndices,

20);

Holds all intermediate PER_VERTEX information.

 

 

 allVertexData = new voVertexData (100000, valuesPerVtx);

aPolygonSetArray = new voIndexedFaceSetPtrPtr

[maxBrickCount];

for (int j1 = 0; j1 < maxBrickCount; j1++) {

aPolygonSetArray[j1] = new voIndexedFaceSetPtr [maxSamplesNumber + 1];

for (int j2 = 0; j2 < maxSamplesNumber + 1; j2++)

 aPolygonSetArray[j1][j2] = new

voIndexedFaceSet (allVertexData, boundFaceCount(tetraCount));

}

return aTetraSet;

}

 

Set Drawing Parameters

 

 

Initializes graphics.

 

 

void my_InitGfx( voIndexedTetraSet *, voBrickSetCollection * aVolume)

{

Optimizes based on performance.

 

 

voAppearanceActions ::volumeOptimize(aVolume,

 voOptimizeVolumeTypeScope::BEST_PERFORMANCE);

}

 

Draw the Volume

 

 

Defines draw method.

 

 

void my_DrawVolume( voIndexedTetraSet * aTetraSet, voBrickSetCollection * aVolume)

{

Chooses wireframe mode.

 

 

 GLboolean wireframe = GL_TRUE;

Type definitions of the number of bricks in the geometry and the modelview and projection matrices.

 

 

int brickNo;

GLdouble modelMatrix[16], projMatrix[16];

Returns the modelview matrix, which is the cumulative product of multiplying viewing and modeling transformation matrices.

 

 

glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix);

Returns the projection matrix, which is the viewing frustum.

 

 

glGetDoublev(GL_PROJECTION_MATRIX, projMatrix);

If using 2D textures, select an appropriate BrickSet from among XY,XZ,YZ. Note, that individual brick sets may have differrent dimensions and thus change certain PER_FRAME properites, for example, BrickCount.

 

if (aVolume->getInterpolationType() == voInterpolationTypeScope::_2D

 aVolume->setCurrentBrickSet(

  voGeometryActions::findClosestAxisIndex (

 modelMatrix, projMatrix, voSamplingModeScope::AXIS_ALIGNED));

int BrickCount = aVolume->getCurrentBrickSet()->

getBrickCount();

Disables texture and draws opaque (embedded) geometry.

 

 

voAppearanceActions ::textureDisable(

 aVolume->getInterpolationType());

Draw tetrahedron wireframes in yellow.

 

 

if (wireframe == GL_TRUE) {

glColor4f(1.0, 1.0, 0.0, 1.0);

glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

voGeometryActions::draw (aTetraSet, interleavedArrayFormat);

glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

Draw brick outlines in blue, but only if bricks are 3D.

 

 

if (aVolume->getInterpolationType() == voInterpolationTypeScope ::_3D) {

glColor4f(0.0, 0.0, 1.0, 1.0);

for (brickNo=0;brickNo<BrickCount; brickNo++)

voGeometryActions::draw (

aVolume->getCurrentBrickSet()->getBrick(

brickNo));

}

glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

}

 

Polygonize the Shape

 

 

Given a aTetraSet, bricks' dimensions, and count, sampling mode and rate, find all slicing polygons and clip them to brick boundaries.

 

 

 

 

 

 

 

If aVolume is three-dimensional, make the face sets viewport aligned; otherwise make them sampling-axis aligned.

 

 

Specify the number of faces in the face set, call the face set aPolygonSetArray.

 

int samplesNumber;

float samplingPeriod[3] = {

samplingRate / modelScale[0],

samplingRate / modelScale[1],

samplingRate / modelScale[2] };

voGeometryActions::polygonize (

aTetraSet, aVolume->getCurrentBrickSet(),

interleavedArrayFormat,

modelMatrix, projMatrix,

aVolume->getInterpolationType() ==

voInterpolationTypeScope ::_3D ?

voSamplingModeScope ::VIEWPORT_ALIGNED :

voSamplingModeScope ::AXIS_ALIGNED,

voSamplingSpaceScope ::OBJECT,samplingPeriod,

maxSamplesNumber,

samplesNumber,aPolygonSetArray);

If not using texgen(), transform texture coords explicitly.

 

if (hasTextureComponent(interleavedArrayFormat))

voAppearanceActions ::xfmVox2TexCoords(

aVolume->getCurrentBrickSet(), samplesNumber,aPolygonSetArray,

interleavedArrayFormat);

Visiblity sort the bricks.

 

End of polygonize.

 

 

voSortAction aSortAction(

aVolume->getCurrentBrickSet(),

modelMatrix, projMatrix);

 

Draw the Shape

 

 

Enable 2D or 3D texturing depending on the interpolation type.

 

 

voAppearanceActions ::textureEnable(

aVolume->getInterpolationType());

If no explicit texture coordinates were given, enable texgen().

 

if(!hasTextureComponent(interleavedArrayFormat))

voAppearanceActions ::texgenEnable();

Set up drawing mode and color

 

 

glEnable(GL_BLEND);

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

glColor4f(1.0, 1.0, 1.0, 1.0);

glDepthMask(GL_FALSE);

Iterate over all bricks and sort them back to front.

 

 

for (brickNo = 0; brickNo < BrickCount; brickNo++) {

int brickSortedNo = aSortAction[brickNo];

voBrick *aBrick = aVolume->

getCurrentBrickSet()->

getBrick(brickSortedNo);

Update texgen equation for the current brick.

 

 

if (!hasTextureComponent(

 interleavedArrayFormat))

  voAppearanceActions ::texgenSetEquation(

aBrick);

Load the texture to texture memory unless it is already there.

 

 

voAppearanceActions ::textureBind(aBrick);

Iterate over all sampling planes and draw them.

 

 

for (int binNo=0; binNo < samplesNumber; binNo++) {

voGeometryActions::draw (

aPolygonSetArray[brickSortedNo][binNo],

interleavedArrayFormat);

}

}

Disable the texture and disable the texture generation.

 

 

  voAppearanceActions ::textureDisable(

aVolume->getInterpolationType());

voAppearanceActions ::texgenDisable();

glDepthMask(GL_TRUE);

} // end of procedure my_DrawVolume

 

Clean Up

 

 

Deletes geometry and textures.

 

 

void my_Cleanup( voIndexedTetraSet *aTetraSet, voBrickSetCollection *aVolume)

{

Frees storage for transient geometry.

 

 

for (int j1 = 0; j1 < maxBrickCount; j1++) {

for (int j2 = 0; j2 < maxSamplesNumber + 1; j2++)

delete aPolygonSetArray[j1][j2];

delete aPolygonSetArray[j1];

}

delete[]aPolygonSetArray;

delete allVertexData;

Frees all voxel data storage.

 

 

  voAppearanceActions::volumeUnoptimize (aVolume);

voBrickSetCollectionIterator

collectionIter(aVolume);

for ( voBrickSet * brickSet; brickSet =

collectionIter();) {

 

Iterate over all bricks within the brick collection and release the memory used for the bricks.

 

 

voBrickSetIterator brickSetIter(brickSet);

for( voBrick * brick; brick=brickSetIter();)

voAppearanceActions ::dataFree(brick);

}

Delete volumetric geometry.

 

 

delete aVolume;

delete aTetraSet;

} // end of my_Cleanup


glwSimpleMain.cxx

glwSimpleMain.cxx contains a simple, trackball user interface, global declarations, and the main loop. The structure of the main loop follows the Motif programming model.

The programatic structure of glwSimpleMain.cxx is:

  1. Inclusions

  2. Global State Declarations

  3. GUI Definitions

  4. Handling Input From a Trackball

  5. Main Routine

    Inclusions

     

     

    X, OpenGL, and standard C++ includes.

     

     

    #include <X11/X.h>

    #include <X11/Intrinsic.h>

    #include <X11/keysym.h>

    #include <Xm/Xm.h>

    #include <GL/gl.h>

    #include <GL/GLwMDrawA.h>

    #include <iostream.h>

    #include <stdio.h>

    #include <stdlib.h>

    Include statements for Volumizer actions.

     

     

    #include < vo /AppearanceActions.h>

    #include < vo /GeometryActions.h>

     

    Global State Declarations

     

     

    Declare a volume's geometry.

     

     

    voIndexedTetraSet *aTetraSet;

    Declare a volume's appearance.

     

     

    voBrickSetCollection *aVolume;

    The coordinates for the center of the cube, the dimensions of its sides, and the scaling factor in three dimensions.

     

     

    float modelCentroid[3] = { 64.0, 64.0, 32.0};

    float modelSize[3] = {128.0, 128.0, 128.0};

    float modelScale[3] = { 1.8, 1.8, 3.0 };

     

    GUI Definitions

     

     

    Type declarations.

     

     

    float latitude = 0, longitude = 0,

    lastLatitude = 0, lastLongitude = 0;

    int lastX = 0, lastY = 0, lastButton = Button1;

    Widget drawArea;

    GLXContext glCtxt;

    External symbols for appearance.

     

     

    extern voBrickSetCollection *my_InitAppearance(

    int argc, char **argv)

    External symbols for geometry.

     

     

    extern voIndexedTetraSet *my_InitGeometry(

      voBrickSetCollection *);

    External symbols for graphics intialization. X and Motif functions are declared as external C functions because the C++ preprocessor mangles C++ function names.

     

     

     

    extern void my_InitGfx( voIndexedTetraSet *, voBrickSetCollection *);

    External symbols for draw action.

     

     

     

    extern void my_DrawVolume( voIndexedTetraSet *, voBrickSetCollection *);

    External symbols for deleting geometry and appearance.

     

     

    extern void my_Cleanup( voIndexedTetraSet *, voBrickSetCollection *);

    Intialize internal data structures, including the visual data structure, XVisualInfo.

     

     

     

    void GinitCB(Widget w, XtPointer , XtPointer )

    {

    Arg arg;

    XVisualInfo *vip;

    XtSetArg(arg, GLwNvisualInfo, &vip);

    XtGetValues(w, &arg, 1);

    glCtxt = glXCreateContext(XtDisplay(w), vip, NULL, GL_TRUE);

    glXMakeCurrent(XtDisplay(w), XtWindow(w), glCtxt);

    my_InitGfx(aTetraSet, aVolume);

    }

    Draw method begins by clearing the screen to black.

     

     

    void Draw()

    {

    glEnable(GL_DEPTH_TEST);

    glClearColor(0.0, 0.0, 0.0, 1.0);

    OpenGL methods to clear the buffer then load it with prescribed rotation, scaling, and translation values.

     

     

    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

    glLoadIdentity();

    glRotated(-latitude-90, 1.0, 0.0, 0.0);

    glRotated(-longitude, 0.0, 0.0, 1.0);

    glScalef(modelScale[0], modelScale[1],

    modelScale[2]);

    glTranslatef(-modelCentroid[0], -modelCentroid[1], -modelCentroid[2]);

    Swap buffers to display the loaded image.

     

     

    my_Draw(aTetraSet, aVolume);

    GLwDrawingAreaSwapBuffers(drawArea);

    }

    Callback for X Window System “expose” event.

     

     

    void ExposeCB(Widget w, XtPointer , XtPointer cal)

    {

    GLwDrawingAreaCallbackStruct *csp = (GLwDrawingAreaCallbackStruct *) cal;

    Makes glCtxt the graphics context and attaches it to an X window.

     

    glXMakeCurrent(XtDisplay(w), XtWindow(w), glCtxt);

    Specifies that the current matrix pertains to the projection

    view.

     

     

    glMatrixMode(GL_PROJECTION);

    Initializes the current projection matrix so that it is unaffected by pre

    vious matrix values.

     

     

     

    glLoadIdentity();

    Specifies the transformation between normalized-de

    vice and window coordinates.

     

     

    glViewport(0, 0, csp->width, csp->height);

    glOrtho(

    -modelSize[0] * 1.2, modelSize[0] * 1.2,

    -modelSize[1] * 1.2, modelSize[1] * 1.2,

    -modelSize[2] * 2.5, modelSize[2] * 2.5);

    Specifies that the current matrix and all future transformations pertains to the model

    view.

     

    Then do the actual draw.

     

    glMatrixMode(GL_MODELVIEW);

     Draw();

    }

    Handling keyboard input. The Escape key terminates the application and deletes the geometry and textures used in the application.

     

     

    void KeybdInput(XKeyEvent * keyEvent)

    {

    KeySym keySym;

    char buf[32];

    XLookupString(keyEvent, buf, 32, &keySym, NULL);

    switch (keySym) {

    case XK_Escape:

    my_Cleanup(aTetraSet,aVolume);

    exit(0);

    }

    }

    Handles button input for a virtual trackball:

     

    This code temporarily stores, in lastButton, lastLatitude and lastLongitude, which button was pushed and the location of the trackball cursor when the button was pushed.

     

     

    void ButtonInput(XButtonEvent * btnEvent)

    {

    switch (btnEvent->button) {

    case Button1:

    lastButton = Button1;

    lastLatitude = latitude;

    lastLongitude = longitude;

    break;

    }

    lastX = btnEvent->x;

    lastY = btnEvent->y;

    }

    Uses the ButtonInput() data to move the shape accordingly. The left button rotates the shape. The movement is scaled by one-tenth.

     

    void MotionInput(XMotionEvent * motionEvent)

    {

    switch (lastButton) {

    case Button1:

    latitude = (float) (lastLatitude + (lastY - motionEvent->y) / 10.0);

    longitude = (float) (lastLongitude + (lastX - motionEvent->x) / 10.0);

    break;

    }

    }

    Callback function for trackball. Handles trackball button press, trackball motion, trackball button release, keyboard input, and notification.

     

     

     

    void InputCB(Widget w, XtPointer, XtPointer callData)

    {

    XmDrawingAreaCallbackStruct *dacs = (XmDrawingAreaCallbackStruct *) callData;

    glXMakeCurrent(XtDisplay(w), XtWindow(w),glCtxt);

    switch (dacs->event->type) {

    case ButtonPress:

    ButtonInput((XButtonEvent *) dacs->event);

    break;

    case MotionNotify:

    MotionInput((XMotionEvent *) dacs->event);

    break;

    case KeyRelease:

    KeybdInput((XKeyEvent *) dacs->event);

    return;

    case ButtonRelease:

    return;

    default:

    break;

    Draw();

    }

     

    Main Routine

     

     

    Handles command line arguments and prints out basic information: version number and mouse button functionality.

     

     

    void main(int argc, char **argv)

    {

    fprintf(stderr,”%s\n”, voVersion ());

    fprintf(stderr,”\nLeft mouse button to rotate.\n”);

    Creates a geometry and an appearance.

     

     

    aVolume = my_InitAppearance(argc, argv);

    aTetraSet = my_InitGeometry(aVolume);

    Type and value declarations.

     

     

    Widget toplevel;

    XtAppContext appCtxt;

    XVisualInfo *visual;

    Cardinal n1 = 0;

    Arg args[4];

    Elements of the attribute group set the graphics state.

     

     

    GLint attribs[] =

    {GLX_RGBA, GLX_DOUBLEBUFFER, GLX_RED_SIZE, 1,

    GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, GLX_DEPTH_SIZE, 1, None};

    Sets up display.

     

     

    toplevel = XtAppInitialize(&appCtxt,

    “Lean”, NULL, 0, &argc, argv, NULL, NULL, 0);

    XtSetArg(args[n1], XmNwidth, 400); n1++;

    XtSetArg(args[n1], XmNheight, 400); n1++;

    XtSetArg(args[n1], GLwNattribList, attribs);  n1++;

    drawArea = GLwCreateMDrawingArea(toplevel, “pb”,

    args, n1);

    visual is a pointer to an XVisualInfo structure, which uses the display, toplevel, and the graphics state, attribs. An error message is printed if the pointer is NULL.

     

    visual = glXChooseVisual(XtDisplay(toplevel), 0, attribs);

    if (!visual)

    fprintf(stderr, “Bad visual\n”);

    Callback registration. Callbacks take action upon user input.

     

     

    XtAddCallback(drawArea, GLwNinputCallback, InputCB, NULL);

    XtAddCallback(drawArea, GLwNginitCallback, GinitCB, NULL);

    XtAddCallback(drawArea, GLwNexposeCallback, ExposeCB, NULL);

    Create a hierarchy of shapes to group shapes into a logical collection.

     

     

    XtManageChild(drawArea);

    Creates an X window on the screen in which the shapes are displayed.

     

     

     

    XtRealizeWidget(toplevel);

    Enter event loop to handle user input.

     

     

    XtAppMainLoop(appCtxt);

    }