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