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