[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:36 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: detail.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.h>
#include <Performer/pfui.h>
#include <Performer/pfdu.h>

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

/* Size of cube */
#define CUBE_SIZE	3.0f

/* 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
{
    /* 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
 ******************************************************************************
 */
 

static pfVec3         verts[] ={{-CUBE_SIZE,-CUBE_SIZE,  CUBE_SIZE},
				{ CUBE_SIZE,-CUBE_SIZE,  CUBE_SIZE},
				{ CUBE_SIZE, CUBE_SIZE,  CUBE_SIZE},
				{-CUBE_SIZE, CUBE_SIZE,  CUBE_SIZE},
				{-CUBE_SIZE,-CUBE_SIZE, -CUBE_SIZE},
				{ CUBE_SIZE,-CUBE_SIZE, -CUBE_SIZE},
				{ CUBE_SIZE, CUBE_SIZE, -CUBE_SIZE},
				{-CUBE_SIZE, CUBE_SIZE, -CUBE_SIZE}};

static ushort         vindex[] ={0, 1, 2, 3,	/* front */
				 0, 3, 7, 4,	/* left */
				 4, 7, 6, 5,	/* back */
				 1, 5, 6, 2,	/* right */
				 3, 2, 6, 7,	/* top */
				 0, 4, 5, 1};	/* bottom */

static pfVec3         norms[] ={{ 0.0f, 0.0f, 1.0f},
				{ 0.0f, 0.0f,-1.0f},
				{ 0.0f, 1.0f, 0.0f},
				{ 0.0f,-1.0f, 0.0f},
				{ 1.0f, 0.0f, 0.0f},
				{-1.0f, 0.0f, 0.0f}};

static ushort         nindex[] ={0, 5, 1, 4, 2, 3};

static pfVec2         tcoords[] ={{0.0f, 0.0f},
				  {1.0f, 0.0f},
				  {1.0f, 1.0f},
				  {0.0f, 1.0f}};

static ushort         tindex[] ={0, 1, 2, 3,
				 0, 1, 2, 3,
				 0, 1, 2, 3,
				 0, 1, 2, 3,
				 0, 1, 2, 3,
				 0, 1, 2, 3};


/* 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 */
static int XInput = PFUINPUT_X;
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;
static int NumCPUs=0;

/* global name strings */
static char TexName[PF_MAXSTRING] = {0};
static char DTexName[PF_MAXSTRING] = {0};
static char ProgName[PF_MAXSTRING] = {0};
static char Name[PF_MAXSTRING] = "Performer Detail Texture Test";

/*
* X Display for opening window and input.  In MP mode, each
* process separately opens the display.
*/

static HaveDetail = 0;

static void CullChannel(pfChannel *chan, void *data);
static void DrawChannel(pfChannel *chan, void *data);
static void OpenPipeWin(pfPipeWindow *pw);
static void InitGraphics(void);
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 (void *);

/******************************************************************************
 *			    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':
	      XInput = PFUINPUT_X;
	      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;
    pfGroup	   *a_tex_cube;
    pfChannel      *chan;
    pfScene        *scene;
    pfPipe         *p;
    pfPipeWindow   *pw;
    pfBox           bbox;
    float	    far = 10000.0f;
    pfFrameStats   *stats;
    void	   *arena;


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

    /* are there two arguments remaining ? */
    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 */
    pfSetVec2(ctl_pts[0], -LOD1, SCALE1);
    pfSetVec2(ctl_pts[1], -LOD2, SCALE2);
    pfSetVec2(ctl_pts[2], -LOD3, SCALE3);
    pfSetVec2(ctl_pts[3], -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 */
    p = pfGetPipe(0);
    pw = pfNewPWin(p);
    pfPWinName(pw,ProgName);
    pfPWinOriginSize(pw, 
	WinXOrigin,             WinYOrigin, 
	WinXOrigin +  WinXSize, WinYOrigin +  WinYSize);
    if (Shared->input == PFUINPUT_X)
	pfPWinType(pw, PFPWIN_TYPE_X);
    pfPWinConfigFunc(pw, OpenPipeWin);
    pfConfigPWin(pw);
    
    pfuInitGUI(pw); 
    pfuInitInput(pw, Shared->input);

    pfFrame();
    
    /*
     * App Process Setup
     */

    /* get the malloc arena */
    arena = pfGetSharedArena();

    scene = pfNewScene();
    Shared->sceneDCS = pfNewDCS();
    pfAddChild(scene, Shared->sceneDCS);

    a_tex_cube = MakeTexCube(arena);
    pfAddChild(Shared->sceneDCS, 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.
     */
    Shared->chan = chan = pfNewChan(p);
    pfChanTravFunc(chan, PFTRAV_CULL, CullChannel);
    pfChanTravFunc(chan, PFTRAV_DRAW, DrawChannel);
    pfChanScene(chan, scene);
    pfChanNearFar(chan, 0.1f, far);
    /* vertical FOV is matched to window aspect ratio */
    pfChanFOV(chan, 45.0f, -1.0f);
    
    
    /* Initialize sun - create a light source in the "south-west" (QIII) */
    Shared->sun = pfNewLSource();
    pfLSourcePos(Shared->sun, -0.3f, -0.3f, 1.0f, 0.0f);
    pfAddChild(Shared->scene, Shared->sun);
    
    stats = pfGetChanFStats(chan);
    pfFStatsClass(stats, 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 */
	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;
	pfAddVec3(view, bbox.min, bbox.max);
	pfScaleVec3(Shared->viewCoord.xyz, 0.5f, view);

	/* offset so all is visible */
	Shared->viewCoord.xyz[PF_Y] -=      sceneSize;
	Shared->viewCoord.xyz[PF_Z] += 0.25f*sceneSize;

	pfCopyVec3(Shared->initView.xyz, Shared->viewCoord.xyz);
	pfSetVec3(Shared->initView.hpr, 0.0f, 0.0f, 0.0f);
    }
    pfCopyVec3(Shared->startXYZ, Shared->initView.xyz);

    pfChanViewport(Shared->chan, 0.0f, 1.0f, 0.3, 1.0f);
    pfuGUIViewport(0.0f, 1.0f, 0.0f, 0.3);
    /* 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);
		pfChanViewport(Shared->chan, 0.0f, 1.0f, top, 1.0f);
	    }
	    else
	    {
		pfuEnableGUI(FALSE);
		pfChanViewport(Shared->chan, 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());

    pfiXformerAutoInput(Shared->xformer, Shared->chan,
	&Shared->mouse, &Shared->events);

    pfiXformerNode(Shared->xformer, Shared->scene);
    pfiXformerAutoPosition(Shared->xformer, Shared->chan, Shared->sceneDCS);
    pfiXformerResetCoord(Shared->xformer, &Shared->initView);
    xformerModel(Shared->xformerModel, Shared->collideMode);
}

static void
IsectFunc(void *data)
{
    (data, data);
    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)
	pfiSelectXformerModel(Shared->xformer, model);
    
    /* Collide with objects in scene */
    if (oldCollMode != collideMode)
    {
	if (collideMode == PF_ON)
	    pfiEnableXformerCollision(Shared->xformer);
	else
	    pfiDisableXformerCollision(Shared->xformer);
	oldCollMode = collideMode;
    }
}

/*
 * Update the current view
 */
static void
UpdateView(pfChannel *chan, pfScene *scene)
{
    (scene);
    
    pfiUpdateXformer(Shared->xformer);
    Shared->xformerModel = pfiGetXformerCurModelIndex(Shared->xformer);
    
    /* if have moving-eyepoint motion model, update eyepoint */
    if (pfIsOfType(pfiGetXformerCurModel(Shared->xformer), 
	    pfiGetIXformTravelClassType()))
    {
	pfiGetXformerCoord(Shared->xformer, &Shared->viewCoord);
    }
}


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

    /* 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 ' ':
			    pfiStopXformer(Shared->xformer);
			    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 pfuGUIString num_strs[] = {"One", "Two", "Three"};
static int num_vals[] = {1, 2, 3};
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 LOD2 = %f\n",
	    pfuGetWidgetLabel(w),
	    pfuGetWidgetId(w), pfuGetWidgetValue(w), LOD2);
	*/
	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)
    {
       pfSetVec2(ctl_pts[0], -LOD1, SCALE1);
       pfSetVec2(ctl_pts[1], -LOD2, SCALE2);
       pfSetVec2(ctl_pts[2], -LOD3, SCALE3);
       pfSetVec2(ctl_pts[3], -LOD4, SCALE4);
       pfTexSpline(tex, 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 GL, 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
     */
    pfOpenPWin(pw);

     /* 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);
    pfSwapPWinBuffers(pw);
}

/******************************************************************************
 *			    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 *data)
{
    static pfVec4 bgclr = {0.10f, 0.10f, 0.10f, 1.0f};
    (data);
    
    /* erase framebuffer and draw Earth-Sky model */
    pfClear(PFCL_COLOR | PFCL_DEPTH, bgclr);

    /* invoke Performer draw-processing for this frame */
    pfDraw();
    
   {
   int err;
   if ((err = glGetError()) != 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)
	pfDrawChanStats(channel);

    /* 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 *channel, void *data)
{
    (channel, data);
    
    /*
     * 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(void *arena)
{
    pfGroup *cube;
    pfGeoSet *gset;
    pfGeoState *gstate;
    pfTexEnv *tenv;
    pfGeode *thiscube = pfNewGeode();
    void *sharedArena = pfGetSharedArena();

    cube = pfNewGroup();

    gset = pfNewGSet(arena);

    /*
     * set the coordinate, normal and color arrays
     * and their corresponding index arrays
     */
    pfGSetAttr(gset, PFGS_COORD3, PFGS_PER_VERTEX, verts, vindex);
    pfGSetAttr(gset, PFGS_NORMAL3, PFGS_PER_PRIM, norms, nindex);
    pfGSetAttr(gset, PFGS_TEXCOORD2, PFGS_PER_VERTEX, tcoords, tindex);
    pfGSetPrimType(gset, PFGS_QUADS);
    pfGSetNumPrims(gset, 6);

    /*
     * create a geostate from shared memory, enable
     * texturing (in case that's not the default), and
     * set the geostate for this geoset
     */
    gstate = pfNewGState(arena);

    /*
     * create a new texture from shared memory,
     * load a texture file and add texture to geostate
     */
    tex = pfNewTex(arena);
    if (!(pfLoadTexFile(tex, TexName)))
    {
       pfNotify(PFNFY_NOTICE,PFNFY_RESOURCE,
       "Detail: could not load base texture %s. Exiting.",TexName);
    }
    dtex = pfNewTex(arena);
    if (!(pfLoadTexFile(dtex, DTexName)))
    {
       pfNotify(PFNFY_NOTICE,PFNFY_RESOURCE,
       "Detail: could not load detail texture %s.",DTexName);
    } 
    else
    {
	HaveDetail = 1;
	pfTexDetail(tex, PFTEX_DEFAULT, dtex);
    }
    pfGStateMode(gstate, PFSTATE_ENTEXTURE, 1);
    pfGStateAttr(gstate, PFSTATE_TEXTURE, tex);

    /* defining the detail texture spline */
    pfTexSpline(tex, PFTEX_DETAIL_SPLINE, ctl_pts, TEX_CLAMP);
    /* if texture has alpha component, enable transparency  and alpha func*/
    {
	uint *i;
	int nc, sx, sy, sz;
	pfGetTexImage(tex, &i, &nc, &sx, &sy, &sz);
	/* if have alpha channel, enable transparency */
	if (nc != 3)
	{
	    pfGStateMode (gstate, PFSTATE_TRANSPARENCY, PFTR_FAST);
	    /* set alpha function to block pixels of 0 alpha for 
		transparent textures */
	    pfGStateMode (gstate, PFSTATE_ALPHAFUNC, PFAF_NOTEQUAL);
	    pfGStateVal (gstate, PFSTATE_ALPHAREF, 0.0f);
	}
    }

    /*
    * create a new texture environment from shared memory,
    * decal the texture since the geoset has no color to
    */
    tenv = pfNewTEnv(arena);
    pfTEnvMode(tenv, PFTE_DECAL);
    pfGStateAttr(gstate, PFSTATE_TEXENV, tenv);

    pfGSetGState(gset, gstate);

    pfAddGSet (thiscube, gset);

    pfAddChild (cube, thiscube);

    return cube;
}