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