From http://www.jwz.org/xscreensaver/xscreensaver-5.38.tar.gz
[xscreensaver] / hacks / glx / glforestfire.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* fire --- 3D fire or rain landscape */
3
4 #if 0
5 static const char sccsid[] = "@(#)fire.c        5.02 2001/09/26 xlockmore";
6 #endif
7
8 /* Copyright (c) E. Lassauge, 2001. */
9
10 /*
11  * Permission to use, copy, modify, and distribute this software and its
12  * documentation for any purpose and without fee is hereby granted,
13  * provided that the above copyright notice appear in all copies and that
14  * both that copyright notice and this permission notice appear in
15  * supporting documentation.
16  *
17  * This file is provided AS IS with no warranties of any kind.  The author
18  * shall have no liability with respect to the infringement of copyrights,
19  * trade secrets or any patents by this file or any part thereof.  In no
20  * event will the author be liable for any lost revenue or profits or
21  * other special, indirect and consequential damages.
22  *
23  * The original code for this mode was written by David Bucciarelli 
24  * (tech.hmw@plus.it) and could be found in the demo package 
25  * of Mesa (Mesa-3.2/3Dfx/demos/). This mode is the result of the merge of
26  * two of the David's demos (fire and rain).
27  *
28  * Eric Lassauge  (October-10-2000) <lassauge@users.sourceforge.net>
29  *                                  http://lassauge.free.fr/linux.html
30  *
31  * REVISION HISTORY:
32  *
33  * E.Lassauge - 26-Sep-2001:
34  *      - add wander option and code 
35  *      - cleanups for xscreensaver
36  *
37  * E.Lassauge - 09-Mar-2001:
38  *      - get rid of my framerate options to use showfps
39  *
40  * E.Lassauge - 12-Jan-2001:
41  *      - add rain particules, selected if count=0 (no fire means rain !)
42  *
43  * E.Lassauge - 28-Nov-2000:
44  *      - modified release part to add freeing of GL objects
45  *
46  * E.Lassauge - 14-Nov-2000:
47  *      - use new common xpm_to_ximage function
48  *
49  * E.Lassauge - 25-Oct-2000:
50  *      - add the trees (with a new resource '-trees')
51  *      - corrected handling of color (textured vs untextured)
52  *      - corrected handling of endiannes for the xpm files
53  *      - inverted ground pixmap file
54  *      - use malloc-ed tree array
55  *
56  * TSchmidt - 23-Oct-2000:
57  *      - added size option like used in sproingies mode
58  *
59  * E.Lassauge - 13-Oct-2000:
60  *      - when trackmouse and window is iconified (login screen): stop tracking
61  *      - add pure GLX handling of framerate display (erased GLUT stuff)
62  *      - made count a per screen variable and update it only if framemode
63  *      - changes for no_texture an wireframe modes
64  *      - change no_texture color for the ground
65  *      - add freeing of texture image
66  *      - misc comments and little tweakings
67  *
68  * TODO:
69  *      - perhaps use a user supplied xpm for ground image (or a whatever image
70  *        file using ImageMagick ?)
71  *      - random number of trees ? change trees at change_fire ?
72  *      - fix wireframe mode: it's too CPU intensive.
73  *      - look how we can get the Wheel events (Button4&5).
74  */
75
76
77 #ifdef STANDALONE       /* xscreensaver mode */
78 #define DEFAULTS "*delay:     10000 \n" \
79                 "*count:        800 \n" \
80                 "*size:           0 \n" \
81                 "*showFPS:    False \n" \
82                 "*wireframe:  False \n" \
83
84 #define MODE_fire
85 #include "xlockmore.h"          /* from the xscreensaver distribution */
86 #include "gltrackball.h"
87 #else                           /* !STANDALONE */
88 #include "xlock.h"              /* from the xlockmore distribution */
89 #include "visgl.h"
90 #endif                          /* !STANDALONE */
91
92 #ifdef MODE_fire
93
94 #define MINSIZE 32
95
96 #if defined( USE_XPM ) || defined( USE_XPMINC ) || defined(STANDALONE)
97 /* USE_XPM & USE_XPMINC in xlock mode ; HAVE_XPM in xscreensaver mode */
98 #include "xpm-ximage.h"
99 #define I_HAVE_XPM
100
101 #ifdef STANDALONE
102 #include "../images/ground.xpm"
103 #include "../images/tree.xpm"
104 #else /* !STANDALONE */
105 #include "pixmaps/ground.xpm"
106 #include "pixmaps/tree.xpm"
107 #endif /* !STANDALONE */
108 #endif /* HAVE_XPM */
109
110 /* vector utility macros */
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 #define vadds(a,dt,b) {\
125   (a)[0]+=(dt)*(b)[0];\
126   (a)[1]+=(dt)*(b)[1];\
127   (a)[2]+=(dt)*(b)[2];\
128 }
129
130 #define vequ(a,b) {\
131   (a)[0]=(b)[0];\
132   (a)[1]=(b)[1];\
133   (a)[2]=(b)[2];\
134 }
135
136 #define vinter(a,dt,b,c) {\
137   (a)[0]=(dt)*(b)[0]+(1.0-dt)*(c)[0];\
138   (a)[1]=(dt)*(b)[1]+(1.0-dt)*(c)[1];\
139   (a)[2]=(dt)*(b)[2]+(1.0-dt)*(c)[2];\
140 }
141
142 #define clamp(a)        ((a) < 0.0 ? 0.0 : ((a) < 1.0 ? (a) : 1.0))
143
144 #define vclamp(v) {\
145   (v)[0]=clamp((v)[0]);\
146   (v)[1]=clamp((v)[1]);\
147   (v)[2]=clamp((v)[2]);\
148 }
149
150 /* Manage option vars */
151 #define DEF_TEXTURE     "True"
152 #define DEF_FOG         "False"
153 #define DEF_SHADOWS     "True"
154 #define DEF_FRAMERATE   "False"
155 #define DEF_WANDER      "True"
156 #define DEF_TREES       "5"
157 #define MAX_TREES       20
158 static Bool do_texture;
159 static Bool do_fog;
160 static Bool do_shadows;
161 static Bool do_wander;
162 static int num_trees;
163 static XFontStruct *mode_font = None;
164
165 static XrmOptionDescRec opts[] = {
166     {"-texture", ".fire.texture", XrmoptionNoArg, "on"},
167     {"+texture", ".fire.texture", XrmoptionNoArg, "off"},
168     {"-fog", ".fire.fog", XrmoptionNoArg, "on"},
169     {"+fog", ".fire.fog", XrmoptionNoArg, "off"},
170     {"-shadows", ".fire.shadows", XrmoptionNoArg, "on"},
171     {"+shadows", ".fire.shadows", XrmoptionNoArg, "off"},
172     {"-wander", ".fire.wander", XrmoptionNoArg, "on"},
173     {"+wander", ".fire.wander", XrmoptionNoArg, "off"},
174     {"-trees", ".fire.trees", XrmoptionSepArg, 0},
175     {"-rain", ".fire.count", XrmoptionNoArg, "0"},
176
177 };
178
179 static argtype vars[] = {
180     {&do_texture,    "texture",    "Texture",    DEF_TEXTURE,    t_Bool},
181     {&do_fog,        "fog",        "Fog",        DEF_FOG,        t_Bool},
182     {&do_shadows,    "shadows",    "Shadows",    DEF_SHADOWS,    t_Bool},
183     {&do_wander,     "wander",     "Wander",     DEF_WANDER,     t_Bool},
184     {&num_trees,     "trees",      "Trees",      DEF_TREES,      t_Int},
185 };
186
187 static OptionStruct desc[] = {
188     {"-/+texture", "turn on/off texturing"},
189     {"-/+fog", "turn on/off fog"},
190     {"-/+shadows", "turn on/off shadows"},
191     {"-/+wander", "turn on/off wandering"},
192     {"-trees num", "number of trees (0 disables)"},
193 };
194
195 ENTRYPOINT ModeSpecOpt fire_opts =
196  { sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc };
197
198 #ifdef USE_MODULES
199 ModStruct fire_description =
200     { "fire", "init_fire", "draw_fire", "release_fire",
201     "draw_fire", "change_fire", (char *) NULL, &fire_opts,
202     10000, 800, 1, 400, 64, 1.0, "",
203     "Shows a 3D fire-like image", 0, NULL
204 };
205 #endif /* USE_MODULES */
206
207 /* misc defines */
208 #define TREEINR         2.5     /* tree min distance */
209 #define TREEOUTR        8.0     /* tree max distance */
210 #define FRAME           50      /* frame count interval */
211 #define DIMP            20.0    /* dimension of ground */
212 #define DIMTP           16.0    /* dimension of ground texture */
213
214 #define RIDCOL          0.4     /* factor for color blending */
215
216 #define AGRAV           -9.8    /* gravity */
217
218 #define NUMPART         7500    /* rain particles */
219
220 /* fire particle struct */
221 typedef struct {
222     int age;
223     float p[3][3];
224     float v[3];
225     float c[3][4];
226 } part;
227
228 /* rain particle struct */
229 typedef struct {
230     float age;
231     float acc[3];
232     float vel[3];
233     float pos[3];
234     float partLength;
235     float oldpos[3];
236 } rain;
237
238 /* colors */
239 static const GLfloat black[3]    = { 0.0, 0.0, 0.0 }; /* shadow color */
240 static const GLfloat partcol1[3] = { 1.0, 0.2, 0.0 }; /* initial color: red-ish */
241 static const GLfloat partcol2[3] = { 1.0, 1.0, 0.0 }; /* blending color: yellow-ish */
242 static const GLfloat fogcolor[4] = { 0.9, 0.9, 1.0, 1.0 };
243
244 /* ground */
245 static const float q[4][3] = {
246     {-DIMP, 0.0, -DIMP},
247     {DIMP, 0.0, -DIMP},
248     {DIMP, 0.0, DIMP},
249     {-DIMP, 0.0, DIMP}
250 };
251
252 /* ground texture */
253 static const float qt[4][2] = {
254     {-DIMTP, -DIMTP},
255     {DIMTP, -DIMTP},
256     {DIMTP, DIMTP},
257     {-DIMTP, DIMTP}
258 };
259
260 /* default values for observer */
261 static const float DEF_OBS[3] = { 2.0f, 1.0f, 0.0f };
262 #define DEV_V           0.0
263 #define DEF_ALPHA       -90.0
264 #define DEF_BETA        90.0
265
266 /* tree struct */
267 typedef struct {
268     float x,y,z;
269 } treestruct;
270
271 /* the mode struct, contains all per screen variables */
272 typedef struct {
273     GLint WIDTH, HEIGHT;        /* display dimensions */
274     GLXContext *glx_context;
275
276     int np;                     /* number of fire particles : set it through 'count' resource */
277     float eject_r;              /* emission radius */
278     float dt, maxage, eject_vy, eject_vl;
279     float ridtri;               /* fire particle size */
280     Bool shadows;               /* misc booleans: set them through specific resources */
281     Bool fog;
282
283     part *p;                    /* fire particles array */
284     rain *r;                    /* rain particles array */
285
286     XImage *gtexture;           /* ground texture image bits */
287     XImage *ttexture;           /* tree texture image bits */
288     GLuint groundid;            /* ground texture id: GL world */
289     GLuint treeid;              /* tree texture id: GL world */
290     GLuint fontbase;            /* fontbase id: GL world */
291
292     int   num_trees;            /* number of trees: set it through 'trees' resource */
293     treestruct *treepos;        /* trees positions: float treepos[num_trees][3] */
294
295     float min[3];               /* raining area */
296     float max[3];
297
298     float obs[3];               /* observer coordinates */
299     float dir[3];               /* view direction */
300     float v;                    /* observer velocity */
301     float alpha;                /* observer angles */
302     float beta;
303
304     trackball_state *trackball;
305     Bool button_down_p;
306     int frame;
307
308 } firestruct;
309
310 /* array of firestruct indexed by screen number */
311 static firestruct *fire = (firestruct *) NULL;
312
313 /*
314  *-----------------------------------------------------------------------------
315  *-----------------------------------------------------------------------------
316  *    Misc funcs.
317  *-----------------------------------------------------------------------------
318  *-----------------------------------------------------------------------------
319  */
320
321 /* utility function for the rain particles */
322 static float gettimerain(void)
323 {
324 #if 0
325   /* Oh yeah, *that's* portable!  WTF. */
326   /* 
327    * I really thought clock() was standard ... EL
328    * I found this on the net:
329    * The clock() function conforms to ISO/IEC 9899:1990 (``ISO C89'') 
330    * */
331
332   static clock_t told= (clock_t)0;
333   clock_t tnew,ris;
334
335   tnew=clock();
336
337   ris=tnew-told;
338
339   told=tnew;
340
341   return (0.0125 + ris/(float)CLOCKS_PER_SEC);
342 #else
343   return 0.0150;
344 #endif
345 }
346
347 /* my RAND */
348 static float vrnd(void)
349 {
350     return ((float) LRAND() / (float) MAXRAND);
351 }
352
353 /* initialise new fire particle */
354 static void setnewpart(firestruct * fs, part * p)
355 {
356     float a, vi[3];
357     const float *c;
358
359     p->age = 0;
360
361     a = vrnd() * M_PI * 2.0;
362
363     vinit(vi, sin(a) * fs->eject_r * vrnd(), 0.15, cos(a) * fs->eject_r * vrnd());
364     vinit(p->p[0], vi[0] + vrnd() * fs->ridtri, vi[1] + vrnd() * fs->ridtri, vi[2] + vrnd() * fs->ridtri);
365     vinit(p->p[1], vi[0] + vrnd() * fs->ridtri, vi[1] + vrnd() * fs->ridtri, vi[2] + vrnd() * fs->ridtri);
366     vinit(p->p[2], vi[0] + vrnd() * fs->ridtri, vi[1] + vrnd() * fs->ridtri, vi[2] + vrnd() * fs->ridtri);
367
368     vinit(p->v, vi[0] * fs->eject_vl / (fs->eject_r / 2),
369           vrnd() * fs->eject_vy + fs->eject_vy / 2,
370           vi[2] * fs->eject_vl / (fs->eject_r / 2));
371
372     c = partcol1;
373
374     vinit4(p->c[0], c[0] * ((1.0 - RIDCOL) + vrnd() * RIDCOL),
375            c[1] * ((1.0 - RIDCOL) + vrnd() * RIDCOL),
376            c[2] * ((1.0 - RIDCOL) + vrnd() * RIDCOL), 1.0);
377     vinit4(p->c[1], c[0] * ((1.0 - RIDCOL) + vrnd() * RIDCOL),
378            c[1] * ((1.0 - RIDCOL) + vrnd() * RIDCOL),
379            c[2] * ((1.0 - RIDCOL) + vrnd() * RIDCOL), 1.0);
380     vinit4(p->c[2], c[0] * ((1.0 - RIDCOL) + vrnd() * RIDCOL),
381            c[1] * ((1.0 - RIDCOL) + vrnd() * RIDCOL),
382            c[2] * ((1.0 - RIDCOL) + vrnd() * RIDCOL), 1.0);
383 }
384
385 /* initialise new rain particle */
386 static void setnewrain(firestruct * fs, rain * r)
387 {
388     r->age=0.0f;
389
390     vinit(r->acc,0.0f,-0.98f,0.0f);
391     vinit(r->vel,0.0f,0.0f,0.0f);
392     
393     r->partLength=0.2f;
394
395     vinit(r->oldpos,fs->min[0]+(fs->max[0]-fs->min[0])*vrnd(),
396                     fs->max[1]+0.2f*fs->max[1]*vrnd(),
397                     fs->min[2]+(fs->max[2]-fs->min[2])*vrnd());
398     vequ(r->pos,r->oldpos);
399     vadds(r->oldpos,-(r->partLength),r->vel);
400
401     r->pos[1]=(fs->max[1]-fs->min[1])*vrnd()+fs->min[1];
402     r->oldpos[1]=r->pos[1]-r->partLength*r->vel[1];
403 }
404
405 /* set fire particle values */
406 static void setpart(firestruct * fs, part * p)
407 {
408     float fact;
409
410     if (p->p[0][1] < 0.1) {
411         setnewpart(fs, p);
412         return;
413     }
414
415     p->v[1] += AGRAV * fs->dt;
416
417     vadds(p->p[0], fs->dt, p->v);
418     vadds(p->p[1], fs->dt, p->v);
419     vadds(p->p[2], fs->dt, p->v);
420
421     p->age++;
422
423     if ((p->age) > fs->maxage) {
424         vequ(p->c[0], partcol2);
425         vequ(p->c[1], partcol2);
426         vequ(p->c[2], partcol2);
427     } else {
428         fact = 1.0 / fs->maxage;
429         vadds(p->c[0], fact, partcol2);
430         vclamp(p->c[0]);
431         p->c[0][3] = fact * (fs->maxage - p->age);
432
433         vadds(p->c[1], fact, partcol2);
434         vclamp(p->c[1]);
435         p->c[1][3] = fact * (fs->maxage - p->age);
436
437         vadds(p->c[2], fact, partcol2);
438         vclamp(p->c[2]);
439         p->c[2][3] = fact * (fs->maxage - p->age);
440     }
441 }
442
443 /* set rain particle values */
444 static void setpartrain(firestruct * fs, rain * r, float dt)
445 {
446     r->age += dt;
447
448     vadds(r->vel,dt,r->acc);
449     vadds(r->pos,dt,r->vel);
450
451     if(r->pos[0]<fs->min[0])
452         r->pos[0]=fs->max[0]-(fs->min[0]-r->pos[0]);
453     if(r->pos[2]<fs->min[2])
454         r->pos[2]=fs->max[2]-(fs->min[2]-r->pos[2]);
455
456     if(r->pos[0]>fs->max[0])
457         r->pos[0]=fs->min[0]+(r->pos[0]-fs->max[0]);
458     if(r->pos[2]>fs->max[2])
459         r->pos[2]=fs->min[2]+(r->pos[2]-fs->max[2]);
460
461     vequ(r->oldpos,r->pos);
462     vadds(r->oldpos,-(r->partLength),r->vel);
463     if(r->pos[1]<fs->min[1])
464         setnewrain(fs, r);
465 }
466
467 /* draw a tree */
468 static int drawtree(float x, float y, float z)
469 {
470     int polys = 0;
471     glBegin(GL_QUADS);
472     glTexCoord2f(0.0,0.0);
473     glVertex3f(x-1.5,y+0.0,z);
474
475     glTexCoord2f(1.0,0.0);
476     glVertex3f(x+1.5,y+0.0,z);
477
478     glTexCoord2f(1.0,1.0);
479     glVertex3f(x+1.5,y+3.0,z);
480
481     glTexCoord2f(0.0,1.0);
482     glVertex3f(x-1.5,y+3.0,z);
483     polys++;
484
485
486     glTexCoord2f(0.0,0.0);
487     glVertex3f(x,y+0.0,z-1.5);
488
489     glTexCoord2f(1.0,0.0);
490     glVertex3f(x,y+0.0,z+1.5);
491
492     glTexCoord2f(1.0,1.0);
493     glVertex3f(x,y+3.0,z+1.5);
494
495     glTexCoord2f(0.0,1.0);
496     glVertex3f(x,y+3.0,z-1.5);
497     polys++;
498
499     glEnd();
500
501     return polys;
502 }
503
504 /* calculate observer position : modified only if trackmouse is used */
505 static void calcposobs(firestruct * fs)
506 {
507     fs->dir[0] = sin(fs->alpha * M_PI / 180.0);
508     fs->dir[2] =
509         cos(fs->alpha * M_PI / 180.0) * sin(fs->beta * M_PI / 180.0);
510     fs->dir[1] = cos(fs->beta * M_PI / 180.0);
511
512     fs->obs[0] += fs->v * fs->dir[0];
513     fs->obs[1] += fs->v * fs->dir[1];
514     fs->obs[2] += fs->v * fs->dir[2];
515
516     if (!fs->np)
517     {
518         vinit(fs->min,fs->obs[0]-7.0f,-0.2f,fs->obs[2]-7.0f);
519         vinit(fs->max,fs->obs[0]+7.0f,8.0f,fs->obs[2]+7.0f);
520     }
521 }
522
523
524 /* initialise textures */
525 static void inittextures(ModeInfo * mi)
526 {
527     firestruct *fs = &fire[MI_SCREEN(mi)];
528
529 #if defined( I_HAVE_XPM )
530     if (do_texture) {
531
532         glGenTextures(1, &fs->groundid);
533 #ifdef HAVE_GLBINDTEXTURE
534         glBindTexture(GL_TEXTURE_2D, fs->groundid);
535 #endif /* HAVE_GLBINDTEXTURE */
536
537         if ((fs->gtexture = xpm_to_ximage(MI_DISPLAY(mi), MI_VISUAL(mi),
538                          MI_COLORMAP(mi), ground)) == None) {
539             (void) fprintf(stderr, "Error reading the ground texture.\n");
540             glDeleteTextures(1, &fs->groundid);
541             do_texture = False;
542             fs->groundid = 0;
543             fs->treeid   = 0;
544             return;
545         }
546
547         glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
548     clear_gl_error();
549         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
550                  fs->gtexture->width, fs->gtexture->height, 0,
551                  GL_RGBA,
552                  /* GL_UNSIGNED_BYTE, */
553                  GL_UNSIGNED_INT_8_8_8_8_REV,
554                  fs->gtexture->data);
555     check_gl_error("texture");
556
557         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
558         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
559
560         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
561         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
562
563         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
564
565         if (fs->num_trees)
566         {
567             glGenTextures(1, &fs->treeid);
568 #ifdef HAVE_GLBINDTEXTURE
569             glBindTexture(GL_TEXTURE_2D,fs->treeid);
570 #endif /* HAVE_GLBINDTEXTURE */
571             if ((fs->ttexture = xpm_to_ximage(MI_DISPLAY(mi), MI_VISUAL(mi),
572                          MI_COLORMAP(mi), tree)) == None) {
573               (void)fprintf(stderr,"Error reading tree texture.\n");
574               glDeleteTextures(1, &fs->treeid);
575               fs->treeid    = 0;
576               fs->num_trees = 0; 
577               return;
578             }
579
580         clear_gl_error();
581             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
582                      fs->ttexture->width, fs->ttexture->height, 0,
583                      GL_RGBA,
584                      /* GL_UNSIGNED_BYTE, */
585                      GL_UNSIGNED_INT_8_8_8_8_REV,
586                      fs->ttexture->data);
587         check_gl_error("texture");
588
589             glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
590             glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
591
592             glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
593             glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
594
595             glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
596         }
597     }
598     else
599     {
600         fs->groundid = 0;       /* default textures */
601         fs->treeid   = 0;
602     }
603 #else /* !I_HAVE_XPM */
604   do_texture = False;
605   fs->groundid = 0;       /* default textures */
606   fs->treeid = 0;
607 #endif /* !I_HAVE_XPM */
608 }
609
610 /* init tree array and positions */
611 static Bool inittree(ModeInfo * mi)
612 {
613     firestruct *fs = &fire[MI_SCREEN(mi)];
614     int i;
615     float dist;
616
617     /* allocate treepos array */
618     if ((fs->treepos = (treestruct *) malloc(fs->num_trees *
619                                         sizeof(treestruct))) == NULL) {
620                 return False;
621     }
622     /* initialise positions */
623     for(i=0;i<fs->num_trees;i++) {
624       do {
625         fs->treepos[i].x =vrnd()*TREEOUTR*2.0-TREEOUTR;
626         fs->treepos[i].y =0.0;
627         fs->treepos[i].z =vrnd()*TREEOUTR*2.0-TREEOUTR;
628         dist = sqrt(fs->treepos[i].x * fs->treepos[i].x +
629                     fs->treepos[i].z * fs->treepos[i].z);
630       } while((dist<TREEINR) || (dist>TREEOUTR));
631     }
632         return True;
633 }
634
635 /*
636  *-----------------------------------------------------------------------------
637  *-----------------------------------------------------------------------------
638  *    GL funcs.
639  *-----------------------------------------------------------------------------
640  *-----------------------------------------------------------------------------
641  */
642
643 ENTRYPOINT void reshape_fire(ModeInfo * mi, int width, int height)
644 {
645
646     firestruct *fs = &fire[MI_SCREEN(mi)];
647     int size = MI_SIZE(mi);
648
649     /* Viewport is specified size if size >= MINSIZE && size < screensize */
650     if (size <= 1) {
651         fs->WIDTH = MI_WIDTH(mi);
652         fs->HEIGHT = MI_HEIGHT(mi);
653     } else if (size < MINSIZE) {
654         fs->WIDTH = MINSIZE;
655         fs->HEIGHT = MINSIZE;
656     } else {
657         fs->WIDTH = (size > MI_WIDTH(mi)) ? MI_WIDTH(mi) : size;
658         fs->HEIGHT = (size > MI_HEIGHT(mi)) ? MI_HEIGHT(mi) : size;
659     }
660     glViewport((MI_WIDTH(mi) - fs->WIDTH) / 2, (MI_HEIGHT(mi) - fs->HEIGHT) / 2, fs->WIDTH, fs->HEIGHT);
661     glMatrixMode(GL_PROJECTION);
662     glLoadIdentity();
663     gluPerspective(70.0, fs->WIDTH / (float) fs->HEIGHT, 0.1, 30.0);
664
665     glMatrixMode(GL_MODELVIEW);
666
667 }
668
669 static void DrawFire(ModeInfo * mi)
670 {
671     int j;
672     firestruct *fs = &fire[MI_SCREEN(mi)];
673     Bool wire = MI_IS_WIREFRAME(mi);
674
675     mi->polygon_count = 0;
676
677     if (do_wander && !fs->button_down_p)
678     {
679         GLfloat x, y, z;
680
681 #       define SINOID(SCALE,SIZE) \
682         ((((1 + sin((fs->frame * (SCALE)) / 2 * M_PI)) / 2.0) * (SIZE)) - (SIZE)/2)
683
684         x = SINOID(0.031, 0.85);
685         y = SINOID(0.017, 0.25);
686         z = SINOID(0.023, 0.85);
687         fs->frame++;
688         fs->obs[0] = x + DEF_OBS[0];
689         fs->obs[1] = y + DEF_OBS[1];
690         fs->obs[2] = z + DEF_OBS[2];
691         fs->dir[1] = y;
692         fs->dir[2] = z;
693     }
694
695     glEnable(GL_DEPTH_TEST);
696
697     if (fs->fog)
698         glEnable(GL_FOG);
699     else
700         glDisable(GL_FOG);
701
702     glDepthMask(GL_TRUE);
703     glClearColor(0.5, 0.5, 0.8, 1.0);   /* sky in the distance */
704     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
705
706     glPushMatrix();
707
708     calcposobs(fs);
709
710     gltrackball_rotate (fs->trackball);
711
712     gluLookAt(fs->obs[0], fs->obs[1], fs->obs[2],
713               fs->obs[0] + fs->dir[0], 
714               fs->obs[1] + fs->dir[1],
715               fs->obs[2] + fs->dir[2],
716               0.0, 1.0, 0.0);
717
718     glEnable(GL_TEXTURE_2D);
719
720     /* draw ground using the computed texture */
721     if (do_texture) {
722         glColor4f(1.0,1.0,1.0,1.0);     /* white to get texture in it's true color */
723 #ifdef HAVE_GLBINDTEXTURE
724         glBindTexture(GL_TEXTURE_2D, fs->groundid);
725 #endif /* HAVE_GLBINDTEXTURE */
726     }
727     else
728         glColor4f(0.54, 0.27, 0.07, 1.0);       /* untextured ground color */
729     glBegin(GL_QUADS);
730     glTexCoord2fv(qt[0]);
731     glVertex3fv(q[0]);
732     glTexCoord2fv(qt[1]);
733     glVertex3fv(q[1]);
734     glTexCoord2fv(qt[2]);
735     glVertex3fv(q[2]);
736     glTexCoord2fv(qt[3]);
737     glVertex3fv(q[3]);
738     mi->polygon_count++;
739     glEnd();
740
741     glAlphaFunc(GL_GEQUAL, 0.9);
742     if (fs->num_trees)
743     {
744         /* here do_texture IS True - and color used is white */
745         glEnable(GL_ALPHA_TEST);
746 #ifdef HAVE_GLBINDTEXTURE
747         glBindTexture(GL_TEXTURE_2D,fs->treeid);
748 #endif /* HAVE_GLBINDTEXTURE */
749         for(j=0;j<fs->num_trees;j++)
750       mi->polygon_count += drawtree(fs->treepos[j].x ,fs->treepos[j].y ,fs->treepos[j].z );
751         glDisable(GL_ALPHA_TEST);
752     }
753     glDisable(GL_TEXTURE_2D);
754     glDepthMask(GL_FALSE);
755
756     if (fs->shadows) {
757         /* draw shadows with black color */
758         glBegin(wire ? GL_LINE_STRIP : GL_TRIANGLES);
759         for (j = 0; j < fs->np; j++) {
760             glColor4f(black[0], black[1], black[2], fs->p[j].c[0][3]);
761             glVertex3f(fs->p[j].p[0][0], 0.1, fs->p[j].p[0][2]);
762
763             glColor4f(black[0], black[1], black[2], fs->p[j].c[1][3]);
764             glVertex3f(fs->p[j].p[1][0], 0.1, fs->p[j].p[1][2]);
765
766             glColor4f(black[0], black[1], black[2], fs->p[j].c[2][3]);
767             glVertex3f(fs->p[j].p[2][0], 0.1, fs->p[j].p[2][2]);
768         mi->polygon_count++;
769         }
770         glEnd();
771     }
772
773     glBegin(wire ? GL_LINE_STRIP : GL_TRIANGLES);
774     for (j = 0; j < fs->np; j++) {
775         /* draw particles: colors are computed in setpart */
776         glColor4fv(fs->p[j].c[0]);
777         glVertex3fv(fs->p[j].p[0]);
778
779         glColor4fv(fs->p[j].c[1]);
780         glVertex3fv(fs->p[j].p[1]);
781
782         glColor4fv(fs->p[j].c[2]);
783         glVertex3fv(fs->p[j].p[2]);
784     mi->polygon_count++;
785
786         setpart(fs, &fs->p[j]);
787     }
788     glEnd();
789
790     /* draw rain particles if no fire particles */
791     if (!fs->np)
792     {
793         float timeused = gettimerain();
794         glDisable(GL_TEXTURE_2D);
795         glShadeModel(GL_SMOOTH);
796         glBegin(GL_LINES);
797         for (j = 0; j < NUMPART; j++) {
798             glColor4f(0.7f,0.95f,1.0f,0.0f);
799             glVertex3fv(fs->r[j].oldpos);
800             glColor4f(0.3f,0.7f,1.0f,1.0f);
801             glVertex3fv(fs->r[j].pos);
802             setpartrain(fs, &fs->r[j],timeused);
803         mi->polygon_count++;
804         }
805         glEnd();
806         glShadeModel(GL_FLAT);
807     }
808
809     glDisable(GL_TEXTURE_2D);
810     glDisable(GL_ALPHA_TEST);
811     glDisable(GL_DEPTH_TEST);
812     glDisable(GL_FOG);
813
814     /* manage framerate display */
815     if (MI_IS_FPS(mi)) do_fps (mi);
816     glPopMatrix();
817 }
818
819
820 static Bool Init(ModeInfo * mi)
821 {
822     int i;
823     firestruct *fs = &fire[MI_SCREEN(mi)];
824
825     /* default settings */
826     fs->eject_r = 0.1 + NRAND(10) * 0.03;
827     fs->dt = 0.015;
828     fs->eject_vy = 4;
829     fs->eject_vl = 1;
830     fs->ridtri = 0.1 + NRAND(10) * 0.005;
831     fs->maxage = 1.0 / fs->dt;
832     vinit(fs->obs, DEF_OBS[0], DEF_OBS[1], DEF_OBS[2]);
833     fs->v = 0.0;
834     fs->alpha = DEF_ALPHA;
835     fs->beta = DEF_BETA;
836
837     /* initialise texture stuff */
838     if (do_texture)
839         inittextures(mi);
840     else
841     {
842         fs->ttexture = (XImage*) NULL;
843         fs->gtexture = (XImage*) NULL;
844     }
845
846     if (MI_IS_DEBUG(mi)) {
847         (void) fprintf(stderr,
848                        "%s:\n\tnum_part=%d\n\ttrees=%d\n\tfog=%s\n\tshadows=%s\n\teject_r=%.3f\n\tridtri=%.3f\n",
849                        MI_NAME(mi),
850                        fs->np,
851                        fs->num_trees,
852                        fs->fog ? "on" : "off",
853                        fs->shadows ? "on" : "off",
854                        fs->eject_r, fs->ridtri);
855     }
856
857     /* initialise particles and trees */
858     for (i = 0; i < fs->np; i++) {
859         setnewpart(fs, &(fs->p[i]));
860     }
861
862     if (fs->num_trees)
863         if (!inittree(mi)) {
864                 return False;
865         }
866
867     /* if no fire particles then initialise rain particles */
868     if (!fs->np)
869     {
870         vinit(fs->min,-7.0f,-0.2f,-7.0f);
871         vinit(fs->max,7.0f,8.0f,7.0f);
872         for (i = 0; i < NUMPART; i++) {
873             setnewrain(fs, &(fs->r[i]));
874         }
875     }
876     
877     return True;
878 }
879
880 /*
881  *-----------------------------------------------------------------------------
882  *-----------------------------------------------------------------------------
883  *    Xlock hooks.
884  *-----------------------------------------------------------------------------
885  *-----------------------------------------------------------------------------
886  */
887
888
889 ENTRYPOINT void
890 free_fire(ModeInfo * mi)
891 {
892         firestruct *fs = &fire[MI_SCREEN(mi)];
893
894         if (mode_font != None && fs->fontbase != None) {
895                 glDeleteLists(fs->fontbase, mode_font->max_char_or_byte2 -
896                         mode_font->min_char_or_byte2 + 1);
897                 fs->fontbase = None;
898         }
899
900         if (fs->p != NULL) {
901                 (void) free((void *) fs->p);
902                 fs->p = (part *) NULL;
903         }
904         if (fs->r != NULL) {
905                 (void) free((void *) fs->r);
906                 fs->r = (rain *) NULL;
907         }
908         if (fs->treepos != NULL) {
909                 (void) free((void *) fs->treepos);
910                 fs->treepos = (treestruct *) NULL;
911         }
912         if (fs->ttexture != None) {
913                 glDeleteTextures(1, &fs->treeid);
914                 XDestroyImage(fs->ttexture);
915                 fs->ttexture = None;
916         }
917         if (fs->gtexture != None) {
918                 glDeleteTextures(1, &fs->groundid);
919                 XDestroyImage(fs->gtexture);
920                 fs->gtexture = None;
921         }
922 }
923
924 /*
925  *-----------------------------------------------------------------------------
926  *    Initialize fire.  Called each time the window changes.
927  *-----------------------------------------------------------------------------
928  */
929
930 ENTRYPOINT void
931 init_fire(ModeInfo * mi)
932 {
933     firestruct *fs;
934
935     MI_INIT (mi, fire);
936     fs = &fire[MI_SCREEN(mi)];
937     fs->np = MI_COUNT(mi);
938     fs->fog = do_fog;
939     fs->shadows = do_shadows;
940     /* initialise fire particles if any */
941     if ((fs->np)&&(fs->p == NULL)) {
942         if ((fs->p = (part *) calloc(fs->np, sizeof(part))) == NULL) {
943             free_fire(mi);
944             return;
945         }
946     }
947     else if (fs->r == NULL) {
948         /* initialise rain particles if no fire particles */
949         if ((fs->r = (rain *) calloc(NUMPART, sizeof(part))) == NULL) {
950             free_fire(mi);
951             return;
952         }
953     }
954
955     /* check tree number */
956     if (do_texture)
957         fs->num_trees = (num_trees<MAX_TREES)?num_trees:MAX_TREES;
958     else
959         fs->num_trees = 0;
960
961     fs->trackball = gltrackball_init (False);
962
963     /* xlock GL stuff */
964     if ((fs->glx_context = init_GL(mi)) != NULL) {
965
966 #ifndef STANDALONE
967         Reshape(mi); /* xlock mode */
968 #else
969         reshape_fire(mi,MI_WIDTH(mi),MI_HEIGHT(mi)); /* xscreensaver mode */
970 #endif
971         glDrawBuffer(GL_BACK);
972         if (!Init(mi)) {
973                 free_fire(mi);
974                 return;
975         }
976     } else {
977         MI_CLEARWINDOW(mi);
978     }
979 }
980
981 /*
982  *-----------------------------------------------------------------------------
983  *    Called by the mainline code periodically to update the display.
984  *-----------------------------------------------------------------------------
985  */
986 ENTRYPOINT void draw_fire(ModeInfo * mi)
987 {
988     firestruct *fs = &fire[MI_SCREEN(mi)];
989
990     Display *display = MI_DISPLAY(mi);
991     Window window = MI_WINDOW(mi);
992
993     MI_IS_DRAWN(mi) = True;
994
995     if (!fs->glx_context)
996         return;
997
998     glXMakeCurrent(display, window, *(fs->glx_context));
999
1000     glShadeModel(GL_FLAT);
1001     glEnable(GL_DEPTH_TEST);
1002
1003     /* makes particles blend with background */
1004     if (!MI_IS_WIREFRAME(mi)||(!fs->np))
1005     {
1006         glEnable(GL_BLEND);
1007         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1008     }
1009
1010     /* fog stuff */
1011     glEnable(GL_FOG);
1012     glFogi(GL_FOG_MODE, GL_EXP);
1013     glFogfv(GL_FOG_COLOR, fogcolor);
1014     glFogf(GL_FOG_DENSITY, 0.03);
1015     glHint(GL_FOG_HINT, GL_NICEST);
1016
1017     glPushMatrix();
1018     glRotatef(current_device_rotation(), 0, 0, 1);
1019     DrawFire(mi);
1020     glPopMatrix();
1021 #ifndef STANDALONE
1022     Reshape(mi); /* xlock mode */
1023 #else
1024     reshape_fire(mi,MI_WIDTH(mi),MI_HEIGHT(mi)); /* xscreensaver mode */
1025 #endif
1026
1027     glFinish();
1028     glXSwapBuffers(display, window);
1029 }
1030
1031
1032 /*
1033  *-----------------------------------------------------------------------------
1034  *    The display is being taken away from us.  Free up malloc'ed
1035  *      memory and X resources that we've alloc'ed.  Only called
1036  *      once, we must zap everything for every screen.
1037  *-----------------------------------------------------------------------------
1038  */
1039
1040 ENTRYPOINT void release_fire(ModeInfo * mi)
1041 {
1042     if (mode_font != None)
1043     {
1044         /* only free-ed when there are no more screens used */
1045         XFreeFont(MI_DISPLAY(mi), mode_font);
1046         mode_font = None;
1047     }
1048     FreeAllGL(mi);
1049 }
1050
1051 ENTRYPOINT Bool
1052 fire_handle_event (ModeInfo *mi, XEvent *event)
1053 {
1054   firestruct *fs = &fire[MI_SCREEN(mi)];
1055
1056   if (gltrackball_event_handler (event, fs->trackball,
1057                                  MI_WIDTH (mi), MI_HEIGHT (mi),
1058                                  &fs->button_down_p))
1059     return True;
1060
1061   return False;
1062 }
1063
1064
1065 #ifndef STANDALONE
1066 ENTRYPOINT void change_fire(ModeInfo * mi)
1067 {
1068     firestruct *fs = &fire[MI_SCREEN(mi)];
1069
1070     if (!fs->glx_context)
1071         return;
1072
1073     glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(fs->glx_context));
1074
1075     /* if available, randomly change some values */
1076     if (do_fog)
1077         fs->fog = LRAND() & 1;
1078     if (do_shadows)
1079         fs->shadows = LRAND() & 1;
1080     /* reset observer position */
1081     frame = 0;
1082     vinit(fs->obs, DEF_OBS[0], DEF_OBS[1], DEF_OBS[2]);
1083     fs->v = 0.0;
1084     /* particle randomisation */
1085     fs->eject_r = 0.1 + NRAND(10) * 0.03;
1086     fs->ridtri = 0.1 + NRAND(10) * 0.005;
1087
1088     if (MI_IS_DEBUG(mi)) {
1089         (void) fprintf(stderr,
1090                        "%s:\n\tnum_part=%d\n\ttrees=%d\n\tfog=%s\n\tshadows=%s\n\teject_r=%.3f\n\tridtri=%.3f\n",
1091                        MI_NAME(mi),
1092                        fs->np,
1093                        fs->num_trees,
1094                        fs->fog ? "on" : "off",
1095                        fs->shadows ? "on" : "off",
1096                        fs->eject_r, fs->ridtri);
1097     }
1098 }
1099 #endif /* !STANDALONE */
1100
1101 XSCREENSAVER_MODULE_2 ("GLForestFire", glforestfire, fire)
1102
1103 #endif /* MODE_fire */