[BACK]Return to movie.c CVS log [TXT][DIR] Up to [Development] / performer / src / pguide / libpfutil

File: [Development] / performer / src / pguide / libpfutil / movie.c (download)

Revision 1.1, Tue Nov 21 21:39:39 2000 UTC (16 years, 10 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: movie.c
 * -------------
 * 
 * $Revision: 1.1 $ 
 *
 * This is an example of using functionalities of libpr, libpf,  and utilities
 *  in libpfutil to achieve animated texture on scene objects.
 *  This program allows you to have multiple movie screens and movies, 
 *  and provides a GUI utility (using libpfutil GLX GUI and input handling
 *  utilities) to allow you to select screens and movies and control the
 *  animation.
 *
 * This example creates a projector for each movie that is loaded.
 * Polygons in the scene are screens - each using a pfTexture that plays
 * a movie by being added to the screen list of a given movie projector.
 * Note that the movie in a projector can also be changed at any time with
 * pfuProjectorMovie(),  however, this is a slightly more expensive operation than 
 * just adding or deleting screens.  When a movie is changed, all screens for that 
 * projector will then start showing the new movie.
 *
 * Command Line Options: (getopt() is used to parse command line)
 * ---------------------
 * -c num pipe channels - use with multipipe to force multiple pipes on a 
 *			single pipe machine.
 * -p procSplit	    - performer multiprocess mode
 * -m "moviename namefmt start end" - a new movie
 *			frames of the movie in the range [start,end] will
 *			be loaded into a movie.  Use a separate -m for each 
 *			movie to be loaded. basetex will be used when the
 *			movie is applied to the screen,  but not running.
 *                      Each frame of the movie should be in a separate 
 *                      image file.  namefmt provides the printf format 
 *			to generate the file names from the frame numbers,
 *			e.g. mymovie%02d.rgb.  start and end are the starting 
 *			and ending frame numbers, respectively. 
 *
 *			movieconvert(1) in dmedia_tools mcan be useful
 *			for generating series of images.  
 *			
 * -M <mode>	    - Multipipe mode\n"
 *           0 -> single pipe mode\n"
 *           1 -> multipipe mode\n"
 *           2 -> hyperpipe mode\n" 
 * -n notifyLevel   - controls verobsity of program (defualt=5=DEBUG)
 * -N		    - set non-degrading priority
 * -R		    - restrict processors - requires root ID
 * -s numScreens    - set the number of movie screens in the scene (default=2)
 * -t texfile	    - set a base texture on the screens for when they are
 *			not playing a movie
 *
 *
 * Run-time controls:
 * ------------------
 *       ESC-key: exits
 */

/* includes */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <getopt.h>

/* X Window includes */
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>

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


/***************************** Defines and Enums *****************************/

#define MAX_MOVIES 16
#define MAX_SCREENS 16
#define MAX_TEXTURES	256

/* Panel Widget Control Tokens */
enum PanelControlToken {
    CTL_QUIT=1, 
    CTL_SCREEN_SELECT, 
    CTL_MOVIE_SELECT, 
    CTL_ADD_SCREEN_TO_PROJECTOR, 
    CTL_RESET, 
    CTL_START, 
    CTL_STOP, 
    CTL_PAUSE, 
    CTL_RESUME, 
    CTL_STEP, 
    CTL_REVERSE, 
    CTL_SPIN, 
    CTL_SPEED, 
    CTL_STATS
};

#define HYPERPIPE 2

/******************************* Typedefs  ***********************************/

typedef struct MovieData
{
    pfSequence	*proj;	/* the "projector" */
    int	length;
    char	name[PF_MAXSTRING];
    pfTexture	*tex;  /* the handle texture for the movie */
    pfList	*tape; /* the tape of movie frames */
} MovieData;

typedef struct ScreenData
{
    int	movieIndex;
    pfTexture	*tex;
}ScreenData;

typedef struct SharedData
{
    int			exitFlag;
    int			movieFlag;
    pfuEventStream	events;		/* event structure */
    pfuPanel		*guiPanel;
    int			spin;
    int			showStats;
    
    pfGroup		*movieRoot;
    int			screenSel;
    int			movieSel;
    int			start, end, direction;
    float		speed;
    pfList		*texList;
    pfTexture		*dftTex; /* for screen with no movie */
    ScreenData		movieScreen[MAX_SCREENS];
    MovieData		movie[MAX_MOVIES];
    pfuMouse		mouse;
} SharedData;

/****************************** Global Vars **********************************/

/* data for geometry */

pfVec2          texcoords[]={   {0.0f, 0.0f},
                                {1.0f, 0.0f},
                                {1.0f, 1.0f},
                                {0.0f, 1.0f} };

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

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

/* initial window position */
static int WinXOrigin = 20, WinYOrigin = 20;
static int WinXSize = 800, WinYSize = 700;

/* global name strings */
static char 	    Name[PF_MAXSTRING] = "OpenGL Performer";
static char 	    ScreenStrs[MAX_SCREENS][PF_MAXSTRING];
/* these are used for holding movie info off the cmdline */
static char	    MovieName[MAX_MOVIES][PF_MAXSTRING] = {"None"};
static char	    MovieFmt[MAX_MOVIES][PF_MAXSTRING];
static char	    TexName[PF_MAXSTRING];

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

static int	    HaveBaseTexture = 0;
static int	    HaveTextureList = 0;
static char	    TexFileNames[MAX_TEXTURES][PF_MAXSTRING];
static int	    NumMovies=0;
static int	    NumTextures = 0;
static int	    MovieStart[MAX_MOVIES], MovieEnd[MAX_MOVIES];

static int	    NumScreens=2;
/* DCSs for spinning screens */
static pfDCS	    *ScreenDCS[MAX_SCREENS];

/* Performer control vars */
int		    NotifyLevel = PFNFY_DEBUG;
unsigned int	    ProcSplit = PFMP_DEFAULT;
int		    RestrictCPUs = 0;
int		    PrioritizeProcs = 0;
int		    Multipipe = 0;
int		    NumPipes = -1;
int		    FastDefine = 0;
int		    ReplaceMode = 0;

/***************************** Static Decls **********************************/

/* GUI stuff */
static pfuPanel	    *InitPanel(void);
static void	    ProcessInputIO(void);
static void	    addScreenToProjector(int m, int s);
static void	    PanelControl(pfuWidget *w);

static void	    OpenPipeWin(pfPipeWindow *pw);
static void	    OpenPipeline (int pipe, uint stage);
static void	    DrawChannel(pfChannel *chan, void *data);
static void	    CullChannel(pfChannel *chan, void *data);
static pfNode	    *MakeScreens(void);
static void	    resetProjector(int _movie);

/********************************* Main **************************************/


static void
Usage (void)
{
    fprintf(stderr, "Usage: movie [-m movie,start,end] [texfile ...]\n");
    pfExit();
    exit(1);
}

static char OptionStr[] = "c:Fm:M:n:Np:rRs:t:?";

static int
docmdline(int argc, char **argv)
{
     extern char *optarg;
     extern int  optind;
     int opt, i;
        
     while ((opt = getopt(argc,argv,OptionStr)) != -1) {
	 switch(opt) {
	 /* custom options */

	 case 'c':
	    NumPipes = atoi(optarg);
	    break;
	 
	 case 'F':
	    FastDefine ^= 1;
	    break;
	    
	 case 'm': 
	    if (NumMovies < MAX_MOVIES)
	    {
		NumMovies += 1;
		sscanf(optarg, "%s %s %d %d",
			&(MovieName[NumMovies]), 
			&(MovieFmt[NumMovies]),
			&(MovieStart[NumMovies]), &(MovieEnd[NumMovies]));
	    }
	    break;
	 
	    /* Enable multipipe mode */
	case 'M':
	    Multipipe = atoi(optarg);
	    break;
	
	case 'n':
	    NotifyLevel =  atoi(optarg);
	    break;
	    
	case 'N':
	    PrioritizeProcs ^= 1;
	    break;
	    
	case 'p':
	    ProcSplit = atoi(optarg);
	    break;
	
	case 'r':
	    ReplaceMode ^= 1;
	    break;
	  
	case 'R':
	    RestrictCPUs ^= 1;
	    break;
	    
	case 's':
	    NumScreens = atoi(optarg);
	    if (NumScreens > MAX_SCREENS)
		NumScreens = MAX_SCREENS;
	    break;
	  
	 case 't':
	    HaveBaseTexture = 1;
	    strcpy(TexName,optarg);
	    break;

	 case '?':
	 default:
	    Usage();
	    exit(0);
	    break;
	} /* switch */
    } /* while */
    
    if (!NumMovies && (optind < argc))
    {
	NumTextures = (argc - optind);
	NumMovies = 1;
	HaveTextureList = 1;
	for (i=0; i < NumTextures && i < MAX_TEXTURES; i++)
	{
	    strcpy(TexFileNames[i], (argv[optind + i]));
	}
	TexFileNames[i][0] = '\0';
	strcpy(MovieName[1], TexFileNames[0]);

	return NumTextures;
    } else
	return 0;
}


int
main (int argc, char *argv[])
{
    pfScene     *scene;
    pfChannel   *chan;
    pfSphere	bsphere;
    pfNode	*root;
    pfEarthSky  *esky;
    pfTexture	*tex;
    pfPipe	*pipe;
    pfPipeWindow	*pipewin;
    int		guiDirty;
    
    float       t = 0.0f;
    int	i, j;
    
    pfNotifyLevel(NotifyLevel);
    
    /* Initialize Performer */
    pfInit();	

    /* init shared mem for libpfutil */
    pfuInit();
  		
    /* Append to PFPATH standard data dirs */
    pfFilePath(".:/usr/share/Performer/data:/usr/demos/data/textures");
    
    /* cmdline options */
    docmdline(argc, argv);
    for (i=0; i < NumScreens; i++)
	sprintf(ScreenStrs[i], "Screen%d", i);

    /* Use default multiprocessing mode based on number of
     * processors.
     */
    pfMultiprocess(ProcSplit);	
    
    /* Figure out the number of channels and pipelines required */
    switch (Multipipe) {
    case TRUE:
	if (NumPipes == (-1))
	    NumPipes = ScreenCount(pfGetCurWSConnection());
	break;
    case FALSE:
	NumPipes = 1;
	break;
#ifdef IRISGL
    case HYPERPIPE:
	/* MUXPIPES not implemented for RealityEngine */
	if (0 && !getgdesc(GD_MUXPIPES))
	{
	    pfNotify(PFNFY_WARN, PFNFY_RESOURCE,
			"This machine not is not capable of Hyperpipe mode");

	    Multipipe = TRUE;
	}
	else
	{
	    NumPipes = getgdesc(GD_NSCRNS);
	}
	NumPipes = 1;
	break;
#endif /* IRISGL */
    }
    
    /* Set number of pfPipes desired. */
    pfMultipipe(NumPipes);
    /* Enable hyperpipe mode */
    if (Multipipe == HYPERPIPE)
    	pfHyperpipe(NumPipes);

    
    /* allocate shared before fork()'ing parallel processes 
     * so that all processes have will the correct Shared address
     */
    Shared = (SharedData*)pfCalloc(1, sizeof(SharedData), pfGetSharedArena());
	
    /* Configure multiprocessing mode and start parallel
     * processes.
     */
    pfConfig();	
    
    /*
     * Assign a non-degrading priority to all Performer processes.
     * User processes spawned from this one will inherit the same priority.
    */
    if (PrioritizeProcs)
        pfuPrioritizeProcs(TRUE);

    if (RestrictCPUs)
	pfuLockDownApp();
    
    scene = pfNewScene();
    
    /* Create a pfLightSource and attach it to scene. */
    {
	pfLightSource *ls = pfNewLSource();
	pfLSourcePos(ls, -0.2f, -0.3f, 1.0f, 0.0f);
	pfAddChild(scene, ls);
    }
    /* make root for movies at start of scene */
    Shared->movieRoot = pfNewGroup();
    /* Turn off culling - this node should always be drawn */
    pfNodeTravMask(Shared->movieRoot, PFTRAV_CULL, 0x0,
                        PFTRAV_SELF | PFTRAV_DESCEND, PF_SET);

    pfAddChild(scene, Shared->movieRoot);
    
    /* set up geomtry and Shared->movieScreen */
    if (HaveBaseTexture) /* base texture for screens for when */
    			 /* no movie is playing */
    {
	Shared->dftTex = pfNewTex(pfGetSharedArena()); 
        pfNotify(PFNFY_NOTICE, PFNFY_PRINT, "base tex file: %s", TexName);	
	pfLoadTexFile(Shared->dftTex, TexName);
    }
    root = MakeScreens();
    pfAddChild(scene, root);
    
    /***** MAKE AND LOAD MOVIE *****/
    {
	/* load Movie */
	if (HaveTextureList)
	{
	    tex = Shared->movie[0].tex = pfNewTex(pfGetSharedArena()); 
	    Shared->movie[0].tape = pfuLoadTexListFiles(NULL, TexFileNames, NumTextures);
	    strcpy(Shared->movie[0].name, TexFileNames[0]);
	} else if (NumMovies) {
	    for (i=0; i < NumMovies; i++)
	    {
		tex = Shared->movie[i].tex = pfNewTex (pfGetSharedArena()); 
		strcpy(Shared->movie[i].name, MovieName[i+1]);
		pfTexName(tex, MovieName[i+1]);
		Shared->movie[i].tape = pfuLoadTexListFmt(NULL, MovieFmt[i+1], MovieStart[i+1], MovieEnd[i+1]);
	    }
	}
	/* Make the projector node (a pfSequence) and add to scene. */
	if (NumMovies)
	{
	    for (i=0; i < NumMovies; i++)
	    {
		Shared->movie[i].proj = pfuNewProjector(Shared->movie[i].tex);
		pfNodeName(Shared->movie[i].proj, Shared->movie[i].name);
		if (!Shared->movie[i].tape)
		    pfNotify(PFNFY_NOTICE, PFNFY_PRINT, 
			"No frames for movie \"%s\"", Shared->movie[i].name);
		else
		{
		    /* this will basically just do a pfTextureList(tex, tape); */
		    pfuProjectorMovie(Shared->movie[i].proj, Shared->movie[i].tape);
		    Shared->movie[i].length = pfGetNum(Shared->movie[i].tape);
		}
		if (ReplaceMode)
		    pfTexLoadMode(Shared->movie[i].tex, PFTEX_LOAD_LIST, PFTEX_LIST_AUTO_SUBLOAD);
		else
		    pfTexLoadMode(Shared->movie[i].tex, PFTEX_LOAD_LIST, PFTEX_LIST_AUTO_IDLE);
	    }
	    
	}
	/* Add all projectors to projection root */
	for (i=0; i < NumMovies; i++)
	    pfAddChild(Shared->movieRoot, Shared->movie[i].proj);
	
	/* start with first movie ready to play on all screens */
	Shared->screenSel = 0;
	Shared->movieSel = 0;
	if (NumMovies) for (i=0; i < NumScreens; i++)
	{
	    addScreenToProjector(Shared->movieSel, i);
	}
	resetProjector(Shared->movieSel);
    }
    /* get list of all textures to pre-def and load */
    Shared->texList = pfuGetSharedTexList();
    
    /* Make all textures Fast-Define */
    if (ReplaceMode)
    {
	if (Shared->dftTex)
	{
	    pfTexFormat(Shared->dftTex,  PFTEX_SUBLOAD_FORMAT, TRUE);
	    if (ReplaceMode)
		pfTexFormat(Shared->dftTex,  PFTEX_SUBLOAD_FORMAT, TRUE);
	    pfTexFilter(Shared->dftTex,  PFTEX_MINFILTER, PFTEX_BILINEAR);
	}
	for (i=0; i < NumMovies; i++)
	{
	    for (j=0; j < Shared->movie[i].length; j++)
	    {
		tex = pfGet(Shared->movie[i].tape, j);
		pfTexFormat(tex,  PFTEX_SUBLOAD_FORMAT, TRUE);
		if (ReplaceMode)
		    pfTexFormat(tex,  PFTEX_SUBLOAD_FORMAT, TRUE);
		pfTexFilter(tex, PFTEX_MINFILTER, PFTEX_BILINEAR);
	    }
	}
    }
    
    /* initialize all pipes */
    pfStageConfigFunc(NULL, PFPROC_DRAW, OpenPipeline);
    pfConfigStage(0, PFPROC_DRAW);

    /* Open window */
    pipe = pfGetPipe(0);
    pipewin = pfNewPWin(pipe);
    pfPWinType(pipewin, PFWIN_TYPE_X);
    pfPWinName(pipewin, "Movie");
    pfPWinOriginSize(pipewin, WinXOrigin, WinYOrigin, WinXSize, WinYSize);

    pfPWinConfigFunc(pipewin, OpenPipeWin);
    pfConfigPWin(pipewin);

    pfFrame();
    
    pfuInitGUI(pipewin);
#ifdef __linux__
    pfuInitInput(pipewin, PFUINPUT_NOFORK_X);
    pfNotify(PFNFY_DEBUG, PFNFY_PRINT, "Forcing Input model to NOFORK_X [%s:%d]\n", __FILE__, __LINE__);
#else
    pfuInitInput(pipewin, PFUINPUT_X);
#endif
    /* determine extent of scene's geometry */
    pfGetNodeBSphere (scene, &bsphere);
    
    /* Create and configure a pfChannel. */
    /* Configure and open GLX window */
    chan = pfNewChan(pipe);
    pfChanTravFunc(chan, PFTRAV_DRAW, DrawChannel);
    pfChanTravFunc(chan, PFTRAV_CULL, CullChannel);
    pfChanScene(chan, scene);
    pfChanNearFar(chan, 1.0f, 20.0f * bsphere.radius);
    /* 45 degrees wide,  vertical=-1 to signal match window aspect */
    pfChanFOV(chan, 45.0f, -1.0f);
    pfFStatsClass(pfGetChanFStats(chan), PFSTATS_ENGFX, PFSTATS_ON);

    /* Create an earth/sky model that draws sky/ground/horizon */
    esky = pfNewESky();
    pfESkyMode(esky, PFES_BUFFER_CLEAR, PFES_SKY_GRND );
    pfESkyAttr(esky, PFES_GRND_HT, -1.0f * bsphere.radius);
    pfESkyColor(esky, PFES_GRND_FAR, 0.3f, 0.1f, 0.0f, 1.0f);
    pfESkyColor(esky, PFES_GRND_NEAR, 0.5f, 0.3f, 0.1f, 1.0f);
    
    /* If multipipe, then we need channels on each pipe */
    if (Multipipe)
    {
	unsigned int share;
	pfChannel *c;
    
	/* share everything */
	share = pfGetChanShare(chan);
	pfChanShare(chan, share | PFCHAN_VIEWPORT | PFCHAN_VIEW_OFFSETS);
	for (i=1; i < NumPipes; i++)
	{
	    c = pfNewChan(pfGetPipe(i));
	    pfAttachChan(chan, c);
	}
    }

    pfChanESky(chan, esky);
    
    pfChanViewport(chan, 0.0f, 0.8f, 0.0f, 1.0);
    pfuGUIViewport(0.8f, 1.0f, 0.0f, 1.0);
    guiDirty = 1;
    
    Shared->guiPanel = InitPanel();
    pfuEnablePanel(Shared->guiPanel);
    
    /* Main simulation loop */
    while (!(Shared->exitFlag))
    {
	float      s, c, r;
	pfCoord	   view;

	/* Go to sleep until next frame time. */
	pfSync();
	
	pfuGetMouse(&Shared->mouse);

	/* Initiate cull/draw for this frame. */
	pfFrame();
	
	if(guiDirty)
	{
    	    pfuEnableGUI(TRUE);
	    guiDirty = 0;
	}
	/* Compute new view position for next frame. */
	if (Shared->spin > 0)
	{
	    t = pfGetTime();
	    pfSinCos(45.0f*t, &s, &c);
	    pfSetVec3(view.hpr, 45.0f*t, -10.0f, 0);
	    pfSetVec3(view.xyz, 2.0f * bsphere.radius * s, 
		    -2.1f * bsphere.radius *c, 
		     0.2f * bsphere.radius);
	} else if (Shared->spin == 0)
	{
	    pfSetVec3(view.xyz, 0.0f, -2.1f * bsphere.radius, 0.2f *
	    	bsphere.radius);
	    pfSetVec3(view.hpr, 0.0f, 0.0f, 0.0f);
	    Shared->spin = -1;
	}
	/* set view position for next frame */
	pfChanView(chan, view.xyz, view.hpr);
	
	/* Get snapshot of event/key queues */
	pfuGetEvents(&Shared->events);
	ProcessInputIO();
	
	/* spin (roll) the screens around y axis */
	r = ((int) (t*100.0f)) % 360;
	for (i=0; i < NumScreens; i++)
	{
	    float ri = r + i*20.0f;
	    if (i & 0x1)
		pfDCSRot(ScreenDCS[i], 0.0f, 0.0f, ri);
	    else
		pfDCSRot(ScreenDCS[i], 0.0f, 0.0f, -ri);
	}
	/*** MOVIE CONTROL HERE ***/
	if (Shared->movieFlag)
	{
	    switch(Shared->movieFlag)
	    {
		case CTL_ADD_SCREEN_TO_PROJECTOR:
		    addScreenToProjector(Shared->movieSel, Shared->screenSel);
		    resetProjector(Shared->movieSel);
		    break;
		case CTL_RESET:
		    if (Shared->movieSel > -1)
		    {
			pfNotify(PFNFY_INFO, PFNFY_PRINT, 
				"Reseting movie %s.", 
				Shared->movie[Shared->movieSel].name);
			resetProjector(Shared->movieSel);
		    }
		    break;
		case CTL_START:
		    if (Shared->movieSel > -1)
		    {
			pfNotify(PFNFY_INFO, PFNFY_PRINT, 
			    "Starting movie %s.", 
			    Shared->movie[Shared->movieSel].name);
			pfSeqInterval(Shared->movie[Shared->movieSel].proj,
				    PFSEQ_CYCLE, Shared->start, Shared->end);
			pfSeqMode(Shared->movie[Shared->movieSel].proj,
				    PFSEQ_START);
		    }
		    break;
		case CTL_STOP:
		    if (Shared->movieSel > -1)
		    {
			pfNotify(PFNFY_INFO, PFNFY_PRINT, 
			    "Stoping movie %s.", 
			    Shared->movie[Shared->movieSel].name);
			pfSeqMode(Shared->movie[Shared->movieSel].proj,
				    PFSEQ_STOP);
		    }
		    break;
		case CTL_PAUSE:
		    if (Shared->movieSel > -1)
		    {
			pfNotify(PFNFY_INFO, PFNFY_PRINT, 
			    "Pause movie %s.", 
			    Shared->movie[Shared->movieSel].name);
			pfSeqMode(Shared->movie[Shared->movieSel].proj,
				    PFSEQ_PAUSE);
		    }
		    break;
		case CTL_RESUME:
		    if (Shared->movieSel > -1)
		    {
			pfNotify(PFNFY_INFO, PFNFY_PRINT, 
			    "Resume movie %s.", 
			    Shared->movie[Shared->movieSel].name);
			pfSeqMode(Shared->movie[Shared->movieSel].proj,
				    PFSEQ_RESUME);
			pfSeqInterval(Shared->movie[Shared->movieSel].proj,
				    PFSEQ_CYCLE, Shared->start, Shared->end);
		    }
		    break;
		case CTL_STEP:
		    if (Shared->movieSel > -1)
		    {
			pfSequence *seq = Shared->movie[Shared->movieSel].proj;
			int repeat;
			int frame = pfGetSeqFrame(seq, &repeat);
			
			frame += Shared->direction;
			if (frame > (Shared->movie[Shared->movieSel].length - 1))
			    frame = 0;
			pfSeqInterval(Shared->movie[Shared->movieSel].proj,
				    PFSEQ_CYCLE, 
				    frame, frame);
		    }
		    break;
		case CTL_REVERSE:
		    if (Shared->movieSel > -1)
		    {
			int tmp;
			pfNotify(PFNFY_INFO, PFNFY_PRINT, 
			    "Reverse movie %s.", 
			    Shared->movie[Shared->movieSel].name);
			
			Shared->direction *= -1;
			tmp = Shared->start;
			Shared->start = Shared->end;
			Shared->end = tmp;
		    }
		    break;
		case CTL_SPEED:
		    if (Shared->movieSel > -1)
		    {
			pfSeqDuration(Shared->movie[Shared->movieSel].proj, 
				    Shared->speed, -1.0f);
		    }
		    break;
	    }
	    Shared->movieFlag = 0;
	}
	pfuUpdateGUI(&Shared->mouse);
    }

     /* Exit from libpfutil and remove shared mem arenas */
    pfuExitUtil();
    pfuExitInput();
    /* Terminate parallel processes and exit. */
    pfExit();

    return 0;
}

static void
resetProjector(int m)
{    
    MovieData *movie;
    
    if (m < 0)
	return;
    
    movie = &(Shared->movie[m]);
    Shared->direction = 1;
    Shared->start = 0;
    Shared->end = movie->length - 1;
    pfSeqInterval(movie->proj, PFSEQ_CYCLE, Shared->start, Shared->end);
    Shared->speed = 1.0f;
    pfSeqDuration(movie->proj, Shared->speed, -1.0f);
    pfSeqMode(movie->proj, PFSEQ_STOP);
}

static void 
addScreenToProjector(int m, int s)
{

    if (m > -1)
	pfNotify(PFNFY_INFO, PFNFY_PRINT, "Putting movie %s on screen %s.", 
		    Shared->movie[m].name, ScreenStrs[s]);
    else
	pfNotify(PFNFY_INFO, PFNFY_PRINT, "Now no movie on screen %s.", 
					    ScreenStrs[s]);
    
    if (Shared->movieScreen[s].movieIndex > -1)
	pfuRemoveProjectorScreen(
	    Shared->movie[Shared->movieScreen[s].movieIndex].proj, 
	    Shared->movieScreen[s].tex);
    if (m > -1)
    { /* put movie on screen and put up first frame if movie isn't started */
	pfuAddProjectorScreen(Shared->movie[m].proj, 
		  Shared->movieScreen[s].tex);
	if (pfGetTexFrame(Shared->movieScreen[s].tex) == -1)
	    pfTexFrame(Shared->movieScreen[s].tex, 0);
    } else {
	/* no movie - put up the screens own default image */
	pfTexFrame(Shared->movieScreen[s].tex, -1);
	pfTexList(Shared->movieScreen[s].tex, NULL);
    }
    Shared->movieScreen[s].movieIndex = m;
}

static void
ProcessInputIO(void)
{
    int i,j,key;
    int dev, val, numDevs;
    pfuEventStream *pfevents;
    
    pfevents = &(Shared->events);
    
    numDevs = pfevents->numDevs;
    for (j=0; j < numDevs; j++)
    {
	dev = pfevents->devQ[j];
	val = pfevents->devVal[j];
	if (pfevents->devCount[dev] > 0)
	{
	    switch(dev)
	    {
	    case PFUDEV_REDRAW:
		pfuRedrawGUI(); /* GUI redraws lazily so must force it here */
		pfevents->devCount[dev] = 0; /* mark device done */
		break;
	    case PFUDEV_WINQUIT:
		Shared->exitFlag = 1;
		pfevents->devCount[dev] = 0; /* mark device done */
		break;
	    
		/* Main Keyboard */
	    case PFUDEV_KEYBD:
		for(i=0; i<pfevents->numKeys; i++)
		{
		    key = pfevents->keyQ[i];
		    if (pfevents->keyCount[key])
		    {	/* key was pressed count times */
			switch(key)
			{
			case 27:	/* ESC */
			    Shared->exitFlag = 1;
			    break;
			case 'g':	/* stats */
			    Shared->showStats ^= 1;
			    break;
			}
		    }
		}
		/* XXX this is very important or else future keybd events
		 * will be lost !!!
		 */
		pfevents->devCount[dev] = 0; /* mark device done */
		break;
	    } /* switch on device */
	} /* if have device */
    } /* for each device */
    pfevents->numDevs = 0;
    pfevents->numKeys = 0;
}


static void
OpenPipeWin(pfPipeWindow *pw)
{
    void *arena = pfGetSharedArena();
    pfOpenPWin(pw);

    /* Create and apply a default material for those models
     * without one.
     */
    pfApplyMtl(pfNewMtl(arena));
    /* Create a default lighting model. */
    pfApplyLModel(pfNewLModel(arena));
    /* enable lighting */
    pfEnable(PFEN_LIGHTING);
    /* enable texture */
    pfApplyTEnv(pfNewTEnv(arena));
    pfEnable(PFEN_TEXTURE);
    /* set alpha function to block pixels of 0 alpha for transparent textures */
    pfAlphaFunc(0, PFAF_NOTEQUAL);
    /* disable backface culling (default is PFCF_BACK) */
    pfCullFace (PFCF_OFF);
    
    pfuDownloadTexList(Shared->texList, PFUTEX_SHOW);
} 


/*
 *	OpenPipeline() -- create a GLX window: set up the
 *      window system, OpenGL, X, and OpenGL Performer. This
 *      procedure is executed in the draw process (when
 *      there is a separate draw process).
 */
static void
OpenPipeline (int pipe, uint stage)
{
    if (RestrictCPUs)
	pfuLockDownDraw(pfGetPipe(pipe));
}


static void
CullChannel(pfChannel *chan, void *data)
{
static first = 1;

    (data, data);
    
    if (first)
    {
	if (RestrictCPUs)
	    pfuLockDownCull(pfGetChanPipe(chan));
	first = 0;
    }    
    pfCull();
}

static void
DrawChannel (pfChannel *channel, void *data)
{
static first = 1;

    (data, data);
    
    pfClearChan(channel);
    pfDraw();
    
    /* draw channel statistics */
    if (Shared->showStats)
	pfDrawChanStats(channel);
}


static pfNode *
MakeScreens(void)
{
    pfGroup	*root;
    pfGeoSet	*gset;
    pfGeode     *geode;
    pfGeoState  *gstate;
    pfDCS	*dcs;
    void	*arena;
    int		comp, sx, sy, sz;
    unsigned int	*image;
    int	i;
    int row	= 1;
    int col	= 0;
    
    arena = pfGetSharedArena();
    
    root = pfNewGroup();
    
    for (i=0; i < NumScreens; i++)
    {
	/* unique for each screen */
	gset = pfNewGSet(arena);
	gstate = pfNewGState (arena);
	pfGSetGState (gset, gstate);
	Shared->movieScreen[i].tex = pfNewTex(arena);
	Shared->movieScreen[i].movieIndex = -1; /* no movie */
	pfTexName(Shared->movieScreen[i].tex, ScreenStrs[i]);
	if (Shared->dftTex)
	{ /* all screens start off with the same default image */
	    pfGetTexImage(Shared->dftTex, &image, &comp, &sx, &sy, &sz);
	    pfTexImage(Shared->movieScreen[i].tex, image, comp, sx, sy, sz);
	}
	pfGStateAttr (gstate, PFSTATE_TEXTURE, Shared->movieScreen[i].tex);
	
	/* Set up geosets */
	pfGSetAttr(gset, PFGS_COORD3, PFGS_PER_VERTEX, coords, NULL);
	pfGSetAttr(gset, PFGS_NORMAL3, PFGS_PER_VERTEX, norms, NULL);
	pfGSetAttr(gset, PFGS_TEXCOORD2, PFGS_PER_VERTEX, texcoords, NULL);
	pfGSetAttr(gset, PFGS_COLOR4, PFGS_OVERALL, colors, NULL);
	pfGSetPrimType(gset, PFGS_QUADS);
	pfGSetNumPrims(gset, 1);
    
	/* set up scene graph */
	geode = pfNewGeode(); 
	dcs = ScreenDCS[i] = pfNewDCS();
	pfDCSTrans(dcs,  row * (2.0f*col+2.0f), col*2.0f, 0.0f);
	
	pfAddGSet(geode, gset);
	pfAddChild(dcs, geode);
	pfAddChild(root, dcs);
    
	row *= -1;
	col += (i & 0x1);
    }
    
    return ((pfNode*)root);
}


static pfuPanel *
InitPanel(void)
{
    float  xo, yo, ys, xs;
    int x, y, yTop, xsize, ysize;
    pfuWidget *wid;
    pfuPanel *pan;
    
    xsize = 170;
    ysize = PFUGUI_BUTTON_YSIZE;
    
    pan = pfuNewPanel();
    pfuGetPanelOriginSize(pan, &xo, &yo, &xs, &ys);
    
    x = (int)xo + 4;
    yTop = (int)(yo + ys - 4);
    y = yTop - PFUGUI_BUTTON_YINC;
       
	/* action button - quit */
    wid = pfuNewWidget(pan, PFUGUI_BUTTON, CTL_QUIT); 
    pfuWidgetDim(wid,  x,  y,  xsize,  PFUGUI_BUTTON_YSIZE);
    pfuWidgetActionFunc(wid, PanelControl);
    pfuWidgetLabel(wid, "Quit");
    
    y -= PFUGUI_BUTTON_YINC;
	
	/* menu button - select */
    wid = pfuNewWidget(pan, PFUGUI_MENU_BUTTON, CTL_SCREEN_SELECT); 
    pfuWidgetDim(wid,  x,  y,  xsize,  PFUGUI_BUTTON_YSIZE);
    pfuWidgetLabel(wid, "Screen");
    pfuWidgetActionFunc(wid, PanelControl);
    pfuWidgetSelections(wid, ScreenStrs, NULL, NULL, NumScreens);
  	
    y -= PFUGUI_BUTTON_YINC;
	
	/* menu button - select */
    wid = pfuNewWidget(pan, PFUGUI_MENU_BUTTON, CTL_MOVIE_SELECT); 
    pfuWidgetDim(wid,  x,  y,  xsize,  PFUGUI_BUTTON_YSIZE);
    pfuWidgetLabel(wid, "Movie");
    pfuWidgetActionFunc(wid, PanelControl);
    pfuWidgetSelections(wid, MovieName, NULL, NULL, NumMovies+1);
    pfuWidgetDefaultSelection(wid, 0);
    pfuWidgetSelection(wid, 0);
  	
    y -= PFUGUI_BUTTON_YINC;
	
	/* action button - put */
    wid = pfuNewWidget(pan, PFUGUI_BUTTON, CTL_ADD_SCREEN_TO_PROJECTOR); 
    pfuWidgetDim(wid,  x,  y,  xsize,  PFUGUI_BUTTON_YSIZE);
    pfuWidgetActionFunc(wid, PanelControl);
    pfuWidgetLabel(wid, "MovieOnScreen");
  	
    y -= PFUGUI_BUTTON_YINC;

	/* action button - start */
    wid = pfuNewWidget(pan, PFUGUI_BUTTON, CTL_START); 
    pfuWidgetDim(wid,  x,  y,  xsize,  PFUGUI_BUTTON_YSIZE);
    pfuWidgetActionFunc(wid, PanelControl);
    pfuWidgetLabel(wid, "Start");
  	
    y -= PFUGUI_BUTTON_YINC;
	
	/* action button - stop */
    wid = pfuNewWidget(pan, PFUGUI_BUTTON, CTL_STOP); 
    pfuWidgetDim(wid,  x,  y,  xsize,  PFUGUI_BUTTON_YSIZE);
    pfuWidgetActionFunc(wid, PanelControl);
    pfuWidgetLabel(wid, "Stop");
    	
    y -= PFUGUI_BUTTON_YINC;
	
	/* action button - pause */
    wid = pfuNewWidget(pan, PFUGUI_BUTTON, CTL_PAUSE); 
    pfuWidgetDim(wid,  x,  y,  xsize,  PFUGUI_BUTTON_YSIZE);
    pfuWidgetActionFunc(wid, PanelControl);
    pfuWidgetLabel(wid, "Pause");
    	
    y -= PFUGUI_BUTTON_YINC;
	
	/* action button - resume */
    wid = pfuNewWidget(pan, PFUGUI_BUTTON, CTL_RESUME); 
    pfuWidgetDim(wid,  x,  y,  xsize,  PFUGUI_BUTTON_YSIZE);
    pfuWidgetActionFunc(wid, PanelControl);
    pfuWidgetLabel(wid, "Resume");
    	
    y -= PFUGUI_BUTTON_YINC;
	
	/* action button - step */
    wid = pfuNewWidget(pan, PFUGUI_BUTTON, CTL_STEP); 
    pfuWidgetDim(wid,  x,  y,  xsize,  PFUGUI_BUTTON_YSIZE);
    pfuWidgetActionFunc(wid, PanelControl);
    pfuWidgetLabel(wid, "Step");
 	
    y -= PFUGUI_BUTTON_YINC;
	
	/* switch button - reverse */
    wid = pfuNewWidget(pan, PFUGUI_SWITCH, CTL_REVERSE); 
    pfuWidgetDim(wid,  x,  y,  xsize,  PFUGUI_BUTTON_YSIZE);
    pfuWidgetValue(wid, 0.0f);
    pfuWidgetActionFunc(wid, PanelControl);
    pfuWidgetLabel(wid, "Reverse");
 	
    y -= PFUGUI_BUTTON_YINC;
		
	/* action button - reset */
    wid = pfuNewWidget(pan, PFUGUI_BUTTON, CTL_RESET); 
    pfuWidgetDim(wid,  x,  y,  xsize,  PFUGUI_BUTTON_YSIZE);
    pfuWidgetActionFunc(wid, PanelControl);
    pfuWidgetLabel(wid, "Reset");
  	
    y -= PFUGUI_BUTTON_YINC;
	
	/* action button - spin */
    wid = pfuNewWidget(pan, PFUGUI_SWITCH, CTL_SPIN); 
    pfuWidgetDim(wid,  x,  y,  xsize,  PFUGUI_BUTTON_YSIZE);
    pfuWidgetValue(wid, 0.0f);
    pfuWidgetActionFunc(wid, PanelControl);
    pfuWidgetLabel(wid, "Spin");
 
    y -= PFUGUI_SLIDER_YINC;
    
	/* horizontal slider */
    wid = pfuNewWidget(pan, PFUGUI_SLIDER_LOG, CTL_SPEED);
    pfuWidgetDim(wid,  x,  y,  xsize,  PFUGUI_SLIDER_YSIZE);
    pfuWidgetLabel(wid, "Speed");
    pfuWidgetRange(wid, 1, 0.01f, 10.0f, 1.0f);
    pfuWidgetActionFunc(wid, PanelControl);
    
    y -= PFUGUI_BUTTON_YINC;
	
	/* switch button - show stats */
    wid = pfuNewWidget(pan, PFUGUI_SWITCH, CTL_STATS); 
    pfuWidgetDim(wid,  x,  y,  xsize,  PFUGUI_BUTTON_YSIZE);
    pfuWidgetValue(wid, 0.0f);
    pfuWidgetActionFunc(wid, PanelControl);
    pfuWidgetLabel(wid, "Stats");

    return pan;
}

/*
 * This is called by the asynch input handling process
 */
static void
PanelControl(pfuWidget *w)
{
    int id;

    switch(id = pfuGetWidgetId(w))
    {
    case CTL_QUIT:
	Shared->exitFlag = 1;
	pfNotify(PFNFY_DEBUG, PFNFY_PRINT, "Have Quit button - bye...\n");
	break;
    case CTL_SCREEN_SELECT:
	Shared->screenSel = (int) pfuGetWidgetValue(w);
	break;
    case CTL_MOVIE_SELECT:
	Shared->movieSel = ((int) pfuGetWidgetValue(w)) - 1;
	break;
    case CTL_SPEED:
	Shared->speed = pfuGetWidgetValue(w);
	Shared->movieFlag = id;
	break;
    case CTL_SPIN:
	Shared->spin = (int) pfuGetWidgetValue(w);
	break;
    case CTL_STATS:
	Shared->showStats ^= 1;
	break;
    case CTL_ADD_SCREEN_TO_PROJECTOR:
    case CTL_RESET:
    case CTL_START:
    case CTL_STOP:
    case CTL_PAUSE:
    case CTL_RESUME:
    case CTL_STEP:
    case CTL_REVERSE:
	Shared->movieFlag = id;
	break;
    }
}