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