/* pyro.c Main source file Copyright (c) 1998-1999, Mike Oliphant and Rob Clark */ #include #include #include #include #ifdef __EMX__ /*os2*/ #include "float.h" #endif #include /* #ifdef XMESA #include #endif */ #include #include "pyro.h" void DeleteWork(struct Firework *work); static void GeneratePart(struct Firework *work,struct Particle *part); static void ExplodeWork(struct Firework *work); static void printstring(void *font, char *string); static void printhelp(void); void DrawWorks(double alphafact); void DisplayScene(void); static void Reshape(int width,int height); static void Idle(void); static void MoveWorks(void); static void Key(unsigned char key,int x,int y); static void Init(void); void ScreenShot(char *filename); void ParseArgs(int num,char *args[]); void Usage(void); struct Firework *works=NULL; int numworks=0; static int DispWindow = 0; static int ScreenWidth, ScreenHeight; double camx,camy,camz; int frames=0; long int initcycles=0; long int cycles=0; long int oldcycles=0; GLubyte *teximg=NULL; GLuint texobj; int help=0; int drawtex=1; int printfps=0; int doreflect=1; double wateralpha=0.1; #ifdef WIN32 int dofog=0; #else int dofog=1; #endif int movecam=1; int dispseq=1; char fpsbuf[10]; double fps=0.0; double camcount=0.0; int fx=0; int fxwin=0; int maxworks; double workprob; char dispfname[256]=AUXDIR"/pyro.dsp"; double camdist=12.0; struct Firework *NewWork(struct FW_Template *template) { struct Firework *work=NULL; int p; work=(struct Firework *)malloc(sizeof(struct Firework)); work->x=template->x; work->y=template->y; work->z=template->z; work->launchvel=template->launchvel; work->launchveldev=template->launchveldev; work->sidevel=template->sidevel; work->dyexplode=template->dyexplode; work->numparts=template->numparts; work->numkinds=template->numkinds; work->ptemplates=template->ptemplates; work->flags=template->flags; work->dx=Rand()*work->sidevel-work->sidevel/2.0; work->dz=Rand()*work->sidevel-work->sidevel/2.0; work->dy=work->launchvel+Rand()*work->launchveldev; work->exploded=0; work->nextwork=NULL; work->parts=(struct Particle *) malloc(work->numparts*sizeof(struct Particle)); if(work->flags&&WORK_SPARKLER) { for(p=0;pnumparts;p++) work->parts[p].a=0.0; work->current_part=0; work->spark_int=0.1; work->last_spark_time=0.0; work->explodetime=glutGet(GLUT_ELAPSED_TIME); work->timer=0; } numworks++; return work; } void DeleteWork(struct Firework *work) { free(work->parts); free(work); } static void GeneratePart(struct Firework *work,struct Particle *part) { double xc,yc,zc; double vel,svel,scale; struct PT_Template *template; template=work->ptemplates[RRand(work->numkinds)]; if(template->r<0.0) part->r=Rand(); else part->r=template->r; if(template->g<0.0) part->g=Rand(); else part->g=template->g; if(template->b<0.0) part->b=Rand(); else part->b=template->b; part->a=template->a; part->expvel=template->expvel; part->expveldev=template->expveldev; part->xexpfact=template->xexpfact; part->yexpfact=template->yexpfact; part->zexpfact=template->zexpfact; part->airres=template->airres+Rand()*.005; part->decrate=template->decrate; if(template->traillen<0.0) part->traillen=Rand()*fabs(template->traillen); else part->traillen=template->traillen; part->cfunc=template->cfunc; part->mfunc=template->mfunc; part->mx=work->x; part->my=work->y; part->mz=work->z; part->x=work->x; part->y=work->y; part->z=work->z; xc=(Rand()-.5)*part->xexpfact; yc=(Rand()-.5)*part->yexpfact; zc=(Rand()-.5)*part->zexpfact; vel=part->expvel+(Rand()*part->expveldev*2.0)-part->expveldev; svel=VecLen(xc,yc,zc); scale=vel/svel; part->odx=work->dx; part->ody=work->dy; part->odz=work->dz; part->dx=xc*scale+work->dx; part->dy=yc*scale+work->dy; part->dz=zc*scale+work->dz; if(part->a==-1.0) part->a=0.5+Rand()*0.5; part->rval=Rand(); /* Make sure the modulated color is initialized the first time round */ if(part->cfunc) part->cfunc(part,work); } static void ExplodeWork(struct Firework *work) { int p; struct Particle *parts; work->explodetime=glutGet(GLUT_ELAPSED_TIME); work->timer=0.0; parts=work->parts; for(p=0;pnumparts;p++) { GeneratePart(work,parts+p); } work->exploded=1; } static void printstring(void *font, char *string) { int len,i; len=(int)strlen(string); for(i=0;iexploded||(work->flags&&WORK_SPARKLER)) { parts=work->parts; glBegin(GL_LINES); for(p=0;pnumparts;p++) { part=parts+p; if(part->mfunc) { if(part->myycfunc?part->mr:part->r, part->cfunc?part->mg:part->g, part->cfunc?part->mb:part->b, 0.0); if(part->mfunc) { ox=part->mx-part->dx*part->traillen; oy=part->my-part->dy*part->traillen; oz=part->mz-part->dz*part->traillen; if(fabs(part->mx-ox)>fabs(part->mx-work->x)) ox=work->x; if(fabs(part->my-oy)>fabs(part->my-work->y)) oy=work->y; if(fabs(part->mz-oz)>fabs(part->mz-work->z)) oz=work->z; } else { ox=part->x-part->dx*part->traillen; oy=part->y-part->dy*part->traillen; oz=part->z-part->dz*part->traillen; if(fabs(part->x-ox)>fabs(part->x-work->x)) ox=work->x; if(fabs(part->y-oy)>fabs(part->y-work->y)) oy=work->y; if(fabs(part->z-oz)>fabs(part->z-work->z)) oz=work->z; } glVertex3f(ox,oy,oz); glColor4f(part->cfunc?part->mr:part->r, part->cfunc?part->mg:part->g, part->cfunc?part->mb:part->b, part->a*alphafact); if(part->mfunc) glVertex3f(part->mx,part->my,part->mz); else glVertex3f(part->x,part->y, part->z); } glEnd(); } else { glBegin(GL_POINTS); glColor3f(0.05,0.05,0.05); glVertex3f(work->x,work->y,work->z); glEnd(); } work=work->nextwork; } } void DisplayScene(void) { double textbright; long int currcycles; MoveWorks(); if(frames&&frames%FPSPRINT==0) { currcycles=glutGet(GLUT_ELAPSED_TIME); fps=(double)FPSPRINT/ ((double)(currcycles-oldcycles)/(double)1000); oldcycles=currcycles; } glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix(); gluLookAt(camdist*sin(camcount),1.0,camdist*cos(camcount), 0.0,1.0,0.0, 0.0,1.0,0.0); if(doreflect) { glPushMatrix(); glScalef(1.0,-1.0,1.0); glTranslatef(0.0,4.0,0.0); DrawWorks(0.3); glPopMatrix(); } if(drawtex) { glEnable(GL_TEXTURE_2D); glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE); glBindTexture(GL_TEXTURE_2D,texobj); } if(dofog) glEnable(GL_FOG); glBegin(GL_POLYGON); if(drawtex) glColor4f(1.0,1.0,1.0,wateralpha); else glColor4f(0.0,0.0,1.0,wateralpha); if(drawtex) glTexCoord2f(0.0,0.0); glVertex3f(-100,GRNDLVL,-100); if(drawtex) glTexCoord2f(40.0,0.0); glVertex3f(100,GRNDLVL,-100); if(drawtex) glTexCoord2f(40.0,40.0); glVertex3f(100,GRNDLVL,100); if(drawtex) glTexCoord2f(0.0,40.0); glVertex3f(-100,GRNDLVL,100); glEnd(); if(dofog) glDisable(GL_FOG); if(drawtex) glDisable(GL_TEXTURE_2D); DrawWorks(1.0); if(cycles<7000000||help||printfps) { glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-0.5,639.5,-0.5,479.5,-1.0,1.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); if(printfps) { glColor3f(1.0,1.0,1.0); glRasterPos2i(20,470); sprintf(fpsbuf,"%4.2f",fps); #ifdef __EMX__ /*os2*/ printstring(GLUT_BITMAP_TIMES_ROMAN_10,fpsbuf); #else printstring(GLUT_BITMAP_HELVETICA_10,fpsbuf); #endif /* ifdef __EMX__ */ } if(cycles<7000) { if(cycles<6000) textbright=1.0; else textbright=1.0-(double)(cycles-6000)/1000.0; glColor3f(textbright,textbright,textbright); glRasterPos2i(350,470); #ifdef __EMX__ /*os2*/ printstring(GLUT_BITMAP_TIMES_ROMAN_10, "PyroTechnics v1.5, Written by Mike Oliphant and Rob Clark"); #else printstring(GLUT_BITMAP_HELVETICA_10, "PyroTechnics v1.5, Written by Mike Oliphant and Rob Clark"); #endif /* ifdef __EMX */ glRasterPos2i(350,457); #ifdef __EMX__ /*os2*/ printstring(GLUT_BITMAP_TIMES_ROMAN_10,"Press ' h' for help"); #else printstring(GLUT_BITMAP_HELVETICA_10,"Press ' h' for help"); #endif /* ifdef __EMX__ */ } glMatrixMode(GL_PROJECTION); if(help) printhelp(); Reshape(ScreenWidth,ScreenHeight); } glPopMatrix(); glutSwapBuffers(); frames++; } static void Reshape(int width,int height) { float ratio; if(fx) { if(width>WIDTH) width=WIDTH; if(height>HEIGHT) height=HEIGHT; } ScreenWidth = width; ScreenHeight = height; ratio=(float)width/(float)height; glViewport(0,0,width,height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustum(-ratio,ratio,-1.0,1.0,5.0,100.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(0.0,0.0,-20.0); } static void Idle(void) { } static void MoveWorks(void) { int p; int c; struct Firework *work,*wp; struct Particle *parts,*part; int partleft; double oldspeed,scale; double elapsed; struct Firework *newfw; elapsed=(double)(glutGet(GLUT_ELAPSED_TIME)-cycles)/(double)1000; if(dispseq) UpdateDisplay(elapsed); if(movecam) camcount+=elapsed*0.03; elapsed*=TIMESCALE; cycles=glutGet(GLUT_ELAPSED_TIME); if(dispseq) { while((c=CheckDisplay())==1 && (newfw=GetWork())) { newfw->nextwork=works; works=newfw; } if(c==-1) dispseq=0; } else { if((numworksnextwork=works; works=work; } } work=works; while(work) { partleft=0; if(work->exploded||(work->flags&&WORK_SPARKLER)) { work->timer=(double)(glutGet(GLUT_ELAPSED_TIME)-work->explodetime)/ (double)1000; work->timer*=TIMESCALE; parts=work->parts; if((work->flags&&WORK_SPARKLER)&&(work->y>=GRNDLVL)) { if(work->last_spark_time==0.0|| ((work->timer-work->last_spark_time)>work->spark_int)) { GeneratePart(work,parts+work->current_part); work->current_part++; if(work->current_part==work->numparts) work->current_part=0; work->last_spark_time=work->timer; } } for(p=0;pnumparts;p++) { part=parts+p; if(part->a>0.0) { part->odx=part->dx; part->ody=part->dy; part->odz=part->dz; part->dy+=GRAVITY*elapsed; oldspeed=VecLen(part->dx,part->dy,part->dz); part->speed=oldspeed-oldspeed*part->airres*elapsed; scale=part->speed/oldspeed; part->dx*=scale; part->dy*=scale; part->dz*=scale; part->x+=part->dx*elapsed; part->y+=part->dy*elapsed; part->z+=part->dz*elapsed; if(part->mfunc) part->mfunc(parts+p,work); part->a-=part->decrate*elapsed; if(part->a<0.0) part->a=0.0; if(part->a!=0.0) partleft=1; if(part->cfunc) part->cfunc(parts+p,work); } } } if(!work->exploded||(work->flags&&WORK_SPARKLER)) { if(!(work->flags&&WORK_SPARKLER)&&(work->dydyexplode)) ExplodeWork(work); else { work->dy+=GRAVITY*elapsed; work->x+=work->dx*elapsed; work->y+=work->dy*elapsed; work->z+=work->dz*elapsed; } if(!(work->flags&&WORK_SPARKLER)) partleft=1; } if(!partleft) { if(works==work) { works=work->nextwork; wp=work; } else { for(wp=works;wp->nextwork!=work; wp=wp->nextwork); wp->nextwork=work->nextwork; wp=work; } work=work->nextwork; DeleteWork(wp); numworks--; if(numworks==0) works=NULL; } else work=work->nextwork; } glutPostRedisplay(); } /* Based on an example provided by Eric Plante */ void ScreenShot(char *filename) { int i, m; FILE *file; static unsigned char *pixels; m=ScreenWidth*3; pixels=(unsigned char *)malloc(ScreenHeight*m); if((file=fopen(filename,"w"))==NULL) { fprintf(stderr,"Failed to write screenshot file: %s\n",filename); return; } glReadPixels(0,0,ScreenWidth,ScreenHeight,GL_RGB,GL_UNSIGNED_BYTE, pixels); fprintf(file,"P6\n#PyroTechnics screenshot\n%d %d\n255\n", ScreenWidth,ScreenHeight); for(i=ScreenHeight-1;i>=0;i--) fwrite(&(pixels[i*m]),sizeof(unsigned char),m,file); fclose(file); free(pixels); } static void Key(unsigned char key,int x,int y) { static char filename[30]; switch(key) { case 27: printf("%f fps\n",(double)frames/ ((double)((glutGet(GLUT_ELAPSED_TIME)-initcycles))/(double)1000)); glutDestroyWindow(DispWindow); exit(0); break; case 'h': help=!help; break; case 't': drawtex=!drawtex; break; case 'f': printfps=!printfps; break; case 'o': dofog=!dofog; break; case 'r': doreflect=!doreflect; break; case 'c': movecam=!movecam; break; case 'd': dispseq=!dispseq; if(dispseq) RestartDisplay(); break; case 's': sprintf(filename,"frame%d.ppm",frames); ScreenShot(filename); break; case '+': case '=': wateralpha+=.05; if(wateralpha>1.0) wateralpha=1.0; break; case '-': case '_': wateralpha-=.05; if(wateralpha<0.0) wateralpha=0.0; break; case ']': camdist-=1.0; if(camdist<0.01) camdist=0.01; break; case '[': camdist+=1.0; if(camdist>70.0) camdist=70.0; break; #ifdef XMESA case 13: if(fx&&glutGetModifiers()==GLUT_ACTIVE_ALT) { fxwin=!fxwin; if(fxwin) glutReshapeWindow(ScreenWidth,ScreenHeight); else glutFullScreen(); /* XMesaSetFXmode(fxwin ? XMESA_FX_WINDOW:XMESA_FX_FULLSCREEN); */ } break; #endif } glutPostRedisplay(); } static void Init(void) { GLfloat fogcol[4]={0.0,0.0,0.0,1.0}; works=NULL; camx=0.0; camy=0.0; camz=1.0; glFogf(GL_FOG_MODE,GL_EXP); glFogf(GL_FOG_DENSITY,0.00001); glFogfv(GL_FOG_COLOR,fogcol); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_LINE_SMOOTH); glEnable(GL_POINT_SMOOTH); glGenTextures(1,&texobj); texobj=glGenLists(1); glBindTexture(GL_TEXTURE_2D,texobj); teximg=read_JPEG_file("water.jpg"); if(!teximg) printf("didn't read\n"); glTexImage2D(GL_TEXTURE_2D,0,3,64,64,0,GL_RGB,GL_UNSIGNED_BYTE,teximg); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT); srand(time(NULL)); } void Usage(void) { printf("\nUsage: pyro [-r] [-d ] [-w ] [-p ]\n"); printf(" [-c ]\n\n"); printf(" -h Display usage\n"); printf(" -r Display random sequence (ignore display file)\n"); printf(" -d Read display sequence from \n"); printf(" -w Maximum of fireworks at a time\n"); printf(" -p New firework with probability per second\n"); printf(" -c Rotate camera units from center of display\n"); printf(" (value should be less than 70.0)\n"); printf("\n"); exit(-1); } void ParseArgs(int numargs,char *args[]) { int num=1; while(num70.0) camdist=70.0; if(camdist<0.01) camdist=0.01; break; default: printf("Unrecognized argument [-%c]\n\n",args[num][1]); Usage(); break; } num++; } } int main(int argc,char *argv[]) { char *glxfx; maxworks=MAXWORKS; workprob=WORKPROB; ParseArgs(argc,argv); if(ParseDisplayFile(dispfname)==-1) exit(-1); glxfx=getenv("MESA_GLX_FX"); if(glxfx) { if(glxfx[0]=='f') { fx=1; putenv("FX_GLIDE_NO_SPLASH="); } else fxwin=1; } glutInitWindowPosition(0,0); glutInitWindowSize(WIDTH,HEIGHT); glutInit(&argc,argv); ScreenWidth=WIDTH; ScreenHeight=HEIGHT; glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH); DispWindow=glutCreateWindow(argv[0]); if (!DispWindow) { printf("Error, couldn't open window\n"); exit(1); } #ifndef __EMX__ /*os2*/ if(fx) glutFullScreen(); #endif /* ifdef __EMX__ */ Init(); glutReshapeFunc(Reshape); glutKeyboardFunc(Key); glutDisplayFunc(DisplayScene); glutIdleFunc(Idle); gluLookAt(0.0,0.0,1.0,0.0,0.0,-1.0,0.0,1.0,0.0); initcycles=oldcycles=cycles=glutGet(GLUT_ELAPSED_TIME); glutMainLoop(); return 0; }