added dither key option
[mesa.git] / progs / demos / fire.c
1 /*
2 * This program is under the GNU GPL.
3 * Use at your own risk.
4 *
5 * written by David Bucciarelli (tech.hmw@plus.it)
6 * Humanware s.r.l.
7 */
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <math.h>
12 #include <time.h>
13
14 #ifdef WIN32
15 #include <windows.h>
16 #include <mmsystem.h>
17 #endif
18
19 #include <GL/glut.h>
20 #include "../util/readtex.c"
21
22 #ifdef XMESA
23 #include "GL/xmesa.h"
24 static int fullscreen = 1;
25 #endif
26
27 #ifndef M_PI
28 #define M_PI 3.1415926535
29 #endif
30
31 #define vinit(a,i,j,k) {\
32 (a)[0]=i;\
33 (a)[1]=j;\
34 (a)[2]=k;\
35 }
36
37 #define vinit4(a,i,j,k,w) {\
38 (a)[0]=i;\
39 (a)[1]=j;\
40 (a)[2]=k;\
41 (a)[3]=w;\
42 }
43
44
45 #define vadds(a,dt,b) {\
46 (a)[0]+=(dt)*(b)[0];\
47 (a)[1]+=(dt)*(b)[1];\
48 (a)[2]+=(dt)*(b)[2];\
49 }
50
51 #define vequ(a,b) {\
52 (a)[0]=(b)[0];\
53 (a)[1]=(b)[1];\
54 (a)[2]=(b)[2];\
55 }
56
57 #define vinter(a,dt,b,c) {\
58 (a)[0]=(dt)*(b)[0]+(1.0-dt)*(c)[0];\
59 (a)[1]=(dt)*(b)[1]+(1.0-dt)*(c)[1];\
60 (a)[2]=(dt)*(b)[2]+(1.0-dt)*(c)[2];\
61 }
62
63 #define clamp(a) ((a) < 0.0 ? 0.0 : ((a) < 1.0 ? (a) : 1.0))
64
65 #define vclamp(v) {\
66 (v)[0]=clamp((v)[0]);\
67 (v)[1]=clamp((v)[1]);\
68 (v)[2]=clamp((v)[2]);\
69 }
70
71 static int WIDTH = 640;
72 static int HEIGHT = 480;
73
74 #define FRAME 50
75 #define DIMP 20.0
76 #define DIMTP 16.0
77
78 #define RIDCOL 0.4
79
80 #define NUMTREE 50
81 #define TREEINR 2.5
82 #define TREEOUTR 8.0
83
84 #define AGRAV -9.8
85
86 typedef struct
87 {
88 int age;
89 float p[3][3];
90 float v[3];
91 float c[3][4];
92 }
93 part;
94
95 static float treepos[NUMTREE][3];
96
97 static float black[3] = { 0.0, 0.0, 0.0 };
98 static float blu[3] = { 0.0, 0.2, 1.0 };
99 static float blu2[3] = { 0.0, 1.0, 1.0 };
100
101 static float fogcolor[4] = { 1.0, 1.0, 1.0, 1.0 };
102
103 static float q[4][3] = {
104 {-DIMP, 0.0, -DIMP},
105 {DIMP, 0.0, -DIMP},
106 {DIMP, 0.0, DIMP},
107 {-DIMP, 0.0, DIMP}
108 };
109
110 static float qt[4][2] = {
111 {-DIMTP, -DIMTP},
112 {DIMTP, -DIMTP},
113 {DIMTP, DIMTP},
114 {-DIMTP, DIMTP}
115 };
116
117 static int win = 0;
118
119 static int np;
120 static float eject_r, dt, maxage, eject_vy, eject_vl;
121 static short shadows;
122 static float ridtri;
123 static int fog = 1;
124 static int help = 1;
125 static int joyavailable = 0;
126 static int joyactive = 0;
127
128 static part *p;
129
130 static GLuint groundid;
131 static GLuint treeid;
132
133 static float obs[3] = { 2.0, 1.0, 0.0 };
134 static float dir[3];
135 static float v = 0.0;
136 static float alpha = -90.0;
137 static float beta = 90.0;
138
139 static float
140 gettime(void)
141 {
142 static clock_t told = 0;
143 clock_t tnew, ris;
144
145 tnew = clock();
146
147 ris = tnew - told;
148
149 told = tnew;
150
151 return (ris / (float) CLOCKS_PER_SEC);
152 }
153
154 static float
155 vrnd(void)
156 {
157 return (((float) rand()) / RAND_MAX);
158 }
159
160 static void
161 setnewpart(part * p)
162 {
163 float a, v[3], *c;
164
165 p->age = 0;
166
167 a = vrnd() * 3.14159265359 * 2.0;
168
169 vinit(v, sin(a) * eject_r * vrnd(), 0.15, cos(a) * eject_r * vrnd());
170 vinit(p->p[0], v[0] + vrnd() * ridtri, v[1] + vrnd() * ridtri,
171 v[2] + vrnd() * ridtri);
172 vinit(p->p[1], v[0] + vrnd() * ridtri, v[1] + vrnd() * ridtri,
173 v[2] + vrnd() * ridtri);
174 vinit(p->p[2], v[0] + vrnd() * ridtri, v[1] + vrnd() * ridtri,
175 v[2] + vrnd() * ridtri);
176
177 vinit(p->v, v[0] * eject_vl / (eject_r / 2),
178 vrnd() * eject_vy + eject_vy / 2, v[2] * eject_vl / (eject_r / 2));
179
180 c = blu;
181
182 vinit4(p->c[0], c[0] * ((1.0 - RIDCOL) + vrnd() * RIDCOL),
183 c[1] * ((1.0 - RIDCOL) + vrnd() * RIDCOL),
184 c[2] * ((1.0 - RIDCOL) + vrnd() * RIDCOL), 1.0);
185 vinit4(p->c[1], c[0] * ((1.0 - RIDCOL) + vrnd() * RIDCOL),
186 c[1] * ((1.0 - RIDCOL) + vrnd() * RIDCOL),
187 c[2] * ((1.0 - RIDCOL) + vrnd() * RIDCOL), 1.0);
188 vinit4(p->c[2], c[0] * ((1.0 - RIDCOL) + vrnd() * RIDCOL),
189 c[1] * ((1.0 - RIDCOL) + vrnd() * RIDCOL),
190 c[2] * ((1.0 - RIDCOL) + vrnd() * RIDCOL), 1.0);
191 }
192
193 static void
194 setpart(part * p)
195 {
196 float fact;
197
198 if (p->p[0][1] < 0.1) {
199 setnewpart(p);
200 return;
201 }
202
203 p->v[1] += AGRAV * dt;
204
205 vadds(p->p[0], dt, p->v);
206 vadds(p->p[1], dt, p->v);
207 vadds(p->p[2], dt, p->v);
208
209 p->age++;
210
211 if ((p->age) > maxage) {
212 vequ(p->c[0], blu2);
213 vequ(p->c[1], blu2);
214 vequ(p->c[2], blu2);
215 }
216 else {
217 fact = 1.0 / maxage;
218 vadds(p->c[0], fact, blu2);
219 vclamp(p->c[0]);
220 p->c[0][3] = fact * (maxage - p->age);
221
222 vadds(p->c[1], fact, blu2);
223 vclamp(p->c[1]);
224 p->c[1][3] = fact * (maxage - p->age);
225
226 vadds(p->c[2], fact, blu2);
227 vclamp(p->c[2]);
228 p->c[2][3] = fact * (maxage - p->age);
229 }
230 }
231
232 static void
233 drawtree(float x, float y, float z)
234 {
235 glBegin(GL_QUADS);
236 glTexCoord2f(0.0, 0.0);
237 glVertex3f(x - 1.5, y + 0.0, z);
238
239 glTexCoord2f(1.0, 0.0);
240 glVertex3f(x + 1.5, y + 0.0, z);
241
242 glTexCoord2f(1.0, 1.0);
243 glVertex3f(x + 1.5, y + 3.0, z);
244
245 glTexCoord2f(0.0, 1.0);
246 glVertex3f(x - 1.5, y + 3.0, z);
247
248
249 glTexCoord2f(0.0, 0.0);
250 glVertex3f(x, y + 0.0, z - 1.5);
251
252 glTexCoord2f(1.0, 0.0);
253 glVertex3f(x, y + 0.0, z + 1.5);
254
255 glTexCoord2f(1.0, 1.0);
256 glVertex3f(x, y + 3.0, z + 1.5);
257
258 glTexCoord2f(0.0, 1.0);
259 glVertex3f(x, y + 3.0, z - 1.5);
260
261 glEnd();
262
263 }
264
265 static void
266 calcposobs(void)
267 {
268 dir[0] = sin(alpha * M_PI / 180.0);
269 dir[2] = cos(alpha * M_PI / 180.0) * sin(beta * M_PI / 180.0);
270 dir[1] = cos(beta * M_PI / 180.0);
271
272 obs[0] += v * dir[0];
273 obs[1] += v * dir[1];
274 obs[2] += v * dir[2];
275 }
276
277 static void
278 printstring(void *font, char *string)
279 {
280 int len, i;
281
282 len = (int) strlen(string);
283 for (i = 0; i < len; i++)
284 glutBitmapCharacter(font, string[i]);
285 }
286
287 static void
288 reshape(int width, int height)
289 {
290 WIDTH = width;
291 HEIGHT = height;
292 glViewport(0, 0, (GLint) width, (GLint) height);
293 glMatrixMode(GL_PROJECTION);
294 glLoadIdentity();
295 gluPerspective(70.0, width / (float) height, 0.1, 30.0);
296
297 glMatrixMode(GL_MODELVIEW);
298 }
299
300 static void
301 printhelp(void)
302 {
303 glColor4f(0.0, 0.0, 0.0, 0.5);
304 glRecti(40, 40, 600, 440);
305
306 glColor3f(1.0, 0.0, 0.0);
307 glRasterPos2i(300, 420);
308 printstring(GLUT_BITMAP_TIMES_ROMAN_24, "Help");
309
310 glRasterPos2i(60, 390);
311 printstring(GLUT_BITMAP_TIMES_ROMAN_24, "h - Togle Help");
312
313 glRasterPos2i(60, 360);
314 printstring(GLUT_BITMAP_TIMES_ROMAN_24, "t - Increase particle size");
315 glRasterPos2i(60, 330);
316 printstring(GLUT_BITMAP_TIMES_ROMAN_24, "T - Decrease particle size");
317
318 glRasterPos2i(60, 300);
319 printstring(GLUT_BITMAP_TIMES_ROMAN_24, "r - Increase emission radius");
320 glRasterPos2i(60, 270);
321 printstring(GLUT_BITMAP_TIMES_ROMAN_24, "R - Decrease emission radius");
322
323 glRasterPos2i(60, 240);
324 printstring(GLUT_BITMAP_TIMES_ROMAN_24, "f - Togle Fog");
325 glRasterPos2i(60, 210);
326 printstring(GLUT_BITMAP_TIMES_ROMAN_24, "s - Togle shadows");
327 glRasterPos2i(60, 180);
328 printstring(GLUT_BITMAP_TIMES_ROMAN_24, "Arrow Keys - Rotate");
329 glRasterPos2i(60, 150);
330 printstring(GLUT_BITMAP_TIMES_ROMAN_24, "a - Increase velocity");
331 glRasterPos2i(60, 120);
332 printstring(GLUT_BITMAP_TIMES_ROMAN_24, "z - Decrease velocity");
333
334 glRasterPos2i(60, 90);
335 if (joyavailable)
336 printstring(GLUT_BITMAP_TIMES_ROMAN_24,
337 "j - Togle jostick control (Joystick control available)");
338 else
339 printstring(GLUT_BITMAP_TIMES_ROMAN_24,
340 "(No Joystick control available)");
341 }
342
343 static void
344 dojoy(void)
345 {
346 #ifdef WIN32
347 static UINT max[2] = { 0, 0 };
348 static UINT min[2] = { 0xffffffff, 0xffffffff }, center[2];
349 MMRESULT res;
350 JOYINFO joy;
351
352 res = joyGetPos(JOYSTICKID1, &joy);
353
354 if (res == JOYERR_NOERROR) {
355 joyavailable = 1;
356
357 if (max[0] < joy.wXpos)
358 max[0] = joy.wXpos;
359 if (min[0] > joy.wXpos)
360 min[0] = joy.wXpos;
361 center[0] = (max[0] + min[0]) / 2;
362
363 if (max[1] < joy.wYpos)
364 max[1] = joy.wYpos;
365 if (min[1] > joy.wYpos)
366 min[1] = joy.wYpos;
367 center[1] = (max[1] + min[1]) / 2;
368
369 if (joyactive) {
370 if (fabs(center[0] - (float) joy.wXpos) > 0.1 * (max[0] - min[0]))
371 alpha +=
372 2.5 * (center[0] - (float) joy.wXpos) / (max[0] - min[0]);
373 if (fabs(center[1] - (float) joy.wYpos) > 0.1 * (max[1] - min[1]))
374 beta += 2.5 * (center[1] - (float) joy.wYpos) / (max[1] - min[1]);
375
376 if (joy.wButtons & JOY_BUTTON1)
377 v += 0.01;
378 if (joy.wButtons & JOY_BUTTON2)
379 v -= 0.01;
380 }
381 }
382 else
383 joyavailable = 0;
384 #endif
385 }
386
387 static void
388 drawfire(void)
389 {
390 static int count = 0;
391 static char frbuf[80];
392 int j;
393 float fr;
394
395 dojoy();
396
397 glEnable(GL_DEPTH_TEST);
398
399 if (fog)
400 glEnable(GL_FOG);
401 else
402 glDisable(GL_FOG);
403
404 glDepthMask(GL_TRUE);
405 glClearColor(1.0, 1.0, 1.0, 1.0);
406 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
407
408 glPushMatrix();
409 calcposobs();
410 gluLookAt(obs[0], obs[1], obs[2],
411 obs[0] + dir[0], obs[1] + dir[1], obs[2] + dir[2],
412 0.0, 1.0, 0.0);
413
414 glColor4f(1.0, 1.0, 1.0, 1.0);
415
416 glEnable(GL_TEXTURE_2D);
417
418 glBindTexture(GL_TEXTURE_2D, groundid);
419 glBegin(GL_QUADS);
420 glTexCoord2fv(qt[0]);
421 glVertex3fv(q[0]);
422 glTexCoord2fv(qt[1]);
423 glVertex3fv(q[1]);
424 glTexCoord2fv(qt[2]);
425 glVertex3fv(q[2]);
426 glTexCoord2fv(qt[3]);
427 glVertex3fv(q[3]);
428 glEnd();
429
430 glEnable(GL_ALPHA_TEST);
431 glAlphaFunc(GL_GEQUAL, 0.9);
432
433 glBindTexture(GL_TEXTURE_2D, treeid);
434 for (j = 0; j < NUMTREE; j++)
435 drawtree(treepos[j][0], treepos[j][1], treepos[j][2]);
436
437 glDisable(GL_TEXTURE_2D);
438 glDepthMask(GL_FALSE);
439 glDisable(GL_ALPHA_TEST);
440
441 if (shadows) {
442 glBegin(GL_TRIANGLES);
443 for (j = 0; j < np; j++) {
444 glColor4f(black[0], black[1], black[2], p[j].c[0][3]);
445 glVertex3f(p[j].p[0][0], 0.1, p[j].p[0][2]);
446
447 glColor4f(black[0], black[1], black[2], p[j].c[1][3]);
448 glVertex3f(p[j].p[1][0], 0.1, p[j].p[1][2]);
449
450 glColor4f(black[0], black[1], black[2], p[j].c[2][3]);
451 glVertex3f(p[j].p[2][0], 0.1, p[j].p[2][2]);
452 }
453 glEnd();
454 }
455
456 glBegin(GL_TRIANGLES);
457 for (j = 0; j < np; j++) {
458 glColor4fv(p[j].c[0]);
459 glVertex3fv(p[j].p[0]);
460
461 glColor4fv(p[j].c[1]);
462 glVertex3fv(p[j].p[1]);
463
464 glColor4fv(p[j].c[2]);
465 glVertex3fv(p[j].p[2]);
466
467 setpart(&p[j]);
468 }
469 glEnd();
470
471 if ((count % FRAME) == 0) {
472 fr = gettime();
473 sprintf(frbuf, "Frame rate: %f", FRAME / fr);
474 }
475
476 glDisable(GL_TEXTURE_2D);
477 glDisable(GL_ALPHA_TEST);
478 glDisable(GL_DEPTH_TEST);
479 glDisable(GL_FOG);
480
481 glMatrixMode(GL_PROJECTION);
482 glLoadIdentity();
483 glOrtho(-0.5, 639.5, -0.5, 479.5, -1.0, 1.0);
484 glMatrixMode(GL_MODELVIEW);
485 glLoadIdentity();
486
487 glColor3f(1.0, 0.0, 0.0);
488 glRasterPos2i(10, 10);
489 printstring(GLUT_BITMAP_HELVETICA_18, frbuf);
490 glRasterPos2i(370, 470);
491 printstring(GLUT_BITMAP_HELVETICA_10,
492 "Fire V1.5 Written by David Bucciarelli (tech.hmw@plus.it)");
493
494 if (help)
495 printhelp();
496
497 reshape(WIDTH, HEIGHT);
498 glPopMatrix();
499
500 glutSwapBuffers();
501
502 count++;
503 }
504
505
506 static void
507 special(int key, int x, int y)
508 {
509 switch (key) {
510 case GLUT_KEY_LEFT:
511 alpha += 2.0;
512 break;
513 case GLUT_KEY_RIGHT:
514 alpha -= 2.0;
515 break;
516 case GLUT_KEY_DOWN:
517 beta -= 2.0;
518 break;
519 case GLUT_KEY_UP:
520 beta += 2.0;
521 break;
522 }
523 }
524
525 static void
526 key(unsigned char key, int x, int y)
527 {
528 switch (key) {
529 case 27:
530 exit(0);
531 break;
532
533 case 'a':
534 v += 0.01;
535 break;
536 case 'z':
537 v -= 0.01;
538 break;
539
540 case 'j':
541 joyactive = (!joyactive);
542 break;
543 case 'h':
544 help = (!help);
545 break;
546 case 'f':
547 fog = (!fog);
548 break;
549 case 's':
550 shadows = !shadows;
551 break;
552 case 'R':
553 eject_r -= 0.03;
554 break;
555 case 'r':
556 eject_r += 0.03;
557 break;
558 case 't':
559 ridtri += 0.005;
560 break;
561 case 'T':
562 ridtri -= 0.005;
563 break;
564 #ifdef XMESA
565 case ' ':
566 XMesaSetFXmode(fullscreen ? XMESA_FX_FULLSCREEN : XMESA_FX_WINDOW);
567 fullscreen = (!fullscreen);
568 break;
569 #endif
570 }
571 }
572
573 static void
574 inittextures(void)
575 {
576 GLenum gluerr;
577 GLubyte tex[128][128][4];
578
579 glGenTextures(1, &groundid);
580 glBindTexture(GL_TEXTURE_2D, groundid);
581
582 glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
583 if (!LoadRGBMipmaps("../images/s128.rgb", GL_RGB)) {
584 fprintf(stderr, "Error reading a texture.\n");
585 exit(-1);
586 }
587
588 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
589 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
590
591 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
592 GL_LINEAR_MIPMAP_LINEAR);
593 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
594
595 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
596
597 glGenTextures(1, &treeid);
598 glBindTexture(GL_TEXTURE_2D, treeid);
599
600 if (1)
601 {
602 int w, h;
603 GLenum format;
604 int x, y;
605 GLubyte *image = LoadRGBImage("../images/tree3.rgb", &w, &h, &format);
606
607 if (!image) {
608 fprintf(stderr, "Error reading a texture.\n");
609 exit(-1);
610 }
611
612 for (y = 0; y < 128; y++)
613 for (x = 0; x < 128; x++) {
614 tex[x][y][0] = image[(y + x * 128) * 3];
615 tex[x][y][1] = image[(y + x * 128) * 3 + 1];
616 tex[x][y][2] = image[(y + x * 128) * 3 + 2];
617 if ((tex[x][y][0] == tex[x][y][1]) &&
618 (tex[x][y][1] == tex[x][y][2]) && (tex[x][y][2] == 255))
619 tex[x][y][3] = 0;
620 else
621 tex[x][y][3] = 255;
622 }
623
624 if ((gluerr = gluBuild2DMipmaps(GL_TEXTURE_2D, 4, 128, 128, GL_RGBA,
625 GL_UNSIGNED_BYTE, (GLvoid *) (tex)))) {
626 fprintf(stderr, "GLULib%s\n", gluErrorString(gluerr));
627 exit(-1);
628 }
629 }
630 else {
631 if (!LoadRGBMipmaps("../images/tree2.rgba", GL_RGBA)) {
632 fprintf(stderr, "Error reading a texture.\n");
633 exit(-1);
634 }
635 }
636
637 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
638 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
639
640 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
641 GL_LINEAR_MIPMAP_LINEAR);
642 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
643
644 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
645 }
646
647 static void
648 inittree(void)
649 {
650 int i;
651 float dist;
652
653 for (i = 0; i < NUMTREE; i++)
654 do {
655 treepos[i][0] = vrnd() * TREEOUTR * 2.0 - TREEOUTR;
656 treepos[i][1] = 0.0;
657 treepos[i][2] = vrnd() * TREEOUTR * 2.0 - TREEOUTR;
658 dist =
659 sqrt(treepos[i][0] * treepos[i][0] +
660 treepos[i][2] * treepos[i][2]);
661 } while ((dist < TREEINR) || (dist > TREEOUTR));
662 }
663
664 int
665 main(int ac, char **av)
666 {
667 int i;
668
669 fprintf(stderr,
670 "Fire V1.5\nWritten by David Bucciarelli (tech.hmw@plus.it)\n");
671
672 /* Default settings */
673
674 WIDTH = 640;
675 HEIGHT = 480;
676 np = 800;
677 eject_r = 0.1;
678 dt = 0.015;
679 eject_vy = 4;
680 eject_vl = 1;
681 shadows = 1;
682 ridtri = 0.1;
683
684 maxage = 1.0 / dt;
685
686 if (ac == 2)
687 np = atoi(av[1]);
688
689 if (ac == 4) {
690 WIDTH = atoi(av[2]);
691 HEIGHT = atoi(av[3]);
692 }
693
694 glutInitWindowPosition(0, 0);
695 glutInitWindowSize(WIDTH, HEIGHT);
696 glutInit(&ac, av);
697
698 glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
699
700 if (!(win = glutCreateWindow("Fire"))) {
701 fprintf(stderr, "Error opening a window.\n");
702 exit(-1);
703 }
704
705 reshape(WIDTH, HEIGHT);
706
707 inittextures();
708
709 glShadeModel(GL_FLAT);
710 glEnable(GL_DEPTH_TEST);
711
712 glEnable(GL_BLEND);
713 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
714
715 glEnable(GL_FOG);
716 glFogi(GL_FOG_MODE, GL_EXP);
717 glFogfv(GL_FOG_COLOR, fogcolor);
718 glFogf(GL_FOG_DENSITY, 0.1);
719 #ifdef FX
720 glHint(GL_FOG_HINT, GL_NICEST);
721 #endif
722
723 p = malloc(sizeof(part) * np);
724
725 for (i = 0; i < np; i++)
726 setnewpart(&p[i]);
727
728 inittree();
729
730 glutKeyboardFunc(key);
731 glutSpecialFunc(special);
732 glutDisplayFunc(drawfire);
733 glutIdleFunc(drawfire);
734 glutReshapeFunc(reshape);
735 glutMainLoop();
736
737 return (0);
738 }