[BACK]Return to perfly.C CVS log [TXT][DIR] Up to [Development] / performer / src / sample / C++ / perfly

File: [Development] / performer / src / sample / C++ / perfly / perfly.C (download)

Revision 1.1, Tue Nov 21 21:39:45 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 1993, 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
 */


/*
 *	perfly.C -- The Performer database fly-thru application
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifndef __linux__
#include <bstring.h>
#endif
#include <math.h>
#include <ctype.h>

#include <Performer/pf/pfChannel.h>
#include <Performer/pf/pfPipeWindow.h>
#include <Performer/pf/pfLightSource.h>
#include <Performer/pf/pfMPClipTexture.h>
#include <Performer/pf/pfPartition.h>
#include <Performer/pf/pfGeode.h>
#include <Performer/pf/pfScene.h>
#include <Performer/pf/pfDCS.h>
#include <Performer/pf/pfSCS.h>
#include <Performer/pr/pfLinMath.h>
#include <Performer/pr/pfGeoState.h>
#include <Performer/pr/pfString.h>
#include <Performer/pr/pfFog.h>
#include <Performer/pr/pfLight.h>
#include <Performer/pr/pfMaterial.h>

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

#include "generic.h"
#include "perfly.h"
#include "gui.h"
#include "keybd.h"

#include <GL/glu.h>

#define streq(a,b) (strcmp(a,b) == 0)
#define strsuffix(s, suf) \
	(strlen(s) >= strlen(suf) && streq(s + (strlen(s)-strlen(suf)), suf))

	/*---------------------- Globals ------------------------*/

int PackedAttrFormat = 0 /* check out PFGS_PA_C4UBN3ST2FV3F == 1 */;


	/*----------------------------------------------------*/

/*
 * No custom channel initialization 
 */
void
initChannel(pfChannel *chan)
{
    pfCalligraphic *callig;
    int pipe, channel;

    if (chan == ViewState->masterChan)
    {
	pfPipeVideoChannel *pVChan = chan->getPVChan();
	pVChan->getSize(&ViewState->vChanXSize, &ViewState->vChanYSize);
	if (ViewState->dvrXSize <= 0)
	{
	    ViewState->dvrXSize = ViewState->vChanXSize;
	    ViewState->dvrYSize = ViewState->vChanYSize;
	}
    }
    /* Is there a Calligraphic channel attached to this Channel ? */
    callig = chan->getCurCallig();
    if (callig != NULL)
    {
	/* this will force SwapReady signal to be active even for a single pipe */
	chan->setCalligEnable(1);
	/* force swap vme twice so calligraphic stays in sync with SwapWin */
	/* we do it more than twice if more than on channel is on that board */
	callig->getChannel(&pipe,&channel);
        pfCalligraphic::swapVME(pipe);
	pfCalligraphic::swapVME(pipe);
    }

    {
        pfList *mpcliptextures = new(pfGetSharedArena()) pfList(
                                   sizeof(pfMPClipTexture*),
                                   1);
        pfuProcessClipCentersWithChannel(ViewState->sceneGroup, mpcliptextures, ViewState->masterChan);
	pfDelete(mpcliptextures);
    }
}

/*
 * Initialize ViewState shared memory structure
 */
void
initViewState(void)
{
    pfWindow	*win;
    int		i, iR=0;
    int 	tmp;
    int		queryvals[4];
static int querytoks[4] = {PFQFTR_MULTISAMPLE, PFQFTR_TAG_CLEAR, PFQFTR_TEXTURE, 0};


    /* window needed for faster queries */
    win = pfWindow::openNewNoPort("Query", -1);

    iR = (strncasecmp(pfGetMachString(), "IR", 2) == 0);

    /* query machine features */
    pfMQueryFeature(querytoks, queryvals);

    win->close();
    pfDelete(win);

    ViewState->haveMultisample = queryvals[0];
    ViewState->haveTagClear = queryvals[1];
    tmp = queryvals[2];

    if (tmp == PFQFTR_FAST)
	ViewState->texture = TRUE;
    else
	ViewState->texture = FALSE;


    /* Set up defaults */
    ViewState->aa		= ViewState->haveMultisample;
    ViewState->fade		= ViewState->haveMultisample;
    for (i = 0; i < MAX_PIPES; ++i)
	ViewState->visualIDs[i]	= -1;

    ViewState->exitFlag		= FALSE;
    ViewState->updateChannels	= TRUE;
    ViewState->gui		= TRUE;
    ViewState->guiFormat	= 0; /* marks unset */
    ViewState->rtView		= TRUE;
    ViewState->phase		= PFPHASE_FREE_RUN;
    ViewState->frameRate	= 72.0f;
    ViewState->backface		= PF_ON;
    ViewState->collideMode	= PF_ON;
    ViewState->earthSkyMode	= PFES_FAST;
#ifdef	PERFORMER_1_2_BACKGROUND_COLOR
    ViewState->earthSkyColor.set(0.5f, 0.5f, 0.8f, 1.0f);
#else
    ViewState->earthSkyColor.set(0.5f*0.14f, 0.5f*0.22f, 0.5f*0.36f, 1.0f);
#endif
    ViewState->scribeColor.set(1.0f, 1.0f, 1.0f, 1.0f);
    ViewState->fadeRange	= 15.0f;
    ViewState->near		= 0.0f;
    ViewState->far		= 0.0f;
    ViewState->nearFogRange	= ViewState->near;
    ViewState->farFogRange	= ViewState->far;
    ViewState->fog		= PFFOG_OFF;
    ViewState->timeOfDay	= 0.8f;
    ViewState->highLoad		= 0.8f;
    ViewState->lighting		= LIGHTING_EYE;
    ViewState->lowLoad		= 0.5f;
    ViewState->stats		= PF_ON;
    ViewState->statsEnable	= PFSTATS_OFF;
    ViewState->stress		= PF_OFF;
    ViewState->stressMax	= 100.0f;
    ViewState->stressScale	= 1.0f;
    ViewState->tree		= 0;
    ViewState->drawStyle	= PFUSTYLE_FILLED;
    ViewState->xformerModel	= PFITDF_TRACKBALL;
    ViewState->resetPosition	= POS_ORIG;
#if defined(__linux__) && !defined(__linux__has_MP__)
    pfNotify(PFNFY_WARN, PFNFY_PRINT,
        "Forcing Input model to NOFORK_X [%s:%d]\n", __FILE__, __LINE__);
    ViewState->input            = PFUINPUT_NOFORK_X;
#else
    ViewState->input		= PFUINPUT_X;
#endif /* linux */
    ViewState->explode		= 0.0f;
    for (i=0; i < MAX_PIPES; i++)
    {
	ViewState->drawFlags[i] = REDRAW_WINDOW;
	ViewState->firstPipeDraw[i] = 1;
    }
    ViewState->redrawOverlay	= 1;
    ViewState->redrawCount	= 0;
    ViewState->procLock		= 0;
    ViewState->cullMode		= PFCULL_ALL;
    ViewState->LODscale		= 1.0f;
    ViewState->lamps		= 0;
    strcpy(ViewState->welcomeText, "OpenGL\nPerformer");
    strcpy(ViewState->overlayText, "OpenGL Performer");
    ViewState->objFontType 	= PFDFONT_EXTRUDED;
    ViewState->objFontName      = strdup("Mistr");
    ViewState->optimizeGStates  = TRUE;    
    ViewState->optimizeTree     = 0;

    ViewState->panScale.set(0.0f, 0.0f, 1.0f); 

    ViewState->rotateCenter	= 1;
    ViewState->iterate		= 1;

    ViewState->combineBillboards= 0;

    ViewState->printStats	= FALSE;
    ViewState->exitCount	= -1;

    ViewState->drawMode         = (iR ? DLIST_GEOSET : DLIST_OFF);
    ViewState->demoMode         = PF_OFF;
    ViewState->recordPath	= PF_OFF;
    ViewState->democnt		= 0;
    ViewState->doDemoPath	= 0;
    ViewState->snapImage        = 0;

    ViewState->doDVR		= DVR_OFF;
    ViewState->showDVR		= 0;
    ViewState->winXSize		= WinSizeX;
    ViewState->winYSize		= WinSizeY;
    ViewState->vChanXSize	= 1280; /* defaults - updated in initChan */
    ViewState->vChanYSize	= 1024;
    ViewState->dvrXSize		= -1;
    ViewState->dvrYSize		= -1;
    ViewState->MCO		= 0;

    ViewState->gridify		= FALSE;
    ViewState->gridified	= FALSE;

    memset(&(ViewState->events), 0, sizeof(ViewState->events));
    memset(&(ViewState->guiWidgets), 0, sizeof(ViewState->guiWidgets));
     
    ViewState->viewMat.makeIdent();
    strcpy(ViewState->initViewNames[0], "Default");
    strcpy(ViewState->tetherViewNames[0], "Under");
    ViewState->tetherViews[0].makeIdent();

    /* special light points */
    ViewState->startCallig = 0;
    ViewState->calligDefocus = 0;
    ViewState->rasterDefocus = 0;
    ViewState->calligFilterSizeX = 1;
    ViewState->calligFilterSizeY = 1;
    ViewState->calligDrawTime = 600;
    ViewState->calligDirty = 1;
    ViewState->calligZFootPrint = 2.;
}

/* Initialize Calligraphic boards */
int
StartCalligraphic(int _numpipes)
{
int     found = 0;
int     i;
int     test;

    /* assume pipe starts a 0 to */
    for (i=0; i< _numpipes; i++)
    {
        if (pfCalligraphic::initBoard(i) == TRUE)
        {
            found ++;
            /* get the memory size */
            pfNotify(PFNFY_NOTICE, PFNFY_RESOURCE,
                "StartCalligraphic: board(%d) has %d Bytes of memory",
                i, pfCalligraphic::getBoardMemSize(i));
        }
    }
    if (found)
    {
	pfQueryFeature(PFQFTR_LIGHTPOINT,&test);
	if (test != PFQFTR_FAST)
	  pfNotify(PFNFY_WARN, PFNFY_RESOURCE,
	      "StartCalligraphic: OpenGL does not have the lpoint_parameter"
	      " extension, VISI bus information will not be correct");
    }
      
    return found;
}

/* called before pfConfig() */ 

void
initConfig(void)
{
int numCalli;

    if (ViewState->startCallig)
    {
	/* lets start the calligraphic boards if any */
	numCalli = StartCalligraphic(NumPipes);
	pfNotify(PFNFY_NOTICE, PFNFY_RESOURCE,
	    "InitConfig() - found %d calligraphic board(s)", numCalli);
	if (numCalli)
	    ViewState->calligBoard = 1;
	else
	    ViewState->calligBoard = 0;
    }
}


/*
 * Initialize the view
 */
void
initView(pfScene *scene)
{
    pfSphere	bsphere;

    /* If asked to run for zero frames, exit now */    
    if (ViewState->exitCount == 0)
	pfExit();

    scene->getBound(&bsphere);

    /* Set initial view position from command line values */
    if (InitXYZ)
        ViewState->viewCoord.xyz.copy(ViewState->initViews[0].xyz);

    /* Otherwise fit view to encompass scene */
    else
    {
        ViewState->viewCoord.xyz.copy(bsphere.center);

        /* Offset view so all visible */
        ViewState->viewCoord.xyz[PF_Y] -= ViewState->sceneSize;

        /* Store initial XYZ position for future reset actions */	
        ViewState->initViews[0].xyz.copy(ViewState->viewCoord.xyz);
    }

    if (ViewState->near == 0.0f)
    {
	/* Calculate near, far based on scene extent */
	ViewState->near = PF_MIN2(ViewState->sceneSize / 10.0f, 1.0f);
	ViewState->far = PF_MAX2(ViewState->sceneSize * 2.0f, 1000.0f);
	ViewState->nearFogRange = ViewState->near;
	ViewState->farFogRange = ViewState->far;
    }

    /* Set initial view angles from command line values ? */
    if (InitHPR)
        ViewState->viewCoord.hpr.copy(ViewState->initViews[0].hpr);
    else
    {
	ViewState->viewCoord.hpr.set(0.0f, 0.0f, 0.0f);

	/* Store initial HPR angles for future reset actions */
	ViewState->initViews[0].hpr.copy(ViewState->viewCoord.hpr);
    }
}

int
initSceneGraph(pfScene *scene)
{
    pfNode     *root;
    int		i;
    int		j;
    int		loaded	= 0;
    pfSphere    bsphere;

    /* Create a DCS for TRACKBALL pfiXformer */
    ViewState->sceneDCS = new pfDCS();
    ViewState->sceneGroup = new pfGroup();
    ViewState->sceneDCS->addChild(ViewState->sceneGroup);
    if (ViewState->xformerModel == PFITDF_TRACKBALL)
    {
	scene->addChild(ViewState->sceneDCS);
    }
    else
	scene->addChild(ViewState->sceneGroup);


    /* Load each of the files named on the command line */
    for (i = 0;	i < NumFiles; i++)
    {
	for (j = 0; j < ViewState->iterate; j++)
	{
		// -- Catch any .perfly files here -- //
	    if (strsuffix(DatabaseFiles[i], ".perfly")) {
		if (loadConfigFile(DatabaseFiles[i]))
		    loaded++;
		continue;
	    }
	    
	    /* Load the database. create a hierarchy under node "root" */
	    root = (pfGroup *)pfdLoadFile(DatabaseFiles[i]);
	    
	    if (root == NULL)
	    {
		pfNotify(PFNFY_NOTICE, PFNFY_PRINT,
			 "WARNING: could not load \"%s\"", DatabaseFiles[i]);
		continue;
	    }

	    /* explode the object (move it from the origin) */
	    if (ViewState->explode > 0.0f)
	    {
		float	 x;
		float	 y;
		float	 z;
		pfMatrix matrix;
		pfSCS	*scs;
		
		x = ViewState->explode*((random() & 0xffff)/65535.0f - 0.5f);
		y = ViewState->explode*((random() & 0xffff)/65535.0f - 0.5f);
		z = ViewState->explode*((random() & 0xffff)/65535.0f - 0.5f);
		
		/* move the object by placing it beneath an SCS node */
		matrix.makeTrans(x, y, z);
		scs = new pfSCS(matrix);
		scs->addChild(root);
		
		/* go ahead and apply the SCS matrix to the geometry */
		scs->flatten(0);
		
		/* detach (transformed) object from SCS; discard SCS */
		scs->removeChild(root);
		pfDelete(scs);
	    }
 	    /* optimize the newly loaded hierarchy */
 	    if (ViewState->optimizeTree > 0)
 	    {
 		if (ViewState->optimizeTree > 1)
  		{
 		    /* convert ordinary pfDCS nodes to pfSCS nodes */
  		    root = pfdFreezeTransforms(root, NULL);
  		}
 		/* discard all redundant nodes and identity pfSCS nodes */
 		root->flatten(0);
 		
 		/* deinstance and apply all pfSCS transformations */
 		root = pfdCleanTree(root, NULL);

 		/* try a parition for intersections */
		if (ViewState->optimizeTree > 2)
		{
		    pfVec3 spacing;
		    pfSphere sphere;
 		    pfPartition *part = new pfPartition;
		    root->getBound(&sphere);
		    
		    /* use approx 100x100 partition */
		    PFSET_VEC3(spacing, 
			       0.01f*sphere.radius, 
			       0.01f*sphere.radius,
			       0.01f*sphere.radius);

		    part->setAttr(PFPART_ORIGIN, &sphere.center);
		    part->setAttr(PFPART_MIN_SPACING, &spacing);
		    part->setAttr(PFPART_MAX_SPACING, &spacing);

		    pfNotify(PFNFY_INFO, PFNFY_PRINT,
			     "Creating pfPartition for %s", 
			     DatabaseFiles[i]);
		    
		    if (pfGetNotifyLevel() >= PFNFY_DEBUG)
			part->setVal(PFPART_DEBUG, 1);

 		    part->addChild(root);
 		    part->build();
 		    part->update();
		    root = part;
		}
	    }
 	    
	    ViewState->sceneGroup->addChild(root);
 	    ++loaded;
	}
    }

#if 0
    /* discard DSO-based loaders if any have been bound to executable */
    pfdFreeFileLoaders();
#endif

    /*
     * optimize sharing of geostate structures and components.
     * Wait until all files are loaded so sharing encompasses
     * entire scene.
     */
    if (loaded)
    {
        pfdMakeShared((pfNode *)ViewState->sceneGroup);

        /* optimize pfLayer nodes via "DISPLACE POLYGON" */
        pfdCombineLayers((pfNode *)ViewState->sceneGroup);

        /* combine sibling pfBillboard nodes */
        if (ViewState->combineBillboards)
            pfdCombineBillboards((pfNode *)ViewState->sceneGroup,
                ViewState->combineBillboards);
    }

   /* write out nodes in sceneGroup */
    if (WriteFile)
    {
        if (!strstr(WriteFile, ".out") && pfdInitConverter(WriteFile))
        {
            /* it's a valid extension, go for it */
            if (!pfdStoreFile((pfNode *)ViewState->sceneGroup, WriteFile))
                pfNotify(PFNFY_WARN, PFNFY_RESOURCE,
                         "No store function available for %s\n", WriteFile);
        }
        else
        {
            /* write the file as ASCII */
            FILE *fp;
            if (fp = fopen(WriteFile, "w"))
            {
                pfPrint(ViewState->sceneGroup, PFTRAV_SELF|PFTRAV_DESCEND,
                    PFPRINT_VB_OFF, fp);
                fclose(fp);
            }
            else
                pfNotify(PFNFY_WARN, PFNFY_RESOURCE,
                         "Could not open scene.out for debug printing.");
        }

        /* remember that file's already been written */
        WriteFile = NULL;
    }

    /*
     * make an optimizing scene geostate that represents the
     * most common modes and attributes present in the scene
     * graph.
     */
    if (loaded && ViewState->optimizeGStates)
    {
        /* compute an optimum scene geostate */
        pfdMakeSharedScene(scene);
    }
    else
    {
        /* use default builder state as scene geostate */
        pfGeoState *gs = new (pfGetSharedArena()) pfGeoState;
        gs->copy((pfGeoState*)pfdGetDefaultGState());
	scene->setGState(gs);
    }

    /* Compute extent of scene */
    if (loaded)
    {
    	scene->getBound(&bsphere);
    	ViewState->sceneSize = PF_MIN2(2.5f*bsphere.radius, 80000.0f);
    }
    else
    	ViewState->sceneSize = 1000.0f;

    /* convert scene to vertex arrays */
    if (loaded && PackedAttrFormat)
    {
	pfNotify(PFNFY_NOTICE, PFNFY_PRINT, "Making Vertex Arrays.");
	/* don't delete any extra attribute arrays since they will be needed
	 * for wireframe mode
	 */
	pfuTravCreatePackedAttrs(scene, PackedAttrFormat, 0x0);
    }


    /* add light sources to the scene */
    for (i = 0; i < ViewState->lamps; i++)
    {
	pfLightSource *lamp = new pfLightSource();

	/* set direction of infinite light source */
	ViewState->lampXYZ[i].normalize();
	lamp->setPos(
		     ViewState->lampXYZ[i][0],
		     ViewState->lampXYZ[i][1],
		     ViewState->lampXYZ[i][2],
		     0.0f);

	/* set light source color */
	lamp->setColor(PFLT_DIFFUSE,
		       ViewState->lampRGB[i][0],
		       ViewState->lampRGB[i][1],
		       ViewState->lampRGB[i][2]);

	/* add lamp to scene */
	scene->addChild(lamp);
    }

    /* Create geode/gset to draw box representing shrunken 
     * culling frustum.
    */
{
    int		*lengths, i;
    pfGeoSet	*gset;
    pfGeoState	*gstate;
    pfVec3	*coords;
    pfVec4	*colors;
    ushort	*index;
    void	*arena = pfGetSharedArena();

    ViewState->cullVol = new pfGeode;
    gset = new(arena) pfGeoSet;
    gstate = new(arena) pfGeoState;

    gset->setPrimType(PFGS_LINESTRIPS);
    gset->setNumPrims(1);
    lengths = (int*)pfMalloc(sizeof(int), arena);
    lengths[0] = 5;
    gset->setPrimLengths(lengths);

    index = (ushort*)pfMalloc(sizeof(ushort)*5, arena);
    for (i=0; i<4; i++)
	index[i] = i;
    index[i] = 0;
    coords = (pfVec3*)pfCalloc(4, sizeof(pfVec3), arena);
    gset->setAttr(PFGS_COORD3, PFGS_PER_VERTEX, coords, index);
    colors = (pfVec4*)pfMalloc(sizeof(pfVec4), arena);
    colors[0].set(1.0f, 0.0f, 0.0f, 1.0f);
    gset->setAttr(PFGS_COLOR4, PFGS_OVERALL, colors, index);

    gstate->makeBasic();
    gset->setGState(gstate);
    ViewState->cullVol->addGSet(gset);
    ViewState->cullVolDCS = new pfDCS;
    ViewState->cullVolDCS->setTravMask(PFTRAV_CULL, 0, PFTRAV_SELF, PF_SET);
    ViewState->cullVolDCS->addChild(ViewState->cullVol);
}

    /* Process all the cliptextures in the scene */
    {
	int i;
        pfPipe *masterPipe = pfGetPipe(0);
        pfList *mpcliptextures;

        mpcliptextures = new(pfGetSharedArena()) pfList(
                                   sizeof(pfMPClipTexture*),
                                   1);
        pfuProcessClipCenters(scene, mpcliptextures);
        (void)pfuAddMPClipTexturesToPipes(mpcliptextures, masterPipe, NULL);
	pfDelete(mpcliptextures);
	
        
	/* set cliptexture incremental state to the visual channel - GUI is chan 0 */
	for(i = pfGetMultipipe() - 1; i >= 0; i--)
	{
	    pfPipe *p = pfGetPipe(i);
	    if(p == masterPipe)
		p->setIncrementalStateChanNum(1);
	    else
		p->setIncrementalStateChanNum(0);
	}
    }

    return(loaded);
}

/*
 * Pre-Frame - latency critical operations
 */
void
localPreFrame(pfChannel *)
{
    /* do video resize for all channels */
    if (pfGetFrameCount() > 3)
    {
	int dvrMode;
	int i, newSize, newMode;
	int oxs,  oys;

	dvrMode = ViewState->doDVR;

	for (i=0; i < ViewState->numChans; i++)
	{
	    pfChannel *chan;
	    pfPipeVideoChannel *pvchan;
	    
	    chan = ViewState->chan[i];
	    pvchan = chan->getPVChan();

	    if (pvchan) 
	    {
		newMode = (pvchan->getDVRMode() != dvrMode);
		if (newMode)
		{
		    /* must also set REDRAW_WINDOW for each pipe with redrawOverlay */
		    ViewState->redrawOverlay	= 1;
		    ViewState->redrawCount	= 0;
		}

		switch (dvrMode)
		{
		case DVR_OFF:
		    if (newMode)
		    {
			int pipeId = pfGetId(chan->getPipe());
			ViewState->drawFlags[pipeId] |= REDRAW_WINDOW;
			pvchan->setDVRMode(PFPVC_DVR_OFF);
			chan->setProjMode(PFCHAN_PROJ_VIEWPORT);
			pfuCursorType(PFU_CURSOR_X);
			if (ViewState->gui && (chan == ViewState->masterChan))
			    pfuRedrawGUI();
		    }
		    break;
		case DVR_MANUAL:
		    /* set pfPipeVideoChannel scale factor for next draw */
		    {
			if (newMode)
			{
			    pvchan->setDVRMode(PFPVC_DVR_MANUAL);
			    /* pvchan->setStressFilter(1.0f, 0.8f, 0.95f, 0.0f, 1.0f, 1.0f); */
			    pfuCursorType(PFU_CURSOR_2D);
			}
			pvchan->getOutputSize(&oxs, &oys);
			newSize =  (newMode || (oxs != ViewState->dvrXSize) || (oys != ViewState->dvrYSize));
			if (newSize)
			{
			    int pipeId = pfGetId(chan->getPipe());
			    ViewState->drawFlags[pipeId] |= REDRAW_WINDOW;
			    if (ViewState->dvrXSize > ViewState->vChanXSize)
				ViewState->dvrXSize = ViewState->vChanXSize;
			    if (ViewState->dvrYSize > ViewState->vChanYSize)
				ViewState->dvrYSize = ViewState->vChanYSize;
			    pvchan->setOutputSize(ViewState->dvrXSize, ViewState->dvrYSize);
			    chan->setProjMode(PFCHAN_PROJ_WINDOW);
			}
			if (ViewState->gui && (chan == ViewState->masterChan) && 
			    (newSize || pfuInGUI(ViewState->mouse.xpos, ViewState->mouse.ypos)))
			{
			    pfuRedrawGUI();
			}
		    }
		    break;
		case DVR_AUTO:
		    {
			int pipeId = pfGetId(chan->getPipe());
			ViewState->drawFlags[pipeId] |= REDRAW_WINDOW;
			if (newMode)
			{
			    float frac=1.0f;
			    if (!Multipipe)
				frac = 1.0f / (float) NumChans;
			    pvchan->setDVRMode(PFPVC_DVR_AUTO);
			    pvchan->setStressFilter(frac, 0.80, 0.95f, 1.5f, 2.0f, 10.0f);
			    /* indicates that pfPipe should calc stress automatically */
			    pfuCursorType(PFU_CURSOR_2D);
			    chan->setProjMode(PFCHAN_PROJ_WINDOW);
			}
			if (ViewState->gui && (chan == ViewState->masterChan))
			    pfuRedrawGUI();
			
		    }
		    break;
		}
		pvchan->getOutputSize(&ViewState->dvrXSize, &ViewState->dvrYSize);
	    }
	}
    }
}

/*
 * Update the current view
 */
void
updateView(pfChannel *chan)
{
    pfMatrix 		mat;
    
    if (ViewState->resetPosition != POS_NORESET)
    {
	resetPosition(ViewState->resetPosition);
	ViewState->resetPosition = POS_NORESET;
    }

    if (ViewState->xformerModel == PFITDF_TETHER)
    {
	pfMatrix mat;
	ViewState->vehicleDCSs[ViewState->curVehicle]->getMat(mat);
	    /* adjust for pfuPath rotating the object CCW */
	mat.preRot(-90.0, 0.0, 0.0, 1.0, mat);

	mat.preMult(ViewState->tetherViews[ViewState->curTetherView]);

	ViewState->xformer->setMat(mat);
    }
    else
    {
	ViewState->xformer->update();
	ViewState->xformerModel = ViewState->xformer->getCurModelIndex();
    }
    
    

    /* if have moving-eyepoint motion model, update eyepoint */
    if ((ViewState->xformer->getCurModel()->isOfType(pfiInputXformTravel::getClassType()))
       || (ViewState->xformerModel == PFITDF_TETHER))
    {
	ViewState->xformer->getCoord(&ViewState->viewCoord);
	ViewState->xformer->getMat(mat);
	chan->setViewMat(mat);
	ViewState->viewMat = mat;

	/* move the sun to the eyepoint */
	if (ViewState->lighting ==  LIGHTING_EYE)
	    ViewState->sunDCS->setMat(ViewState->viewMat);
    }
    
    {
    /*
     * Update box which represents shrunken culling frustum
     */
    static float	prevCullDelta = -1.0f;
    static pfFrustum	*frust = NULL;
    if (prevCullDelta != ViewState->cullDelta)
    {
	if (ViewState->cullDelta < 1.0f)
	{
	    pfVec3			*coords;
	    ushort			*foo;
	    int				i;
	    float			near, far, fudge;

	    if (frust == NULL)
		frust = new pfFrustum;

	    ViewState->cullVol->getGSet(0)->getAttrLists(PFGS_COORD3, 
						(void**)&coords, &foo);

	    ViewState->masterChan->getBaseFrust(frust);
	    frust->getNearFar(&near, &far);
	    frust->getNear(coords[0], coords[1], coords[3], coords[2]); 
	    
	    /*
	     * Need a fudge factor to multiply the
	     * coords of the rectangle by
	     * so they are safely away from the real front clip plane.
	     *
	     * fudge-1 (the percentage increase) should be proportional
	     * to (far/near); choose the constant so that in the default
	     * case (near=1, far=2000) the increase is the same as adding .001
	     * (somewhat arbitrarily; actually it would be more appropriate
	     * to choose this constant based on the z depth...)
	     *
	     * I.e. (fudge-1) = C * (far/near)
	     *	         .001 = C * 2000/1
	     *              C = 5e-7.
	     * So fudge = 1 + 5e-7 * far/near;
	     * Multiply the x and z coords by this same scale factor
	     * so that the vertices will end up at the
	     * exact same position on the screen that
	     * they would have otherwise.
	     */
	    fudge = 1.0f + 5e-7f*far/near;

	    for (i=0; i<4; i++)
	    {
		coords[i][0] *= ViewState->cullDelta * fudge;
		coords[i][1] *= fudge;
		coords[i][2] *= ViewState->cullDelta * fudge;
	    }

	    frust->setNearFar(near*fudge, far);
	    frust->makePersp(coords[0][0], coords[2][0], 
			            coords[0][2], coords[2][2]); 

	    ViewState->masterChan->setCullPtope((pfPolytope*)frust);

	    if (prevCullDelta >= 1.0f)
		ViewState->scene->addChild(ViewState->cullVolDCS);
	}
	else	
	{
	    /* Don't draw culling box */
	    if (prevCullDelta < 1.0f)
		ViewState->scene->removeChild(ViewState->cullVolDCS);

	    /* Disable special cull region */
	    ViewState->masterChan->setCullPtope((pfPolytope*)NULL);
	}

	prevCullDelta = ViewState->cullDelta;
    }

    /* Place cull volume geometry in front of viewer */
    if (ViewState->cullDelta < 1.0f)	
    {
	pfMatrix 		mat;
	chan->getOffsetViewMat( mat);
	ViewState->cullVolDCS->setMat(mat);
    }
    }
    
}

/*
 * Reset view and Xformer to original XYZ position and HPR Euler angles
 */
void
resetPosition(int pos)
{
    pfMatrix	mat;

    ViewState->xformer->stop();
    switch (pos)
    {
    case POS_CENTER:
	ViewState->xformer->center();
	break;

    default:
    case POS_ORIG:
	ViewState->xformer->setCoord(&(ViewState->initViews[pos]));
	break;
    }
    ViewState->xformer->getMat(ViewState->viewMat);
    ViewState->masterChan->setViewMat(ViewState->viewMat);
}

static void
updateWin(int pipeId)
{
    static int	winIndex = PFWIN_GFX_WIN;
    int 	doWinIndex = 0;
    int		i;

    /* Select proper visual in the window stack to render to  */
    if (ViewState->stats && 
	(ViewState->statsEnable & PFSTATSHW_ENGFXPIPE_FILL))
    {
	pfPipeVideoChannel *pvchan;
	ViewState->doDVR = PFPVC_DVR_OFF;
	for (i=0; i < ViewState->numChans; i++)
	{
	    pvchan = ViewState->chan[i]->getPVChan();
	    pvchan->setDVRMode(PFPVC_DVR_OFF);
	}
	ViewState->doDVR = PFPVC_DVR_OFF;
	pvchan->setDVRMode(PFPVC_DVR_OFF);
	if(winIndex != PFWIN_STATS_WIN)
	{
	    winIndex = PFWIN_STATS_WIN;
	    doWinIndex = 1;
	}
    }
    else 
    {
	if (winIndex != PFWIN_GFX_WIN)
	{
	    winIndex = PFWIN_GFX_WIN;
	    doWinIndex = 1;
	}
    }
    if (doWinIndex)
    {
	int	i;
	int pipeCount = Multipipe != HYPERPIPE ? NumPipes : 1;
	for (i=0; i < pipeCount; i++)
	{
	    ViewState->pw[i]->setIndex(winIndex);
	    if (!(ViewState->pw[i]->getSelect()))
	    {
		ViewState->pw[i]->setConfigFunc(ConfigPWin);
		ViewState->pw[i]->config();
	    }
	}
	ViewState->drawFlags[pipeId] |= UPDATE_MODES;
    }  
}

static void
updateStats(void)
{
    /*
     * Note: if turning on or off fill stats, we must redraw panel because
     * multisampling might be toggled as well.
    */
    static int     on = -1, en = 0;
    static int     first = 1;
    int i;

    pfFrameStats   *fsp;
    pfCalligraphic *callig;

    if (on && (!ViewState->stats))	/* Reset to minimal fast stats collection */
	{
	    for (i=0; i < ViewState->numChans; i++)
		{
		    pfChannel *chan;
	    
		    chan = ViewState->chan[i];
		    fsp = chan->getFStats();
		    /* put everything back to defaults */
		    fsp->setClass(PFSTATS_ALL, PFSTATS_DEFAULT); 
		    /* set the fast process timing stats */
		    fsp->setClassMode(PFFSTATS_PFTIMES, PFFSTATS_PFTIMES_BASIC, PFSTATS_SET);
		    on = 0;
		    en = 0;
		}
	}
    else if (ViewState->stats && (en != ViewState->statsEnable))
    {
        for (i=0; i < ViewState->numChans; i++)
	{
	    pfChannel *chan;

	    chan = ViewState->chan[i];

	    /* Set new stats enables */
	    fsp = chan->getFStats();

	    /* First turn off old stats */
	    if (en)
		fsp->setClass(en & ~(PFSTATSHW_ENGFXPIPE_TIMES), PFSTATS_OFF);
	    if (ViewState->statsEnable & PFSTATSHW_ENGFXPIPE_FILL)
		fsp->setClass(PFSTATSHW_ENGFXPIPE_TIMES, PFSTATS_OFF);

	    /* Now enable new stats graph */
            if (ViewState->statsEnable)
	    {
	        fsp->setClass(ViewState->statsEnable, PFSTATS_ON);
		fsp->setClassMode(PFFSTATS_PFTIMES, PFFSTATS_PFTIMES_MASK, PFSTATS_ON);
		if (ViewState->statsEnable & PFSTATSHW_ENGFXPIPE_TIMES) /* do just the status line */
		    chan->setStatsMode(PFCSTATS_DRAW, 0);
		else
		    chan->setStatsMode(PFCSTATS_DRAW, PFSTATS_ALL);
		if (first)
		{       /* set some stats class modes that are not
		     * on by default. this only needs to be done
		     * the first time */
		    fsp->setClassMode(PFSTATSHW_GFXPIPE_FILL,
				      PFSTATSHW_GFXPIPE_FILL_DEPTHCMP |
				      PFSTATSHW_GFXPIPE_FILL_TRANSPARENT,
				      PFSTATS_ON);
		    /*
		     * This will also enable the tmesh statistics - not on by
		     * default because they are somewhat expensive
		     */
		    fsp->setClassMode(PFSTATS_GFX,
				      PFSTATS_GFX_ATTR_COUNTS |
				      PFSTATS_GFX_TSTRIP_LENGTHS,
				      PFSTATS_ON);
		    fsp->setAttr(PFFSTATS_UPDATE_SECS, 2.0f);
		    callig = chan->getCurCallig();
		    if (callig != NULL)
		        fsp->setClass(PFSTATS_ENCALLIG, PF_ON);
		    if (i == ViewState->numChans-1)
			first = 0; /* did it on all the channels once */
		}

  	    }
	}
	en = ViewState->statsEnable;
	on = 1;
    }
    if (ViewState->printStats)
    {
	if (!ViewState->stats)
	    ViewState->stats = TRUE;
	else 
	{
	    fsp = ViewState->masterChan->getFStats();
	    pfPrint(fsp, PFFSTATS_BUF_AVG | PFFSTATS_BUF_PREV |
		    fsp->getClass(PFSTATS_ALL),
		    PFPRINT_VB_INFO, 0);
	    
	    ViewState->printStats=0;
	}
    }
}


/*
 * Perform non-latency-critical per-frame updates
 */
void
updateSim(pfChannel *chan)
{
    int pipeId;
    
    pipeId = pfGetId(chan->getPipe());
    
    /* Process keyboard input */
    processKeybdInput();

    /* Adjust load management filter */
    if (ViewState->stress)
	chan->setStressFilter(1.0f, ViewState->lowLoad, ViewState->highLoad,
	    ViewState->stressScale, ViewState->stressMax);
    else
	chan->setStressFilter(1.0f, ViewState->lowLoad, ViewState->highLoad,
	    0.0f, 1.0f);

    if (ViewState->updateChannels)
    {
	updateGUI(ViewState->gui);
	ViewState->updateChannels = 0;
    }
    else if (ViewState->gui || ViewState->tree)
	pfuUpdateGUI(&ViewState->mouse);
    else
	pfuUpdateGUICursor();

    if ((ViewState->cullMode != -1)&&
	    (ViewState->cullMode !=
	     ViewState->masterChan->getTravMode(PFTRAV_CULL)))
	ViewState->masterChan->setTravMode(PFTRAV_CULL, ViewState->cullMode);

    if (ViewState->LODscale != ViewState->masterChan->getLODAttr(PFLOD_SCALE))
	ViewState->masterChan->setLODAttr(PFLOD_SCALE, ViewState->LODscale);


    { /* remove light source from scene when lighting is off */
	static int lightInScene = 1;
	if ((ViewState->lighting != 0) != lightInScene)
	{
	    if (ViewState->lighting)
	    {
		ViewState->scene->addChild(ViewState->sunDCS);
		lightInScene = 1;
	    }
	    else
	    {
		ViewState->scene->removeChild(ViewState->sunDCS);
		lightInScene = 0;
	    }
	}
    }

    updateTimeOfDay(ViewState->timeOfDay);
    updateStats();
    updateEnvironment();
    updateWin(pipeId);

    ViewState->masterChan->getViewMat(ViewState->viewMat);
    ViewState->masterChan->getFOV(&ViewState->fov[0],
				  &ViewState->fov[1]);
 
    /* update display lists based on draw mode */
    {
    static int oldDrawMode = -1;

    if (ViewState->drawMode != oldDrawMode)
    {
	oldDrawMode = ViewState->drawMode;

	switch(oldDrawMode)
	{
	case DLIST_OFF:
	    pfuTravSetDListMode(ViewState->scene, 0);
	    /*fprintf(stderr, "**** geoset display list DISABLED\n");*/
	    break;
	case DLIST_GEOSET: /* set dlist mode on pfGeoSets */
	    pfuTravSetDListMode(ViewState->scene, 1);
	    /*fprintf(stderr, "**** geoset display list COMPILING\n");*/
	    ViewState->drawMode = DLIST_COMPILE_GEOSET;
	    break;
	case DLIST_COMPILE_GEOSET: 
	    /* keep getting in here so long as draw procs are compiling */
	    oldDrawMode = DLIST_GEOSET;
	    {
		int done=1;
		int i;
		for (i=0; i < NumPipes; i++)
		{
		    if (ViewState->firstPipeDraw[i])
		    {
			done = 0;
			break;
		    }
		}
		if (done)
		{
		    ViewState->drawMode = DLIST_GEOSET;
		    /*fprintf(stderr, "**** geoset display list DONE\n");*/
		}
	    }
	    break;
	}
    }
    }
}


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

    if (model == PFITDF_TETHER)
    {
	return; /* xformer model and mouse is ignored while model is tether */
    }
    
    
    if ((model == oldModel && oldCollMode == collideMode) || !ViewState->xformer)
	return;

    if (model == PFITDF_FLY)
    {
    	ViewState->xformer->selectModel(model);
        pfiInputXformFly *fly = (pfiInputXformFly*)ViewState->xformer->getCurModel();
	fly->setMode(PFIXFLY_MODE_PITCH, PFIXFLY_PITCH_FOLLOW);
    }
    else if (model == PFITDF_FLIGHTSTICK)
    {
        ViewState->xformer->selectModel(PFITDF_FLY);
        pfiInputXformFly *fly = (pfiInputXformFly*)ViewState->xformer->getCurModel();
	fly->setMode(PFIXFLY_MODE_PITCH, PFIXFLY_PITCH_FLIGHT);
    }
    else ViewState->xformer->selectModel(model);

    /* If not in trackball model, remove trackball DCS for better performance */
    if ((oldModel != model) && (oldModel == PFITDF_TRACKBALL))
    {
	ViewState->scene->removeChild(ViewState->sceneDCS);
	ViewState->scene->addChild(ViewState->sceneGroup);
    }
    /* Restore trackball DCS to scene */
    else if ((oldModel != model) && (model == PFITDF_TRACKBALL))
    {
	ViewState->scene->removeChild(ViewState->sceneGroup);
	if (ViewState->sceneDCS->getNumParents() == 0)
	    ViewState->scene->addChild(ViewState->sceneDCS);
    }
 
    /* Collide with objects in scene */
    if (oldCollMode != collideMode)
    {
	if (collideMode == PF_ON)
	    ViewState->xformer->enableCollision();
	else
	    ViewState->xformer->disableCollision();
	oldCollMode = collideMode;
    }
    
    oldModel = model;
}


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


static void
drawMessage(pfPipeWindow *pw)
{
    pfFont	*fnt;
    pfString	*str;
    pfLight	*lt;
    pfMatrix	mat;
    pfFrustum	*frust;
    const pfBox	*bbox;
    float	dist;
    double	start, t;

    fnt = pfdLoadFont_type1(ViewState->objFontName, ViewState->objFontType);

    if (!fnt)
	return;

    pfPushState();
    pfPushIdentMatrix();
    
    str = new((void*)NULL) pfString;
    str->setMode(PFSTR_JUSTIFY, PFSTR_MIDDLE);
    str->setFont(fnt);
    str->setColor(1.0f, 0.0f, 0.8f, 1.0f);
    str->setString(ViewState->welcomeText);
    mat.makeScale(1.0f, 1.0f, 1.5f);
    str->setMat(mat);
    str->flatten();
    bbox = str->getBBox();
    dist = PF_MAX2(bbox->max[PF_Z] - bbox->min[PF_Z],
		   bbox->max[PF_X] - bbox->min[PF_X]);

    dist = (dist/2.0f) / pfTan(45.0f/2.0f);

    frust = new pfFrustum();

    if (InitFOV) {
	frust->setNearFar(1.0f, 1000.0f);
	frust->makePersp(
		- pfTan (ViewState->fov[0]/2.0f),
		  pfTan (ViewState->fov[0]/2.0f),
		- pfTan (ViewState->fov[1]/2.0f),
		  pfTan (ViewState->fov[1]/2.0f) );
    } else
	frust->makeSimple(45.0f);

    frust->apply();
    pfDelete(frust);

    pfRotate(PF_X, -90);

    pfEnable(PFEN_LIGHTING);
    lt = new pfLight();
    lt->setPos(.2f, -1.0f, 1.0f, 0.0f);
    lt->on();

    start = pfGetTime();

    /* Twirl text for 0.25f seconds */
    do
    {
	pfClear(PFCL_COLOR | PFCL_DEPTH, NULL);
	
	t = (pfGetTime() - start) / 0.25f;
	t = PF_MIN2(t, 1.0f);

	mat.makeRot((1.0f - 0.5f*t) * -90.0f, 
		     1.0f, 0.0f, 0.0f); 

	t *= t;
	mat.postTrans(mat, 
		       0.0f, 
 		       3.0f * t * dist +  6.0f * (1.0f - t) * dist,
		       0.0f);

	pfPushMatrix();
	pfMultMatrix(mat);
	str->draw();
	pfPopMatrix();

	pw->swapBuffers();

    } while (t < 1.0f);

    // Get image in both front and back buffers
    pfClear(PFCL_COLOR | PFCL_DEPTH, NULL);
    pfPushMatrix();
    pfMultMatrix(mat);
    str->draw();
    pfPopMatrix();

    pw->swapBuffers();

    lt->off();

    pfPopMatrix();
    pfPopState();

    pfDelete(lt);
    pfDelete(str);
}

/* this will only be initialized in the draw process */
static int Overlay = -1;

void
initPipe(pfPipeWindow *pw)
{
    char	path[PF_MAXSTRING];

    static char msg[] = "Welcome to OpenGL Performer";

    if (Multipipe == HYPERPIPE) return;

    pfPushState();
    pfBasicState();

	Overlay = ((pw->getOverlayWin()) ? 1 : 0);

    if ((ViewState->welcomeText[0] == '\0') ||
	(!(pfFindFile("Times-Elfin.of", path, R_OK))))
    {
	pfClear(PFCL_COLOR, NULL);
	/* 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();

	pfClear(PFCL_COLOR, NULL);
	/* 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();
    }
    else
	drawMessage(pw);

    pfPopState();
}

static void
snapImage(int snapAlpha)
{
    FILE 		*fp;
    static char 	str[80];
    static int 		count = 0;
    pfPipe* 		cpipe = ViewState->masterChan->getPipe();
    int    		id = pfGetId(cpipe)*10 + count++;

    sprintf(str, "perfly.%d.%s", id,
	    (snapAlpha) ? "rgba" : "rgb");
    pfNotify(PFNFY_INFO, PFNFY_PRINT, "Saving pipe %d image in file %s\n",
	    pfGetId(cpipe), str);

    pfuSaveImage(str, 0, 0,
		 ViewState->mouse.winSizeX,
		 ViewState->mouse.winSizeY,
		 (snapAlpha) ? 1 : 0);

    /* create info file to go with image */
    sprintf(str, "perfly.%d.info", id);
    if (fp = fopen(str,"w"))
    {
	float h, v;
	fprintf(fp, "Viewing parameters for snap %d of perfly\n\n", id);
	fprintf(fp, "XYZ: %f %f %f\n",
		ViewState->viewCoord.xyz[0],
		ViewState->viewCoord.xyz[1],
		ViewState->viewCoord.xyz[2]);
	fprintf(fp, "HPR: %f %f %f\n",
		ViewState->viewCoord.hpr[0],
		ViewState->viewCoord.hpr[1],
		ViewState->viewCoord.hpr[2]);
	fprintf(fp, "NEAR/FAR: %f %f \n",
		ViewState->near, ViewState->far);
	ViewState->masterChan->getFOV(&h, &v);
	fprintf(fp, "FOV: horiz=%f vert=%f\n", h, v);
	fclose(fp);
    }

    pfNotify(PFNFY_INFO, PFNFY_PRINT, "Done\n");
}


static void
drawOverlayName(pfPipeWindow *pw)
{
    static int 	mapped = 0;
    pfWindow 	*pwOver = pw->getOverlayWin();

    pfPushState();
    pfBasicState();

    
    if(ViewState->doDVR)
    {
	/* set the viewport to the window to completely clear the overlays */
	int xs, ys;
	pw->getSize(&xs, &ys);
	glViewport(0, 0, xs, ys);
    }

    {
	if (pwOver)
	    pwOver->select();
	else
	    return;
    }

	pfClear(PFCL_COLOR, NULL);
	
    if(!ViewState->doDVR)
    {

    if (!mapped)
    {
	static pfVec3 clrs[2] =
 	{
 	    pfVec3(0.4f, 0.0f, 0.5f), 	   /* tex - purple */
 	    pfVec3(0.125f, 0.06f, 00.125f) /* shadow - drk grey */
        };
	pfuMapWinColors(pwOver, clrs, 1, 2);
	mapped = 1;
    }	
    pfuDrawMessageCI(ViewState->masterChan,
	    ViewState->overlayText, PFU_MSG_CHAN, PFU_RIGHT_JUSTIFIED,
	    1.0f, 1.0f, PFU_FONT_SMALL, 1, 2);
	    	    

    }

    pw->select();

    pfPopState();
}

/* convey draw style from localPreDraw to localPostDraw */
static int selectedDrawStyle = 0;

void
localPreDraw(pfChannel *chan, void *)
{
    pfPipe 		*pipe = chan->getPipe();
    pfPipeWindow 	*pw = chan->getPWin();
    int 		 pipeId = pfGetId(pipe);
    
    if ((ViewState->firstPipeDraw[pipeId]) && 
	(ViewState->drawMode == DLIST_COMPILE_GEOSET))
    {
	pfuTravCompileDLists(ViewState->sceneGroup, PFU_ATTRS_NONE);
	ViewState->firstPipeDraw[pipeId] = 0;
    }

    if (ViewState->stats && 
	(ViewState->statsEnable & PFSTATSHW_ENGFXPIPE_FILL))
    {
	if (ViewState->backface)
	{
	    /* leave backface removal to loader state */
	    pfOverride(PFSTATE_CULLFACE, 0); 
	    pfCullFace(PFCF_BACK);
	}
	else
	{
	    pfCullFace(PFCF_OFF);
	    /* really force backface removal off */
	    pfOverride(PFSTATE_CULLFACE, 1); 
	}
    }

    
    /*
     * Update new draw modes
     */
    if (ViewState->drawFlags[pipeId] & UPDATE_MODES)
    {
	{ /* put this first because it may destroy the old win and
	   * create a new one (in GLX mode) which will require a
	   * window redraw.
	   */
	    static int aa=-1;
	    if (aa == -1)
		aa = ViewState->aa;
	    else if (aa != ViewState->aa)
	    {
		if (ViewState->aa)
		    pfAntialias(PFAA_ON);
		else
		    pfAntialias(PFAA_OFF);
		aa = ViewState->aa;
	    }
	}

	if (ViewState->backface)
	{
	    /* leave backface removal to loader state */
	    pfOverride(PFSTATE_CULLFACE, 0); 
	    pfCullFace(PFCF_BACK);
	}
	else
	{
	    pfCullFace(PFCF_OFF);
	    /* really force backface removal off */
	    pfOverride(PFSTATE_CULLFACE, 1); 
	}

	if (ViewState->lighting == LIGHTING_OFF)
	{
	    pfOverride(PFSTATE_ENLIGHTING | PFSTATE_FRONTMTL, 0);
	    pfDisable(PFEN_LIGHTING);
	    pfOverride(PFSTATE_ENLIGHTING | PFSTATE_FRONTMTL, 1);
	}
	else
	    pfOverride(PFSTATE_ENLIGHTING | PFSTATE_FRONTMTL, 0);

	if (ViewState->texture == FALSE)
	{
	    pfOverride(PFSTATE_ENTEXTURE | PFSTATE_TEXTURE | PFSTATE_ENTEXGEN, 0);
	    pfDisable(PFEN_TEXTURE);
	    pfDisable(PFEN_TEXGEN);
	    pfOverride(PFSTATE_ENTEXTURE | PFSTATE_TEXTURE | PFSTATE_ENTEXGEN, 1);
	}
	else
	    pfOverride(PFSTATE_ENTEXTURE | PFSTATE_TEXTURE | PFSTATE_ENTEXGEN, 0);

	ViewState->drawFlags[pipeId] &= ~UPDATE_MODES;
    }

    /* 
     * remember draw style in case it changes between now 
     * and the time localPostDraw() gets called.
     */
    selectedDrawStyle = ViewState->drawStyle;

    /* handle draw style */
    pfuPreDrawStyle(selectedDrawStyle, ViewState->scribeColor);
}

void
localPostDraw(pfChannel *chan, void *)
{
    pfPipe *mPipe, *cPipe;
    pfPipeWindow 	*pw = chan->getPWin();
    int 		 pipeId;
    
    /* handle draw style */
    pfuPostDrawStyle(selectedDrawStyle);

    mPipe = ViewState->masterChan->getPipe();
    cPipe = chan->getPipe();
    pipeId = pfGetId(cPipe);

    
    if (ViewState->exitCount == 0)
	ViewState->exitFlag = TRUE;

    if (ViewState->exitCount > 0)
	ViewState->exitCount--;

    if (ViewState->printStats > 0)
	ViewState->printStats--;

    /* take snapshot of image */
    if (ViewState->snapImage)
    {
	snapImage(ViewState->snapImage == 2);
	ViewState->snapImage = 0;
    }

    /* Handle REDRAW event */
    if (ViewState->drawFlags[pipeId] & REDRAW_WINDOW)
    {
	/* master channel controls master pipe */
	if (pipeId == pfGetId(ViewState->masterChan->getPipe()))
	{
	    /* Only draw message into master channel */
	    if (chan == ViewState->masterChan)
	    {
		if (ViewState->redrawOverlay)
		{
		    if (Overlay)
			drawOverlayName(pw);
		    /* make sure enough draw frames happened to fill both buffers with 
		     * info trickled down from the draw (2 frames potential latency
		     */
		    if ((pfGetPipeDrawCount(pipeId) - ViewState->redrawCount) > 4) 
			ViewState->redrawOverlay = 0;
		}
		else
		    ViewState->drawFlags[pipeId] &= ~REDRAW_WINDOW;
	    }
	}
	else
	    ViewState->drawFlags[pipeId] &= ~REDRAW_WINDOW;

	pfClear(PFCL_DEPTH, NULL); /* needed for Extreme when move window 
				       * with sky-ground earth-sky
				       */
    }

    if (ViewState->gui)
	updateGUIViewMsg();

    if (cPipe == mPipe)
    {
	/* draw scene-graph hierarchy */
	if (ViewState->tree && chan == ViewState->masterChan)
	    pfuDrawTree(chan, (pfNode*)ViewState->scene, ViewState->panScale);
	
	/* draw 2D cursor */
	if ((pfuGetCursorType() == PFU_CURSOR_2D) && ViewState->mouse.inWin)
	{
	    pfPipeWindow *pw = chan->getPWin();
	    pfuGUICursor(PFU_CURSOR_MAIN, PFU_CURSOR_OFF);
	    pfuGUICursor(PFU_CURSOR_GUI, PFU_CURSOR_OFF);
	    pfuDrawPWin2DCursor(pw, ViewState->mouse.xpos, ViewState->mouse.ypos);
	}
	else
	{
	    pfuGUICursor(PFU_CURSOR_MAIN, PFU_CURSOR_DEFAULT);
	    pfuGUICursor(PFU_CURSOR_GUI, PFU_CURSOR_DEFAULT);
	}

    }
}
	

void
initXformer(void)
{
    ViewState->xformer = new(pfGetSharedArena()) pfiTDFXformer;

    ViewState->xformer->setAutoInput(ViewState->masterChan,
	&ViewState->mouse, &ViewState->events);

    ViewState->xformer->setNode(ViewState->sceneGroup);
    ViewState->xformer->setAutoPosition(NULL, ViewState->sceneDCS);
    ViewState->xformer->setResetCoord(&(ViewState->initViews[0]));
    xformerModel(ViewState->xformerModel, ViewState->collideMode);

    resetPosition(POS_ORIG);
}

void
drawModesChanged(void)
{
    int	i;

    for (i=0; i<NumPipes; i++)
	ViewState->drawFlags[i] |= UPDATE_MODES;
}

/* ARGSUSED (suppress compiler warn) */
void
preApp(pfChannel *chan, void *data)
{
    return;
}

/* ARGSUSED (suppress compiler warn) */
void
postApp(pfChannel *chan, void *data)
{
    return;
}

/* ARGSUSED (suppress compiler warn) */
void
preCull(pfChannel *chan, void *data)
{
    /*
     * Handle gridify toggle.
     * Only the input process modifies ViewState->gridify, and only the
     * cull process modifies ViewState->gridified (always attempting to make
     * it equal to ViewState->gridify, and doing the actual gridification/
     * ungridification).  This way there are no races
     * and the current state of gridification is unambiguous from the
     * point of view of either process.
     *
     * Gridify should only do master cliptextures; the slaves own no
     * tiles, and invalidating the master will invalidate slaves as well.
     */
    if(chan != ViewState->masterChan)
	return;

    if (ViewState->gridify != ViewState->gridified)
    {
	int i;
	for (i = pfGetMultipipe()-1; i >= 0; --i)
	{
	    pfPipe *pipe = pfGetPipe(i);
	    int j;
	    for (j = pipe->getNumMPClipTextures()-1; j >= 0; --j)
	    {
		pfClipTexture *cliptex =
		  pipe->getMPClipTexture(j)->getClipTexture();
		if (ViewState->gridified)
		    (void)pfuUnGridifyClipTexture(cliptex);
		else
		    (void)pfuGridifyClipTexture(cliptex);
	    }
	}

	ViewState->gridified = !ViewState->gridified;
    }
}

/* ARGSUSED (suppress compiler warn) */
void
postCull(pfChannel *chan, void *data)
{
    return;
}

/* ARGSUSED (suppress compiler warn) */
void
preLpoint(pfChannel *chan, void *data)
{
   pfCalligraphic *calligraphic = pfGetCurCallig();

   if (calligraphic != NULL && ViewState->calligDirty)
   {
	calligraphic->setDrawTime(ViewState->calligDrawTime);
	calligraphic->setFilterSize(ViewState->calligFilterSizeX,
	    ViewState->calligFilterSizeY);
	calligraphic->setDefocus(ViewState->calligDefocus);
	calligraphic->setRasterDefocus(ViewState->rasterDefocus);
	calligraphic->setZFootPrintSize(ViewState->calligZFootPrint);
	ViewState->calligDirty = 0;
   }
   return;
}

/* ARGSUSED (suppress compiler warn) */
void
postLpoint(pfChannel *chan, void *data)
{
    pfCalligraphic *calligraphic = pfGetCurCallig();

    if (calligraphic != NULL)
    {
    }
}