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