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

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

Revision 1.1, Tue Nov 21 21:39:37 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 1995, 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
//
//
// file: detailC.C
// --------------
//
// OpenGL Performer tool allowing one to load a specific base texture
// and detail texture.  The textures are applied to a cube and displayed.
// The gui interface contains 9 horizontal sliders which corresponds
// to the control points for pfTexSpline.  The use can manipulate
// the control points and view results.
//
// A sample texture and matching detail texture can be found in the 
// data directory: road.rgb and road_detail.rgb
//
// dtail_tex <base texture> <detail texture>
//
// Run-time controls:
// ------------------
//       ESC-key: exits
//        F1-key: display profile stats
//     SPACE-key: stop
//	   r-key: re-position
//    Left-mouse: moves forward
//  Middle-mouse: stop
//   Right-mouse: moves backward
//

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

// general includes
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <getopt.h> // for cmdline handler

// 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/pfChannel.h>
#include <Performer/pf/pfDCS.h>
#include <Performer/pf/pfLightSource.h>
#include <Performer/pf/pfGeode.h>
#include <Performer/pr/pfTexture.h>
#include <Performer/pr/pfGeoSet.h>


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


/******************************************************************************
*				Defines and Typedefs
******************************************************************************
*/

// Default detail stuff
#define DEF_LOD1	0.0f
#define DEF_SCALE1	0.0f
#define DEF_LOD2	3.0f
#define DEF_SCALE2	1.5f
#define DEF_LOD3	4.0f
#define DEF_SCALE3	2.0f
#define DEF_LOD4	6.5f
#define DEF_SCALE4	3.25f

// Panel Widget Control Tokens
enum PanelControlToken {
    CTL_RESET_ALL=1,
    CTL_REPOSITION,
    CTL_QUIT,
    CTL_MODEL,
    CTL_DIAGS,
    CTL_LOD1_SLIDER,
    CTL_LOD2_SLIDER,
    CTL_LOD3_SLIDER,
    CTL_LOD4_SLIDER,
    CTL_SCALE1_SLID,
    CTL_SCALE2_SLID,
    CTL_SCALE3_SLID,
    CTL_SCALE4_SLID,
    CTL_CLAMP
    };

// SharedData:
// structure that resides in shared memory so that the
// application, cull, and draw processes can access it.
//
typedef struct SharedData
{
    // main channel stuff
    pfChannel		*chan;
    pfVec3		startXYZ;
    float		sceneSize;
    int			drawStats;
    int			exitFlag;
    pfLightSource	*sun;		// sun light source
    // 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;
    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
    // flags for GUI
    int			updateChannels;
    int			gui, redraw, guiDirty;
    pfuPanel		*guiPanel;
    pfuWidget		*wResetAll, *wKillGUI, *wPosXYZ, *wStats, *wFModel;
}SharedData;

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

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

// texture stuff
static pfTexture	*tex;
static pfTexture	*dtex;

static float LOD1 = DEF_LOD1;
static float SCALE1 = DEF_SCALE1;
static float LOD2 = DEF_LOD2;
static float SCALE2 = DEF_SCALE2;
static float LOD3 = DEF_LOD3;
static float SCALE3 = DEF_SCALE3;
static float LOD4 = DEF_LOD4;
static float SCALE4 = DEF_SCALE4;
static float TEX_CLAMP = -1;

// update pfTexSpline boolean
int Detail_mod = FALSE;

static pfVec2	ctl_pts[4];

// X control variables
#ifndef __linux__
static int XInput = PFUINPUT_X;
#else
static int XInput = PFUINPUT_NOFORK_X;
#endif
static int WinXOrigin = 10, WinYOrigin = 10;
static int WinXSize = 500, WinYSize = 500;
/*  static int WinXSize = 1280, WinYSize = 1024;  */

// process control variables
static int ProcSplit = PFMP_APPCULLDRAW;

// global name strings
static char TexName[PF_MAXSTRING];
static char DTexName[PF_MAXSTRING];
static char ProgName[PF_MAXSTRING];

static int HaveDetail = 0;

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 Usage(void);
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);

// make the textured cube
static pfGroup	*MakeTexCube ();

/******************************************************************************
*			    Main and Commandline Processing
******************************************************************************
*/

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

char OptionStr[] = "m:p:x?";

static void
Usage (void)
{
    fprintf(stderr, "Usage: %s <base texture> <detail texture> \n",
	    ProgName);
    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 'm':
	case 'p':
	    ProcSplit = atoi(optarg);
	    break;
	case 'x':
#ifndef __linux__
	    XInput = PFUINPUT_X;
#else
	    XInput = PFUINPUT_NOFORK_X;
#endif
	    break;
	case '?':
	    Usage();
	    break;
	}
    }
    return optind;
}

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

int
main (int argc, char *argv[])
{
    int		    arg, detail;
    float	    far = 10000.0f;
    
    
    pfNotifyLevel(PFNFY_NOTICE);
    pfInit();
    pfFilePath(".:/usr/share/Performer/data:/usr/demos/data/textures");

    pfQueryFeature(PFQFTR_TEXTURE_DETAIL, &detail);
    if (!detail)
	pfNotify(PFNFY_NOTICE, PFNFY_PRINT, 
	"Detail texture not supported on this platform.");
    
    arg = docmdline(argc, argv);
    
    if ((argc - arg) < 2)
	Usage();
    
    pfMultiprocess(ProcSplit);
    
    // grab base texture name
    strcpy(TexName, argv[arg]);
    
    // grab detail texture name
    strcpy(DTexName, argv[arg+1]);
    
    // Initialize ctl_pts
    ctl_pts[0].set(-LOD1, SCALE1);
    ctl_pts[1].set(-LOD2, SCALE2);
    ctl_pts[2].set(-LOD3, SCALE3);
    ctl_pts[3].set(-LOD4, SCALE4);
    
    
    // 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_FLY;
    Shared->input		= XInput;
    Shared->collideMode		= PF_ON;
    Shared->gui			= 1; // enable gui
    Shared->updateChannels	= 1;
    Shared->guiDirty = Shared->redraw = 1;
    
    // initiate multi-processing mode set in pfMultiprocess call
    // FORKS HAPPEN HERE !!!
    //
    pfConfig();
    
    // init shared mem for utility libraries
    pfuInit();
    pfiInit();

    
    // 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(ProgName);
    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;
    Shared->sceneDCS = new pfDCS;
    scene->addChild(Shared->sceneDCS);
    
    pfGroup *a_tex_cube = MakeTexCube();
    Shared->sceneDCS->addChild(a_tex_cube);
    
    // Set intersection callback.
    pfIsectFunc(IsectFunc);
    // do intersection setup
    pfuCollideSetup(scene, PFUCOLLIDE_STATIC, 0xffffffff);
    
    Shared->scene = scene;
    
    // 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(0.1f, 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);
    
    // set up scene bounding box and initial viewing position
    {
	float sceneSize;
	pfVec3 view;
	// Set initial view to be "in front" of scene
	
	// view point at center of bbox
	// determine extent of scene's geometry
	pfBox bbox;
	pfuTravCalcBBox(scene, &bbox);
	
	// find max dimension
	sceneSize = bbox.max[PF_X] - bbox.min[PF_X];
	sceneSize = PF_MAX2(sceneSize, bbox.max[PF_Y] -
			    bbox.min[PF_Y]);
	sceneSize = PF_MAX2(sceneSize, bbox.max[PF_Z] -
			    bbox.min[PF_Z]);
	sceneSize = PF_MIN2(sceneSize, 0.5f * far);
	Shared->sceneSize = sceneSize;
	view.add(bbox.min, bbox.max);
	Shared->viewCoord.xyz.scale(0.5f, view);
	
	// offset so all is visible
	Shared->viewCoord.xyz[PF_Y] -=      sceneSize;
	Shared->viewCoord.xyz[PF_Z] += 0.25f*sceneSize;
	
	Shared->viewCoord.hpr.set(0.0f, 0.0f, 0.0f);
	chan->setView(Shared->viewCoord.xyz,
		      Shared->viewCoord.hpr);
	Shared->initView = Shared->viewCoord;
    }
    Shared->startXYZ.copy(Shared->viewCoord.xyz);
    
    Shared->chan->setViewport(0.0f, 1.0f, 0.3f, 1.0f);
    pfuGUIViewport(0.0f, 1.0f, 0.0f, 0.3f);
    // init the control panel for perfly
    Shared->guiPanel = InitPanel();
    pfuEnablePanel(Shared->guiPanel);
    
    // Initialize channel viewing model for interactive motion
    InitXformer();
    
    // 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);
	UpdateView(Shared->chan, Shared->scene);
	
	// 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 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;
}

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();

    // reset all detail texture gui to defaults
    LOD1 = DEF_LOD1;
    SCALE1 = DEF_SCALE1;
    LOD2 = DEF_LOD2;
    SCALE2 = DEF_SCALE2;
    LOD3 = DEF_LOD3;
    SCALE3 = DEF_SCALE3;
    LOD4 = DEF_LOD4;
    SCALE4 = DEF_SCALE4;
    TEX_CLAMP = -1;
}


/******************************************************************************
*			    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[] = {"Drive", "Fly", "Trackball"};
static int viewModel_vals[] = {
    PFITDF_DRIVE, PFITDF_FLY, PFITDF_TRACKBALL};


static pfuPanel *
InitPanel(void)
{
    float  xo, yo, ys, xs;
    int x, y, yTop;
    pfuWidget *wid;
    pfuPanel *pan;
    
    pan = pfuNewPanel();
    
    pfuGetPanelOriginSize(pan, &xo, &yo, &xs, &ys);
    
    x = xo;
    yTop = (yo + ys);
    y = yTop - PFUGUI_BUTTON_YINC;
    
    // 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  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 = xo;
    y = yTop - 2*PFUGUI_BUTTON_YINC;
    
    // 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;
    
    // menu button - view model
    wid = pfuNewWidget(pan, PFUGUI_MENU_BUTTON, CTL_MODEL);
    pfuWidgetDim(wid,  x,  y,  PFUGUI_BUTTON_VLONG_XSIZE,
		 PFUGUI_BUTTON_YSIZE);
    pfuWidgetLabel(wid, "Motion");
    pfuWidgetActionFunc(wid, PanelControl);
    pfuWidgetSelections(wid, viewModel_strs, viewModel_vals, NULL, 3);
    Shared->wFModel = wid;
    x += PFUGUI_BUTTON_VLONG_XINC;
    
    // switch button - stats
    wid = pfuNewWidget(pan, PFUGUI_SWITCH, CTL_DIAGS);
    pfuWidgetDim(wid, x, y, PFUGUI_SCALE_XSIZE(80), PFUGUI_BUTTON_YSIZE);
    pfuWidgetLabel(wid, "Stats");
    pfuWidgetActionFunc(wid, PanelControl);
    Shared->wStats = wid;
    
    x = xo;
    y -= PFUGUI_SLIDER_YINC;
    
    // horizontal slider LOD Ctrl Pt 1
    wid = pfuNewWidget(pan, PFUGUI_SLIDER, CTL_LOD1_SLIDER);
    pfuWidgetDim(wid,  x,  y,  PFUGUI_SLIDER_XSIZE,  PFUGUI_SLIDER_YSIZE);
    pfuWidgetLabel(wid, "LOD Ctrl Pt 1");
    pfuWidgetRange(wid, 1, 0.0f, 6.5f, LOD1);
    pfuWidgetActionFunc(wid, PanelControl);
    
    x += PFUGUI_SLIDER_XINC;
    
    // horizontal slider SCALE Ctrl Pt 1
    wid = pfuNewWidget(pan, PFUGUI_SLIDER, CTL_SCALE1_SLID);
    pfuWidgetDim(wid,  x,  y,  PFUGUI_SLIDER_XSIZE,  PFUGUI_SLIDER_YSIZE);
    pfuWidgetLabel(wid, "SCALE Ctrl Pt 1");
    pfuWidgetRange(wid, 1, 0.0f, 6.5f, SCALE1);
    pfuWidgetActionFunc(wid, PanelControl);
    
    x = xo;
    y -= PFUGUI_SLIDER_YINC;
    
    // horizontal slider LOD Ctrl Pt 2
    wid = pfuNewWidget(pan, PFUGUI_SLIDER, CTL_LOD2_SLIDER);
    pfuWidgetDim(wid,  x,  y,  PFUGUI_SLIDER_XSIZE,  PFUGUI_SLIDER_YSIZE);
    pfuWidgetLabel(wid, "LOD Ctrl Pt 2");
    pfuWidgetRange(wid, 1, 0.0f, 6.5f, LOD2);
    pfuWidgetActionFunc(wid, PanelControl);
    
    x += PFUGUI_SLIDER_XINC;
    
    // horizontal slider SCALE Ctrl Pt 2
    wid = pfuNewWidget(pan, PFUGUI_SLIDER, CTL_SCALE2_SLID);
    pfuWidgetDim(wid,  x,  y,  PFUGUI_SLIDER_XSIZE,  PFUGUI_SLIDER_YSIZE);
    pfuWidgetLabel(wid, "SCALE Ctrl Pt 2");
    pfuWidgetRange(wid, 1, 0.0f, 6.5f, SCALE2);
    pfuWidgetActionFunc(wid, PanelControl);
    
    x = xo;
    y -= PFUGUI_SLIDER_YINC;
    
    // horizontal slider LOD Ctrl Pt 3
    wid = pfuNewWidget(pan, PFUGUI_SLIDER, CTL_LOD3_SLIDER);
    pfuWidgetDim(wid,  x,  y,  PFUGUI_SLIDER_XSIZE,  PFUGUI_SLIDER_YSIZE);
    pfuWidgetLabel(wid, "LOD Ctrl Pt 3");
    pfuWidgetRange(wid, 1, 0.0f, 6.5f, LOD3);
    pfuWidgetActionFunc(wid, PanelControl);
    
    x += PFUGUI_SLIDER_XINC;
    
    // horizontal slider SCALE Ctrl Pt 3
    wid = pfuNewWidget(pan, PFUGUI_SLIDER, CTL_SCALE3_SLID);
    pfuWidgetDim(wid,  x,  y,  PFUGUI_SLIDER_XSIZE,  PFUGUI_SLIDER_YSIZE);
    pfuWidgetLabel(wid, "SCALE Ctrl Pt 3");
    pfuWidgetRange(wid, 1, 0.0f, 6.5f, SCALE3);
    pfuWidgetActionFunc(wid, PanelControl);
    
    x = xo;
    y -= PFUGUI_SLIDER_YINC;
    
    // horizontal slider LOD Ctrl Pt 4
    wid = pfuNewWidget(pan, PFUGUI_SLIDER, CTL_LOD4_SLIDER);
    pfuWidgetDim(wid,  x,  y,  PFUGUI_SLIDER_XSIZE,  PFUGUI_SLIDER_YSIZE);
    pfuWidgetLabel(wid, "LOD Ctrl Pt 4");
    pfuWidgetRange(wid, 1, 0.0f, 6.5f, LOD4);
    pfuWidgetActionFunc(wid, PanelControl);
    
    x += PFUGUI_SLIDER_XINC;
    
    // horizontal slider SCALE Ctrl Pt 4
    wid = pfuNewWidget(pan, PFUGUI_SLIDER, CTL_SCALE4_SLID);
    pfuWidgetDim(wid,  x,  y,  PFUGUI_SLIDER_XSIZE,  PFUGUI_SLIDER_YSIZE);
    pfuWidgetLabel(wid, "SCALE Ctrl Pt 4");
    pfuWidgetRange(wid, 1, 0.0f, 6.5f, SCALE4);
    pfuWidgetActionFunc(wid, PanelControl);
    
    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_DIAGS:
	if (pfuGetWidgetValue(w) == 0.0f)
	    Shared->drawStats = 0;
	else
	    Shared->drawStats = 1;
	break;
    case CTL_LOD1_SLIDER:
	LOD1 = pfuGetWidgetValue(w);
	Detail_mod = TRUE;
	/*
	    pfNotify(PFNFY_DEBUG, PFNFY_PRINT,
		     "Have GUI Widget \"%s\",  id=%d, val = %f\n",
		     pfuGetWidgetLabel(w),
		     pfuGetWidgetId(w), pfuGetWidgetValue(w));
	*/
	break;
    case CTL_SCALE1_SLID:
	SCALE1 = pfuGetWidgetValue(w);
	Detail_mod = TRUE;
	/*
	    pfNotify(PFNFY_DEBUG, PFNFY_PRINT,
		     "Have GUI Widget \"%s\",  id=%d, val = %f\n",
		     pfuGetWidgetLabel(w),
		     pfuGetWidgetId(w), pfuGetWidgetValue(w));
	*/
	break;
    case CTL_LOD2_SLIDER:
	LOD2 = pfuGetWidgetValue(w);
	Detail_mod = TRUE;
	/*
	    pfNotify(PFNFY_DEBUG, PFNFY_PRINT,
		     "Have GUI Widget \"%s\",  id=%d, val = %f\n",
		     pfuGetWidgetLabel(w),
		     pfuGetWidgetId(w), pfuGetWidgetValue(w));
	*/
	break;
    case CTL_SCALE2_SLID:
	SCALE2 = pfuGetWidgetValue(w);
	Detail_mod = TRUE;
	/*
	    pfNotify(PFNFY_DEBUG, PFNFY_PRINT,
		     "Have GUI Widget \"%s\",  id=%d, val = %f\n",
		     pfuGetWidgetLabel(w),
		     pfuGetWidgetId(w), pfuGetWidgetValue(w));
	*/
	break;
    case CTL_LOD3_SLIDER:
	LOD3 = pfuGetWidgetValue(w);
	Detail_mod = TRUE;
	/*
	    pfNotify(PFNFY_DEBUG, PFNFY_PRINT,
		     "Have GUI Widget \"%s\",  id=%d, val = %f\n",
		     pfuGetWidgetLabel(w),
		     pfuGetWidgetId(w), pfuGetWidgetValue(w));
	*/
	break;
    case CTL_SCALE3_SLID:
	SCALE3 = pfuGetWidgetValue(w);
	Detail_mod = TRUE;
	/*
	    pfNotify(PFNFY_DEBUG, PFNFY_PRINT,
		     "Have GUI Widget \"%s\",  id=%d, val = %f\n",
		     pfuGetWidgetLabel(w),
		     pfuGetWidgetId(w), pfuGetWidgetValue(w));
	*/
	break;
    case CTL_LOD4_SLIDER:
	LOD4 = pfuGetWidgetValue(w);
	Detail_mod = TRUE;
	/*
	    pfNotify(PFNFY_DEBUG, PFNFY_PRINT,
		     "Have GUI Widget \"%s\",  id=%d, val = %f\n",
		     pfuGetWidgetLabel(w),
		     pfuGetWidgetId(w), pfuGetWidgetValue(w));
	*/
	break;
    case CTL_SCALE4_SLID:
	SCALE4 = pfuGetWidgetValue(w);
	Detail_mod = TRUE;
	/*
	    pfNotify(PFNFY_DEBUG, PFNFY_PRINT,
		     "Have GUI Widget \"%s\",  id=%d, val = %f\n",
		     pfuGetWidgetLabel(w),
		     pfuGetWidgetId(w), pfuGetWidgetValue(w));
	*/
	break;
    default:
	/*
	    pfNotify(PFNFY_DEBUG, PFNFY_PRINT,
		     "Have GUI Widget \"%s\": id=%d, val = %d\n",
		     pfuGetWidgetLabel(w), pfuGetWidgetId(w), pfuGetWidgetValue(w));
	*/
	break;
    }
    // Check if detail texture sliders changed, if so
    // modify pfTexSpline
    //
    if (Detail_mod)
    {
	ctl_pts[0].set(-LOD1, SCALE1);
	ctl_pts[1].set(-LOD2, SCALE2);
	ctl_pts[2].set(-LOD3, SCALE3);
	ctl_pts[3].set(-LOD4, SCALE4);
	tex->setSpline(PFTEX_DETAIL_SPLINE, ctl_pts, TEX_CLAMP);
	Detail_mod = FALSE;
	
	/* pfNotify(PFNFY_DEBUG,PFNFY_PRINT,"Updating pfTexSpline\n"); */
    }
}

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)
{
    static const pfVec4  clrt(0.15f, 0.15f, 0.15f, 1.0f);
    static char msg[] = "Welcome to OpenGL Performer";
    
    // use pfu utility to open the window so that the pfu input
    // handler will be able to attach to it
    //
    pw->open();
    
    // Draw introductory message
    pfClear(PFCL_COLOR, &clrt);
    // Message will be draw in in purple - the pfuDrawMessage() color
    pfuDrawMessage(NULL, msg, PFU_MSG_PIPE, PFU_CENTER_JUSTIFIED, 
		   0.5f, 0.5f, PFU_FONT_BIG, PFU_RGB);
    pw->swapBuffers();
}

/******************************************************************************
*			    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.10f, 0.10f, 0.10f, 1.0f);
    
    // erase framebuffer and draw Earth-Sky model
    pfClear(PFCL_COLOR | PFCL_DEPTH, &bgclr);
    
    // invoke Performer draw-processing for this frame
    pfDraw();
    
    {
	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();
    
    // 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();
}

//
// texcube.c		routine for constructing a textured cube geoset
//
//

static pfGroup*
MakeTexCube()
{
    pfGeode *thiscube = new pfGeode;
    pfGroup *cube = new pfGroup;
    pfGeoSet *gset = new pfGeoSet;
    
    pfVec3 *verts = (pfVec3*) new(8*sizeof(pfVec3)) pfMemory;
    verts[0].set(-3.0f, -3.0f,  3.0f);
    verts[1].set( 3.0f, -3.0f,  3.0f);
    verts[2].set( 3.0f,  3.0f,  3.0f);
    verts[3].set(-3.0f,  3.0f,  3.0f);
    verts[4].set(-3.0f, -3.0f, -3.0f);
    verts[5].set( 3.0f, -3.0f, -3.0f);
    verts[6].set( 3.0f,  3.0f, -3.0f);
    verts[7].set(-3.0f,  3.0f, -3.0f);
    
    ushort *vindex = (ushort*) new(24*sizeof(ushort)) pfMemory;
    vindex[0]  = 0; vindex[1]  = 1; vindex[2]  = 2; vindex[3]  = 3; /* front */
    vindex[4]  = 0; vindex[5]  = 3; vindex[6]  = 7; vindex[7]  = 4; /* left */
    vindex[8]  = 4; vindex[9]  = 7; vindex[10] = 6; vindex[11] = 5; /* back */
    vindex[12] = 1; vindex[13] = 5; vindex[14] = 6; vindex[15] = 2; /* right */
    vindex[16] = 3; vindex[17] = 2; vindex[18] = 6; vindex[19] = 7; /* top */
    vindex[20] = 0; vindex[21] = 4; vindex[22] = 5; vindex[23] = 1; /* bottom */
    
    pfVec3 *norms = (pfVec3*) new(6*sizeof(pfVec3)) pfMemory;
    norms[0].set( 0.0f,  0.0f,  1.0f);
    norms[1].set( 0.0f,  0.0f, -1.0f);
    norms[2].set( 0.0f,  1.0f,  0.0f);
    norms[3].set( 0.0f, -1.0f,  0.0f);
    norms[4].set( 1.0f,  0.0f,  0.0f);
    norms[5].set(-1.0f,  0.0f,  0.0f);
    
    ushort *nindex = (ushort*) new(24*sizeof(ushort)) pfMemory;
    nindex[0]  = nindex[1]  = nindex[2]  = nindex[3]  = 0;
    nindex[4]  = nindex[5]  = nindex[6]  = nindex[7]  = 5;
    nindex[8]  = nindex[9]  = nindex[10] = nindex[11] = 1;
    nindex[12] = nindex[13] = nindex[14] = nindex[15] = 4;
    nindex[16] = nindex[17] = nindex[18] = nindex[19] = 2;
    nindex[20] = nindex[21] = nindex[22] = nindex[23] = 3;
    
    pfVec2 *tcoords = (pfVec2*) new(4*sizeof(pfVec2)) pfMemory;
    tcoords[0].set(0.0f, 0.0f);
    tcoords[1].set(1.0f, 0.0f);
    tcoords[2].set(1.0f, 1.0f);
    tcoords[3].set(0.0f, 1.0f);
    
    ushort *tindex = (ushort*) new(24*sizeof(ushort)) pfMemory;
    tindex[0]  = 0; tindex[1]  = 1; tindex[2]  = 2; tindex[3]  = 3;
    tindex[4]  = 0; tindex[5]  = 1; tindex[6]  = 2; tindex[7]  = 3;
    tindex[8]  = 0; tindex[9]  = 1; tindex[10] = 2; tindex[11] = 3;
    tindex[12] = 0; tindex[13] = 1; tindex[14] = 2; tindex[15] = 3;
    tindex[16] = 0; tindex[17] = 1; tindex[18] = 2; tindex[19] = 3;
    tindex[20] = 0; tindex[21] = 1; tindex[22] = 2; tindex[23] = 3;
    
    //
    // set the coordinate, normal and color arrays
    // and their corresponding index arrays
    //
    gset->setAttr(PFGS_COORD3, PFGS_PER_VERTEX, verts, vindex);
    gset->setAttr(PFGS_NORMAL3, PFGS_PER_PRIM, norms, nindex);
    gset->setAttr(PFGS_TEXCOORD2, PFGS_PER_VERTEX, tcoords, tindex);
    gset->setPrimType(PFGS_QUADS);
    gset->setNumPrims(6);
    
    //
    // create a geostate from shared memory, enable
    // texturing (in case that's not the default), and
    // set the geostate for this geoset
    //
    pfGeoState *gstate = new pfGeoState;
    
    //
    // create a new texture from shared memory,
    // load a texture file and add texture to geostate
    //
    tex = new pfTexture;
    if( !(tex->loadFile(TexName)) )
    {
	pfNotify(PFNFY_NOTICE, PFNFY_RESOURCE,
		 "Detail: could not load base texture %s. Exiting",TexName);
    }
    dtex = new pfTexture;
    if( !(dtex->loadFile(DTexName)) )
    {
	pfNotify(PFNFY_NOTICE, PFNFY_RESOURCE,
		 "Detail: could not load detail texture %s. Exiting",DTexName);
    }
    else
    {
	HaveDetail = 1;
	tex->setDetail(PFTEX_DEFAULT, dtex);
    }
    gstate->setMode(PFSTATE_ENTEXTURE,1);
    gstate->setAttr(PFSTATE_TEXTURE, tex);
    
    // defining the detail texture spline
    tex->setSpline(PFTEX_DETAIL_SPLINE, ctl_pts, TEX_CLAMP);
    // if texture has alpha component, enable transparency  and alpha func
    {
	uint *i;
	int nc, sx, sy, sz;
	tex->getImage(&i, &nc, &sx, &sy, &sz);
	// if have alpha channel, enable transparency
	if (nc != 3)
	{
	    gstate->setMode(PFSTATE_TRANSPARENCY, PFTR_FAST);
	    // set alpha function to block pixels of 0 alpha for 
	    // transparent textures
	    gstate->setMode(PFSTATE_ALPHAFUNC, PFAF_NOTEQUAL);
	    gstate->setVal(PFSTATE_ALPHAREF, 0.0f);
	}
    }
    
    //
    // create a new texture environment from shared memory,
    // decal the texture since the geoset has no color to
    //
    pfTexEnv *tenv = new pfTexEnv;
    tenv->setMode(PFTE_DECAL);
    gstate->setAttr(PFSTATE_TEXENV, tenv);
    
    gset->setGState(gstate);
    
    thiscube->addGSet(gset);
    
    cube->addChild(thiscube);
    
    return cube;
}