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

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

Revision 1.1, Tue Nov 21 21:39:46 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 1999, 2000, 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
//
// draw.C
//

// 
// Run-time controls:
//        F1-key: profile
//    Left-mouse: advance
//  Middle-mouse: stop
//   Right-mouse: retreat
//
// Command line options to try:
// patchy fog
//   $PFPATH/town_ogl_pfi.pfb -bf -bfi 1 -fd 3 -bfs 1
//   $PFPATH/town_ogl_pfi.pfb -bf -bfi 2 -fd 2 -bfr 1 -bfv 0.5
//   $PFPATH/town_ogl_pfi.pfb -bf -bfi 1 -fm exp -bfs 1 -fd 0.003
// animated patchy fog
//   $PFPATH/town_ogl_pfi.pfb -bf -bfi 3 -fd 0.6
// layered fog
//   $PFPATH/town_ogl_pfi.pfb -lf -fd 0.5
//   $PFPATH/town_ogl_pfi.pfb -lf -fd 0.001 -fm exp2 
//   $PFPATH/town_ogl_pfi.pfb -clf -fd 1
//
// NOTE: on a machine with no hw 3D texture support (such as 02) add 
//       option -f2d when a layered fog is defined
// 
// combined
//   $PFPATH/town_ogl_pfi.pfb -blf -fd 2 -bfi 1 -fdb 0.02 -bfs 0.5
//   $PFPATH/town_ogl_pfi.pfb -blf -fd 0.0015 -fm exp 
//
// Set PFPATH to /usr/share/Performer/data/town/
//

#include <stdlib.h>
#include <string.h>
#include <math.h>
#ifdef __linux__
#include <pthread.h>
#define __USE_UNIX98
#endif
#include <signal.h> // for sigset for forked X event handler process 
#include <getopt.h> // for cmdline handler 
#include <X11/keysym.h>

#include <Performer/pf/pfNode.h>
#include <Performer/pf/pfPipe.h>
#include <Performer/pf/pfChannel.h>
#include <Performer/pf/pfVolFog.h>
#include <Performer/pr/pfMaterial.h>

#include <Performer/pr/pfLight.h>

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

#include "draw.h"
#include "fog.h"
#include "smoke.h"

int sizex = 1024, sizey = 768; // XXX make this static and use Shared 

#define NO_FOG          0
#define OPENGL_FOG      1
#define LAYERED_FOG     2
#define LAYERED_MULTICOLORED_FOG  4
#define FOG_BOUNDARIES  8

#define MAX_FILES 50

static int fogType = NO_FOG;

///////////////////////////////////////////////////////////////////////////
//
// structure that resides in shared memory so that the
// application, cull, and draw processes can access it.

typedef struct
{
    pfPipeWindow    *pw;
    int		    exitFlag;
    int		    inWindow, reset;
    float	    mouseX, mouseY;
    int		    winSizeX, winSizeY;
    int		    mouseButtons;
    pfCoord	    view, viewOrig;
    float	    accelRate;
    float	    sceneSize;
    int		    drawStats;
    int		    XInputInited;
    pfVolFog        *volFog;
    int             animateSmoke;
    pfNode          *smokeNode;
} SharedData;

static SharedData *Shared;

//
// APP process variables

// write out scene upon read-in - uses pfDebugPrint 
static int WriteScene = 0;
static int FullScreen = 0;
static int WinType = PFPWIN_TYPE_X;
static int NoBorder = 0;
static int ForkedXInput = 0;

static int fogIndex = 1;
static int fogMode = PFVFOG_LINEAR;
static int force2d = 0;
static float fogDensity = 1, fogHeight = 0;
static float fogDensityBias = 0;
static float fogRandomness = 0;
static int fasterPatchy = 0;

float fogScale = 0.2; 
static float fogVariance = 0;

// light source created and updated in DRAW-process 
//static pfLight *Sun;

static void CullChannel(pfChannel *chan, void *data);
static void DrawChannel(pfChannel *chan, void *data);
static void OpenPipeWin(pfPipeWindow *pw);
static void UpdateView(void);
static void InitXInput(pfWSConnection dsp);
static void DoXInput(void);
static void GetXInput(Display *dsp);


static void Usage(void)
{
    printf("The program draws a scene with layered or patchy fog.\n\n"
	   "USAGE: volfog [options] <database files>\n"
	   "  -glf     OpenGL fog\n"
	   "  -bf      fog specified by its boundaries\n"
	   "  -lf      layered fog\n"
	   "  -clf     multicolor layered fog\n"
	   "\n"
	   "  -fd F    fog density \n"
	   "  -fm m    fog mode (m=linear,exp,exp2)\n"
	   "\n"
	   "  -s x y   window size\n"
	   "  -fs      fullscreen\n"
	   "\n"
	   "  -f2d     force the use of 2d textures (layered fog)\n"
	   "  -fdb F   fog density bias\n"
	   "  -bfi N   fog index (different boundaries: 0(layer),1(box),2(ground))\n"
	   "  -bfi F   fog variance (irregularity of ground fog 0-1)\n"
	   "  -bfh F   fog height (ground fog only)\n"
	   "  -bfr F   fog randomness 0-1 (ground fog only)\n"
	   "  -bfs F   fog resolution (to avoid overflows in color buffer)\n"
	   "  -bff     faster patchy fog\n"
	   "\nCommand line options to try:\n"
	   "patchy fog\n"
	   "  $PFPATH/town_ogl_pfi.pfb -bf -bfi 1 -fd 3 -bfs 1\n"
	   "  $PFPATH/town_ogl_pfi.pfb -bf -bfi 2 -fd 2 -bfr 1 -bfv 0.5\n"
	   "  $PFPATH/town_ogl_pfi.pfb -bf -bfi 1 -fm exp -bfs 1 -fd 0.003\n"
	   "animated patchy fog\n"
	   "  $PFPATH/town_ogl_pfi.pfb -bf -bfi 3 -fd 0.6\n"
	   "layered fog\n"
	   "  $PFPATH/town_ogl_pfi.pfb -lf -fd 0.5\n"
	   "  $PFPATH/town_ogl_pfi.pfb -lf -fd 0.001 -fm exp2 \n"
	   "  $PFPATH/town_ogl_pfi.pfb -clf -fd 1\n\n"
	   "NOTE: on a machine with no hw 3D texture support (such as 02) add \n"
	   "      option -f2d when a layered fog is defined\n"
	   "\ncombined\n"
	   "  $PFPATH/town_ogl_pfi.pfb -blf -fd 2 -bfi 1 -fdb 0.02 -bfs 0.5\n"
	   "  $PFPATH/town_ogl_pfi.pfb -blf -fd 0.0015 -fm exp \n"
	   "\nSet PFPATH to /usr/share/Performer/data/town/\n"
	   "\nRun-time controls:\n"
	   "       F1-key: profile\n"
	   "   Left-mouse: advance\n"
	   " Middle-mouse: stop\n"
	   "  Right-mouse: retreat\n"
	   "        t-key: faster/slower patchy fog\n"
	   );
}

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


static int
docmdline(int argc, char *argv[], int *fargc, char *fargv[])
{
    int i, found;

    if(argc <= 1) {
	Usage();
	exit(-1);
    }

    found = 0;

    /* process commmand line args */
    for (i =1 ; i < argc; ++i) {
	if(argv[i][0] != '-') {
	    if(found == MAX_FILES) {
		fprintf(stderr,
			"Cannot read more than %d files!\n", MAX_FILES);
		continue;
	    }
	    fargv[found++] = argv[i];
	    continue; //skip over databases
	}

        if (!strcmp("-s", argv[i])) {
	    sizex = atoi(argv[++i]);
	    sizey = atoi(argv[++i]);
	}
	else if (!strcmp("-fs", argv[i])) {
	    FullScreen = 1;
	}
	else if (!strcmp("-f2d", argv[i])) {
	    force2d = 1;
	}
	else if (!strcmp("-glf", argv[i])) {
	    fogType = OPENGL_FOG;
	}
	else if (!strcmp("-bf", argv[i])) {
	    fogType = FOG_BOUNDARIES;
	}
	else if (!strcmp("-lf", argv[i])) {
	    fogType = LAYERED_FOG;
	}
	else if (!strcmp("-blf", argv[i])) {
	    fogType = LAYERED_FOG | FOG_BOUNDARIES;
	}
	else if (!strcmp("-clf", argv[i])) {
	    fogType = LAYERED_MULTICOLORED_FOG;
	}
	else if (!strcmp("-bfi", argv[i])) {
	    fogIndex = atoi(argv[++i]);
	}
	else if (!strcmp("-bfv", argv[i])) {
	    fogVariance = atof(argv[++i]);
	}
	else if (!strcmp("-bff", argv[i])) {
	    fasterPatchy = 1;
	}
	else if (!strcmp("-fd", argv[i])) {
	    fogDensity = atof(argv[++i]);
	}
	else if (!strcmp("-fdb", argv[i])) {
	    fogDensityBias = atof(argv[++i]);
	}
	else if (!strcmp("-bfs", argv[i])) {
	    fogScale = atof(argv[++i]);
	}
	else if (!strcmp("-bfh", argv[i])) {
	    fogHeight = atof(argv[++i]);
	}
	else if (!strcmp("-bfr", argv[i])) {
	    fogRandomness = atof(argv[++i]);
	}
	else if (!strcmp("-fm", argv[i])) {
	    i++;
	    if(!strcmp("linear", argv[i]))
		fogMode = PFVFOG_LINEAR;
	    else if(!strcmp("exp", argv[i]))
		fogMode = PFVFOG_EXP;
	    else if(!strcmp("exp2", argv[i]))
		fogMode = PFVFOG_EXP2;
	}
	else {
	    printf("\nUnknown option: %s\n\n", argv[i]);
 	    Usage();
	    exit(1);
	}
    }

    *fargc = found;
    if(found == 0) {
	Usage();
	exit(-1);
    }
	    
    return found;
}


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


int
main (int argc, char *argv[])
{
    int		    arg;
    pfPipe         *p;
    pfBox           bbox;
    float	    far = 10000.0f;
    pfWSConnection  dsp=NULL;
    int             fargc;
    char            *fargv[MAX_FILES];
    
    docmdline(argc, argv, &fargc, fargv);


    pfInit();
    
    // configure multi-process selection 
    pfMultiprocess(PFMP_APPCULLDRAW);
    //pfMultiprocess(PFMP_DEFAULT);
    
    // allocate shared before fork()'ing parallel processes 
    Shared = (SharedData*)pfCalloc(1, sizeof(SharedData), pfGetSharedArena());
    Shared->drawStats = 1;
    
    // Load all loader DSO's before pfConfig() forks 
    for (arg = 0; arg < fargc; arg++)
	if(fargv[arg][0] != '-') //is not an option, must be db file
	    pfdInitConverter(fargv[arg]);

    // initiate multi-processing mode set in pfMultiprocess call 
    // FORKs for Performer processes,  CULL and DRAW, etc. happen here.
    
    pfConfig();
 
    //iR = (strncasecmp(pfGetMachString(), "IR", 2) == 0);
   
    // configure pipes and windows 
    p = pfGetPipe(0);
    Shared->pw = new pfPipeWindow(p);
    Shared->pw->setName("OpenGL Performer");
    Shared->pw->setWinType(WinType);
    if (NoBorder)
	Shared->pw->setMode(PFWIN_NOBORDER, 1);
    // Open and configure the GL window. 
    Shared->pw->setConfigFunc(OpenPipeWin);

    int constraints[] = {
	PFFB_DOUBLEBUFFER,
	PFFB_RGBA,
	PFFB_RED_SIZE, 8, 
	PFFB_GREEN_SIZE, 8, 
	PFFB_BLUE_SIZE, 8, 
	PFFB_ALPHA_SIZE, 8, 
	PFFB_STENCIL_SIZE, 8, 
	PFFB_DEPTH_SIZE, 15, 
#ifndef __linux__
	PFFB_SAMPLES, 0,
	GLX_SAMPLE_BUFFERS_SGIS, 0,
#endif
	(int)NULL};

    Shared->pw->chooseFBConfig(constraints);

    Shared->pw->config();

    if (FullScreen)
	Shared->pw->setFullScreen();
    else
	Shared->pw->setOriginSize(0, 0, sizex, sizey);

    // set off the draw process to open windows and call init callbacks 
    pfFrame();
    
    int rgb_bits;
    Shared->pw->query(PFQWIN_RGB_BITS, &rgb_bits);
    printf("RGB bits: %d\n", rgb_bits);
    Shared->pw->query(PFQWIN_ALPHA_BITS, &rgb_bits);
    printf("Alpha bits: %d\n", rgb_bits);

    int stencil_bits = -1;
    Shared->pw->query(PFQWIN_STENCIL_BITS, &stencil_bits);
    printf("Stencil bits: %d\n", stencil_bits);

    // create forked XInput handling process 
    // since the Shared pointer has already been initialized, that structure
    // will be visible to the XInput process. Nothing else created in the
    // application after this fork whose handles are not put in shared memory
    // (such as the database and channels) will be visible to the
    // XInput process.
    
    if (WinType & PFPWIN_TYPE_X)
    {
	pid_t	    fpid = 0;
	if (ForkedXInput)
	{
	    if ((fpid = fork()) < 0)
		pfNotify(PFNFY_FATAL, PFNFY_SYSERR, "Fork of XInput process failed.");
	    else if (fpid)
		pfNotify(PFNFY_NOTICE,PFNFY_PRINT,"XInput running in forked process %d",
			 fpid);
	    else if (!fpid)
		DoXInput();
	}
	else
	{
	    dsp = pfGetCurWSConnection();
	}
    }
    
    // specify directories where geometry and textures exist 
    if (!(getenv("PFPATH")))
        pfFilePath(
                   "."
                   ":./data"
                   ":../data"
                   ":../../data"
                   ":/usr/share/Performer/data"
                   );
    pfNotify(PFNFY_INFO, PFNFY_PRINT,"FilePath: %s\n", pfGetFilePath());
    
    pfScene *scene = new pfScene();

    int i;
    
    // load files named by command line arguments 
    for (i=0 ; i<fargc; i++) {
	pfNode *root;
	if ((root = pfdLoadFile(fargv[i])) != NULL)
	{
	    scene->addChild(root);
	}
    }
    
    // Write out nodes in scene (for debugging) 
    if (WriteScene)
    {
	FILE *fp;
	if (fp = fopen("scene.out", "w"))
	{
	    pfPrint(scene, PFTRAV_SELF|PFTRAV_DESCEND, PFPRINT_VB_DEBUG, fp);
	    fclose(fp);
	}
	else
	    pfNotify(PFNFY_WARN, PFNFY_RESOURCE,
		     "Could not open scene.out for debug printing.");
    }
    
    // determine extent of scene's geometry 
    pfuTravCalcBBox(scene, &bbox);
    
    //pfFrameRate(30.0f);
    pfPhase(PFPHASE_FREE_RUN);
    
    pfChannel *chan = new pfChannel(p);
    Shared->pw->addChan(chan);
    chan->setTravFunc(PFTRAV_CULL, CullChannel);
    chan->setTravFunc(PFTRAV_DRAW, DrawChannel);
    chan->setScene(scene);
    chan->setNearFar(10.0f, far);
    
    chan->setTravMode(PFTRAV_CULL,  PFCULL_ALL);

    Shared->animateSmoke = 0;

    // force the use of 2d textures on O2 or on linux
#ifndef __linux__
    if(!strncmp(pfGetMachString(), "CRM", 3))
#endif
	force2d = 1;


    switch(fogType) {
    case OPENGL_FOG: {
	float color[4] = {0.9,0.9,1,1};
	
	glEnable(GL_FOG);
	glFogi(GL_FOG_MODE, GL_LINEAR);
	glFogfv(GL_FOG_COLOR, color);
	
	glFogf(GL_FOG_START, 100);
	glFogf(GL_FOG_END, 550);
	glFogf(GL_FOG_DENSITY, 0.002);
	
	glHint(GL_FOG_HINT, GL_NICEST);
	break;
    }
    
    case LAYERED_MULTICOLORED_FOG:
	Shared->volFog = new pfVolFog;

	Shared->volFog->addColoredPoint(20,0, 0.9, 0.9, 1);
	Shared->volFog->addColoredPoint(40, fogDensity,
					   0.9 ,0.9 , 1);
	Shared->volFog->addColoredPoint(120, fogDensity,
					   0.67,0.45, 0.1);
	Shared->volFog->addColoredPoint(140, 0, 0.67,0.45, 0.1);

	Shared->volFog->addColoredPoint(180, 0, 0.6, 0.7, 1);
	Shared->volFog->addColoredPoint(200, fogDensity*0.2, 
					   0.6, 0.7, 1);
	Shared->volFog->addColoredPoint(250, fogDensity*0.2, 
					   0.6,0.7, 1);
	Shared->volFog->addColoredPoint(270, 0, 0.6,0.7, 1);

	Shared->volFog->setVal(PFVFOG_MODE, (float)fogMode);

	if(force2d)
	    Shared->volFog->setFlags(PFVFOG_FLAG_FORCE_2D_TEXTURE, 1);

	Shared->volFog->addChannel(chan);
	Shared->volFog->apply(scene);
	break;

    case LAYERED_FOG:
    case FOG_BOUNDARIES | LAYERED_FOG:
	Shared->volFog = new pfVolFog;

	Shared->volFog->addPoint(100,0);
	Shared->volFog->addPoint(100,fogDensity);
	Shared->volFog->addPoint(200,fogDensity);
	Shared->volFog->addPoint(200,0);

	Shared->volFog->setColor(0.6, 0.6, 1);

	Shared->volFog->setVal(PFVFOG_MODE, (float)fogMode);

	if(force2d)
	    Shared->volFog->setFlags(PFVFOG_FLAG_FORCE_2D_TEXTURE, 1);

	if(fogType == LAYERED_FOG) {
	    // only layered fog

	    Shared->volFog->addChannel(chan);
	    Shared->volFog->apply(scene);
	    break;
	}

    case FOG_BOUNDARIES:
	pfNode *root;

	if(fogType == FOG_BOUNDARIES)
	    Shared->volFog = new pfVolFog;

	switch(fogIndex) {
	case 0:
	    root = MakeOneLayerFog(&bbox, 100, 20);
	    break;
	case 1:
            {
	    pfBox bbox2;
	    float vec[3];
	    
	    vec[0] = 0.45 * (bbox.max[0] - bbox.min[0]);
	    vec[1] = 0.45 * (bbox.max[1] - bbox.min[1]);

	    bbox2.min[0] = bbox.min[0] + vec[0];
	    bbox2.min[1] = bbox.min[1] + vec[1];
	    bbox2.min[2] = bbox.min[2];
	    bbox2.max[0] = bbox.max[0] - vec[0];
	    bbox2.max[1] = bbox.max[1] - vec[1];
	    bbox2.max[2] = bbox.max[2];

	    root = MakeOneLayerFog(&bbox2, 80, -10);
            }
	    break;

	case 2:
	    root = MakeGroundFog(fogHeight, 
				 fogRandomness, fogVariance, &bbox);
	    break;

	case 3: {
	    float pos[3] = {2500,3000,1};
	    float speed[3] = {0,0,1};
	    float wind[3] = {0.002,0.000,0}; //{0.003,0.000,0};
	    int texSize[3];

	    root = MakeSmoke(pos, 10, 4, speed, wind);

	    Shared->smokeNode = root;
	    Shared->animateSmoke = 1;
	    Shared->volFog->setColor(0.1, 0.05, 0);
	    //Shared->volFog->setColor(1, 1, 1); // test

#if 1
	    // set max distance, as low as possible, but still bigger than
	    // size of the fog object
	    Shared->volFog->setVal(PFVFOG_MAX_DISTANCE, 200);

	    // define density of smoke using layered fog
	    Shared->volFog->setFlags(PFVFOG_FLAG_LAYERED_PATCHY_FOG, 1);
	    
	    // Note, the fog area should not contain any scene objects for now

#define SCALE 2
	    // too high densities cause overflows
	    Shared->volFog->addPoint(-10,0.1*SCALE); 
	    Shared->volFog->addPoint(30,0.05*SCALE); 
	    Shared->volFog->addPoint(60,0.025*SCALE); 
	    Shared->volFog->addPoint(80,0.008*SCALE); 
	    Shared->volFog->addPoint(95,0.0*SCALE);
	    Shared->volFog->addPoint(200,0.0*SCALE);

	    // it doesn't work with multicolored layered fog

	    texSize[0] = 64;
	    texSize[1] = 64;
	    texSize[2] = 64;

	    Shared->volFog->setAttr(PFVFOG_3D_TEX_SIZE, (void *)texSize);

	    if(force2d)
	      Shared->volFog->setFlags(PFVFOG_FLAG_FORCE_2D_TEXTURE, 1);
#endif
	    break;
	}
	}
	
	scene->addChild(root);

	Shared->volFog->addNode(root);
	Shared->volFog->setVal(PFVFOG_RESOLUTION, fogScale);
	Shared->volFog->setDensity(fogDensity);
	Shared->volFog->setVal(PFVFOG_DENSITY_BIAS, fogDensityBias);

	Shared->volFog->setVal(PFVFOG_MODE, (float)fogMode);

	if(fasterPatchy)
	    /* flag not exposed yet, use its value */
	    Shared->volFog->setFlags(PFVFOG_FASTER_PATCHY_FOG, 1);
	    
	Shared->volFog->addChannel(chan);
	Shared->volFog->apply(scene);	    

	break;
    }
    
    // vertical FOV is matched to window aspect ratio 
    chan->setFOV(45.0f, -1.0f);
    if (1)
    {
	float sceneSize;
	// Set initial view to be "in front" of scene 
	
	// view point at center of bbox 
	Shared->view.xyz.add(bbox.min, bbox.max);
	Shared->view.xyz.scale(0.5f, Shared->view.xyz);
	Shared->view.xyz[PF_Z] -= 0.15*(bbox.max[PF_Z] - bbox.min[PF_Z]);
	
	// 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;	
    }  else
    {
	Shared->view.xyz.set(0.0f, 0.0f, 100.0f);
	PFSET_VEC3(bbox.min, -5000.0f, -5000.0f, -1000000.0f);
	PFSET_VEC3(bbox.max, 5000.0f, 5000.0f, 10000000.0f);
	Shared->sceneSize = 10000.0f;
    }
    Shared->view.hpr.set(0.0f, 0.0f, 0.0f);
    chan->setView(Shared->view.xyz, Shared->view.hpr);
    PFCOPY_VEC3(Shared->viewOrig.xyz, Shared->view.xyz);
    PFCOPY_VEC3(Shared->viewOrig.hpr, Shared->view.hpr);

    // light
    pfLight *light = new pfLight;

    light->setColor(PFLT_AMBIENT, 0.8, 0.8, 0.8);
    light->setColor(PFLT_DIFFUSE, 1.2, 1.2, 1.2);
    light->setColor(PFLT_SPECULAR, 0.0, 0.0, 0.0);
    light->setPos(0.25, 0.5, 1, 0);

    light->on();

    // enable lighting

    //glEnable(GL_COLOR_MATERIAL);
    //glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);

    // light model
    pfLightModel *lm = new pfLightModel;
    lm->setAmbient(0.0f, 0.0f, 0.0f);
    lm->apply();

    // material
    //pfMaterial *mat = new pfMaterial;
    //mat->setColor(PFMTL_AMBIENT, 0.0, 0.0, 0.0);
    //mat->setColor(PFMTL_DIFFUSE, 0.64, 0.64, 0.64);
    //mat->setColor(PFMTL_SPECULAR, 0.0, 0.0, 0.0);
    //mat->setShininess(10.0f);
    //mat->setColorMode(PFMTL_BOTH, PFMTL_CMODE_AD);
    //mat->apply();

    // main simulation loop 
    while (!Shared->exitFlag)
    {
	// wait until next frame boundary 
	pfSync();
	
	pfFrame();
	
	// Set view parameters for next frame 
	UpdateView();

	if(Shared->animateSmoke == 1) {
	    UpdateSmoke();

	    // to update the bounding box
	    Shared->volFog->addNode(Shared->smokeNode);
	}

	chan->setView(Shared->view.xyz, Shared->view.hpr);
	if(Shared->volFog)
	    Shared->volFog->updateView();

	// initiate traversal using current state 
    
	if (!ForkedXInput  && (WinType & PFPWIN_TYPE_X))
	{
	    if (!Shared->XInputInited)
		InitXInput(dsp);
	    if (Shared->XInputInited)
		GetXInput(dsp);
	}
    }
    
    // terminate cull and draw processes (if they exist) 
    pfExit();
    
    // exit to operating system 
    return 0;
}

static void 
InitXInput(pfWSConnection dsp)
{
    Window w;
    
    /* wait for X Window to exist in Performer shared memory */
   if (w = Shared->pw->getWSWindow())
   {
	XSelectInput(dsp, w, PointerMotionMask |
			ButtonPressMask | ButtonReleaseMask | 
			KeyPressMask | KeyReleaseMask);
	XMapWindow(dsp, w);
	XFlush(dsp);
	Shared->XInputInited = 1;
    }
}

//
// DoXInput() runs an asychronous forked even handling process.
//  Shared memory structures can be read from this process
//  but NO performer calls that set any structures should be 
//  issues by routines in this process.

void
DoXInput(void)
{
    // windows from draw should now exist so can attach X input handling
    // to the X window 
    
    Display *dsp = pfGetCurWSConnection();
#ifndef __linux__    
    prctl(PR_TERMCHILD);        // Exit when parent does 
#else
    prctl(PR_SET_PDEATHSIG, SIGHUP, 0, 0, 0);
#endif    
    sigset(SIGHUP, SIG_DFL);    // Exit when sent SIGHUP by TERMCHILD 
    InitXInput(dsp);
    
    while (1)
    {
	XEvent          event;
	if (!Shared->XInputInited)
	    InitXInput(dsp);
	if (Shared->XInputInited)
	{
	    XPeekEvent(dsp, &event);
	    GetXInput(dsp);
	}
    }
}

// 
//	UpdateView() updates the eyepoint based on the information
//	placed in shared memory by GetInput().

static void    
UpdateView(void)
{
    static float speed = 0.0f;
    pfCoord *view = &Shared->view;
    float cp;
    float mx, my;
    static double thisTime = -1.0f;
    double prevTime;
    float deltaTime;

    prevTime = thisTime;
    thisTime = pfGetTime();

    if (prevTime < 0.0f)
	return;

    if (!Shared->inWindow || Shared->reset)
    {
	speed = 0;
	Shared->reset = 0;
	Shared->accelRate = 0.003f * Shared->sceneSize;
	return;
    }

    deltaTime = thisTime - prevTime;
    switch (Shared->mouseButtons)
    {
    case Button1Mask: /* LEFTMOUSE: faster forward or slower backward*/
    case Button1Mask|Button2Mask:
	speed += Shared->accelRate * deltaTime;
	if (speed > Shared->sceneSize)
	    speed = Shared->sceneSize;
	break;
    case Button3Mask: /* RIGHTMOUSE: faster backward or slower foreward*/
    case Button3Mask|Button2Mask:
	speed -= Shared->accelRate * deltaTime;
	if (speed < -Shared->sceneSize)
	    speed = -Shared->sceneSize;
	break;
    }
    if (Shared->mouseButtons)
    {
	mx = 2.0f * (Shared->mouseX / (float)Shared->winSizeX) - 1.0f;
	my = 2.0f * (Shared->mouseY / (float)Shared->winSizeY) - 1.0f;
				     
	/* update view direction */
	view->hpr[PF_H] -= mx * PF_ABS(mx) * 30.0f * deltaTime;
	view->hpr[PF_P] += my * PF_ABS(my) * 30.0f * deltaTime;
	view->hpr[PF_R]  = 0.0f;
	
	/* update view position */
	cp = cosf(PF_DEG2RAD(view->hpr[PF_P]));
	view->xyz[PF_X] += speed*sinf(-PF_DEG2RAD(view->hpr[PF_H]))*cp;
	view->xyz[PF_Y] += speed*cosf(-PF_DEG2RAD(view->hpr[PF_H]))*cp;
	view->xyz[PF_Z] += speed*sinf( PF_DEG2RAD(view->hpr[PF_P]));

    }
    else
    {
	speed = 0.0f;
	Shared->accelRate = 0.003f * Shared->sceneSize;
    }
}

//
//	CullChannel() -- traverse the scene graph and generate a
// 	display list for the draw process.  This procedure is 
//	executed in the CULL process.


static void
CullChannel(pfChannel *, void *)
{
    // 
    // pfDrawGeoSet or other display listable Performer routines
    // could be invoked before or after pfCull()
    pfCull();
}

//
//	OpenPipeWin() -- create a win: setup 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)
{
    pw->open();
    
    // create a light source in the "south-west" (QIII) 
    //Sun = new pfLight();
    //Sun->setPos(-0.3f, -0.3f, 1.0f, 0.0f);
}


//
//	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 *)
{
    // rebind light so it stays fixed in position 
    //Sun->on();
    
    // erase framebuffer and draw Earth-Sky model 
    channel->clear();
    
    switch(fogType) {
    case FOG_BOUNDARIES:
    case LAYERED_FOG:
    case FOG_BOUNDARIES | LAYERED_FOG:
    case LAYERED_MULTICOLORED_FOG:
	Shared->volFog->draw(channel);
	break;

    default:
	// invoke Performer draw-processing for this frame 
	pfDraw();
    }
    
    // draw Performer throughput statistics 
    
    if (Shared->drawStats)
	channel->drawStats();
    
    // read window origin and size (it may have changed) 
    channel->getPWin()->getSize(&Shared->winSizeX, &Shared->winSizeY);
    
}


static void
GetXInput(pfWSConnection dsp)
{
    static int x=0, y=0;
    
    if (XEventsQueued(dsp, QueuedAfterFlush))
#ifdef __linux__
    while (XPending(dsp))
#else
    while (XEventsQueued(dsp, QueuedAlready))
#endif    
    {
	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;
	    }
	    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;
		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: 
		    Shared->exitFlag = 1;
		    exit(0);
		    break;
		case XK_space:
		    Shared->reset = 1;
		    PFCOPY_VEC3(Shared->view.xyz, Shared->viewOrig.xyz);
		    PFCOPY_VEC3(Shared->view.hpr, Shared->viewOrig.hpr);
		    pfNotify(PFNFY_NOTICE, PFNFY_PRINT,  "Reset");
		    break;
		case XK_g:
		    Shared->drawStats = !Shared->drawStats;
		    break;
		case XK_s:
		    Shared->animateSmoke ^= 0x02;
		    break;
		case XK_t:
		    fasterPatchy = !fasterPatchy;
		    Shared->volFog->setFlags(PFVFOG_FASTER_PATCHY_FOG, fasterPatchy);
		    break;
		default:
		    break;
		}
	    }
	    break;
	default:
	    break;
	}// switch 
    }
    Shared->mouseX = x;
    Shared->mouseY = y;
}