fbo_firecube.c revision 7ec3b29a
1/*
2 * Draw the "fire" test program on the six faces of a cube using
3 * fbo render-to-texture.
4 *
5 * Much of the code comes from David Bucciarelli's "fire"
6 * test program. The rest basically from Brian Paul's "gearbox" and
7 * fbotexture programs.
8 *
9 * By pressing the 'q' key, you can make the driver choose different
10 * internal texture RGBA formats by giving it different "format" and "type"
11 * arguments to the glTexImage2D function that creates the texture
12 * image being rendered to. If the driver doesn't support a texture image
13 * format as a render target, it will usually fall back to software when
14 * drawing the "fire" image, and frame-rate should drop considerably.
15 *
16 * Performance:
17 * The rendering speed of this program is usually dictated by fill rate
18 * and the fact that software fallbacks for glBitMap makes the driver
19 * operate synchronously. Low-end UMA hardware will probably see around
20 * 35 fps with the help screen disabled and 32bpp window- and user
21 * frame-buffers (2008).
22 *
23 * This program is released under GPL, following the "fire" licensing.
24 *
25 * Authors:
26 *   David Bucciarelli ("fire")
27 *   Brian Paul ("gearbox", "fbotexture")
28 *   Thomas Hellstrom (Putting it together)
29 *
30 */
31
32#include <math.h>
33#include <stdlib.h>
34#include <stdio.h>
35#include <string.h>
36#include <GL/glew.h>
37#include "glut_wrap.h"
38#include "readtex.h"
39
40
41/*
42 * Format of texture we render to.
43 */
44
45#define TEXINTFORMAT GL_RGBA
46
47static GLuint texTypes[] =
48   {GL_UNSIGNED_BYTE,
49    GL_UNSIGNED_INT_8_8_8_8_REV,
50    GL_UNSIGNED_SHORT_1_5_5_5_REV,
51    GL_UNSIGNED_SHORT_4_4_4_4_REV,
52    GL_UNSIGNED_INT_8_8_8_8,
53    GL_UNSIGNED_SHORT_5_5_5_1,
54    GL_UNSIGNED_SHORT_4_4_4_4,
55    GL_UNSIGNED_INT_8_8_8_8_REV,
56    GL_UNSIGNED_SHORT_1_5_5_5_REV,
57    GL_UNSIGNED_SHORT_4_4_4_4_REV,
58    GL_UNSIGNED_INT_8_8_8_8,
59    GL_UNSIGNED_SHORT_5_5_5_1,
60    GL_UNSIGNED_SHORT_4_4_4_4,
61    GL_UNSIGNED_SHORT_5_6_5,
62    GL_UNSIGNED_SHORT_5_6_5_REV,
63    GL_UNSIGNED_SHORT_5_6_5,
64    GL_UNSIGNED_SHORT_5_6_5_REV};
65
66static GLuint texFormats[] =
67   {GL_RGBA,
68    GL_RGBA,
69    GL_RGBA,
70    GL_RGBA,
71    GL_RGBA,
72    GL_RGBA,
73    GL_RGBA,
74    GL_BGRA,
75    GL_BGRA,
76    GL_BGRA,
77    GL_BGRA,
78    GL_BGRA,
79    GL_BGRA,
80    GL_RGB,
81    GL_RGB,
82    GL_BGR,
83    GL_BGR};
84
85static const char *texNames[] =
86   {"GL_RGBA GL_UNSIGNED_BYTE",
87    "GL_RGBA GL_UNSIGNED_INT_8_8_8_8_REV",
88    "GL_RGBA GL_UNSIGNED_SHORT_1_5_5_5_REV",
89    "GL_RGBA GL_UNSIGNED_SHORT_4_4_4_4_REV",
90    "GL_RGBA GL_UNSIGNED_INT_8_8_8_8",
91    "GL_RGBA GL_UNSIGNED_INT_5_5_5_1",
92    "GL_RGBA GL_UNSIGNED_INT_4_4_4_4",
93    "GL_BGRA GL_UNSIGNED_INT_8_8_8_8_REV",
94    "GL_BGRA GL_UNSIGNED_SHORT_1_5_5_5_REV",
95    "GL_BGRA GL_UNSIGNED_SHORT_4_4_4_4_REV",
96    "GL_BGRA GL_UNSIGNED_INT_8_8_8_8",
97    "GL_BGRA GL_UNSIGNED_INT_5_5_5_1",
98    "GL_BGRA GL_UNSIGNED_INT_4_4_4_4",
99    "GL_RGB GL_UNSIGNED_INT_5_6_5",
100    "GL_RGB GL_UNSIGNED_INT_5_6_5_REV",
101    "GL_BGR GL_UNSIGNED_INT_5_6_5",
102    "GL_BGR GL_UNSIGNED_INT_5_6_5_REV"};
103
104
105
106
107#ifndef M_PI
108#define M_PI 3.1415926535
109#endif
110
111#define vinit(a,i,j,k) {			\
112      (a)[0]=i;					\
113      (a)[1]=j;					\
114      (a)[2]=k;					\
115   }
116
117#define vinit4(a,i,j,k,w) {			\
118      (a)[0]=i;					\
119      (a)[1]=j;					\
120      (a)[2]=k;					\
121      (a)[3]=w;					\
122   }
123
124
125#define vadds(a,dt,b) {				\
126      (a)[0]+=(dt)*(b)[0];			\
127      (a)[1]+=(dt)*(b)[1];			\
128      (a)[2]+=(dt)*(b)[2];			\
129   }
130
131#define vequ(a,b) {				\
132      (a)[0]=(b)[0];				\
133      (a)[1]=(b)[1];				\
134      (a)[2]=(b)[2];				\
135   }
136
137#define vinter(a,dt,b,c) {			\
138      (a)[0]=(dt)*(b)[0]+(1.0-dt)*(c)[0];	\
139      (a)[1]=(dt)*(b)[1]+(1.0-dt)*(c)[1];	\
140      (a)[2]=(dt)*(b)[2]+(1.0-dt)*(c)[2];	\
141   }
142
143#define clamp(a)        ((a) < 0.0 ? 0.0 : ((a) < 1.0 ? (a) : 1.0))
144
145#define vclamp(v) {				\
146      (v)[0]=clamp((v)[0]);			\
147      (v)[1]=clamp((v)[1]);			\
148      (v)[2]=clamp((v)[2]);			\
149   }
150
151static GLint WinWidth = 800, WinHeight = 800;
152static GLint TexWidth, TexHeight;
153static GLuint TexObj;
154static GLuint MyFB;
155static GLuint DepthRB;
156static GLboolean WireFrame = GL_FALSE;
157static GLboolean Anim = GL_TRUE;
158static GLint texType = 0;
159
160static GLint T0 = 0;
161static GLint Frames = 0;
162static GLint Win = 0;
163
164static GLfloat ViewRotX = 20.0, ViewRotY = 30.0, ViewRotZ = 0.0;
165static GLfloat CubeRot = 0.0;
166
167
168static void
169idle(void);
170
171
172static void
173CheckError(int line)
174{
175   GLenum err = glGetError();
176   if (err) {
177      printf("GL Error 0x%x at line %d\n", (int) err, line);
178      exit(1);
179   }
180}
181
182
183static void
184cleanup(void)
185{
186   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
187   glDeleteFramebuffersEXT(1, &MyFB);
188   glDeleteRenderbuffersEXT(1, &DepthRB);
189   glDeleteTextures(1, &TexObj);
190   glutDestroyWindow(Win);
191}
192
193static GLint NiceFog = 1;
194
195#define DIMP 20.0
196#define DIMTP 16.0
197
198#define RIDCOL 0.4
199
200#define NUMTREE 50
201#define TREEINR 2.5
202#define TREEOUTR 8.0
203
204#define AGRAV -9.8
205
206typedef struct
207{
208   int age;
209   float p[3][3];
210   float v[3];
211   float c[3][4];
212}
213   part;
214
215static float treepos[NUMTREE][3];
216
217static float black[3] = { 0.0, 0.0, 0.0 };
218static float blu[3] = { 1.0, 0.2, 0.0 };
219static float blu2[3] = { 1.0, 1.0, 0.0 };
220
221static float fogcolor[4] = { 1.0, 1.0, 1.0, 1.0 };
222
223static float q[4][3] = {
224   {-DIMP, 0.0, -DIMP},
225   {DIMP, 0.0, -DIMP},
226   {DIMP, 0.0, DIMP},
227   {-DIMP, 0.0, DIMP}
228};
229
230static float qt[4][2] = {
231   {-DIMTP, -DIMTP},
232   {DIMTP, -DIMTP},
233   {DIMTP, DIMTP},
234   {-DIMTP, DIMTP}
235};
236
237static int np;
238static float eject_r, dt, maxage, eject_vy, eject_vl;
239static short shadows;
240static float ridtri;
241static int fog = 0;
242static int help = 1;
243
244static part *p;
245
246static GLuint groundid;
247static GLuint treeid;
248
249static float obs[3] = { 2.0, 1.0, 0.0 };
250static float dir[3];
251static float v = 0.0;
252static float alpha = -84.0;
253static float beta = 90.0;
254
255static float
256vrnd(void)
257{
258   return (((float) rand()) / RAND_MAX);
259}
260
261static void
262setnewpart(part * p)
263{
264   float a, v[3], *c;
265
266   p->age = 0;
267
268   a = vrnd() * 3.14159265359 * 2.0;
269
270   vinit(v, sin(a) * eject_r * vrnd(), 0.15, cos(a) * eject_r * vrnd());
271   vinit(p->p[0], v[0] + vrnd() * ridtri, v[1] + vrnd() * ridtri,
272	 v[2] + vrnd() * ridtri);
273   vinit(p->p[1], v[0] + vrnd() * ridtri, v[1] + vrnd() * ridtri,
274	 v[2] + vrnd() * ridtri);
275   vinit(p->p[2], v[0] + vrnd() * ridtri, v[1] + vrnd() * ridtri,
276	 v[2] + vrnd() * ridtri);
277
278   vinit(p->v, v[0] * eject_vl / (eject_r / 2),
279	 vrnd() * eject_vy + eject_vy / 2, v[2] * eject_vl / (eject_r / 2));
280
281   c = blu;
282
283   vinit4(p->c[0], c[0] * ((1.0 - RIDCOL) + vrnd() * RIDCOL),
284	  c[1] * ((1.0 - RIDCOL) + vrnd() * RIDCOL),
285	  c[2] * ((1.0 - RIDCOL) + vrnd() * RIDCOL), 1.0);
286   vinit4(p->c[1], c[0] * ((1.0 - RIDCOL) + vrnd() * RIDCOL),
287	  c[1] * ((1.0 - RIDCOL) + vrnd() * RIDCOL),
288	  c[2] * ((1.0 - RIDCOL) + vrnd() * RIDCOL), 1.0);
289   vinit4(p->c[2], c[0] * ((1.0 - RIDCOL) + vrnd() * RIDCOL),
290	  c[1] * ((1.0 - RIDCOL) + vrnd() * RIDCOL),
291	  c[2] * ((1.0 - RIDCOL) + vrnd() * RIDCOL), 1.0);
292}
293
294static void
295setpart(part * p)
296{
297   float fact;
298
299   if (p->p[0][1] < 0.1) {
300      setnewpart(p);
301      return;
302   }
303
304   p->v[1] += AGRAV * dt;
305
306   vadds(p->p[0], dt, p->v);
307   vadds(p->p[1], dt, p->v);
308   vadds(p->p[2], dt, p->v);
309
310   p->age++;
311
312   if ((p->age) > maxage) {
313      vequ(p->c[0], blu2);
314      vequ(p->c[1], blu2);
315      vequ(p->c[2], blu2);
316   }
317   else {
318      fact = 1.0 / maxage;
319      vadds(p->c[0], fact, blu2);
320      vclamp(p->c[0]);
321      p->c[0][3] = fact * (maxage - p->age);
322
323      vadds(p->c[1], fact, blu2);
324      vclamp(p->c[1]);
325      p->c[1][3] = fact * (maxage - p->age);
326
327      vadds(p->c[2], fact, blu2);
328      vclamp(p->c[2]);
329      p->c[2][3] = fact * (maxage - p->age);
330   }
331}
332
333static void
334drawtree(float x, float y, float z)
335{
336   glBegin(GL_QUADS);
337   glTexCoord2f(0.0, 0.0);
338   glVertex3f(x - 1.5, y + 0.0, z);
339
340   glTexCoord2f(1.0, 0.0);
341   glVertex3f(x + 1.5, y + 0.0, z);
342
343   glTexCoord2f(1.0, 1.0);
344   glVertex3f(x + 1.5, y + 3.0, z);
345
346   glTexCoord2f(0.0, 1.0);
347   glVertex3f(x - 1.5, y + 3.0, z);
348
349
350   glTexCoord2f(0.0, 0.0);
351   glVertex3f(x, y + 0.0, z - 1.5);
352
353   glTexCoord2f(1.0, 0.0);
354   glVertex3f(x, y + 0.0, z + 1.5);
355
356   glTexCoord2f(1.0, 1.0);
357   glVertex3f(x, y + 3.0, z + 1.5);
358
359   glTexCoord2f(0.0, 1.0);
360   glVertex3f(x, y + 3.0, z - 1.5);
361
362   glEnd();
363
364}
365
366static void
367calcposobs(void)
368{
369   dir[0] = sin(alpha * M_PI / 180.0);
370   dir[2] = cos(alpha * M_PI / 180.0) * sin(beta * M_PI / 180.0);
371   dir[1] = cos(beta * M_PI / 180.0);
372
373   if (dir[0] < 1.0e-5 && dir[0] > -1.0e-5)
374      dir[0] = 0;
375   if (dir[1] < 1.0e-5 && dir[1] > -1.0e-5)
376      dir[1] = 0;
377   if (dir[2] < 1.0e-5 && dir[2] > -1.0e-5)
378      dir[2] = 0;
379
380   obs[0] += v * dir[0];
381   obs[1] += v * dir[1];
382   obs[2] += v * dir[2];
383}
384
385static void
386printstring(void *font, const char *string)
387{
388   int len, i;
389
390   len = (int) strlen(string);
391   for (i = 0; i < len; i++)
392      glutBitmapCharacter(font, string[i]);
393}
394
395
396static void
397printhelp(void)
398{
399   glColor4f(0.0, 0.0, 0.0, 0.5);
400   glRecti(40, 40, 600, 440);
401
402   glColor3f(1.0, 0.0, 0.0);
403   glRasterPos2i(300, 420);
404   printstring(GLUT_BITMAP_TIMES_ROMAN_24, "Help");
405
406   glRasterPos2i(60, 390);
407   printstring(GLUT_BITMAP_TIMES_ROMAN_24, "h - Toggle Help");
408
409   glRasterPos2i(60, 360);
410   printstring(GLUT_BITMAP_TIMES_ROMAN_24, "t - Increase particle size");
411   glRasterPos2i(60, 330);
412   printstring(GLUT_BITMAP_TIMES_ROMAN_24, "T - Decrease particle size");
413
414   glRasterPos2i(60, 300);
415   printstring(GLUT_BITMAP_TIMES_ROMAN_24, "r - Increase emission radius");
416   glRasterPos2i(60, 270);
417   printstring(GLUT_BITMAP_TIMES_ROMAN_24, "R - Decrease emission radius");
418
419   glRasterPos2i(60, 240);
420   printstring(GLUT_BITMAP_TIMES_ROMAN_24, "f - Toggle Fog");
421   glRasterPos2i(60, 210);
422   printstring(GLUT_BITMAP_TIMES_ROMAN_24, "s - Toggle shadows");
423   glRasterPos2i(60, 180);
424   printstring(GLUT_BITMAP_TIMES_ROMAN_24, "q - Toggle texture format & type");
425   glRasterPos2i(60, 150);
426   printstring(GLUT_BITMAP_TIMES_ROMAN_24, "a - Increase velocity");
427   glRasterPos2i(60, 120);
428   printstring(GLUT_BITMAP_TIMES_ROMAN_24, "z - Decrease velocity");
429   glRasterPos2i(60, 90);
430   printstring(GLUT_BITMAP_TIMES_ROMAN_24,  "Arrow Keys - Rotate");
431}
432
433
434static void
435drawfire(void)
436{
437   static char frbuf[80] = "";
438   int j;
439   static double t0 = -1.;
440   double t = glutGet(GLUT_ELAPSED_TIME) / 1000.0;
441   if (t0 < 0.0)
442      t0 = t;
443   dt = (t - t0) * 1.0;
444   t0 = t;
445
446   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, MyFB);
447
448   glDisable(GL_LIGHTING);
449
450   glShadeModel(GL_FLAT);
451
452   glEnable(GL_BLEND);
453   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
454
455   glEnable(GL_FOG);
456   glFogi(GL_FOG_MODE, GL_EXP);
457   glFogfv(GL_FOG_COLOR, fogcolor);
458   glFogf(GL_FOG_DENSITY, 0.1);
459
460
461   glViewport(0, 0, (GLint) TexWidth, (GLint) TexHeight);
462   glMatrixMode(GL_PROJECTION);
463   glLoadIdentity();
464   gluPerspective(70.0, TexWidth/ (float) TexHeight, 0.1, 30.0);
465
466   glMatrixMode(GL_MODELVIEW);
467   glLoadIdentity();
468
469   if (NiceFog)
470      glHint(GL_FOG_HINT, GL_NICEST);
471   else
472      glHint(GL_FOG_HINT, GL_DONT_CARE);
473
474   glEnable(GL_DEPTH_TEST);
475
476   if (fog)
477      glEnable(GL_FOG);
478   else
479      glDisable(GL_FOG);
480
481   glDepthMask(GL_TRUE);
482   glClearColor(1.0, 1.0, 1.0, 1.0);
483   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
484
485
486   glPushMatrix();
487   calcposobs();
488   gluLookAt(obs[0], obs[1], obs[2],
489	     obs[0] + dir[0], obs[1] + dir[1], obs[2] + dir[2],
490	     0.0, 1.0, 0.0);
491
492   glColor4f(1.0, 1.0, 1.0, 1.0);
493
494   glEnable(GL_TEXTURE_2D);
495
496   glBindTexture(GL_TEXTURE_2D, groundid);
497   glBegin(GL_QUADS);
498   glTexCoord2fv(qt[0]);
499   glVertex3fv(q[0]);
500   glTexCoord2fv(qt[1]);
501   glVertex3fv(q[1]);
502   glTexCoord2fv(qt[2]);
503   glVertex3fv(q[2]);
504   glTexCoord2fv(qt[3]);
505   glVertex3fv(q[3]);
506   glEnd();
507
508   glEnable(GL_ALPHA_TEST);
509   glAlphaFunc(GL_GEQUAL, 0.9);
510
511   glBindTexture(GL_TEXTURE_2D, treeid);
512   for (j = 0; j < NUMTREE; j++)
513      drawtree(treepos[j][0], treepos[j][1], treepos[j][2]);
514
515   glDisable(GL_TEXTURE_2D);
516   glDepthMask(GL_FALSE);
517   glDisable(GL_ALPHA_TEST);
518
519   if (shadows) {
520      glBegin(GL_TRIANGLES);
521      for (j = 0; j < np; j++) {
522	 glColor4f(black[0], black[1], black[2], p[j].c[0][3]);
523	 glVertex3f(p[j].p[0][0], 0.1, p[j].p[0][2]);
524
525	 glColor4f(black[0], black[1], black[2], p[j].c[1][3]);
526	 glVertex3f(p[j].p[1][0], 0.1, p[j].p[1][2]);
527
528	 glColor4f(black[0], black[1], black[2], p[j].c[2][3]);
529	 glVertex3f(p[j].p[2][0], 0.1, p[j].p[2][2]);
530      }
531      glEnd();
532   }
533
534   glBegin(GL_TRIANGLES);
535   for (j = 0; j < np; j++) {
536      glColor4fv(p[j].c[0]);
537      glVertex3fv(p[j].p[0]);
538
539      glColor4fv(p[j].c[1]);
540      glVertex3fv(p[j].p[1]);
541
542      glColor4fv(p[j].c[2]);
543      glVertex3fv(p[j].p[2]);
544
545      setpart(&p[j]);
546   }
547   glEnd();
548
549
550   glDisable(GL_TEXTURE_2D);
551   glDisable(GL_ALPHA_TEST);
552   glDisable(GL_DEPTH_TEST);
553   glDisable(GL_FOG);
554
555   glMatrixMode(GL_PROJECTION);
556   glLoadIdentity();
557   glOrtho(-0.5, 639.5, -0.5, 479.5
558	   , -1.0, 1.0);
559   glMatrixMode(GL_MODELVIEW);
560   glLoadIdentity();
561
562   glColor3f(1.0, 0.0, 0.0);
563   glRasterPos2i(10, 10);
564   printstring(GLUT_BITMAP_HELVETICA_18, frbuf);
565   glColor3f(0.0, 0.0, 1.0);
566   glRasterPos2i(10, 450);
567   printstring(GLUT_BITMAP_HELVETICA_18, texNames[texType]);
568   glColor3f(1.0, 0.0, 0.0);
569   glRasterPos2i(10, 470);
570   printstring(GLUT_BITMAP_HELVETICA_10,
571	       "Fire V1.5 Written by David Bucciarelli (tech.hmw@plus.it)");
572
573   if (help)
574      printhelp();
575
576   glPopMatrix();
577   glDepthMask(GL_TRUE);
578   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
579   Frames++;
580
581   {
582      GLint t = glutGet(GLUT_ELAPSED_TIME);
583      if (t - T0 >= 2000) {
584	 GLfloat seconds = (t - T0) / 1000.0;
585	 GLfloat fps = Frames / seconds;
586	 sprintf(frbuf, "Frame rate: %f", fps);
587	 printf("%s\n", frbuf);
588	 fflush(stdout);
589	 T0 = t;
590	 Frames = 0;
591      }
592   }
593
594}
595
596static void
597regen_texImage(void)
598{
599   glBindTexture(GL_TEXTURE_2D, TexObj);
600   glTexImage2D(GL_TEXTURE_2D, 0, TEXINTFORMAT, TexWidth, TexHeight, 0,
601		texFormats[texType], texTypes[texType], NULL);
602   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, MyFB);
603   glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
604			     GL_TEXTURE_2D, TexObj, 0);
605   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
606}
607
608static void
609key(unsigned char key, int x, int y)
610{
611   switch (key) {
612   case 27:
613      cleanup();
614      exit(0);
615      break;
616   case ' ':
617      Anim = !Anim;
618      glutIdleFunc(Anim ? idle : NULL);
619      break;
620   case 'a':
621      v += 0.0005;
622      break;
623   case 'z':
624      v -= 0.0005;
625      break;
626   case 'h':
627      help = (!help);
628      break;
629   case 'f':
630      fog = (!fog);
631      break;
632   case 's':
633      shadows = !shadows;
634      break;
635   case 'R':
636      eject_r -= 0.03;
637      break;
638   case 'r':
639      eject_r += 0.03;
640      break;
641   case 't':
642      ridtri += 0.005;
643      break;
644   case 'T':
645      ridtri -= 0.005;
646      break;
647   case 'v':
648      ViewRotZ += 5.0;
649      break;
650   case 'V':
651      ViewRotZ -= 5.0;
652      break;
653   case 'w':
654      WireFrame = !WireFrame;
655      break;
656   case 'q':
657      if (++texType > 16)
658	 texType = 0;
659      regen_texImage();
660      break;
661   case 'n':
662      NiceFog = !NiceFog;
663      printf("NiceFog %d\n", NiceFog);
664      break;
665   }
666   glutPostRedisplay();
667}
668
669static void
670inittextures(void)
671{
672   GLenum gluerr;
673   GLubyte tex[128][128][4];
674
675   glGenTextures(1, &groundid);
676   glBindTexture(GL_TEXTURE_2D, groundid);
677
678   glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
679   if (!LoadRGBMipmaps(DEMOS_DATA_DIR "s128.rgb", GL_RGB)) {
680      fprintf(stderr, "Error reading a texture.\n");
681      exit(-1);
682   }
683
684   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
685   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
686
687   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
688		   GL_LINEAR_MIPMAP_LINEAR);
689   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
690
691   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
692
693   glGenTextures(1, &treeid);
694   glBindTexture(GL_TEXTURE_2D, treeid);
695
696   if (1)
697      {
698	 int w, h;
699	 GLenum format;
700	 int x, y;
701	 GLubyte *image = LoadRGBImage(DEMOS_DATA_DIR "tree3.rgb",
702				       &w, &h, &format);
703
704	 if (!image) {
705	    fprintf(stderr, "Error reading a texture.\n");
706	    exit(-1);
707	 }
708
709	 for (y = 0; y < 128; y++)
710	    for (x = 0; x < 128; x++) {
711	       tex[x][y][0] = image[(y + x * 128) * 3];
712	       tex[x][y][1] = image[(y + x * 128) * 3 + 1];
713	       tex[x][y][2] = image[(y + x * 128) * 3 + 2];
714	       if ((tex[x][y][0] == tex[x][y][1]) &&
715		   (tex[x][y][1] == tex[x][y][2]) && (tex[x][y][2] == 255))
716		  tex[x][y][3] = 0;
717	       else
718		  tex[x][y][3] = 255;
719	    }
720
721	 if ((gluerr = gluBuild2DMipmaps(GL_TEXTURE_2D, 4, 128, 128, GL_RGBA,
722					 GL_UNSIGNED_BYTE, (GLvoid *) (tex)))) {
723	    fprintf(stderr, "GLULib%s\n", (char *) gluErrorString(gluerr));
724	    exit(-1);
725	 }
726      }
727   else {
728      if (!LoadRGBMipmaps(DEMOS_DATA_DIR "tree2.rgba", GL_RGBA)) {
729	 fprintf(stderr, "Error reading a texture.\n");
730	 exit(-1);
731      }
732   }
733
734   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
735   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
736
737   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
738		   GL_LINEAR_MIPMAP_LINEAR);
739   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
740
741   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
742}
743
744static void
745inittree(void)
746{
747   int i;
748   float dist;
749
750   for (i = 0; i < NUMTREE; i++)
751      do {
752	 treepos[i][0] = vrnd() * TREEOUTR * 2.0 - TREEOUTR;
753	 treepos[i][1] = 0.0;
754	 treepos[i][2] = vrnd() * TREEOUTR * 2.0 - TREEOUTR;
755	 dist =
756	    sqrt(treepos[i][0] * treepos[i][0] +
757		 treepos[i][2] * treepos[i][2]);
758      } while ((dist < TREEINR) || (dist > TREEOUTR));
759}
760
761static int
762init_fire(int ac, char *av[])
763{
764   int i;
765
766   np = 800;
767   eject_r = -0.65;
768   dt = 0.015;
769   eject_vy = 4;
770   eject_vl = 1;
771   shadows = 1;
772   ridtri = 0.25;
773
774   maxage = 1.0 / dt;
775
776   if (ac == 2)
777      np = atoi(av[1]);
778
779
780   inittextures();
781
782   p = (part *) malloc(sizeof(part) * np);
783
784   for (i = 0; i < np; i++)
785      setnewpart(&p[i]);
786
787   inittree();
788
789   return (0);
790}
791
792
793
794
795
796
797static void
798DrawCube(void)
799{
800   static const GLfloat texcoords[4][2] = {
801      { 0, 0 }, { 1, 0 }, { 1, 1 }, { 0, 1 }
802   };
803   static const GLfloat vertices[4][2] = {
804      { -1, -1 }, { 1, -1 }, { 1, 1 }, { -1, 1 }
805   };
806   static const GLfloat xforms[6][4] = {
807      {   0, 0, 1, 0 },
808      {  90, 0, 1, 0 },
809      { 180, 0, 1, 0 },
810      { 270, 0, 1, 0 },
811      {  90, 1, 0, 0 },
812      { -90, 1, 0, 0 }
813   };
814   static const GLfloat mat[4] = { 1.0, 1.0, 0.5, 1.0 };
815   GLint i, j;
816
817   glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat);
818   glEnable(GL_TEXTURE_2D);
819
820   glPushMatrix();
821   glRotatef(ViewRotX, 1.0, 0.0, 0.0);
822   glRotatef(15, 1, 0, 0);
823   glRotatef(CubeRot, 0, 1, 0);
824   glScalef(4, 4, 4);
825
826   for (i = 0; i < 6; i++) {
827      glPushMatrix();
828      glRotatef(xforms[i][0], xforms[i][1], xforms[i][2], xforms[i][3]);
829      glTranslatef(0, 0, 1.1);
830      glBegin(GL_POLYGON);
831      glNormal3f(0, 0, 1);
832      for (j = 0; j < 4; j++) {
833	 glTexCoord2fv(texcoords[j]);
834	 glVertex2fv(vertices[j]);
835      }
836      glEnd();
837      glPopMatrix();
838   }
839   glPopMatrix();
840
841   glDisable(GL_TEXTURE_2D);
842}
843
844
845static void
846draw(void)
847{
848   float ar;
849   static GLfloat pos[4] = {5.0, 5.0, 10.0, 0.0};
850
851   drawfire();
852
853   glLightfv(GL_LIGHT0, GL_POSITION, pos);
854   glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL,
855		 GL_SEPARATE_SPECULAR_COLOR);
856
857   glEnable(GL_LIGHTING);
858   glEnable(GL_LIGHT0);
859   glEnable(GL_DEPTH_TEST);
860   glEnable(GL_NORMALIZE);
861   glDisable(GL_BLEND);
862   glDisable(GL_FOG);
863
864   glMatrixMode(GL_MODELVIEW);
865   glLoadIdentity();
866   glTranslatef(0.0, 0.0, -40.0);
867
868   glClear(GL_DEPTH_BUFFER_BIT);
869
870   /* draw textured cube */
871
872   glViewport(0, 0, WinWidth, WinHeight);
873   glClearColor(0.5, 0.5, 0.8, 0.0);
874   glClear(GL_COLOR_BUFFER_BIT);
875
876   ar = (float) (WinWidth) / WinHeight;
877   glMatrixMode(GL_PROJECTION);
878   glLoadIdentity();
879   glFrustum(-ar, ar, -1.0, 1.0, 5.0, 60.0);
880   glMatrixMode(GL_MODELVIEW);
881   glBindTexture(GL_TEXTURE_2D, TexObj);
882
883   DrawCube();
884
885   /* finish up */
886   glutSwapBuffers();
887}
888
889
890static void
891idle(void)
892{
893   static double t0 = -1.;
894   double dt, t = glutGet(GLUT_ELAPSED_TIME) / 1000.0;
895   if (t0 < 0.0)
896      t0 = t;
897   dt = t - t0;
898   t0 = t;
899
900   CubeRot = fmod(CubeRot + 15.0 * dt, 360.0);  /* 15 deg/sec */
901
902   glutPostRedisplay();
903}
904
905
906/* change view angle */
907static void
908special(int k, int x, int y)
909{
910   (void) x;
911   (void) y;
912   switch (k) {
913   case GLUT_KEY_UP:
914      ViewRotX += 5.0;
915      break;
916   case GLUT_KEY_DOWN:
917      ViewRotX -= 5.0;
918      break;
919   case GLUT_KEY_LEFT:
920      ViewRotY += 5.0;
921      break;
922   case GLUT_KEY_RIGHT:
923      ViewRotY -= 5.0;
924      break;
925   default:
926      return;
927   }
928   glutPostRedisplay();
929}
930
931
932/* new window size or exposure */
933static void
934reshape(int width, int height)
935{
936   WinWidth = width;
937   WinHeight = height;
938}
939
940
941static void
942init_fbotexture(void)
943{
944   static const GLenum depthFormats[] = {
945      GL_DEPTH_COMPONENT,
946      GL_DEPTH_COMPONENT16,
947      GL_DEPTH_COMPONENT32
948   };
949   static int numDepthFormats = sizeof(depthFormats) / sizeof(depthFormats[0]);
950   GLint i;
951   GLenum stat;
952
953   /* gen framebuffer id, delete it, do some assertions, just for testing */
954   glGenFramebuffersEXT(1, &MyFB);
955   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, MyFB);
956   glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &i);
957
958   /* Make texture object/image */
959   glGenTextures(1, &TexObj);
960   glBindTexture(GL_TEXTURE_2D, TexObj);
961   /* make one image level. */
962   glTexImage2D(GL_TEXTURE_2D, 0, TEXINTFORMAT, TexWidth, TexHeight, 0,
963		texFormats[texType], texTypes[texType], NULL);
964
965   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
966   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
967   glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
968   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
969   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
970
971   CheckError(__LINE__);
972
973   /* Render color to texture */
974   glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
975			     GL_TEXTURE_2D, TexObj, 0);
976   CheckError(__LINE__);
977
978
979   /* make depth renderbuffer */
980   glGenRenderbuffersEXT(1, &DepthRB);
981   glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, DepthRB);
982   /* we may have to try several formats */
983   for (i = 0; i < numDepthFormats; i++) {
984      glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, depthFormats[i],
985                               TexWidth, TexHeight);
986      CheckError(__LINE__);
987
988      glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
989                                   GL_RENDERBUFFER_EXT, DepthRB);
990      CheckError(__LINE__);
991      stat = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
992      if (stat == GL_FRAMEBUFFER_COMPLETE_EXT) {
993         break;
994      }
995   }
996
997   if (stat != GL_FRAMEBUFFER_COMPLETE_EXT) {
998      fprintf(stderr, "Error: unable to get usable FBO combination!\n");
999      exit(1);
1000   }
1001
1002   glGetRenderbufferParameterivEXT(GL_RENDERBUFFER_EXT,
1003                                   GL_RENDERBUFFER_DEPTH_SIZE_EXT, &i);
1004   CheckError(__LINE__);
1005   printf("Depth renderbuffer size = %d bits\n", i);
1006
1007   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
1008
1009   /*
1010    * Check for completeness.
1011    */
1012
1013}
1014
1015
1016static void
1017init(int argc, char *argv[])
1018{
1019   GLint i;
1020
1021   if (!glutExtensionSupported("GL_EXT_framebuffer_object")) {
1022      fprintf(stderr, "Sorry, GL_EXT_framebuffer_object is required!\n");
1023      exit(1);
1024   }
1025
1026   TexWidth = 512;
1027   TexHeight = 512;
1028
1029   init_fbotexture();
1030   init_fire(argc, argv);
1031
1032
1033   for ( i=1; i<argc; i++ ) {
1034      if (strcmp(argv[i], "-info")==0) {
1035	 printf("GL_RENDERER   = %s\n", (char *) glGetString(GL_RENDERER));
1036	 printf("GL_VERSION    = %s\n", (char *) glGetString(GL_VERSION));
1037	 printf("GL_VENDOR     = %s\n", (char *) glGetString(GL_VENDOR));
1038	 printf("GL_EXTENSIONS = %s\n", (char *) glGetString(GL_EXTENSIONS));
1039      }
1040   }
1041}
1042
1043
1044static void
1045visible(int vis)
1046{
1047   if (vis == GLUT_VISIBLE)
1048      glutIdleFunc(Anim ? idle : NULL);
1049   else
1050      glutIdleFunc(NULL);
1051}
1052
1053
1054int
1055main(int argc, char *argv[])
1056{
1057   glutInitWindowSize(WinWidth, WinHeight);
1058   glutInit(&argc, argv);
1059
1060   glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
1061
1062   Win = glutCreateWindow("fbo_firecube");
1063   glewInit();
1064   init(argc, argv);
1065
1066   glutDisplayFunc(draw);
1067   glutReshapeFunc(reshape);
1068   glutKeyboardFunc(key);
1069   glutSpecialFunc(special);
1070   glutVisibilityFunc(visible);
1071
1072   glutMainLoop();
1073   return 0;             /* ANSI C requires main to return int. */
1074}
1075