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

File: [Development] / performer / src / pguide / libpf / C / stats.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: stats.c
 * -------------
 * 
 * OpenGL Performer example using cull and draw process callbacks
 * and demonstrating Performer Statistics and Debugging utilities.
 * 
 * $Revision: 1.1 $ 
 * $Date: 2000/11/21 21:39:37 $
 * 
 * Commandline options:
 * --------------------
 *  'e': use an earth-sky model instead of just clearing screen
 *  'D': set on PFMP_CULL_DL_DRAW
 *  'o': turn on PFMP_CULLoDRAW - needs forked draw
 *  'p' or 'm' : arg to pfMultiprocess
 *  'S': do sleeps in APP, CULL, and DRAW so can see mp history timing lines
 *  'w': write out scene to scene.out at start of prog
 *  '?': get usage
 *
 * Run-time controls:
 * -------------------
 *    Left-mouse: advance
 *  Middle-mouse: stop
 *   Right-mouse: retreat
 * PRINTSCREEN-key: snap picture of window to a stats.%d.rgb file
 *       ESC-key: exits
 *        F1-key: profile
 *	  F2-key: toggle use of second channel for stats
 *    UPARROWKEY: increase number of frames of stats PFTIMES history 
 *  DOWNARROWKEY: decrease number of frames of stats PFTIMES history 
 *	      p/P: print currently enabled statistics to stderr
 *	      a/A: toggle entire statistics class enable
 *	      s/S: toggle system CPU statistics class enable
 *	      d/D: toggle database statistics class enable
 *	      c/C: toggle cull statistics class enable
 *	      f/F: toggle fill stats - depth complexity
 *	        g: toggle graphics statistics class enable
 *	        G: toggle geometry attribute statistics - counts for 
 *				normals, colors, texcoords)
 *	      h/F: toggle proc frame time history stats - the timing graph
 *	      m/M: toggle mp process frame time stats - all frame time stats
 *	      q/Q: do a pfQuery and print results to stderr
 *	      r/R: reset view
 *	        t: toggle tmesh lengths graph
 *		T: toggle transparency fill stats -  is part of fill stats
 *	      v/V: toggle Database Node Visibility stats
 *	      z/Z: toggle z-compare check in fill stats
 */

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <getopt.h> /* for cmdline handler */

#include <X11/X.h>
#include <X11/keysym.h>

#include <Performer/pf.h>
#include <Performer/pfutil.h>
#include <Performer/pfdu.h>

static void CullChannel(pfChannel *chan, void *data);
static void DrawChannel(pfChannel *chan, void *data);
static void IsectFunc(void *data);
static void DrawStatsChannel (pfChannel *channel, void *data);
static void CullStatsChannel(pfChannel * ch, void *data);
static void OpenPipeWin(pfPipeWindow *pw);
static void UpdateView(void);
static void GetXInput(void);
static void Usage(void);
static void EnableStatsChan(int flag);


#define HISPEED		0.1f
#define LOSPEED		0.001f


/*
 * structure that resides in shared memory so that the
 * application, cull, and draw processes can access it.
 */
typedef struct
{
    pfPipeWindow *pw;
    int        exitFlag;
    int        reset;
    int        inWindow;
    float       mouseX;
    float	mouseY;
    int		mouseButtons;
    int		winSizeX;
    int		winSizeY;
    pfCoord	view;
    float	sceneSize;
    int		drawStats;
    pfFrameStats *frameStats;
    int		statsInterval;
    int		statsEnable, statsClass, statsClassMode;
    int		statsHistFrames;
    int		printStats;
    int		doStatsQuery;
    pfChannel	*chan, *statsChan;
    int	haveStatsChan;
    float	speed;
    int		snapImage;
    int         Sleep;
    float	SleepApp;
    float	SleepDraw;
    float	SleepCull;
    float	SleepIsect;
} SharedData;

static SharedData *Shared;

/* light source created and updated in draw-process */

static pfLight *Sun;
static int ProcSplit = PFMP_DEFAULT;
static int CulloDraw = 0;
static int CullDLDraw = 0;
static int WriteScene = 0;
static int ESky = 0;
static int CachedCull = 0;
static int WinOriginX=0, WinOriginY=0, WinSizeX=800, WinSizeY=600;
static int WinType = PFPWIN_TYPE_X;

static void
Nap(double t)
{
    static int	firstTime = 1, nSpins = 1;
    int		i;
    double	start;

    if (firstTime)
    {
	char	*spin;

	firstTime = 0;
	if (spin = getenv("PFSPINCOUNT"))
	    nSpins = atoi(spin);
    }
 
    if (nSpins < 0)
    {
	sginap(1);
	return;
    }

    t *= 0.001;

    start = pfGetTime();

    do
    {    
    	for (i=0; i<nSpins; i++)
	    sginap(0);

    } while (pfGetTime() - start < t);
}


/******************************************************************************
 *			    Main and Commandline Processing
 ******************************************************************************
 */
char ProgName[PF_MAXSTRING];
char OptionStr[] = "CDeom:p:Swx?";


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

static void
Usage (void)
{
    fprintf(stderr, 
	    "Usage: %s [-ewS] [-p procSplit] [file.ext ...]\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 'e': /* use an earth-sky model instead of just clearing screen */
	    ESky = 1;
	    break;
	case 'D':  /* set on PFMP_CULL_DL_DRAW */
	    CullDLDraw = PFMP_CULL_DL_DRAW;
	    break;
	case 'C': /* Cache cull results */
	    CachedCull = 1;
	    break;
	case 'o':  /* turn on PFMP_CULLoDRAW - needs forked draw */
	    CulloDraw = PFMP_CULLoDRAW;
	    break;
	case 'm': /* arg to pfMultiprocess */
	case 'p': /* arg to pfMultiprocess */
	    ProcSplit = atoi(optarg);
	    break;
	case 'S': /* do sleeps in APP,CULL, and DRAW so can see app timing lines */
	    Shared->Sleep ^= 1;
	    break;
	case 'w': /* write out scene to scene.out at start of prog */
	    WriteScene = 1;
	    break;
	case 'W': /* Set the window size */
	    if (sscanf(optarg, "%ld,%ld,%ld,%ld", 
		    &WinOriginX, &WinOriginY,
		    &WinSizeX, &WinSizeY) != 4)
	    {
		if (sscanf(optarg, "%ld,%ld", &WinSizeX, &WinSizeY) != 2)
		{
		    if (sscanf(optarg, "%ld", &WinSizeX) == 1)
		    {
			WinSizeY = WinSizeX;
			WinOriginX = -1;
		    }
		    else
			WinSizeX = -1;
		} 
		else
		    WinOriginX = -1;
	    }
	    break;
	case 'x':
	    WinType ^= PFPWIN_TYPE_X;
	    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[])
{
    int		    arg;
    int		    found;
    pfNode	   *root;
    pfChannel      *chan, *statsChan;
    pfScene        *scene;
    pfPipe         *p;
    pfEarthSky     *eSky;
    pfBox           bbox;
    float	    far = 10000.0f;
    FILE	   *fp=0;
    pfCoord	    reset;
    
#if 0
    if (argc < 2)
	Usage();
#endif    

    arg = docmdline(argc, argv);
    
    pfInit();
    pfNotifyLevel(PFNFY_DEBUG);
    
    pfMultiprocess(ProcSplit | CulloDraw | CullDLDraw);
    
    /* allocate and init shared before fork()'ing parallel processes */
    Shared = (SharedData*)pfCalloc(1, sizeof(SharedData), pfGetSharedArena());
    Shared->haveStatsChan = 1;
    Shared->statsEnable = 0;
    Shared->statsClass = 0;
    Shared->statsHistFrames = 4;
    Shared->statsClassMode = 0;
    Shared->speed = 0.0f;
    
    Shared->winSizeX = WinSizeX;
    Shared->winSizeY = WinSizeY;

    /* Load all loader DSO's before pfConfig() forks */
    for (found =  arg; found < argc; found++)
	pfdInitConverter(argv[found]);
    
    /* initiate multi-processing mode set in pfMultiprocess call */
    pfConfig();
    
    scene = pfNewScene();
    
    if (arg < argc)
    {
	/* specify directories where geometry and textures exist */
	if (!(getenv("PFPATH")))
	    pfFilePath(
		       "."
		       ":./data"
		       ":../data"
		       ":../../../../data"
		       ":/usr/share/Performer/data"
		       );
	
	/* load files named by command line arguments */
	for (found = 0; arg < argc; arg++)
	{
	    if ((root = pfdLoadFile(argv[arg])) != NULL)
	    {
		pfAddChild(scene, root);
		found++;
	    }
	}
    }
    
    p = pfGetPipe(0);
    pfPhase(PFPHASE_FLOAT);
    
    Shared->pw = pfNewPWin(p);
    pfPWinName(Shared->pw, "OpenGL Performer");
    pfPWinType(Shared->pw, WinType);
    pfPWinOriginSize(Shared->pw, WinOriginX, WinOriginY, WinSizeX, WinSizeY);
    /* Open and configure the GL window. */
    pfPWinConfigFunc(Shared->pw, OpenPipeWin);
    pfConfigPWin(Shared->pw);
    
    pfFrameRate(15.0f);
    
    chan = pfNewChan(p);
    pfChanTravFunc(chan, PFTRAV_CULL, CullChannel);
    pfChanTravFunc(chan, PFTRAV_DRAW, DrawChannel);
    pfIsectFunc(IsectFunc);
    pfChanScene(chan, scene);
    pfChanNearFar(chan, 0.1f, far);
    /* vertical FOV is matched to window aspect ratio */
    pfChanFOV(chan, 45.0f, -1.0f);
    Shared->chan = chan;
    
    /* Create an earth/sky model that draws sky/ground/horizon */
    eSky = pfNewESky();
    if (ESky)
    {
	pfESkyMode(eSky, PFES_BUFFER_CLEAR, PFES_SKY_GRND);
	pfESkyAttr(eSky, PFES_GRND_HT, -10.0f);
    } else {
	pfESkyMode(eSky, PFES_BUFFER_CLEAR, PFES_FAST);
	pfESkyColor(eSky, PFES_CLEAR, .3f, .3f, .7f, 1.0f);
    }
   
    pfChanESky(chan, eSky);

    /* create stats channel */
    statsChan = pfNewChan(p);
    pfChanTravFunc(statsChan, PFTRAV_CULL, CullStatsChannel);
    pfChanTravFunc(statsChan, PFTRAV_DRAW, DrawStatsChannel);
    pfChanViewport(statsChan, 0.5, 1.0, 0.0, 1.0);
    /* don't do any statistics collection for the stats-display chan */
    pfFStatsClass(pfGetChanFStats(statsChan), PFSTATS_ALL, PFSTATS_OFF );
    Shared->statsChan = statsChan;

    pfChanScene(statsChan, scene);
    pfChanTravMode(statsChan, PFTRAV_DRAW, PFDRAW_OFF);
    pfChanStatsMode(statsChan, PFCSTATS_DRAW, PFSTATS_ALL );
    
    /* Set initial view to be "in front" of scene */
    if (found)
    {	
	pfVec3 view;
	float sceneSize;
	/* determine extent of scene's geometry */
	pfuTravCalcBBox(scene, &bbox);
	/* view point at center of bbox */
	pfAddVec3(view, bbox.min, bbox.max);
	pfScaleVec3(Shared->view.xyz, 0.5f, view);
	
	/* 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;
	
	/* offset so all is visible */
	Shared->view.xyz[PF_Y] -=      sceneSize;
	Shared->view.xyz[PF_Z] += 0.25f*sceneSize;
	pfSetVec3(Shared->view.hpr, 0.0f, 0.0f, 0.0f);
	pfChanView(chan, Shared->view.xyz, Shared->view.hpr);
    }
    else
    {
	pfSetVec3(Shared->view.xyz, 0.0f, 0.0f, 100.0f);
	PFSET_VEC3(bbox.min, -5000.0, -5000.0, -1000000.0);
	PFSET_VEC3(bbox.max, 5000.0, 5000.0, 10000000.0);
    }
    pfCopyVec3(reset.xyz, Shared->view.xyz);
    pfCopyVec3(reset.hpr, Shared->view.hpr);
     
    pfChanTravMode(chan, PFTRAV_CULL, PFCULL_ALL);
    
    /* initialize channel statistics for mail channel */
    Shared->frameStats = pfGetChanFStats(chan);
    /* this would enable only proc frame times stats to be
     * displayed in the main channel
     *
     * pfChanStatsMode(chan, PFCSTATS_DRAW, PFFSTATS_ENPFTIMES);
     */
    pfFStatsAttr(Shared->frameStats, PFFSTATS_UPDATE_FRAMES, 30.0f);
    
    if (found && WriteScene)
    {
	fp = fopen ("scene.out","w");
	pfPrint(scene, PFTRAV_SELF|PFTRAV_DESCEND, PFPRINT_VB_DEBUG, fp);
	fclose(fp);
    }
    {
	pfFrameStats *fstats = pfNewFStats();
	/* pfuTravCountDB enables GFX and DB stats and
	 * counts the scene stats into the CUR framestats 
	 */
	pfuTravCountDB((pfNode*)scene, fstats);
	/* print the enabled and counted stats */
	pfPrint(fstats, PFFSTATS_BUF_CUR | pfGetFStatsClass(fstats, PFSTATS_ALL),
			PFPRINT_VB_ON, fp);
	pfDelete(fstats);
    }
    
    if (CachedCull)
	pfuTravCachedCull((pfNode *) scene, 1);
	
    /* main simulation loop */
    while (!Shared->exitFlag)
    {
	/* sleep so can see line */
	if (Shared->Sleep)
	    Nap(Shared->SleepApp);
	
	/* wait until next frame boundary */
	pfSync();

	/* sleep so can see line */
	if (Shared->Sleep)
	    Nap(Shared->SleepApp);
	
	/* Set view parameters. */
	if (Shared->reset)
	{
	    pfCopyVec3(Shared->view.xyz, reset.xyz);
	    pfCopyVec3(Shared->view.hpr, reset.hpr);
	    Shared->reset = 0;
	}
	else
	    UpdateView();
	pfChanView(chan, Shared->view.xyz, Shared->view.hpr);
	if (CachedCull)
	{ /* Flag all channel viewpoints as dirty */
	    pfuChanCullCache  *ccc = pfGetChanData(chan);
	    ccc->chanId = 0;
	    ccc->updateFrame = pfGetFrameCount();
	    pfPassChanData(chan);
	}
	
	/* initiate traversal using current state */
	pfFrame();
	
	{ /* set stats channel */
	    static haveStatsChan = -1;
	    if (haveStatsChan != Shared->haveStatsChan)
		EnableStatsChan(Shared->haveStatsChan);
		haveStatsChan = Shared->haveStatsChan;
	}
	
	/* process stats selections */
	if (Shared->statsEnable)
	{
	    if (pfGetFStatsClass(Shared->frameStats, Shared->statsEnable))
		pfFStatsClass(Shared->frameStats, Shared->statsEnable, PFSTATS_OFF);
	    else
		pfFStatsClass(Shared->frameStats, Shared->statsEnable, PFSTATS_ON);
	    Shared->statsEnable = 0;
	}
	if (Shared->statsClassMode)
	{
	    /* toggle the class mode settings */
	    pfFStatsClassMode(Shared->frameStats, Shared->statsClass, Shared->statsClassMode, 
		!(pfGetFStatsClassMode(Shared->frameStats, Shared->statsClass) &
			Shared->statsClassMode));
	    Shared->statsClassMode = 0;
	}
	if (Shared->printStats)
	{ /* print all enabled statistics */
	    pfPrint(Shared->frameStats, 
			PFFSTATS_BUF_PREV | PFFSTATS_BUF_AVG |
			pfGetFStatsClass(Shared->frameStats, PFSTATS_ALL), 
			PFPRINT_VB_INFO, NULL);
	    Shared->printStats = 0;
	}
	if (Shared->doStatsQuery)
	{ /* show how to query a few individual statistics */
	  /* note: if the corresponding stats class and mode is not enabled
	   * 	then the query will simply return 0 for that value.
	   */
	    unsigned int  qtmp[5];
	    float ftmp[5];
	    int ret;

	    qtmp[0] = PFFSTATS_BUF_PREV | PFSTATSVAL_GFX_GEOM_TRIS;
	    qtmp[1] = PFFSTATS_BUF_PREV | PFFSTATSVAL_PFTIMES_PROC_TOTAL;
	    qtmp[2] = PFFSTATS_BUF_PREV | PFSTATSVAL_CPU_SYS_BUSY;
	    qtmp[3] = NULL;
	    ret = pfMQueryFStats(Shared->frameStats, qtmp, ftmp, 0);
	    fprintf(stderr, "ret = %d bytes\n", ret);
	    fprintf(stderr, "Query num tris: %.0f\n", ftmp[0]);
	    fprintf(stderr, "Query frame time: %.0f msecs\n", ftmp[1]*1000.0f);	
	    fprintf(stderr, "Query  sys busy: %.0f%%\n", ftmp[2]);	
	    Shared->doStatsQuery = 0;
	}
	if (pfGetFStatsClass(Shared->frameStats, PFFSTATS_ENPFTIMES) &&
	    (pfGetFStatsClassMode(Shared->frameStats, PFFSTATS_PFTIMES) &
		    PFFSTATS_PFTIMES_HIST) &&
		(((int) pfGetFStatsAttr(Shared->frameStats, 
			    PFFSTATS_PFTIMES_HIST_FRAMES))
		    != Shared->statsHistFrames))
	{
	    pfFStatsAttr(Shared->frameStats, PFFSTATS_PFTIMES_HIST_FRAMES, 
		    Shared->statsHistFrames);
	    Shared->statsHistFrames = pfGetFStatsAttr(Shared->frameStats, 
				    PFFSTATS_PFTIMES_HIST_FRAMES);
	    /* fprintf(stderr, "Set stats history frames to %d\n", 
		    Shared->statsHistFrames); */
	}
    }
    
    /* terminate cull and draw processes (if they exist) */
    pfExit();
    
    /* exit to operating system */
    return 0;
}

static void 
EnableStatsChan(int flag)
{
    if (!Shared->statsChan)
    {
	pfChanViewport(Shared->chan, 0.0, 1.0, 0.0, 1.0);
	return;
    }
    if (flag)
    {
	/* put view on the left,stats on the right */
	pfChanViewport(Shared->chan, 0.0, 0.5, 0.0, 1.0);
	pfChanTravMode(Shared->statsChan, PFTRAV_DRAW, PFDRAW_ON);
    } else {
	/* put view on the left,stats on the right */
	pfChanViewport(Shared->chan, 0.0, 1.0, 0.0, 1.0);
	pfChanTravMode(Shared->statsChan, PFTRAV_DRAW, PFDRAW_OFF);
    }
}

/* 
 *	UpdateView() updates the eyepoint based on the information
 *	placed in shared memory by GetInput().
 */
static void    
UpdateView(void)
{
    pfCoord *view = &Shared->view;
    float mx, my;
    float cp;

    if (!Shared->inWindow)
    {
	Shared->speed = 0.0f;
	return;
    }
    switch (Shared->mouseButtons)
    {
    case Button1Mask: /* LEFTMOUSE: faster forward or slower backward*/
	if (Shared->speed > 0.0f)
	    Shared->speed *= 1.2f;
	else
	    Shared->speed /= 1.2f;
	
	if (PF_ABSLT(Shared->speed, LOSPEED * Shared->sceneSize))
	    Shared->speed = LOSPEED * Shared->sceneSize;
	else if (Shared->speed >=  HISPEED * Shared->sceneSize)
	    Shared->speed = HISPEED * Shared->sceneSize;
	break;
    case Button2Mask:	/* MIDDLEMOUSE: stop moving and pick */
	Shared->speed = 0.0f;
	break;
    case Button3Mask: /* RIGHTMOUSE: faster backward or slower foreward*/
	if (Shared->speed < 0.0f)
	    Shared->speed *= 1.2f;
	else
	    Shared->speed /= 1.2f;
	
	if (PF_ABSLT(Shared->speed, LOSPEED * Shared->sceneSize))
	    Shared->speed = -LOSPEED * Shared->sceneSize;
	else if (Shared->speed <=  -HISPEED * Shared->sceneSize)
	    Shared->speed = -HISPEED * Shared->sceneSize;
	break;
    }

    if (Shared->mouseButtons & (Button1Mask | Button2Mask | Button3Mask))
    {
	/* update view direction */
	mx = 2.0f * (Shared->mouseX / (float)Shared->winSizeX) - 1.0f;
	my = 2.0f * (Shared->mouseY / (float)Shared->winSizeY) - 1.0f;
	view->hpr[PF_H] -= mx * PF_ABS(mx);
	view->hpr[PF_P]  = my * PF_ABS(my) * 90.0f;
	view->hpr[PF_R]  = 0.0f;
    }
    
    /* update view position */
    cp = cosf(PF_DEG2RAD(view->hpr[PF_P]));
    view->xyz[PF_X] += Shared->speed*sinf(-PF_DEG2RAD(view->hpr[PF_H]))*cp;
    view->xyz[PF_Y] += Shared->speed*cosf(-PF_DEG2RAD(view->hpr[PF_H]))*cp;
    view->xyz[PF_Z] += Shared->speed*sinf( PF_DEG2RAD(view->hpr[PF_P]));
}

/*
 *	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).
 */

Display *Dsp=NULL;

static void
OpenPipeWin(pfPipeWindow *pw)
{
    Window win;

    pfOpenPWin(pw);
    
    {  
	Dsp = pfGetCurWSConnection();
	win = pfGetPWinWSWindow(pw);
	XSelectInput(Dsp, win, StructureNotifyMask | PointerMotionMask |
			FocusChangeMask | ButtonPressMask | ButtonReleaseMask | 
			KeyPressMask);
	XMapWindow(Dsp, win);
    }
    /* create a light source in the "south-west" (QIII) */
    Sun = pfNewLight(NULL);
    pfLightPos(Sun, -0.3f, -0.3f, 1.0f, 0.0f);
}

static void
IsectFunc(void *data)
{
    /* sleep to show time in timing graph */
    Nap(2 * Shared->SleepIsect);
}

/*
 *	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)
{
    /* 
     * pfDrawGeoSet or other display listable Performer routines
     * could be invoked before or after pfCull()
     */
    
    pfCull();
    
    if (Shared->Sleep)
	Nap(Shared->SleepCull);
}

/*
 *	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)
{
    /* rebind light so it stays fixed in position */
    pfLightOn(Sun);

    /* erase framebuffer and draw Earth-Sky model */
    pfClearChan(channel);
    
    /* spin loop to show time before pfDraw() in timing graph */
    if (Shared->Sleep)
	Nap(Shared->SleepDraw);
     
    /* snap picture of screen */
    

    /* take snapshot of image */
    if (Shared->snapImage)
    {
	static char str[80];
	static count = 0;
	FILE *fp;
	pfPipe *cpipe = pfGetChanPipe(channel);
	int id = pfGetId(cpipe)*10 + count++;

	sprintf(str, "stats.%d.rgb", id);
        fprintf(stderr, "Saving pipe %d image in file %s\n",
		pfGetId(cpipe), str);

	/* read from FRONT-buffer to get channel stats info */
        glReadBuffer(GL_FRONT);
	pfuSaveImage(str, 0, 0, 
	    Shared->winSizeX, 
	    Shared->winSizeY, 0);

	Shared->snapImage = 0;
        fprintf(stderr, "Done\n");
    }


    /* invoke Performer draw-processing for this frame */
    pfDraw();
    
    /* draw Performer throughput statistics */
    if (Shared->drawStats)
	pfDrawChanStats(channel);
	
    pfGetPWinSize(Shared->pw, &Shared->winSizeX, &Shared->winSizeY);

	GetXInput();
    
    /* spin loop to show time after pfDraw() in timing graph */
    if (Shared->Sleep)
	Nap(Shared->SleepDraw);
    
}


static void
GetXInput(void)
{
    static int x, y;
    int haveMotion = 0;
    pfWSConnection dsp = pfGetCurWSConnection();
    

    if (XEventsQueued(dsp, QueuedAfterFlush))
    while (XEventsQueued(dsp, QueuedAlready))
    {
	XEvent event;
	    
	XNextEvent(Dsp, &event);
	switch (event.type) 
	{
	case ConfigureNotify:
	    break;
	case FocusIn:
	    Shared->inWindow = 1;
	    break;
	case FocusOut:
	    Shared->inWindow = 0;
	    break;
	case MotionNotify: 
	    {
		XMotionEvent *motion_event = (XMotionEvent *) &event;
		x =  motion_event->x;
		y =  Shared->winSizeY - motion_event->y;;
		haveMotion = 1;
		Shared->inWindow = 1;
	    }
	    break;
	case ButtonPress: 
	{
	    XButtonEvent *button_event = (XButtonEvent *) &event;
	    x = event.xbutton.x;
	    y = Shared->winSizeY - event.xbutton.y;
	    Shared->inWindow = 1;
	    switch (button_event->button) {
		case Button1:
		    Shared->mouseButtons |= Button1Mask;
		    break;
		case Button2:
		    Shared->mouseButtons |= Button2Mask;
		    break;
		case Button3:
		    Shared->mouseButtons |= Button3Mask;
		    break;
	    }
	}
	break;
	case ButtonRelease:
	{
	    XButtonEvent *button_event = (XButtonEvent *) &event;
	    x = event.xbutton.x;
	    y = Shared->winSizeY - event.xbutton.y;
	    switch (button_event->button) {
		case Button1:
		    Shared->mouseButtons &= ~Button1Mask;
		    break;
		case Button2:
		    Shared->mouseButtons &= ~Button2Mask;
		    break;
		case Button3:
		    Shared->mouseButtons &= ~Button3Mask;
		    break;
	    }
	}
	break;
	case KeyPress:
	{
	    char buf[100];
	    int rv;
	    KeySym ks;
	    rv = XLookupString(&event.xkey, buf, sizeof(buf), &ks, 0);
	    switch(ks) {
	    case XK_Escape: 
		exit(0);
		break;
	    case XK_space:  /* stop */
		Shared->speed = 0.0f;
		break;
	    case XK_r:
	    case XK_R:
		Shared->reset = 1;
		break;
	    case XK_F1:
		Shared->drawStats = !Shared->drawStats;
		break;
		/* F2-key toggles separate channel for stats display */
	    case XK_F2:
		Shared->haveStatsChan = !Shared->haveStatsChan;
		break;
	    case XK_Print:
		Shared->snapImage = 1;
		break;
	    case XK_Up:
		Shared->statsHistFrames++;
		break;
	    case XK_Down:
		Shared->statsHistFrames--;
		break;
	    case XK_p: /* print currently enabled statistics to stderr */
		Shared->printStats = 1;
		break;
	    case XK_a: /* toggle entire statistics class enable */
	    case XK_A:
		Shared->statsEnable = PFSTATS_ALL;
		break;
	    case XK_s: /* toggle system CPU statistics class enable */
	    case XK_S:
		Shared->statsEnable = PFSTATSHW_ENCPU;
		break;
	    case XK_d: /* toggle database statistics class enable */
	    case XK_D:
		Shared->statsEnable = PFFSTATS_ENDB;
		break;
	    case XK_c: /* toggle cull statistics class enable */
	    case XK_C:
		Shared->statsEnable = PFFSTATS_ENCULL;
		break;
	    case XK_f: /* toggle fill stats - depth complexity */
	    case XK_F:
		Shared->statsEnable = PFSTATSHW_ENGFXPIPE_FILL;
		break;
	    case XK_g:
		Shared->statsEnable =  PFSTATS_ENGFX;
		break;
	    case XK_G: /* toggle geometry attribute statistics 
		       * (normals, colors, texcoords)
		       */
		Shared->statsClass = PFSTATS_GFX;
		Shared->statsClassMode = PFSTATS_GFX_ATTR_COUNTS;
		break;
	    case XK_h: /* toggle proc frame time history stats */
	    case XK_H:
		Shared->statsClass = PFFSTATS_PFTIMES;
		Shared->statsClassMode = PFFSTATS_PFTIMES_HIST;
		break;
	    case XK_n: /* raise nap values */
		Shared->Sleep = 1;
		Shared->SleepApp += 1.0f;
		Shared->SleepIsect = Shared->SleepApp;
		Shared->SleepCull = Shared->SleepApp * 3.0f;
		Shared->SleepDraw = Shared->SleepApp;
		break;
	    case XK_N:
		Shared->Sleep = 1;
		Shared->SleepApp -= 1.0f; /* lower nap values */
		Shared->SleepIsect = Shared->SleepApp;
		Shared->SleepCull = Shared->SleepApp * 3.0f;
		Shared->SleepDraw = Shared->SleepApp;
		break;
	    case XK_Pause: /* toggle naps */
		Shared->Sleep = 0;
		if (!Shared->Sleep)
		break;
	    case XK_m: /* toggle mp process frame time stats */
	    case XK_M:
		Shared->statsEnable = PFFSTATS_ENPFTIMES;
		break;
	    case XK_q:
	    case XK_Q:
		Shared->doStatsQuery = 1;
		break;
	    case XK_t: /* toggle tmesh lengths graph */
		Shared->statsClass = PFSTATS_GFX;
		Shared->statsClassMode = PFSTATS_GFX_TSTRIP_LENGTHS;
		break;
	    case XK_T: /* toggle transparency fill stats */
		Shared->statsClass = PFSTATSHW_GFXPIPE_FILL;
		Shared->statsClassMode = PFSTATSHW_GFXPIPE_FILL_TRANSPARENT;
		break;
	    case XK_v: /* toggle database visiblity stats */
	    case XK_V:
		Shared->statsClass = PFFSTATS_DB;
		Shared->statsClassMode = PFFSTATS_DB_VIS;
		break;
	    case XK_z: /* toggle depth-complexity z-compare stats */
	    case XK_Z:
		Shared->statsClass = PFSTATSHW_GFXPIPE_FILL;
		Shared->statsClassMode = PFSTATSHW_GFXPIPE_FILL_DEPTHCMP;
		break;
		}
	    }
	}/* switch */
    }
    Shared->mouseX = x;
    Shared->mouseY = y;
}


/* Stats channel Callbacks */

static void 
CullStatsChannel(pfChannel * ch, void *data) 
{
    /* empty */
}

static pfVec4  statsbclr = { 0.3f, 0.3f, 0.3f, 1.0f};
static void
DrawStatsChannel (pfChannel *channel, void *data)
{
    pfClear(PFCL_COLOR, statsbclr);
    /* draw Performer throughput statistics */
    pfDrawFStats(Shared->frameStats, channel);
}