[BACK]Return to simpleASD.C CVS log [TXT][DIR] Up to [Development] / performer / src / pguide / libpf / C++

File: [Development] / performer / src / pguide / libpf / C++ / simpleASD.C (download)

Revision 1.1, Tue Nov 21 21:39:38 2000 UTC (16 years, 11 months ago) by flynnt
Branch: MAIN
CVS Tags: HEAD

Initial check-in based on OpenGL Performer 2.4 tree.
-flynnt

//
// Copyright 1997, Silicon Graphics, Inc.
// ALL RIGHTS RESERVED
//
//
// This source code ("Source Code") was originally derived from a
// code base owned by Silicon Graphics, Inc. ("SGI")
// 
// LICENSE: SGI grants the user ("Licensee") permission to reproduce,
// distribute, and create derivative works from this Source Code,
// provided that: (1) the user reproduces this entire notice within
// both source and binary format redistributions and any accompanying
// materials such as documentation in printed or electronic format;
// (2) the Source Code is not to be used, or ported or modified for
// use, except in conjunction with OpenGL Performer; and (3) the
// names of Silicon Graphics, Inc.  and SGI may not be used in any
// advertising or publicity relating to the Source Code without the
// prior written permission of SGI.  No further license or permission
// may be inferred or deemed or construed to exist with regard to the
// Source Code or the code base of which it forms a part. All rights
// not expressly granted are reserved.
// 
// This Source Code is provided to Licensee AS IS, without any
// warranty of any kind, either express, implied, or statutory,
// including, but not limited to, any warranty that the Source Code
// will conform to specifications, any implied warranties of
// merchantability, fitness for a particular purpose, and freedom
// from infringement, and any warranty that the documentation will
// conform to the program, or any warranty that the Source Code will
// be error free.
// 
// IN NO EVENT WILL SGI BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT
// LIMITED TO DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES,
// ARISING OUT OF, RESULTING FROM, OR IN ANY WAY CONNECTED WITH THE
// SOURCE CODE, WHETHER OR NOT BASED UPON WARRANTY, CONTRACT, TORT OR
// OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
// PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM,
// OR AROSE OUT OF USE OR RESULTS FROM USE OF, OR LACK OF ABILITY TO
// USE, THE SOURCE CODE.
// 
// Contact information:  Silicon Graphics, Inc., 
// 1600 Amphitheatre Pkwy, Mountain View, CA  94043, 
// or:  http://www.sgi.com
//
// simpleASD.C
// --------------
//
//  Simple Performer demo of basic ASD functionality
//
//  simpleASD [-T]
//
//	-T: Use texturing
//
//
//  $Revision: 1.1 $ 
//  $Date: 2000/11/21 21:39:38 $
//
//

/******************************************************************************
*				Includes
******************************************************************************
*/

// general includes
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <iostream.h>

// proecess control includes
#include <sys/types.h>
#include <sys/prctl.h>
#ifndef __linux__
#include <sys/sysmp.h>
#include <sys/schedctl.h>
#endif

// X Window includes
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>

// GL includes


// Performer includes
#include <Performer/pf.h>
#include <Performer/pf/pfChannel.h>
#include <Performer/pf/pfDCS.h>
#include <Performer/pf/pfLightSource.h>
#include <Performer/pf/pfGeode.h>
#include <Performer/pr/pfGeoSet.h>
#include <Performer/pf/pfScene.h>
#include <Performer/pr/pfMemory.h>
#include <Performer/pr/pfMaterial.h>
#include <Performer/pr/pfHighlight.h>

#include <Performer/pfutil.h>
#include <Performer/pfdu.h>
#include <Performer/pfui/pfiXformer.h>
#include <Performer/pfui.h>
#include <Performer/pfui/pfiPick.h>

#include <Performer/pf/pfASD.h>

// ---------------------------------------------------- //
//		Defines and Typedefs                    //
// ---------------------------------------------------- //

// Panel Widget Control Tokens
enum PanelControlToken {
    CTL_RESET_ALL=1,
    CTL_REPOSITION,
    CTL_QUIT,
    CTL_MODEL,
    CTL_DRAWSTYLE,
    CTL_EVAL_METHOD,  
    CTL_DIAGS,
    CTL_TREE,
    CTL_LIGHTING,
    CTL_DRAW_NORMS,    
    CTL_ASD_DIST,
    CTL_MORPH_BUFFER, 
    CTL_MORPH_COORDS, 
    CTL_MORPH_NORMS, 
    CTL_MORPH_COLORS     
    };

    // --- Structs for ASD attribute data --- //
typedef struct normalAttr
{
    pfVec3 normal;
    pfVec3 normalDiff;	    // The difference between the vector that is normal to the 
			    // vertex and the vector that is normal to the reference point
} normalAttr;

typedef struct colorAttr
{
    pfVec4 color;
    pfVec4 colorDiff;	    // The difference between the color to the 
			    // color of the reference point
} colorAttr;

typedef struct ASDAttr
{
    normalAttr normal;
    colorAttr color;  
} ASDattr;

// SharedData:
// structure that resides in shared memory so that the
// APP, CULL, and DRAW processes can access it.
//
typedef struct SharedData
{
	// --- main channel stuff --- //
    pfChannel		*chan;
    int			drawStats;	// Should I draw stats
    int			exitFlag;	// Set when it is time to exit
    pfLightSource	*sun;		// sun light source
    int			drawTree;	// Should I draw the tree
    int			drawStyle;	// What special draw style are we using
    
	// --- Input handling structures --- //
    int			input;		// PFUINPUT_GL or PFUINPUT_X
    pfuMouse		mouse;		// mouse structure
    pfuEventStream	events;		// event structure
    
	// --- flight model and collision detection sructures --- //
    pfScene		*scene;
    pfGeoState*		globalSceneState;   // Global Scene state
    pfDCS		*sceneDCS;	// DCS for trackball interface
    pfCoord		viewCoord;	// current view position, direction
    pfCoord		initView;	// initial view position, direction
    float		near;		// near clipping plane
    float		far;		// far clipping plane
    pfiXformer		*xformer;	// interactive movement model
    int			xformerModel;	// Fly, Drive, or Trackball
    int			collideMode;	// collision & terrain following flag
    
	// --- GUI Flags --- //
    int			updateChannels;
    int			gui, redraw, guiDirty;
    
	// --- handles to the actual GUI objects --- //
    pfuPanel		*guiPanel;
    pfuWidget		*wResetAll, *wKillGUI, *wPosXYZ, *wStats, *wDrawStyle, *wTree, *wLighting, 
			*wFModel, *wEvalMethod, *wASDDist, *wMorphBuffer, *wMorphCoords, *wMorphNorms,
			*wDrawNorms, *wMorphColors;
    
	// --- ASD Data Structure --- //
    pfASD*	theASDNode;	    // Our ASD node
    int		geometryType;	    // Use orignal or textured geometry
    pfGeoState** geoStateArray;	    // An array of geo states that the ASD uses
    pfASDVert*	verts;		    // List of vertices for the ASD
    pfASDFace*	faces;		    // List of the faces used by the ASD
    pfASDLODRange* lods;		    // The LOD list for the ASD
    
//    normalAttr* normalAttrs;	    // ASD attributes for the normals
//    colorAttr* colorAttrs;	    // ASD attributes for the colors
    ASDAttr*	attrs;		    // All the ASD attributes
    
	// --- Normals stuff --- //
    pfHighlight*    normalHighlight;	// A highlight mode to draw normals	
    int		    drawNormals;		// Are we supposed to be drawing normals
    
    pfVec4  baseColor;		    // The "regular" color of the ASD object
    pfVec4  morphedColor;	    // The color that the morphed vertices are set to
	
} SharedData;



/******************************************************************************
*				Static Declarations
******************************************************************************
*/

// shared memory handle
static SharedData *Shared=0;


// X control variables
static int XInput = PFUINPUT_X;

static int WinXOrigin = 100, WinYOrigin = 100;
static int WinXSize = 800, WinYSize = 600;

// process control variables
static int ProcSplit =  PFMP_APPCULLDRAW;


static void CullChannel(pfChannel *chan, void *data);
static void DrawChannel(pfChannel *chan, void *data);
static void OpenPipeWin(pfPipeWindow *pw);
static pfuPanel *InitPanel(void);
static void ProcessInputIO(void);
static void PanelControl(pfuWidget *w);
static void resetPosition(void);
static void KillGUI(pfuWidget *w);
static void InitXformer(void);
static void IsectFunc(void *data);
static void UpdateView(pfChannel *chan, pfScene *scene);
static void updateGUIViewMsg(pfCoord *viewpt);
static void xformerModel(int model, int collideMode);


    // -- ASD Creation Routine -- //
pfASD* createASDNode(int whichGeometry);
    const int OrigGeom = 0;
    const int TexGeom = 1;
    
void buildGState(pfGeoState *gs);
void buildTexGState(pfGeoState *gs, char* texname);
pfMaterial* defaultMaterial();
void switchASDLighting();
void switchMorphAttrs();

void setNewSwitchingValue(float newValue);
void setMorphBufferValue(float newValue);

pfHighlight* createNormalHighlight();

//
//	Main and Commandline Processing
//

char ProgName[PF_MAXSTRING];
char OptionStr[] = "Tt?";


//
//	Usage() -- print usage advice and exit. This procedure
//	is executed in the application process.


static void
Usage (void)
{
    fprintf(stderr, 
	    "Usage: %s [-T]\n", ProgName);
    fprintf(stderr, "\t-T: Uses texture geometry.");	    
    exit(1);
}

//
//	docmdline() -- use getopt to get command-line arguments, 
//	executed at the start of the application process.


static int
docmdline(int argc, char *argv[])
{
    int	    opt;
    
    strcpy(ProgName, argv[0]);
    
    // process command-line arguments 
    while ((opt = getopt(argc, argv, OptionStr)) != -1)
    {
	switch (opt)
	{
	case 't': // Use Texturing
	case 'T': 
	    Shared->geometryType = TexGeom;
	    break;
	case '?': // get usage 
	    Usage();
	}
    }
    return optind;
}


//
//	main() -- program entry point. this procedure
//	is executed in the application process.
//

int
main (int argc, char *argv[])
{
    pfNotifyLevel(PFNFY_DEBUG);	    // Set Error handling level

    pfInit();
    pfuInit();
    pfiInit();

    pfFilePath(".:/usr/share/Performer/data");

 
    pfMultiprocess(ProcSplit);
    
 
	// allocate shared before fork()'ing parallel processes
	// so that all processes have will the correct Shared address
	//
    Shared = (SharedData*)pfCalloc(1, sizeof(SharedData), pfGetSharedArena());
    
	// init shared memory structures
    Shared->xformerModel	= PFITDF_TRACKBALL;
    Shared->input		= XInput;
    Shared->collideMode		= PF_ON;
    Shared->gui			= 1; // enable gui
    Shared->drawTree		= 0;	// Don't start drawing the tree
    Shared->updateChannels	= 1;
    Shared->guiDirty = Shared->redraw = 1;
    Shared->near = 0.01f;
    Shared->far = 1000.0f;
    Shared->geometryType = OrigGeom;	// Set to a default
    
//    Shared->baseColor.set(0.15f, 0.8f, 0.87f, 0.0f);
//    Shared->morphedColor.set(0.87f, 0.0f, 0.0f, 0.0f);
    Shared->baseColor.set(0.0f, 0.0f, 0.9f, 0.0f);
    Shared->morphedColor.set(0.0f, 0.9f, 0.0f, 0.0f);
//    Shared->baseColor.set(1.0f, 1.0f, 1.0f, 1.0f);
//    Shared->morphedColor.set(1.0f, 0.0f, 0.0f, 1.0f);

    
    	// ----- parse commandline ----- //
    docmdline(argc, argv);


	// initiate multi-processing mode set in pfMultiprocess call
	// FORKS HAPPEN HERE !!!
    pfConfig();

    
    // Initialize input structure (X or GL) for mouse and event inputs
    // then Open and configure full screen GL or GLX window.
    //
    
    // Configure window for the pipe
    pfPipe *p = pfGetPipe(0);
    pfPipeWindow *pw = new pfPipeWindow(p);
    pw->setName("simpleASD");
    pw->setOriginSize(WinXOrigin, WinYOrigin,
		      WinXOrigin +  WinXSize, WinYOrigin +  WinYSize);
    if (Shared->input == PFUINPUT_X)
	pw->setWinType(PFPWIN_TYPE_X);
    
    pw->setConfigFunc(OpenPipeWin);
    pw->config();
    
    pfFrame();
    
    pfuInitGUI(pw); 
    pfuInitInput(pw, Shared->input);
    
    //
    // App Process Setup
    //
    
    pfScene *scene = new pfScene;	    // Declare pfScene Object
    Shared->scene = scene;
    Shared->sceneDCS = new pfDCS;	    // Declare a sceneDCS
    scene->addChild(Shared->sceneDCS);	    // Add the DCS to the top of tree
    
	// --- Add the ASD Data to the scene --- //
    Shared->theASDNode = createASDNode(Shared->geometryType);      // Create and return the ASD
    Shared->sceneDCS->addChild(Shared->theASDNode);

    Shared->normalHighlight = createNormalHighlight();
    pfuTravNodeHlight(Shared->scene, Shared->normalHighlight); 
	    
    // Set intersection callback.
    pfIsectFunc(IsectFunc);
    // do intersection setup
    pfuCollideSetup(scene, PFUCOLLIDE_STATIC, 0xffffffff);
    
    
    // Create pfChannels and assign draw callback functions.
    // Channels will automatically be assigned to the first created window
    // on the pfPipe.
    //
    
    pfChannel *chan = new pfChannel(p);
    Shared->chan = chan;
    
    chan->setTravFunc(PFTRAV_CULL, CullChannel);
    chan->setTravFunc(PFTRAV_DRAW, DrawChannel);
    chan->setScene(scene);
    chan->setNearFar(Shared->near, Shared->far);
    
	// vertical FOV is matched to window aspect ratio
    chan->setFOV(45.0f, -1.0f);
    
    // Initialize sun - create a light source in the "south-west" (QIII)
    Shared->sun = new pfLightSource;
    Shared->sun->setPos(-0.3f, -0.3f, 1.0f, 0.0f);
    Shared->scene->addChild(Shared->sun);
    
    pfFrameStats *stats = chan->getFStats();
    stats->setClass(PFSTATS_ENGFX, PF_ON);
    
    
	// --- Just set up a very basic view --- //
    pfVec3 zeros(0.0f, 0.0f, 0.0f);
    chan->setView(zeros, zeros);
    
       
    Shared->chan->setViewport(0.0f, 1.0f, 0.25f, 1.0f);
    pfuGUIViewport(0.0f, 1.0f, 0.0f, 0.25f);
    // init the control panel for perfly
    Shared->guiPanel = InitPanel();
    pfuEnablePanel(Shared->guiPanel);
    
    // Initialize channel viewing model for interactive motion
    InitXformer();
    
    pfEnable(PFEN_TEXTURE);
    
    // main simulation loop
    while (!Shared->exitFlag)
    {
	    // wait until next frame boundary
	pfSync();
	
	    //
	    // All latency critical processing should be done here. This is
	    // typically the viewing position.
	    // One should also read latency critical devices here.
	    //
	pfuGetMouse(&Shared->mouse);	    // Copy data into the mouse structure
	
	UpdateView(Shared->chan, Shared->scene);
	
        if (Shared->drawNormals)
	    pfuTravNodeHlight(Shared->scene, Shared->normalHighlight); 
	else
	    pfuTravNodeHlight(Shared->scene, NULL);
	    
	    // initiate traversal using current state
	pfFrame();
	
	    // Get snapshot of event/key queues
	pfuGetEvents(&Shared->events);
	ProcessInputIO();
	
	if (Shared->updateChannels)
	{
	    if (Shared->gui)
	    {
		float	foo, top;
		
		Shared->redraw = Shared->guiDirty = 1;
		pfuEnableGUI(TRUE);
		pfuGetGUIViewport(&foo, &foo, &foo, &top);
		Shared->chan->setViewport(0.0f, 1.0f, top, 1.0f);
	    }
	    else
	    {
		pfuEnableGUI(FALSE);
		Shared->chan->setViewport(0.0f, 1.0f, 0.0f, 1.0f);
	    }
	    Shared->guiDirty = Shared->updateChannels = 0;
	} else if (Shared->gui)
	{
	    updateGUIViewMsg(&(Shared->viewCoord));
	    pfuUpdateGUI(&(Shared->mouse));
	}
    }
    
    // --- EXIT THE PROGRAM --- //
    
	// exit GUI and print avg time to draw GUI - optional
    pfuExitGUI();
	// Exit from libpfutil and remove shared mem arenas
    pfuExitUtil();
    pfuExitInput();
    
	// terminate cull and draw processes (if they exist)
    pfExit();
    
	// exit to operating system
    return 0;
}

    // v0 is vertex, v1 and v2 are the other points on the triangle
pfVec3 vertNormal(pfVec3 v0, pfVec3 v1, pfVec3 v2)
{
    pfVec3 retval;
    retval.cross((v1-v0), (v2-v0));
    retval.normalize();
    return retval;
}

//-----------------------------------------------------------------
// createASDNode
//	
// PURPOSE:  Creates the ASD node and the Terrain Data structure
//	whichGeometry ==> 0  - Regular simple geometry (OrigGeom)
//		      ==> 1  - Geometry designed to show texture capabilities (TexGeom)
//-----------------------------------------------------------------
pfASD* createASDNode(int whichGeometry)
{
    const int numVerts = 9;
    const int numFaces = 10;
    pfASDVert* verts;	// A pfASDVert array. Contains all of the verts for the terrain
    
    verts = (pfASDVert*)pfCalloc(numVerts, sizeof(pfASDVert), pfGetSharedArena());
    Shared->verts = verts;
    
	// ------- Fill the verts array ------- //
    pfVec3 V1, V2, V3, V4, N1, N2, N3, N4, N5;
    
    V1.set(-2.0f, -1.0f, 0.0f); V2.set(-1.0f, 1.0f, 0.0f); V3.set(1.0f, -1.0f, 0.0f); V4.set(2.0f, 1.0f, 0.0f); 
    pfVec3 R1((V1+V2)/2), R2((V2+V3)/2), R3((V3+V1)/2), 
	       R4((V2+V4)/2), R5((V4+V3)/2);
	       	
    if(whichGeometry == OrigGeom) {
	N1.set(-3.0f, 0.0f, 1.0f); N2.set(0.0f, 0.0f, 1.0f); N3.set(-1.0f, -2.0f, 1.0f); 
	N4.set(1.0f, 2.0f, 1.0f); N5.set(3.0f, 0.0f, 1.0f);
    } else {
	N1 = R1; N1[0] -= 0.5f; N1[1] += 0.5f; N1[2] += 0.5f;
	N2 = R2; N2[0] -= 0.25f; N2[1] -= 0.25f; N2[2] -= 0.20f;
	N3 = R3; N3[0] -= 0.5f; N3[1] -= 0.5f; N3[2] += 0.5f;
	N4 = R4; N4[0] -= 0.0f; N4[1] += 0.1f; N4[2] += 0.0f;
	N5 = R5; N5[0] -= 0.0f; N5[1] -= 0.1f; N5[2] += 0.0f;
    }   
    		

    	   
	// Vertexes are entered in the array V1-V4,N1-N5
      // V1
    verts[0].v0 = V1;
    verts[0].vd.set(0.0f, 0.0f, 0.0f);	    // Set base to have zero diffs.
    verts[0].neighborid[0] = PFASD_NIL_ID;
    verts[0].neighborid[1] = PFASD_NIL_ID;
      // V2
    verts[1].v0 = V2;
    verts[1].vd.set(0.0f, 0.0f, 0.0f);
    verts[1].neighborid[0] = PFASD_NIL_ID;
    verts[1].neighborid[1] = PFASD_NIL_ID;
      // V3
    verts[2].v0 = V3;
    verts[2].vd.set(0.0f, 0.0f, 0.0f);
    verts[2].neighborid[0] = PFASD_NIL_ID;
    verts[2].neighborid[1] = PFASD_NIL_ID;
      // V4
    verts[3].v0 = V4;
    verts[3].vd.set(0.0f, 0.0f, 0.0f);
    verts[3].neighborid[0] = PFASD_NIL_ID;
    verts[3].neighborid[1] = PFASD_NIL_ID;
      // N1
    verts[4].v0 = N1;
    verts[4].vd.copy(R1-N1);
    verts[4].neighborid[0] = 0;
    verts[4].neighborid[1] = PFASD_NIL_ID;
      // N2
    verts[5].v0 = N2;
    verts[5].vd.copy(R2-N2);
    verts[5].neighborid[0] = 0;
    verts[5].neighborid[1] = 1;
      // N3
    verts[6].v0 = N3;
    verts[6].vd.copy(R3-N3);
    verts[6].neighborid[0] = 0;
    verts[6].neighborid[1] = PFASD_NIL_ID;
      // N4
    verts[7].v0 = N4;
    verts[7].vd.copy(R4-N4);
    verts[7].neighborid[0] = 1;
    verts[7].neighborid[1] = PFASD_NIL_ID;
      // N5
    verts[8].v0 = N5;
    verts[8].vd.copy(R5-N5);
    verts[8].neighborid[0] = 1;
    verts[8].neighborid[1] = PFASD_NIL_ID;
    
    
	
    
    
    pfASDFace* faces;	// A pfASDFae array. Contains all of the pfFaces for the terrain
    
    faces = (pfASDFace*)pfCalloc(numFaces, sizeof(pfASDFace), pfGetSharedArena());
    Shared->faces = faces;
	// ------- Fill the faces array ------- //
	    // --- LEVEL 0 --- //
	// 0
    faces[0].level = 0;
    faces[0].tsid = 0;
    faces[0].vert[0] = 0;  // V1
    faces[0].vert[1] = 1;  // V2
    faces[0].vert[2] = 2;  // V3
    faces[0].refvert[0] = 4; // N1
    faces[0].refvert[1] = 5; // N2
    faces[0].refvert[2] = 6; // N3
    faces[0].child[0] = 2;
    faces[0].child[1] = 3;
    faces[0].child[2] = 4;
    faces[0].child[3] = 5;
	// 1
    faces[1].level = 0;
    faces[1].tsid = 0;
    faces[1].vert[0] = 1;  // V2
    faces[1].vert[1] = 3;  // V4
    faces[1].vert[2] = 2;  // V3
    faces[1].refvert[0] = 7; // N4
    faces[1].refvert[1] = 8; // N5
    faces[1].refvert[2] = 5; // N2
    faces[1].child[0] = 6;
    faces[1].child[1] = 7;
    faces[1].child[2] = 8;
    faces[1].child[3] = 9;
    
    if(whichGeometry == OrigGeom) {
    
		// --- LEVEL 1 --- //
	    // 2
	faces[2].level = 1;
	faces[2].tsid = 0;
	faces[2].vert[0] = 0;  // V1
	faces[2].vert[1] = 4;  // N1
	faces[2].vert[2] = 6;  // N3
	faces[2].refvert[0] = PFASD_NIL_ID;
	faces[2].refvert[1] = PFASD_NIL_ID;
	faces[2].refvert[2] = PFASD_NIL_ID;
	faces[2].child[0] = PFASD_NIL_ID;
	faces[2].child[1] = PFASD_NIL_ID;
	faces[2].child[2] = PFASD_NIL_ID;
	faces[2].child[3] = PFASD_NIL_ID;
	    // 3
	faces[3].level = 1;
	faces[3].tsid = 0;
	faces[3].vert[0] = 4;  // N1
	faces[3].vert[1] = 1;  // V2
	faces[3].vert[2] = 5;  // N2
	faces[3].refvert[0] = PFASD_NIL_ID;
	faces[3].refvert[1] = PFASD_NIL_ID;
	faces[3].refvert[2] = PFASD_NIL_ID;
	faces[3].child[0] = PFASD_NIL_ID;
	faces[3].child[1] = PFASD_NIL_ID;
	faces[3].child[2] = PFASD_NIL_ID;
	faces[3].child[3] = PFASD_NIL_ID;
	    // 4
	faces[4].level = 1;
	faces[4].tsid = 0;
	faces[4].vert[0] = 4;  // N1
	faces[4].vert[1] = 5;  // N2
	faces[4].vert[2] = 6;  // N3
	faces[4].refvert[0] = PFASD_NIL_ID;
	faces[4].refvert[1] = PFASD_NIL_ID;
	faces[4].refvert[2] = PFASD_NIL_ID;
	faces[4].child[0] = PFASD_NIL_ID;
	faces[4].child[1] = PFASD_NIL_ID;
	faces[4].child[2] = PFASD_NIL_ID;
	faces[4].child[3] = PFASD_NIL_ID;
	    // 5
	faces[5].level = 1;
	faces[5].tsid = 0;
	faces[5].vert[0] = 6;  // N3
	faces[5].vert[1] = 5;  // N2
	faces[5].vert[2] = 2;  // V3
	faces[5].refvert[0] = PFASD_NIL_ID;
	faces[5].refvert[1] = PFASD_NIL_ID;
	faces[5].refvert[2] = PFASD_NIL_ID;
	faces[5].child[0] = PFASD_NIL_ID;
	faces[5].child[1] = PFASD_NIL_ID;
	faces[5].child[2] = PFASD_NIL_ID;
	faces[5].child[3] = PFASD_NIL_ID;
	    // 6
	faces[6].level = 1;
	faces[6].tsid = 0;
	faces[6].vert[0] = 1;  // V2
	faces[6].vert[1] = 7;  // N4
	faces[6].vert[2] = 5;  // N2
	faces[6].refvert[0] = PFASD_NIL_ID;
	faces[6].refvert[1] = PFASD_NIL_ID;
	faces[6].refvert[2] = PFASD_NIL_ID;
	faces[6].child[0] = PFASD_NIL_ID;
	faces[6].child[1] = PFASD_NIL_ID;
	faces[6].child[2] = PFASD_NIL_ID;
	faces[6].child[3] = PFASD_NIL_ID;
	
	    // 7
	faces[7].level = 1;
	faces[7].tsid = 0;
	faces[7].vert[0] = 5;  // N2
	faces[7].vert[1] = 7;  // N4
	faces[7].vert[2] = 8;  // N5
	faces[7].refvert[0] = PFASD_NIL_ID;
	faces[7].refvert[1] = PFASD_NIL_ID;
	faces[7].refvert[2] = PFASD_NIL_ID;
	faces[7].child[0] = PFASD_NIL_ID;
	faces[7].child[1] = PFASD_NIL_ID;
	faces[7].child[2] = PFASD_NIL_ID;
	faces[7].child[3] = PFASD_NIL_ID;
	
	    // 8
	faces[8].level = 1;
	faces[8].tsid = 0;
	faces[8].vert[0] = 5;  // N2
	faces[8].vert[1] = 8;  // N5
	faces[8].vert[2] = 2;  // V3
	faces[8].refvert[0] = PFASD_NIL_ID;
	faces[8].refvert[1] = PFASD_NIL_ID;
	faces[8].refvert[2] = PFASD_NIL_ID;
	faces[8].child[0] = PFASD_NIL_ID;
	faces[8].child[1] = PFASD_NIL_ID;
	faces[8].child[2] = PFASD_NIL_ID;
	faces[8].child[3] = PFASD_NIL_ID;
	
	    // 9
	faces[9].level = 1;
	faces[9].tsid = 0;
	faces[9].vert[0] = 7;  // N4
	faces[9].vert[1] = 3;  // V4
	faces[9].vert[2] = 8;  // N5
	faces[9].refvert[0] = PFASD_NIL_ID;
	faces[9].refvert[1] = PFASD_NIL_ID;
	faces[9].refvert[2] = PFASD_NIL_ID;
	faces[9].child[0] = PFASD_NIL_ID;
	faces[9].child[1] = PFASD_NIL_ID;
	faces[9].child[2] = PFASD_NIL_ID;
	faces[9].child[3] = PFASD_NIL_ID;
    
    } else {	    // ************* TEXTURABLE GEOMETRY *************** //
    
		// --- LEVEL 1 --- //
	    // 2
	faces[2].level = 1;
	faces[2].tsid = 0;
	faces[2].vert[0] = 4;  // V1
	faces[2].vert[1] = 1;  // N1
	faces[2].vert[2] = 5;  // N3
	faces[2].refvert[0] = PFASD_NIL_ID;
	faces[2].refvert[1] = PFASD_NIL_ID;
	faces[2].refvert[2] = PFASD_NIL_ID;
	faces[2].child[0] = PFASD_NIL_ID;
	faces[2].child[1] = PFASD_NIL_ID;
	faces[2].child[2] = PFASD_NIL_ID;
	faces[2].child[3] = PFASD_NIL_ID;
	    // 3
	faces[3].level = 1;
	faces[3].tsid = 0;
	faces[3].vert[0] = 4;  // N1
	faces[3].vert[1] = 5;  // V2
	faces[3].vert[2] = 0;  // N2
	faces[3].refvert[0] = PFASD_NIL_ID;
	faces[3].refvert[1] = PFASD_NIL_ID;
	faces[3].refvert[2] = PFASD_NIL_ID;
	faces[3].child[0] = PFASD_NIL_ID;
	faces[3].child[1] = PFASD_NIL_ID;
	faces[3].child[2] = PFASD_NIL_ID;
	faces[3].child[3] = PFASD_NIL_ID;
	    // 4
	faces[4].level = 1;
	faces[4].tsid = 0;
	faces[4].vert[0] = 0;  // N1
	faces[4].vert[1] = 5;  // N2
	faces[4].vert[2] = 6;  // N3
	faces[4].refvert[0] = PFASD_NIL_ID;
	faces[4].refvert[1] = PFASD_NIL_ID;
	faces[4].refvert[2] = PFASD_NIL_ID;
	faces[4].child[0] = PFASD_NIL_ID;
	faces[4].child[1] = PFASD_NIL_ID;
	faces[4].child[2] = PFASD_NIL_ID;
	faces[4].child[3] = PFASD_NIL_ID;
	    // 5
	faces[5].level = 1;
	faces[5].tsid = 0;
	faces[5].vert[0] = 6;  // N3
	faces[5].vert[1] = 5;  // N2
	faces[5].vert[2] = 2;  // V3
	faces[5].refvert[0] = PFASD_NIL_ID;
	faces[5].refvert[1] = PFASD_NIL_ID;
	faces[5].refvert[2] = PFASD_NIL_ID;
	faces[5].child[0] = PFASD_NIL_ID;
	faces[5].child[1] = PFASD_NIL_ID;
	faces[5].child[2] = PFASD_NIL_ID;
	faces[5].child[3] = PFASD_NIL_ID;
	    // 6
	faces[6].level = 1;
	faces[6].tsid = 0;
	faces[6].vert[0] = 1;  // V2
	faces[6].vert[1] = 7;  // N4
	faces[6].vert[2] = 5;  // N2
	faces[6].refvert[0] = PFASD_NIL_ID;
	faces[6].refvert[1] = PFASD_NIL_ID;
	faces[6].refvert[2] = PFASD_NIL_ID;
	faces[6].child[0] = PFASD_NIL_ID;
	faces[6].child[1] = PFASD_NIL_ID;
	faces[6].child[2] = PFASD_NIL_ID;
	faces[6].child[3] = PFASD_NIL_ID;
	
	    // 7
	faces[7].level = 1;
	faces[7].tsid = 0;
	faces[7].vert[0] = 5;  // N2
	faces[7].vert[1] = 7;  // N4
	faces[7].vert[2] = 3;  // N5
	faces[7].refvert[0] = PFASD_NIL_ID;
	faces[7].refvert[1] = PFASD_NIL_ID;
	faces[7].refvert[2] = PFASD_NIL_ID;
	faces[7].child[0] = PFASD_NIL_ID;
	faces[7].child[1] = PFASD_NIL_ID;
	faces[7].child[2] = PFASD_NIL_ID;
	faces[7].child[3] = PFASD_NIL_ID;
	
	    // 8
	faces[8].level = 1;
	faces[8].tsid = 0;
	faces[8].vert[0] = 5;  // N2
	faces[8].vert[1] = 3;  // N5
	faces[8].vert[2] = 8;  // V3
	faces[8].refvert[0] = PFASD_NIL_ID;
	faces[8].refvert[1] = PFASD_NIL_ID;
	faces[8].refvert[2] = PFASD_NIL_ID;
	faces[8].child[0] = PFASD_NIL_ID;
	faces[8].child[1] = PFASD_NIL_ID;
	faces[8].child[2] = PFASD_NIL_ID;
	faces[8].child[3] = PFASD_NIL_ID;
	
	    // 9
	faces[9].level = 1;
	faces[9].tsid = 0;
	faces[9].vert[0] = 5;  // N4
	faces[9].vert[1] = 8;  // V4
	faces[9].vert[2] = 2;  // N5
	faces[9].refvert[0] = PFASD_NIL_ID;
	faces[9].refvert[1] = PFASD_NIL_ID;
	faces[9].refvert[2] = PFASD_NIL_ID;
	faces[9].child[0] = PFASD_NIL_ID;
	faces[9].child[1] = PFASD_NIL_ID;
	faces[9].child[2] = PFASD_NIL_ID;
	faces[9].child[3] = PFASD_NIL_ID;
    }
    
    faces[0].gstateid = 0;
    faces[1].gstateid = 1;
    faces[2].gstateid = 0;
    faces[3].gstateid = 0;
    faces[4].gstateid = 0;
    faces[5].gstateid = 0;
    faces[6].gstateid = 1;
    faces[7].gstateid = 1;
    faces[8].gstateid = 1;
    faces[9].gstateid = 1;
    
    pfASDLODRange* lods;	// A pfASDLODRange array. 
    
    lods = (pfASDLODRange*)pfCalloc(2, sizeof(pfASDLODRange), pfGetSharedArena());
    Shared->lods = lods;
    
	// -- How the heck does this work??? --- //
    lods[0].switchin = 0.0f;	// lod[0] does not matter
    lods[0].morph = 0.0f;
    lods[1].switchin = 15.0f;	// This is the cut off where morphing starts	
    lods[1].morph = 7.0f;	// This is the size of the morphing "buffer"
    
    
	// ----- ATTRIBUTE SETUP ----- //
	// Set the attribute for the face vertex to point into the attribute arrays in
	// such a was that the attribute array has one attribute per vertex
    for(int fIndex =0;fIndex<numFaces;fIndex++)
    {
	faces[fIndex].attr[0] = faces[fIndex].vert[0];
	faces[fIndex].attr[1] = faces[fIndex].vert[1];
	faces[fIndex].attr[2] = faces[fIndex].vert[2];
	
	if (fIndex <= 1) {
		// --- Setup refvert attrs --- //
	    faces[fIndex].sattr[0] = faces[fIndex].refvert[0];
	    faces[fIndex].sattr[1] = faces[fIndex].refvert[1];
	    faces[fIndex].sattr[2] = faces[fIndex].refvert[2];
	} else {
	    faces[fIndex].sattr[0] = PFASD_NIL_ID;
	    faces[fIndex].sattr[1] = PFASD_NIL_ID;
	    faces[fIndex].sattr[2] = PFASD_NIL_ID;  
	}
    }
    
	
    
    
    // ----- FILL UP ATTRIBUTE ARRAY ----- //
    pfVec3* norms = (pfVec3*)pfCalloc(9, sizeof(pfVec3), pfGetSharedArena());;
    /* NV1 */    norms[0] = (vertNormal(V1, V3, V2));
    /* NV2 */    norms[1] = (vertNormal(V2, V1, V4));
    /* NV3 */    norms[2] = (vertNormal(V3, N4, V1));
    /* NV4 */    norms[3] = (vertNormal(V4, V2, V3));

if(whichGeometry == OrigGeom) {	

    /* NN1 */    norms[4] = ((vertNormal(N1, V1, N3) + vertNormal(N1, N3, N2) + vertNormal(N1, N2, V2))/3);
    /* NN2 */    norms[5] = ((vertNormal(N2, N4, V2) + vertNormal(N2, V2, N1) + vertNormal(N2, N1, N3) + vertNormal(N2, N3, V3) + vertNormal(N2, V3, N5) + vertNormal(N2, N5, N4)) / 6.0);  
    /* NN3 */    norms[6] = ((vertNormal(N3, V3, N2) + vertNormal(N3, N2, N1) + vertNormal(N3, N1, V1)) / 3.0f);
    /* NN4 */    norms[7] = ((vertNormal(N4, V2, N2) + vertNormal(N4, N2, N5) + vertNormal(N4, N5, V4)) / 3.0f);
    /* NN5 */    norms[8] = ((vertNormal(N5, V4, N4) + vertNormal(N5, N4, N2) + vertNormal(N5, N2, V3)) / 3.0f);

} else {

    /* NN1 */    norms[4] = ((vertNormal(N1, V1, N2) + vertNormal(N1, N3, N2)) / 2.0f);
    /* NN2 */    norms[5] = ((vertNormal(N2, N4, V2) + vertNormal(N2, V2, N1) + vertNormal(N2, N1, V1) + vertNormal(N2, V1, N3) + vertNormal(N2, N3, V3) + vertNormal(N2, V3, N5) + vertNormal(N2, N5, V4) + vertNormal(N2, V4, N4)) / 8.0f);  
    /* NN3 */    norms[6] = ((vertNormal(N3, V3, N2) + vertNormal(N3, N2, V1)) / 2.0f);
    /* NN4 */    norms[7] = ((vertNormal(N4, V2, N2) + vertNormal(N4, N2, V4)) / 2.0f);
    /* NN5 */    norms[8] = ((vertNormal(N5, V4, N2) + vertNormal(N5, N2, V3)) / 2.0f);

}    

    for(int nIndex=0;nIndex<9;nIndex++)
	norms[nIndex].normalize();
    
    ASDAttr* attrs = (ASDAttr*)pfCalloc(numVerts, sizeof(ASDAttr), pfGetSharedArena());
    Shared->attrs = attrs;
    
	// -- Fill with normal data -- //
    pfVec3 referencePtNormal(0.0, 0.0, 1.0);	// All points have the same reference normal point
    
    for(int vIndex=0;vIndex<numVerts;vIndex++)
    {
	attrs[vIndex].normal.normal = norms[vIndex];
	attrs[vIndex].normal.normalDiff = (norms[vIndex] - referencePtNormal);
    }
    
    attrs[0].normal.normalDiff = pfVec3(0.3f, 0.4f, 0.2f);
	// --- Fill with color data -- //
    
    for(int cIndex=0;cIndex<numVerts;cIndex++)
    {
	if (cIndex < 4)  // One of the base verts
	{
	    attrs[cIndex].color.color = Shared->baseColor;
	    attrs[cIndex].color.colorDiff.set(0.0, 0.0, 0.0, 0.0f);   
	} else {
	    attrs[cIndex].color.color = Shared->morphedColor;
	    attrs[cIndex].color.colorDiff = (Shared->baseColor - Shared->morphedColor);     
	}
    }    
    
    pfASD* asdNode = new pfASD;
    asdNode->setAttr(PFASD_LODS, 0, 2, Shared->lods);
    asdNode->setAttr(PFASD_COORDS, 0, numVerts, Shared->verts);
    asdNode->setAttr(PFASD_FACES, 0, numFaces, Shared->faces);
//    asdNode->setAttr(PFASD_PER_VERTEX_ATTR, PFASD_NORMALS, numVerts, (float*)Shared->normalAttrs);
//    asdNode->setAttr(PFASD_PER_VERTEX_ATTR, PFASD_COLORS, numVerts, (float*)Shared->colorAttrs);
    asdNode->setAttr(PFASD_PER_VERTEX_ATTR, PFASD_NORMALS | PFASD_COLORS, numVerts, (float*)Shared->attrs);
    asdNode->setNumBaseFaces(2);
    
    asdNode->setEvalMethod(PFASD_DIST);
    asdNode->setMorphAttrs(PFASD_COORDS | PFASD_NORMALS | PFASD_COLORS);	
    asdNode->setMaxMorphDepth(1, 0.0);	    // 1 is the finest level I want to use 

    pfGeoState** gsa;
    pfGeoState *gs1, *gs2;
    gsa = (pfGeoState**)pfMalloc(2*sizeof(pfGeoState*));
    Shared->geoStateArray = gsa;
    gs1 = new pfGeoState;
    gs2 = new pfGeoState;
    
    if(whichGeometry == OrigGeom) {
	buildGState(gs1);
	buildGState(gs2);
    } else {    
	buildTexGState(gs1, "wood.rgb");
	buildTexGState(gs2, "bark.rgb");
    }
    
    gsa[0] = gs1;
    gsa[1] = gs2;
    
    asdNode->setGStates(gsa,2);
    asdNode->config();		    // Tell the ASD it can setup itself now
    return asdNode;
    
}

void switchMorphAttrs()		// Call to update which params are to be morphed
{
    Shared->theASDNode->setMorphAttrs(
	(pfuGetWidgetValue(Shared->wMorphCoords) == 1.0 ? PFASD_COORDS : 0) |
	(pfuGetWidgetValue(Shared->wMorphNorms) == 1.0 ? PFASD_NORMALS : 0) |
	(pfuGetWidgetValue(Shared->wMorphColors) == 1.0 ? PFASD_COLORS : 0) );
	   
}

void setNewSwitchingValue(float newValue)
{
    Shared->lods[1].switchin = newValue;
    Shared->theASDNode->setAttr(PFASD_LODS, 0, 2, Shared->lods);    
}

void setMorphBufferValue(float newValue)
{
    Shared->lods[1].morph = newValue;
    Shared->theASDNode->setAttr(PFASD_LODS, 0, 2, Shared->lods);    
}


void buildGState(pfGeoState *gs)
{
    gs->setMode(PFSTATE_ENLIGHTING, PF_ON);
    gs->setMode(PFSTATE_CULLFACE, PFCF_OFF);	// Turn off backface culling
    //pfGStateMode(gs, PFSTATE_ENTEXTURE, PF_ON);
    //ViewState->mtl = defaultMaterial();
    gs->setAttr(PFSTATE_FRONTMTL, defaultMaterial());
    gs->setAttr(PFSTATE_BACKMTL, defaultMaterial());   
}

void buildTexGState(pfGeoState *gs, char* texname)
{
	// --- Setup pfTexture --- //
    pfTexture *tex = new pfTexture;
    
    tex->setFilter(PFTEX_MINFILTER, PFTEX_BILINEAR);
    
    if (!tex->loadFile(texname))
	pfNotify(PFNFY_WARN, PFNFY_PRINT, "TEXTURE: Texture not loaded.\n");
    else
	pfNotify(PFNFY_NOTICE, PFNFY_PRINT, "TEXTURE: Texture %s was loaded.\n", texname);
	
    uint *image;
    int nc, sx, sy, sz;
    
    tex->getImage(&image, &nc, &sx, &sy, &sz);
	
	// if have alpha channel, enable transparency
    if (nc != 3)
	gs->setMode(PFSTATE_TRANSPARENCY, PFTR_FAST);
    
	// set alpha function to block pixels of 0 alpha for 
	//   transparent textures
    //gs->setMode(PFSTATE_ALPHAFUNC, PFAF_NOTEQUAL);
    //gs->setVal(PFSTATE_ALPHAREF, 0.0f);
    gs->setAttr(PFSTATE_TEXTURE, tex);
    gs->setMode(PFSTATE_ENTEXTURE,1);
    gs->setMode(PFSTATE_ENLIGHTING,0);
    gs->setMode(PFSTATE_CULLFACE,PFCF_OFF);
    
	// --- Setup Texture Environment --- //
    pfTexEnv* tev = new pfTexEnv;
    
    tev->setMode(PFTE_DECAL);
    //tev->setMode(PFTE_BLEND);
    //tev->setBlendColor(0.0f, 0.0f, 0.0f, 1.0f);
    
    gs->setAttr(PFSTATE_TEXENV, tev);
    
	// ---- Setup TexGen ---- //
    pfTexGen* texGen = new pfTexGen;
    texGen->setMode(PF_S, PFTG_OBJECT_LINEAR);
    texGen->setMode(PF_T, PFTG_OBJECT_LINEAR);   
    
    gs->setAttr(PFSTATE_TEXGEN, texGen);
    gs->setMode(PFSTATE_ENTEXGEN, PF_ON);
}

/* Keep a default Material Handy */
pfMaterial* defaultMaterial()
{
    static pfMaterial* material = NULL;

    if (material == NULL)
    {
        material = new pfMaterial;
        material->setColor(PFMTL_AMBIENT,  0.15f, 0.8f, 0.87f);
        material->setColor(PFMTL_DIFFUSE,  0.15f, 0.8f, 0.87f);
        material->setColor(PFMTL_SPECULAR, 1.0f, 1.0f, 1.0f);
        material->setShininess(0.0f);
	material->setAlpha(1.0f);

        //material->setColorMode( PFMTL_FRONT, PFMTL_CMODE_AMBIENT_AND_DIFFUSE);
    }

	// -- return pointer to default material --- //
    return material;
}

pfHighlight* createNormalHighlight()
{
    pfHighlight* hLight = new pfHighlight;
    hLight->setMode(PFHL_NORMALS);
    hLight->setLineWidth(1.0f);
    hLight->setNormalLength(1.0f, 0.0f);
    hLight->setColor(PFHL_FGCOLOR, 1.0f, 0.0f, 0.0f);
    hLight->setColor(PFHL_BGCOLOR, 1.0f, 0.0f, 0.0f);
    return hLight;
}

void switchASDLighting()
{
    if(Shared->geoStateArray[0]->getMode(PFSTATE_ENLIGHTING) == PF_ON) {
	Shared->geoStateArray[0]->setMode(PFSTATE_ENLIGHTING, PF_OFF);
	Shared->geoStateArray[1]->setMode(PFSTATE_ENLIGHTING, PF_OFF);
    } else {
	Shared->geoStateArray[0]->setMode(PFSTATE_ENLIGHTING, PF_ON);
	Shared->geoStateArray[1]->setMode(PFSTATE_ENLIGHTING, PF_ON);
    }    
}

// -------------- XFORMER STUFF --------------- //
static void
InitXformer(void)
{
    Shared->xformer = (pfiXformer *)pfiNewTDFXformer(pfGetSharedArena());
    Shared->xformer->setAutoInput(Shared->chan, &Shared->mouse, &Shared->events);
    Shared->xformer->setNode(Shared->scene);
    Shared->xformer->setAutoPosition(Shared->chan, Shared->sceneDCS);
    Shared->xformer->setResetCoord(&Shared->initView);
    xformerModel(Shared->xformerModel, Shared->collideMode);

}

static void
IsectFunc(void *)
{
//    pfiCollideXformer(Shared->xformer);
}


/******************************************************************************
*			    App Viewing Model Routines
******************************************************************************
*/

//
// Thes routines use the viewing and tranformation utilities in
// libpfutil to implement several fiewing modes: drive, fly, and trackball.
//

static void
xformerModel(int model, int collideMode)
{
    static int		oldModel = -1, oldCollMode = -1;

    if ((model == oldModel && oldCollMode == collideMode) || !Shared->xformer)
	return;

    if (oldModel != model)
	Shared->xformer->selectModel(model);
    
    /* Collide with objects in scene */
    if (oldCollMode != collideMode)
    {
	if (collideMode == PF_ON)
	    Shared->xformer->enableCollision();
	else
	    Shared->xformer->disableCollision();
	oldCollMode = collideMode;
    }
}

//
// Update the current view
//
static void
UpdateView(pfChannel *, pfScene *)
{    
    pfiUpdateXformer(Shared->xformer);
    Shared->xformerModel = Shared->xformer->getCurModelIndex();
    
    /* if have moving-eyepoint motion model, update eyepoint */
    if (Shared->xformer->getCurModel()->isOfType(
	    pfiInputXformTravel::getClassType()))
    {
	Shared->xformer->getCoord(&Shared->viewCoord);
    }
}


//
// Reset view to original XYZ position and HPR Euler angles
//
static void
resetPosition(void)
{
    Shared->xformer->stop();
    Shared->xformer->resetPosition();
}


/******************************************************************************
*			    App Input Handling
******************************************************************************
*/

static void
ProcessInputIO(void)
{
    int i,j,key;
    int dev, val, numDevs;
    pfuEventStream *pfevents;
    
    pfevents = &(Shared->events);
    
    numDevs = pfevents->numDevs;
    for (j=0; j < numDevs; j++)
    {
	dev = pfevents->devQ[j];
	val = pfevents->devVal[j];
	if (pfevents->devCount[dev] > 0)
	{
	    switch(dev)
	    {
	    case PFUDEV_REDRAW:
		// worst case in APP_CULL_DRAW mode it takes 3 frames to
		// propagate new window size back down to draw process.
		//
		Shared->redraw = 3;
		pfuRedrawGUI();
		pfevents->devCount[dev] = 0; // mark device done
		break;
		
	    case PFUDEV_WINQUIT:
		Shared->exitFlag = 1;
		pfevents->devCount[dev] = 0; // mark device done
		break;
		
		// Main Keyboard
	    case PFUDEV_KEYBD:
		for(i=0; i<pfevents->numKeys; i++)
		{
		    key = pfevents->keyQ[i];
		    if (pfevents->keyCount[key])
		    {	// key was pressed count times
			switch(key)
			{
			case 27:	// ESC
			    Shared->exitFlag = 1;
			    break;
			case 'g':
			case 'G':
			    Shared->drawStats = !Shared->drawStats;
			    pfuWidgetValue(Shared->wStats,
					   Shared->drawStats);
			    break;
			case 'r':
			case 'R':
			    resetPosition();
			    break;
			case ' ':
			    Shared->xformer->stop();
			    break;
			default:
			    break;
			}
		    }
		}
		// XXX this is very important or else future keybd events
                // will be lost !!!
                //
                pfevents->devCount[dev] = 0; // mark device done
		break;
	    case PFUDEV_F1KEY:
		Shared->gui = !Shared->gui;
		pfuWidgetValue(Shared->wKillGUI, Shared->gui);
		Shared->updateChannels = 1;
		pfevents->devCount[dev] = 0; // mark device done
		break;
	    } // switch on device
	} // if have device
    } // for each device
    pfevents->numDevs = 0;
    pfevents->numKeys = 0;
}

/******************************************************************************
*			    Panel Creation and Management
******************************************************************************
*/

static pfuGUIString viewModel_strs[] = {"Trackball", "Fly", "Drive"};
static int viewModel_vals[] = {
    PFITDF_TRACKBALL, PFITDF_FLY, PFITDF_DRIVE};
static pfuGUIString drawStyle_strs[] = {"Filled", "Scribed", "Lines", "Hidden"};
static int drawStyle_vals[] = { PFUSTYLE_FILLED, PFUSTYLE_SCRIBED, PFUSTYLE_LINES, PFUSTYLE_HIDDEN};

static pfuGUIString evalMethod_strs[] = {"Dist", "Dist^2"};
static int evalMethod_vals[] = { PFASD_DIST, PFASD_DIST_SQUARE};


static pfuPanel *
InitPanel(void)
{
    float  xOrigin, yOrigin, ySize, xSize;
    int x, y, yTop;
    pfuWidget *wid;
    pfuPanel *pan;
    
    pan = pfuNewPanel();
    
    pfuGetPanelOriginSize(pan, &xOrigin, &yOrigin, &xSize, &ySize);
    
    x = xOrigin;
    yTop = (yOrigin + ySize);
    y = yTop - PFUGUI_BUTTON_YINC;

	// -------------------- //
	// ------- ROW 1 ------ //
	// -------------------- //
    // action  buton - disable the gui (F1KEY to get it back)
    wid = pfuNewWidget(pan, PFUGUI_SWITCH, 0);
    pfuWidgetDim(wid,  x,  y,  24,  24);
    pfuWidgetValue(wid, 1.0f);
    pfuWidgetDefaultValue(wid, 1.0f);
    pfuWidgetActionFunc(wid, KillGUI);
    Shared->wKillGUI = wid;
    x += 24 + PFUGUI_PANEL_FRAME;
    
    // action button - quit
    wid = pfuNewWidget(pan, PFUGUI_BUTTON, CTL_QUIT);
    pfuWidgetDim(wid,  x,  y,  PFUGUI_BUTTON_XSIZE,  PFUGUI_BUTTON_YSIZE);
    pfuWidgetActionFunc(wid, PanelControl);
    pfuWidgetLabel(wid, "Quit");
    x += PFUGUI_BUTTON_XINC;
    
    // action  buton - reset to origin button
    wid = pfuNewWidget(pan, PFUGUI_BUTTON, CTL_REPOSITION);
    pfuWidgetDim(wid,  x,  y,  PFUGUI_BUTTON_VLONG_XSIZE,
		 PFUGUI_BUTTON_YSIZE);
    pfuWidgetLabel(wid, "Reposition");
    pfuWidgetActionFunc(wid, PanelControl);
    x += PFUGUI_BUTTON_VLONG_XINC;
    
    // the message bar with positions
    wid = pfuNewWidget(pan, PFUGUI_MESSAGE, 0);
    pfuWidgetDim(wid,  x,  y,  PFUGUI_MESSAGE_XSIZE,
		 PFUGUI_MESSAGE_YSIZE);
    pfuWidgetActionFunc(wid, PanelControl);
    x += PFUGUI_MESSAGE_XINC;
    Shared->wPosXYZ = wid;
    
    
    // action button - reset all
    wid = pfuNewWidget(pan, PFUGUI_BUTTON, CTL_RESET_ALL);
    pfuWidgetDim(wid,  x,  y,  PFUGUI_BUTTON_VLONG_XSIZE,
		 PFUGUI_BUTTON_YSIZE);
    pfuWidgetLabel(wid, "Reset All");
    pfuWidgetActionFunc(wid, PanelControl);
    Shared->wResetAll = wid;
    
    x = xOrigin;
    y = yTop - 2*PFUGUI_BUTTON_YINC;
    
	// -------------------- //
	// ------- ROW 2 ------ //
	// -------------------- //
    
    // menu button - view model
    wid = pfuNewWidget(pan, PFUGUI_MENU_BUTTON, CTL_MODEL);
    pfuWidgetDim(wid,  x,  y,  2*PFUGUI_BUTTON_LONG_XSIZE,
		 PFUGUI_BUTTON_YSIZE);
    pfuWidgetLabel(wid, "Motion");
    pfuWidgetActionFunc(wid, PanelControl);
    pfuWidgetSelections(wid, viewModel_strs, viewModel_vals, NULL, 3);
    Shared->wFModel = wid;
    x += (2*PFUGUI_BUTTON_LONG_XINC);
    
    // menu button - draw style
    wid = pfuNewWidget(pan, PFUGUI_MENU_BUTTON, CTL_DRAWSTYLE);
    pfuWidgetDim(wid,  x,  y,  PFUGUI_BUTTON_VLONG_XSIZE,
		 PFUGUI_BUTTON_YSIZE);
    pfuWidgetLabel(wid, "Style:");
    pfuWidgetActionFunc(wid, PanelControl);
    pfuWidgetSelections(wid, drawStyle_strs, drawStyle_vals, NULL, 4);
    Shared->wDrawStyle = wid;
    x += PFUGUI_BUTTON_VLONG_XINC;
    
    // menu button - ASD Eval Method
    wid = pfuNewWidget(pan, PFUGUI_MENU_BUTTON, CTL_EVAL_METHOD);
    pfuWidgetDim(wid,  x,  y,  PFUGUI_BUTTON_VLONG_XSIZE,
		 PFUGUI_BUTTON_YSIZE);
    pfuWidgetLabel(wid, "Eval:");
    pfuWidgetActionFunc(wid, PanelControl);
    pfuWidgetSelections(wid, evalMethod_strs, evalMethod_vals, NULL, 2);
    Shared->wEvalMethod = wid;
    x += PFUGUI_BUTTON_VLONG_XINC;
    
    
    // switch button - stats
    wid = pfuNewWidget(pan, PFUGUI_SWITCH, CTL_DIAGS);
    pfuWidgetDim(wid, x, y, PFUGUI_BUTTON_MED_XSIZE, PFUGUI_BUTTON_YSIZE);
    pfuWidgetLabel(wid, "Stats");
    pfuWidgetActionFunc(wid, PanelControl);
    Shared->wStats = wid;
    x += PFUGUI_BUTTON_MED_XINC;
    
    // switch button - tree
    wid = pfuNewWidget(pan, PFUGUI_SWITCH, CTL_TREE);
    pfuWidgetDim(wid, x, y, PFUGUI_SCALE_XSIZE(80), PFUGUI_BUTTON_YSIZE);
    pfuWidgetLabel(wid, "Tree");
    pfuWidgetActionFunc(wid, PanelControl);
    Shared->wTree = wid;
    x += PFUGUI_BUTTON_MED_XINC;
    
    // switch button - Lighting
    wid = pfuNewWidget(pan, PFUGUI_SWITCH, CTL_LIGHTING);
    pfuWidgetDim(wid, x, y, PFUGUI_BUTTON_MED_XSIZE, PFUGUI_BUTTON_YSIZE);
    pfuWidgetLabel(wid, "No Light");
    pfuWidgetActionFunc(wid, PanelControl);
    Shared->wLighting = wid;
    x += PFUGUI_BUTTON_MED_XINC;
    
    // switch button - draw Normals
    wid = pfuNewWidget(pan, PFUGUI_SWITCH, CTL_DRAW_NORMS);
    pfuWidgetDim(wid, x, y, PFUGUI_SCALE_XSIZE(80), PFUGUI_BUTTON_YSIZE);
    pfuWidgetLabel(wid, "Draw Norms");
    pfuWidgetActionFunc(wid, PanelControl);
    Shared->wDrawNorms = wid;
    x += PFUGUI_BUTTON_MED_XINC;
    
    
    x = xOrigin;
    y -= PFUGUI_SLIDER_YINC;

	// -------------------- //
	// ------- ROW 3 ------ //
	// -------------------- //
	
	// --- Morph switchin value --- //
    wid = pfuNewWidget(pan, PFUGUI_SLIDER, CTL_ASD_DIST);
    pfuWidgetDim(wid,  x,  y,  PFUGUI_SLIDER_BIG_XSIZE, PFUGUI_SLIDER_YSIZE);
    pfuWidgetLabel(wid, "ASD SwitchIn Distance");
    pfuWidgetActionFunc(wid, PanelControl);
    pfuWidgetRange(wid, PFUGUI_SLIDER, 0.0, 25.0, 15.0);
    Shared->wASDDist = wid;
    x += PFUGUI_SLIDER_BIG_XINC;
    
	// --- Morph Buffer value --- //
    wid = pfuNewWidget(pan, PFUGUI_SLIDER, CTL_MORPH_BUFFER);
    pfuWidgetDim(wid,  x,  y,  PFUGUI_SLIDER_BIG_XSIZE, PFUGUI_SLIDER_YSIZE);
    pfuWidgetLabel(wid, "ASD Morph Buffer");
    pfuWidgetActionFunc(wid, PanelControl);
    pfuWidgetRange(wid, PFUGUI_SLIDER, 0.0, 20.0, 7.0);
    Shared->wMorphBuffer = wid;
    x += PFUGUI_SLIDER_BIG_XINC;
    
    
    x = xOrigin;
    y -= PFUGUI_SLIDER_YINC;
	// -------------------- //
	// ------- ROW 4 ------ //
	// -------------------- //
    
    // switch button - morph Coords
    wid = pfuNewWidget(pan, PFUGUI_SWITCH, CTL_MORPH_COORDS);
    pfuWidgetDim(wid, x, y, PFUGUI_BUTTON_MED_XSIZE, PFUGUI_BUTTON_YSIZE);
    pfuWidgetLabel(wid, "Coords");
    pfuWidgetActionFunc(wid, PanelControl);
    Shared->wMorphCoords = wid;
    x += PFUGUI_BUTTON_MED_XINC;
    
     // switch button - morph Norms
    wid = pfuNewWidget(pan, PFUGUI_SWITCH, CTL_MORPH_NORMS);
    pfuWidgetDim(wid, x, y, PFUGUI_BUTTON_MED_XSIZE, PFUGUI_BUTTON_YSIZE);
    pfuWidgetLabel(wid, "Norms");
    pfuWidgetActionFunc(wid, PanelControl);
    Shared->wMorphNorms = wid;
    x += PFUGUI_BUTTON_MED_XINC;
    
     // switch button - morph Colors
    wid = pfuNewWidget(pan, PFUGUI_SWITCH, CTL_MORPH_COLORS);
    pfuWidgetDim(wid, x, y, PFUGUI_BUTTON_MED_XSIZE, PFUGUI_BUTTON_YSIZE);
    pfuWidgetLabel(wid, "Colors");
    pfuWidgetActionFunc(wid, PanelControl);
    Shared->wMorphColors = wid;
    x += PFUGUI_BUTTON_MED_XINC;
         
    return pan;
}


static void
KillGUI(pfuWidget *w)
{
    if (pfuGetWidgetValue(w) == 0.0f)
    {
	Shared->gui = 0;
	Shared->updateChannels = 1;
    }
}

//
// PanelControl
// -------------
// This function is called from the draw process from the currently
// active widget.  
//

static void
PanelControl(pfuWidget *w)
{
    float val;
    switch(pfuGetWidgetId(w))
    {
    case CTL_RESET_ALL:
	pfuResetGUI();
	resetPosition();
	//Detail_mod = TRUE;
	break;
    case CTL_REPOSITION:
	resetPosition();
	//Detail_mod = TRUE;
	break;
    case CTL_QUIT:
	Shared->exitFlag = 1;
	
	    pfNotify(PFNFY_DEBUG, PFNFY_PRINT,
		     " Have Quit button - bye...\n");
	
	break;
    case CTL_MODEL:
	val = pfuGetWidgetValue(w);
	Shared->xformerModel = val;
	xformerModel(val, Shared->collideMode);
	
	    pfNotify(PFNFY_DEBUG, PFNFY_PRINT,
		     " Have Viewing Model: %.0f\n", val);
	
	break;
    
    case CTL_DRAWSTYLE:
	val = pfuGetWidgetValue(w);
	Shared->drawStyle = val;
	break;
	
    case CTL_EVAL_METHOD:
	Shared->theASDNode->setEvalMethod(pfuGetWidgetValue(Shared->wEvalMethod));
	break;
	
    case CTL_DIAGS:
	Shared->drawStats = (pfuGetWidgetValue(w) == 1.0f);
	break;
    
    case CTL_TREE:
	Shared->drawTree = (pfuGetWidgetValue(w) == 1.0f);
	break;
	
    case CTL_LIGHTING:
	switchASDLighting();
	break;
	
    case CTL_DRAW_NORMS:
	Shared->drawNormals = (pfuGetWidgetValue(w) == 1.0f);
	break;
	
    case CTL_ASD_DIST:
	setNewSwitchingValue(pfuGetWidgetValue(w));
	break;
	
    case CTL_MORPH_BUFFER:
	setMorphBufferValue(pfuGetWidgetValue(w));
	break;
    
    case CTL_MORPH_COORDS:
    case CTL_MORPH_NORMS:
    case CTL_MORPH_COLORS:
	switchMorphAttrs();	// Call funtion to set the new morph mode
	break;

    default:
	
	    pfNotify(PFNFY_DEBUG, PFNFY_PRINT,
		     "Have GUI Widget \"%s\": id=%d, val = %d\n",
		     pfuGetWidgetLabel(w), pfuGetWidgetId(w), pfuGetWidgetValue(w));
	
	break;
    }

}

static void
updateGUIViewMsg(pfCoord *viewpt)
{
    static char     posBuf[256];
    
    sprintf(posBuf," X%5.1f Y%5.1f Z%5.1f H%5.1f P%5.1f R%5.1f",
	    viewpt->xyz[PF_X],viewpt->xyz[PF_Y],viewpt->xyz[PF_Z],
	    viewpt->hpr[PF_H],viewpt->hpr[PF_P],viewpt->hpr[PF_R]);
    pfuWidgetLabel(Shared->wPosXYZ, posBuf);
}

/******************************************************************************
*			    Draw Process: Graphics Initialization
******************************************************************************
*/

//
//	OpenPipeWin() -- create a pipeline: setup the window system,
//	the OpenGL, and OpenGL Performer. this procedure is executed in
//	the draw process (when there is a separate draw process).
//

static void
OpenPipeWin(pfPipeWindow *pw)
{
    // use pfu utility to open the window so that the pfu input
    // handler will be able to attach to it
    //
    pw->open();
    
}

/******************************************************************************
*			    Draw Process Routines
******************************************************************************
*/

//
//	DrawChannel() -- draw a channel and read input queue. this
//	procedure is executed in the draw process (when there is a
//	separate draw process).
//
static void
DrawChannel (pfChannel *channel, void *)
{
    static pfVec4 bgclr(0.0f, 0.0f, 0.0f, 1.0f);
    static pfVec4 scribeColor(1.0f, 0.0f, 0.0f, 1.0f);
    
    // erase framebuffer and draw Earth-Sky model
    pfClear(PFCL_COLOR | PFCL_DEPTH, &bgclr);
    
    // invoke Performer draw-processing and style for this frame
    pfuPreDrawStyle(Shared->drawStyle, scribeColor); 
    pfDraw();
    pfuPostDrawStyle(Shared->drawStyle);
    
    {
	int err = glGetError();
	if (err != GL_NO_ERROR)
	    pfNotify(PFNFY_NOTICE,PFNFY_USAGE,
		     "OpenGL Error 0x%x - %s",err, gluErrorString(err));
    }
    
    
    // Enable drawing of Performer throughput statistics
    if (Shared->drawStats)
	channel->drawStats();
    
    static pfVec3 treeScale(0.0f, 0.0f, 1.0f);
    if(Shared->drawTree)
	pfuDrawTree(channel, (pfNode*)Shared->scene, treeScale);
	
    // Check the mouse and keyboard = only needed for GL input
    // This is done after the draw becuase it can happen in parallel
    // with the pipe finishing the rendering of the frame.
    //
    pfuCollectInput();
}

/******************************************************************************
*			    Cull Process Routines
******************************************************************************
*/

//
//	CullChannel() -- traverse the scene graph and generate a
// 	display list for the draw process.  This procedure is
//	executed in the cull process.
//
static void
CullChannel(pfChannel *, void *)
{
    //
    // pfDrawGeoSet or other display listable Performer routines
    // could be invoked before or after pfCull()
    // 
    
    pfCull();
}