1 /* lavalite --- 3D Simulation a Lava Lite, written by jwz.
2  *
3  * This software Copyright (c) 2002-2017 Jamie Zawinski <jwz@jwz.org>
4  *
5  * Permission to use, copy, modify, distribute, and sell this software and its
6  * documentation for any purpose is hereby granted without fee, provided that
7  * the above copyright notice appear in all copies and that both that
8  * copyright notice and this permission notice appear in supporting
9  * documentation.  No representations are made about the suitability of this
10  * software for any purpose.  It is provided "as is" without express or
11  * implied warranty.
12  *
13  * LAVA�, LAVA LITE�, LAVA WORLD INTERNATIONAL� and the configuration of the
14  * LAVA� brand motion lamp are registered trademarks of Haggerty Enterprises,
15  * Inc.  The configuration of the globe and base of the motion lamp are
16  * registered trademarks of Haggerty Enterprises, Inc. in the U.S.A. and in
17  * other countries around the world.
18  *
19  * Official Lava Lite web site: http://www.lavaworld.com/
20  *
21  * Implementation details:
22  *
23  * The blobs are generated using metaballs.  For an explanation of what
24  * those are, see http://astronomy.swin.edu.au/~pbourke/modelling/implicitsurf/
25  * or http://www.fifi.org/doc/povray-manual/pov30005.htm
26  *
27  * Basically, each bubble of lava is a few (4) overlapping spherical metaballs
28  * of various sizes following similar but slightly different steep, slow
29  * parabolic arcs from the bottom to the top and back.
30  *
31  * We then polygonize the surface of the lava using the marching squares
32  * algorithm implemented in marching.c.
33  *
34  * Like real lavalites, this program is very slow.
35  *
36  * Surprisingly, it's loading the CPU and not the graphics engine: the speed
37  * bottleneck is in *computing* the scene rather than *rendering* it.  We
38  * actually don't use a huge number of polygons, but computing the mesh for
39  * the surface takes a lot of cycles.
40  *
41  * I eliminated all the square roots, but there is still a lot of
42  * floating-point multiplication in here.  I tried optimizing away the
43  * fp divisions, but that didn't seem to make a difference.
44  *
45  *     -style            lamp shape: classic, giant, cone, rocket, or random.
46  *     -speed            frequency at which new blobs launch.
47  *     -resolution       density of the polygon mesh.
48  *     -count            max number of blobs allowed at once.
49  *     -wander, -spin    whether to roll the scene around the screen.
50  *     -lava-color       color of the blobbies
51  *     -fluid-color      color of the stuff the blobbies float in
52  *     -base-color       color of the base of the lamp
53  *     -table-color      color of the table under the lamp
54  *     -impatient        at startup, skip forward in the animation so
55  *                       that at least one blob is fully exposed.
56  *
57  * TODO:
58  *
59  *    - make the table look better, somehow.
60  *    - should the lava be emissive?  should the one at the bottom be
61  *      more brightly colored than the ones at the top?  light distance?
62  *    - is there some way to put a specular reflection on the front glass
63  *      itself?  Maybe render it twice with FRONT/BACK tweaked, or alpha
64  *      with depth buffering turned off?
65  */
66 
67 #define DEFAULTS	"*delay:	30000       \n" \
68 			"*showFPS:      False       \n" \
69 			"*wireframe:    False       \n" \
70 			"*geometry:	600x900\n"      \
71 			"*count:      " DEF_COUNT " \n" \
72 
73 # define release_lavalite 0
74 
75 
76 #define BLOBS_PER_GROUP 4
77 
78 #define GRAVITY         0.000013    /* odwnward acceleration */
79 #define CONVECTION      0.005       /* initial upward velocity (bell curve) */
80 #define TILT            0.00166666  /* horizontal velocity (bell curve) */
81 
82 #undef countof
83 #define countof(x) (sizeof((x))/sizeof((*x)))
84 
85 #undef ABS
86 #define ABS(n) ((n)<0?-(n):(n))
87 #undef SIGNOF
88 #define SIGNOF(n) ((n)<0?-1:1)
89 
90 #include "xlockmore.h"
91 #include "marching.h"
92 #include "rotator.h"
93 #include "gltrackball.h"
94 #include "ximage-loader.h"
95 #include <ctype.h>
96 
97 #ifdef USE_GL /* whole file */
98 
99 
100 #define DEF_SPIN        "Z"
101 #define DEF_WANDER      "False"
102 #define DEF_SPEED       "0.003"
103 #define DEF_RESOLUTION  "40"
104 #define DEF_SMOOTH      "True"
105 #define DEF_COUNT       "3"
106 #define DEF_STYLE       "random"
107 #define DEF_IMPATIENT   "False"
108 #define DEF_LCOLOR	"#FF0000" /* lava */
109 #define DEF_FCOLOR	"#00AAFF" /* fluid */
110 #define DEF_BCOLOR	"#666666" /* base */
111 #define DEF_TCOLOR	"#000000" /*"#00FF00"*/ /* table */
112 
113 #define DEF_FTEX	"(none)"
114 #define DEF_BTEX	"(none)"
115 #define DEF_TTEX	"(none)"
116 
117 typedef struct metaball metaball;
118 
119 struct metaball {
120 
121   Bool alive_p;
122   Bool static_p;
123 
124   double r;		/* hard radius */
125   double R;		/* radius of field of influence */
126 
127   double z;		/* vertical position */
128   double pos_r;		/* position on horizontal circle */
129   double pos_th;	/* position on horizontal circle */
130   double dr, dz;	/* current velocity */
131 
132   double x, y;		/* h planar position - compused from the above */
133 
134   metaball *leader;	/* stay close to this other ball */
135 };
136 
137 
138 typedef enum { CLASSIC = 0, GIANT, CONE, ROCKET } lamp_style;
139 typedef enum { CAP = 100, BOTTLE, BASE } lamp_part;
140 
141 typedef struct {
142   lamp_part part;
143   GLfloat elevation;
144   GLfloat radius;
145   GLfloat texture_elevation;
146 } lamp_geometry;
147 
148 static const lamp_geometry classic_lamp[] = {
149   { CAP,    1.16, 0.089, 0.00 },
150   { BOTTLE, 0.97, 0.120, 0.40 },
151   { BOTTLE, 0.13, 0.300, 0.87 },
152   { BOTTLE, 0.07, 0.300, 0.93 },
153   { BASE,   0.00, 0.280, 0.00 },
154   { BASE,  -0.40, 0.120, 0.50 },
155   { BASE,  -0.80, 0.280, 1.00 },
156   { 0, 0, 0, 0 },
157 };
158 
159 static const lamp_geometry giant_lamp[] = {
160   { CAP,    1.12, 0.105, 0.00 },
161   { BOTTLE, 0.97, 0.130, 0.30 },
162   { BOTTLE, 0.20, 0.300, 0.87 },
163   { BOTTLE, 0.15, 0.300, 0.93 },
164   { BASE,   0.00, 0.230, 0.00 },
165   { BASE,  -0.18, 0.140, 0.20 },
166   { BASE,  -0.80, 0.280, 1.00 },
167   { 0, 0, 0, 0 },
168 };
169 
170 static const lamp_geometry cone_lamp[] = {
171   { CAP,    1.35, 0.001, 0.00 },
172   { CAP,    1.35, 0.020, 0.00 },
173   { CAP,    1.30, 0.055, 0.05 },
174   { BOTTLE, 0.97, 0.120, 0.40 },
175   { BOTTLE, 0.13, 0.300, 0.87 },
176   { BASE,   0.00, 0.300, 0.00 },
177   { BASE,  -0.04, 0.320, 0.04 },
178   { BASE,  -0.60, 0.420, 0.50 },
179   { 0, 0, 0, 0 },
180 };
181 
182 static const lamp_geometry rocket_lamp[] = {
183   { CAP,    1.35, 0.001, 0.00 },
184   { CAP,    1.34, 0.020, 0.00 },
185   { CAP,    1.30, 0.055, 0.05 },
186   { BOTTLE, 0.97, 0.120, 0.40 },
187   { BOTTLE, 0.13, 0.300, 0.87 },
188   { BOTTLE, 0.07, 0.300, 0.93 },
189   { BASE,   0.00, 0.280, 0.00 },
190   { BASE,  -0.50, 0.180, 0.50 },
191   { BASE,  -0.75, 0.080, 0.75 },
192   { BASE,  -0.80, 0.035, 0.80 },
193   { BASE,  -0.90, 0.035, 1.00 },
194   { 0, 0, 0, 0 },
195 };
196 
197 
198 
199 typedef struct {
200   GLXContext *glx_context;
201   lamp_style style;
202   const lamp_geometry *model;
203   rotator *rot;
204   rotator *rot2;
205   trackball_state *trackball;
206   Bool button_down_p;
207 
208   GLfloat max_bottle_radius;	   /* radius of widest part of the bottle */
209 
210   GLfloat launch_chance;	   /* how often to percolate */
211   int blobs_per_group;		   /* how many metaballs we launch at once */
212   Bool just_started_p;		   /* so we launch some goo right away */
213 
214   int grid_size;		   /* resolution for marching-cubes */
215   int nballs;
216   metaball *balls;
217 
218   GLuint bottle_list;
219   GLuint ball_list;
220 
221   int bottle_poly_count;	   /* polygons in the bottle only */
222 
223 } lavalite_configuration;
224 
225 static lavalite_configuration *bps = NULL;
226 
227 static char *do_spin;
228 static char *do_style;
229 static GLfloat speed;
230 static Bool do_wander;
231 static int resolution;
232 static Bool do_smooth;
233 static Bool do_impatient;
234 
235 static char *lava_color_str, *fluid_color_str, *base_color_str,
236   *table_color_str;
237 static char *fluid_tex, *base_tex, *table_tex;
238 
239 static GLfloat lava_color[4], fluid_color[4], base_color[4], table_color[4];
240 
241 static const GLfloat lava_spec[4] = {1.0, 1.0, 1.0, 1.0};
242 static const GLfloat lava_shininess = 128.0;
243 static const GLfloat foot_color[4] = {0.2, 0.2, 0.2, 1.0};
244 
245 static const GLfloat light0_pos[4] = {-0.6, 0.0, 1.0, 0.0};
246 static const GLfloat light1_pos[4] = { 1.0, 0.0, 0.2, 0.0};
247 static const GLfloat light2_pos[4] = { 0.6, 0.0, 1.0, 0.0};
248 
249 
250 
251 static XrmOptionDescRec opts[] = {
252   { "-style",  ".style",  XrmoptionSepArg, 0 },
253   { "-spin",   ".spin",   XrmoptionSepArg, 0 },
254   { "+spin",   ".spin",   XrmoptionNoArg, "" },
255   { "-speed",  ".speed",  XrmoptionSepArg, 0 },
256   { "-wander", ".wander", XrmoptionNoArg, "True" },
257   { "+wander", ".wander", XrmoptionNoArg, "False" },
258   { "-resolution", ".resolution", XrmoptionSepArg, 0 },
259   { "-smooth", ".smooth", XrmoptionNoArg, "True" },
260   { "+smooth", ".smooth", XrmoptionNoArg, "False" },
261   { "-impatient", ".impatient", XrmoptionNoArg, "True" },
262   { "+impatient", ".impatient", XrmoptionNoArg, "False" },
263 
264   { "-lava-color",   ".lavaColor",   XrmoptionSepArg, 0 },
265   { "-fluid-color",  ".fluidColor",  XrmoptionSepArg, 0 },
266   { "-base-color",   ".baseColor",   XrmoptionSepArg, 0 },
267   { "-table-color",  ".tableColor",  XrmoptionSepArg, 0 },
268 
269   { "-fluid-texture",".fluidTexture",  XrmoptionSepArg, 0 },
270   { "-base-texture", ".baseTexture",   XrmoptionSepArg, 0 },
271   { "-table-texture",".tableTexture",  XrmoptionSepArg, 0 },
272 };
273 
274 static argtype vars[] = {
275   {&do_style,     "style",      "Style",      DEF_STYLE,      t_String},
276   {&do_spin,      "spin",       "Spin",       DEF_SPIN,       t_String},
277   {&do_wander,    "wander",     "Wander",     DEF_WANDER,     t_Bool},
278   {&speed,        "speed",      "Speed",      DEF_SPEED,      t_Float},
279   {&resolution,   "resolution", "Resolution", DEF_RESOLUTION, t_Int},
280   {&do_smooth,    "smooth",     "Smooth",     DEF_SMOOTH,     t_Bool},
281   {&do_impatient, "impatient",  "Impatient",  DEF_IMPATIENT,  t_Bool},
282 
283   {&lava_color_str,  "lavaColor",    "LavaColor",  DEF_LCOLOR, t_String},
284   {&fluid_color_str, "fluidColor",   "FluidColor", DEF_FCOLOR, t_String},
285   {&base_color_str,  "baseColor",    "BaseColor",  DEF_BCOLOR, t_String},
286   {&table_color_str, "tableColor",   "TableColor", DEF_TCOLOR, t_String},
287 
288   {&fluid_tex,       "fluidTexture", "FluidTexture", DEF_FTEX, t_String},
289   {&base_tex,        "baseTexture",  "BaseTexture",  DEF_BTEX, t_String},
290   {&table_tex,       "tableTexture", "BaseTexture",  DEF_TTEX, t_String},
291 };
292 
293 ENTRYPOINT ModeSpecOpt lavalite_opts = {countof(opts), opts, countof(vars), vars, NULL};
294 
295 
296 /* Window management, etc
297  */
298 ENTRYPOINT void
reshape_lavalite(ModeInfo * mi,int width,int height)299 reshape_lavalite (ModeInfo *mi, int width, int height)
300 {
301   GLfloat h = (GLfloat) height / (GLfloat) width;
302   int y = 0;
303 
304   if (width > height * 5) {   /* tiny window: show middle */
305     height = width * 3;
306     y = -height/2;
307     h = height / (GLfloat) width;
308   }
309 
310   glViewport (0, y, (GLint) width, (GLint) height);
311 
312   glMatrixMode(GL_PROJECTION);
313   glLoadIdentity();
314   gluPerspective (30.0, 1/h, 1.0, 100.0);
315 
316   glMatrixMode(GL_MODELVIEW);
317   glLoadIdentity();
318   gluLookAt( 0.0, 0.0, 30.0,
319              0.0, 0.0, 0.0,
320              0.0, 1.0, 0.0);
321 
322   glClear(GL_COLOR_BUFFER_BIT);
323 }
324 
325 
326 
327 /* Textures
328  */
329 
330 static Bool
load_texture(ModeInfo * mi,const char * filename)331 load_texture (ModeInfo *mi, const char *filename)
332 {
333   Display *dpy = mi->dpy;
334   Visual *visual = mi->xgwa.visual;
335   char buf[1024];
336   XImage *image;
337 
338   if (!filename ||
339       !*filename ||
340       !strcasecmp (filename, "(none)"))
341     {
342       glDisable (GL_TEXTURE_2D);
343       return False;
344     }
345 
346   image = file_to_ximage (dpy, visual, filename);
347   if (!image) return False;
348 
349   clear_gl_error();
350   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
351                image->width, image->height, 0,
352                GL_RGBA, GL_UNSIGNED_BYTE, image->data);
353   sprintf (buf, "texture: %.100s (%dx%d)",
354            filename, image->width, image->height);
355   check_gl_error(buf);
356 
357   glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
358   glPixelStorei (GL_UNPACK_ROW_LENGTH, image->width);
359   glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
360   glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
361   glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
362   glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
363   glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
364 
365   glEnable (GL_TEXTURE_2D);
366   return True;
367 }
368 
369 
370 
371 /* Generating the lamp's bottle, caps, and base.
372  */
373 
374 static int
draw_disc(GLfloat r,GLfloat z,int faces,Bool up_p,Bool wire)375 draw_disc (GLfloat r, GLfloat z, int faces, Bool up_p, Bool wire)
376 {
377   int j;
378   GLfloat th;
379   GLfloat step = M_PI * 2 / faces;
380   int polys = 0;
381   GLfloat x, y;
382 
383   glFrontFace (up_p ? GL_CW : GL_CCW);
384   glNormal3f (0, (up_p ? 1 : -1), 0);
385   glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLES);
386 
387   x = r;
388   y = 0;
389 
390   for (j = 0, th = 0; j <= faces; j++)
391     {
392       glTexCoord2f (-j / (GLfloat) faces, 1);
393       glVertex3f (0, z, 0);
394 
395       glTexCoord2f (-j / (GLfloat) faces, 0);
396       glVertex3f (x, z, y);
397 
398       th += step;
399       x  = r * cos (th);
400       y  = r * sin (th);
401 
402       glTexCoord2f (-j / (GLfloat) faces, 0);
403       glVertex3f (x, z, y);
404 
405       polys++;
406     }
407   glEnd();
408 
409   return polys;
410 }
411 
412 
413 static int
draw_tube(GLfloat r0,GLfloat r1,GLfloat z0,GLfloat z1,GLfloat t0,GLfloat t1,int faces,Bool inside_out_p,Bool smooth_p,Bool wire)414 draw_tube (GLfloat r0, GLfloat r1,
415            GLfloat z0, GLfloat z1,
416            GLfloat t0, GLfloat t1,
417            int faces, Bool inside_out_p, Bool smooth_p, Bool wire)
418 {
419   int polys = 0;
420   GLfloat th;
421   GLfloat x, y, x0=0, y0=0;
422   GLfloat step = M_PI * 2 / faces;
423   GLfloat s2 = step/2;
424   int i;
425 
426   glFrontFace (inside_out_p ? GL_CW : GL_CCW);
427   glBegin (wire ? GL_LINES : (smooth_p ? GL_QUAD_STRIP : GL_QUADS));
428 
429   th = 0;
430   x = 1;
431   y = 0;
432 
433   if (!smooth_p)
434     {
435       x0 = cos (s2);
436       y0 = sin (s2);
437     }
438 
439   if (smooth_p) faces++;
440 
441   for (i = 0; i < faces; i++)
442     {
443       int nsign = (inside_out_p ? -1 : 1);
444 
445       if (smooth_p)
446         glNormal3f (x  * nsign, z1, y  * nsign);
447       else
448         glNormal3f (x0 * nsign, z1, y0 * nsign);
449 
450       glTexCoord2f (nsign * -i / (GLfloat) faces, 1-t1);
451       glVertex3f (x * r1, z1, y * r1);
452 
453       glTexCoord2f (nsign * -i / (GLfloat) faces, 1-t0);
454       glVertex3f (x * r0, z0, y * r0);
455 
456       th += step;
457       x  = cos (th);
458       y  = sin (th);
459 
460       if (!smooth_p)
461         {
462           x0 = cos (th + s2);
463           y0 = sin (th + s2);
464 
465           glTexCoord2f (nsign * -(i+1) / (double) faces, 1-t0);
466           glVertex3f (x * r0, z0, y * r0);
467 
468           glTexCoord2f (nsign * -(i+1) / (double) faces, 1-t1);
469           glVertex3f (x * r1, z1, y * r1);
470         }
471 
472       polys++;
473     }
474   glEnd();
475 
476   return polys;
477 }
478 
479 
480 static int
draw_table(GLfloat z,Bool wire)481 draw_table (GLfloat z, Bool wire)
482 {
483   GLfloat faces = 6;
484   GLfloat step = M_PI * 2 / faces;
485   GLfloat s = 8;
486   GLfloat th;
487   int j;
488   int polys = 0;
489 
490   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, table_color);
491 
492   glFrontFace(GL_CW);
493   glNormal3f(0, 1, 0);
494   glBegin(wire ? GL_LINE_LOOP : GL_TRIANGLE_FAN);
495 
496   if (! wire)
497     {
498       glTexCoord2f (-0.5, 0.5);
499       glVertex3f(0, z, 0);
500     }
501 
502   for (j = 0, th = 0; j <= faces; j++)
503     {
504       GLfloat x = cos (th);
505       GLfloat y = sin (th);
506       glTexCoord2f (-(x+1)/2.0, (y+1)/2.0);
507       glVertex3f(x*s, z, y*s);
508       th += step;
509       polys++;
510     }
511   glEnd();
512   return polys;
513 }
514 
515 
516 static int
draw_wing(GLfloat w,GLfloat h,GLfloat d,Bool wire)517 draw_wing (GLfloat w, GLfloat h, GLfloat d, Bool wire)
518 {
519   static const int coords[2][8][2] = {
520     { {  0,   0 },
521       { 10,  10 },
522       { 20,  23 },
523       { 30,  41 },
524       { 40,  64 },
525       { 45,  81 },
526       { 50, 103 },
527       { 53, 134 } },
528     { {  0,  54 },
529       { 10,  57 },
530       { 20,  64 },
531       { 30,  75 },
532       { 40,  92 },
533       { 45, 104 },
534       { 50, 127 },
535       { 51, 134 }
536     }
537   };
538 
539   int polys = 0;
540   int maxx = coords[0][countof(coords[0])-1][0];
541   int maxy = coords[0][countof(coords[0])-1][1];
542   unsigned int x;
543 
544   for (x = 1; x < countof(coords[0]); x++)
545     {
546       GLfloat px0 = (GLfloat) coords[0][x-1][0] / maxx * w;
547       GLfloat py0 = (GLfloat) coords[0][x-1][1] / maxy * h;
548       GLfloat px1 = (GLfloat) coords[1][x-1][0] / maxx * w;
549       GLfloat py1 = (GLfloat) coords[1][x-1][1] / maxy * h;
550       GLfloat px2 = (GLfloat) coords[0][x  ][0] / maxx * w;
551       GLfloat py2 = (GLfloat) coords[0][x  ][1] / maxy * h;
552       GLfloat px3 = (GLfloat) coords[1][x  ][0] / maxx * w;
553       GLfloat py3 = (GLfloat) coords[1][x  ][1] / maxy * h;
554       GLfloat zz = d/2;
555 
556       /* Left side
557        */
558       glFrontFace (GL_CW);
559       glNormal3f (0, 0, -1);
560       glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
561 
562       glTexCoord2f (px0, py0); glVertex3f (px0, -py0, -zz);
563       glTexCoord2f (px1, py1); glVertex3f (px1, -py1, -zz);
564       glTexCoord2f (px3, py3); glVertex3f (px3, -py3, -zz);
565       glTexCoord2f (px2, py2); glVertex3f (px2, -py2, -zz);
566       polys++;
567       glEnd();
568 
569       /* Right side
570        */
571       glFrontFace (GL_CCW);
572       glNormal3f (0, 0, -1);
573       glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
574       glTexCoord2f(px0, py0); glVertex3f (px0, -py0, zz);
575       glTexCoord2f(px1, py1); glVertex3f (px1, -py1, zz);
576       glTexCoord2f(px3, py3); glVertex3f (px3, -py3, zz);
577       glTexCoord2f(px2, py2); glVertex3f (px2, -py2, zz);
578       polys++;
579       glEnd();
580 
581       /* Top edge
582        */
583       glFrontFace (GL_CCW);
584       glNormal3f (1, -1, 0); /* #### wrong */
585       glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
586       glTexCoord2f(px0, py0); glVertex3f (px0, -py0, -zz);
587       glTexCoord2f(px0, py0); glVertex3f (px0, -py0,  zz);
588       glTexCoord2f(px2, py2); glVertex3f (px2, -py2,  zz);
589       glTexCoord2f(px2, py2); glVertex3f (px2, -py2, -zz);
590       polys++;
591       glEnd();
592 
593       /* Bottom edge
594        */
595       glFrontFace (GL_CW);
596       glNormal3f (-1, 1, 0); /* #### wrong */
597       glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
598       glTexCoord2f(px1, py1); glVertex3f (px1, -py1, -zz);
599       glTexCoord2f(px1, py1); glVertex3f (px1, -py1,  zz);
600       glTexCoord2f(px3, py3); glVertex3f (px3, -py3,  zz);
601       glTexCoord2f(px3, py3); glVertex3f (px3, -py3, -zz);
602       polys++;
603       glEnd();
604 
605 
606     }
607 
608   return polys;
609 
610 }
611 
612 
613 static void
generate_bottle(ModeInfo * mi)614 generate_bottle (ModeInfo *mi)
615 {
616   lavalite_configuration *bp = &bps[MI_SCREEN(mi)];
617   int wire = MI_IS_WIREFRAME(mi);
618   int faces = resolution * 1.5;
619   Bool smooth = do_smooth;
620 
621   const lamp_geometry *top_slice = bp->model;
622   const char *current_texture = 0;
623   lamp_part last_part = 0;
624 
625   if (faces < 3)  faces = 3;
626   else if (wire && faces > 20) faces = 20;
627   else if (faces > 60) faces = 60;
628 
629   bp->bottle_poly_count = 0;
630 
631   glNewList (bp->bottle_list, GL_COMPILE);
632   glPushMatrix();
633 
634   glRotatef (90, 1, 0, 0);
635   glTranslatef (0, -0.5, 0);
636 
637   /* All parts of the lamp use the same specularity and shininess. */
638   glMaterialfv (GL_FRONT, GL_SPECULAR,  lava_spec);
639   glMateriali  (GL_FRONT, GL_SHININESS, lava_shininess);
640 
641   while (1)
642     {
643       const lamp_geometry *bot_slice = top_slice + 1;
644 
645       const char *texture = 0;
646       GLfloat *color = 0;
647       GLfloat t0, t1;
648 
649       glDisable (GL_LIGHT2);
650 
651       switch (top_slice->part)
652         {
653         case CAP:
654         case BASE:
655           texture = base_tex;
656           color   = base_color;
657           break;
658         case BOTTLE:
659           texture = fluid_tex;
660           color   = fluid_color;
661           if (!wire) glEnable (GL_LIGHT2);   /* light2 affects only fluid */
662           break;
663         default:
664           abort();
665           break;
666         }
667 
668       if (!wire && texture && texture != current_texture)
669         {
670           current_texture = texture;
671           load_texture (mi, current_texture);
672         }
673 
674       /* Color the discs darker than the tube walls. */
675       glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, foot_color);
676 
677       /* Do a top disc if this is the first slice of the CAP or BASE.
678        */
679       if ((top_slice->part == CAP  && last_part == 0) ||
680           (top_slice->part == BASE && last_part == BOTTLE))
681         bp->bottle_poly_count +=
682           draw_disc (top_slice->radius, top_slice->elevation, faces,
683                      True, wire);
684 
685       /* Do a bottom disc if this is the last slice of the CAP or BASE.
686        */
687       if ((top_slice->part == CAP  && bot_slice->part == BOTTLE) ||
688           (top_slice->part == BASE && bot_slice->part == 0))
689         {
690           const lamp_geometry *sl = (bot_slice->part == 0
691                                      ? top_slice : bot_slice);
692           bp->bottle_poly_count +=
693             draw_disc (sl->radius, sl->elevation, faces, False, wire);
694         }
695 
696       if (bot_slice->part == 0)    /* done! */
697         break;
698 
699       /* Do a tube or cone
700        */
701       glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
702 
703       t0 = top_slice->texture_elevation;
704       t1 = bot_slice->texture_elevation;
705 
706       /* Restart the texture coordinates for the glass.
707        */
708       if (top_slice->part == BOTTLE)
709         {
710           Bool first_p = (top_slice[-1].part != BOTTLE);
711           Bool last_p  = (bot_slice->part    != BOTTLE);
712           if (first_p) t0 = 0;
713           if (last_p)  t1 = 1;
714         }
715 
716       bp->bottle_poly_count +=
717         draw_tube (top_slice->radius, bot_slice->radius,
718                    top_slice->elevation, bot_slice->elevation,
719                    t0, t1,
720                    faces,
721                    (top_slice->part == BOTTLE),
722                    smooth, wire);
723 
724       last_part = top_slice->part;
725       top_slice++;
726     }
727 
728   if (bp->style == ROCKET)
729     {
730       int i;
731       for (i = 0; i < 3; i++)
732         {
733           glPushMatrix();
734           glRotatef (120 * i, 0, 1, 0);
735           glTranslatef (0.14, -0.05, 0);
736           bp->bottle_poly_count += draw_wing (0.4, 0.95, 0.02, wire);
737           glPopMatrix();
738         }
739       glTranslatef (0, -0.1, 0);  /* move floor down a little */
740     }
741 
742 
743   if (!wire) load_texture (mi, table_tex);
744   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, table_color);
745   bp->bottle_poly_count += draw_table (top_slice->elevation, wire);
746 
747 
748   glPopMatrix ();
749   glDisable (GL_TEXTURE_2D);   /* done with textured objects */
750   glEndList ();
751 }
752 
753 
754 
755 
756 /* Generating blobbies
757  */
758 
759 static double
bellrand(double extent)760 bellrand (double extent)    /* like frand(), but a bell curve. */
761 {
762   return (((frand(extent) + frand(extent) + frand(extent)) / 3)
763           - (extent/2));
764 }
765 
766 
767 static void move_ball (ModeInfo *mi, metaball *b);
768 
769 /* Bring a ball into play, and re-randomize its values.
770  */
771 static void
reset_ball(ModeInfo * mi,metaball * b)772 reset_ball (ModeInfo *mi, metaball *b)
773 {
774 /*  lavalite_configuration *bp = &bps[MI_SCREEN(mi)]; */
775 
776   b->r = 0.00001;
777   b->R = 0.12 + bellrand(0.10);
778 
779   b->pos_r = bellrand (0.9);
780   b->pos_th = frand(M_PI*2);
781   b->z = 0;
782 
783   b->dr = bellrand(TILT);
784   b->dz = CONVECTION;
785 
786   b->leader = 0;
787 
788   if (!b->alive_p)
789     b->alive_p = True;
790 
791   move_ball (mi, b);
792 }
793 
794 
795 /* returns the first metaball that is not in use, or 0.
796  */
797 static metaball *
get_ball(ModeInfo * mi)798 get_ball (ModeInfo *mi)
799 {
800   lavalite_configuration *bp = &bps[MI_SCREEN(mi)];
801   int i;
802   for (i = 0; i < bp->nballs; i++)
803     {
804       metaball *b = &bp->balls[i];
805       if (!b->alive_p)
806         return b;
807     }
808   return 0;
809 }
810 
811 
812 /* Generate the blobs that don't move: the ones at teh top and bottom
813    that are part of the scenery.
814  */
815 static void
generate_static_blobs(ModeInfo * mi)816 generate_static_blobs (ModeInfo *mi)
817 {
818   lavalite_configuration *bp = &bps[MI_SCREEN(mi)];
819   metaball *b0, *b1;
820   int i;
821 
822   b0 = get_ball (mi);
823   if (!b0) abort();
824   b0->static_p = True;
825   b0->alive_p = True;
826   b0->R = 0.6;
827   b0->r = 0.3;
828 
829   /* the giant blob at the bottom of the bottle.
830    */
831   b0->pos_r = 0;
832   b0->pos_th = 0;
833   b0->dr = 0;
834   b0->dz = 0;
835   b0->x = 0;
836   b0->y = 0;
837   b0->z = -0.43;
838 
839   /* the small blob at the top of the bottle.
840    */
841   b1 = get_ball (mi);
842   if (!b1) abort();
843 
844   *b1 = *b0;
845   b1->R = 0.16;
846   b1->r = 0.135;
847   b1->z = 1.078;
848 
849   /* Some extra blobs at the bottom of the bottle, to jumble the surface.
850    */
851   for (i = 0; i < bp->blobs_per_group; i++)
852     {
853       b1 = get_ball (mi);
854       if (!b1) abort();
855       reset_ball (mi, b1);
856       b1->static_p = True;
857       b1->z = frand(0.04);
858       b1->dr = 0;
859       b1->dz = 0;
860     }
861 }
862 
863 
864 static GLfloat
max_bottle_radius(lavalite_configuration * bp)865 max_bottle_radius (lavalite_configuration *bp)
866 {
867   GLfloat r = 0;
868   const lamp_geometry *slice;
869   for (slice = bp->model; slice->part != 0; slice++)
870     {
871       if (slice->part == BOTTLE && slice->radius > r)
872         r = slice->radius;      /* top */
873       if (slice[1].radius > r)
874         r = slice[1].radius;    /* bottom */
875     }
876   return r;
877 }
878 
879 
880 static GLfloat
bottle_radius_at(lavalite_configuration * bp,GLfloat z)881 bottle_radius_at (lavalite_configuration *bp, GLfloat z)
882 {
883   GLfloat topz = -999, botz = -999, topr = 0, botr = 0;
884   const lamp_geometry *slice;
885   GLfloat ratio;
886 
887   for (slice = bp->model; slice->part != 0; slice++)
888     if (z > slice->elevation)
889       {
890         slice--;
891         topz = slice->elevation;
892         topr = slice->radius;
893         break;
894       }
895   if (topz == -999) return 0;
896 
897   for (; slice->part != 0; slice++)
898     if (z > slice->elevation)
899       {
900         botz = slice->elevation;
901         botr = slice->radius;
902         break;
903       }
904   if (botz == -999) return 0;
905 
906   ratio = (z - botz) / (topz - botz);
907 
908   return (botr + ((topr - botr) * ratio));
909 }
910 
911 
912 static void
move_ball(ModeInfo * mi,metaball * b)913 move_ball (ModeInfo *mi, metaball *b)
914 {
915   lavalite_configuration *bp = &bps[MI_SCREEN(mi)];
916   double gravity = GRAVITY;
917   double real_r;
918 
919   if (b->static_p) return;
920 
921   b->pos_r += b->dr;
922   b->z     += b->dz;
923 
924   b->dz -= gravity;
925 
926   if (b->pos_r > 0.9)
927     {
928       b->pos_r = 0.9;
929       b->dr = -b->dr;
930     }
931   else if (b->pos_r < 0)
932     {
933       b->pos_r = -b->pos_r;
934       b->dr = -b->dr;
935     }
936 
937   real_r = b->pos_r * bottle_radius_at (bp, b->z);
938 
939   b->x = cos (b->pos_th) * real_r;
940   b->y = sin (b->pos_th) * real_r;
941 
942   if (b->z < -b->R)  /* dropped below bottom of glass - turn it off */
943     b->alive_p = False;
944 }
945 
946 
947 /* This function makes sure that balls that are part of a group always stay
948    relatively close to each other.
949  */
950 static void
clamp_balls(ModeInfo * mi)951 clamp_balls (ModeInfo *mi)
952 {
953   lavalite_configuration *bp = &bps[MI_SCREEN(mi)];
954   int i;
955   for (i = 0; i < bp->nballs; i++)
956     {
957       metaball *b = &bp->balls[i];
958       if (b->alive_p && b->leader)
959         {
960           double zslack = 0.1;
961           double minz = b->leader->z - zslack;
962           double maxz = b->leader->z + zslack;
963 
964           /* Try to keep the Z values near those of the leader.
965              Don't let it go out of range (above or below) and clamp it
966              if it does.  If we've clamped it, make sure dz will be
967              moving it in the right direction (back toward the leader.)
968 
969              We aren't currently clamping r, only z -- doesn't seem to
970              be needed.
971 
972              This is kind of flaky, I think.  Sometimes you can see
973              the blobbies "twitch".  That's no good.
974            */
975 
976           if (b->z < minz)
977             {
978               if (b->dz < 0) b->dz = -b->dz;
979               b->z = minz - b->dz;
980             }
981 
982           if (b->z > maxz)
983             {
984               if (b->dz > 0) b->dz = -b->dz;
985               b->z = maxz + b->dz;
986             }
987         }
988     }
989 }
990 
991 
992 static void
move_balls(ModeInfo * mi)993 move_balls (ModeInfo *mi)   /* for great justice */
994 {
995   lavalite_configuration *bp = &bps[MI_SCREEN(mi)];
996   int i;
997   for (i = 0; i < bp->nballs; i++)
998     {
999       metaball *b = &bp->balls[i];
1000       if (b->alive_p)
1001         move_ball (mi, b);
1002     }
1003 
1004   clamp_balls (mi);
1005 }
1006 
1007 
1008 
1009 /* Rendering blobbies using marching cubes.
1010  */
1011 
1012 static double
compute_metaball_influence(lavalite_configuration * bp,double x,double y,double z,int nballs,metaball * balls)1013 compute_metaball_influence (lavalite_configuration *bp,
1014                             double x, double y, double z,
1015                             int nballs, metaball *balls)
1016 {
1017   double vv = 0;
1018   int i;
1019 
1020   for (i = 0; i < nballs; i++)
1021     {
1022       metaball *b = &balls[i];
1023       double dx, dy, dz;
1024       double d2, r, R, r2, R2;
1025 
1026       if (!b->alive_p) continue;
1027 
1028       dx = x - b->x;
1029       dy = y - b->y;
1030       dz = z - b->z;
1031       R = b->R;
1032 
1033       if (dx > R || dx < -R ||    /* quick check before multiplying */
1034           dy > R || dy < -R ||
1035           dz > R || dz < -R)
1036         continue;
1037 
1038       d2 = (dx*dx + dy*dy + dz*dz);
1039       r = b->r;
1040 
1041       r2 = r*r;
1042       R2 = R*R;
1043 
1044       if (d2 <= r2)             /* (d <= r)   inside the hard radius */
1045         vv += 1;
1046       else if (d2 > R2)         /* (d > R)   outside the radius of influence */
1047         ;
1048       else          /* somewhere in between: linear drop-off from r=1 to R=0 */
1049         {
1050           /* was: vv += 1 - ((d-r) / (R-r)); */
1051           vv += 1 - ((d2-r2) / (R2-r2));
1052         }
1053     }
1054 
1055   return vv;
1056 }
1057 
1058 
1059 /* callback for marching_cubes() */
1060 static void *
obj_init(double grid_size,void * closure)1061 obj_init (double grid_size, void *closure)
1062 {
1063   lavalite_configuration *bp = (lavalite_configuration *) closure;
1064   bp->grid_size = grid_size;
1065 
1066   return closure;
1067 }
1068 
1069 
1070 /* Returns True if the given point is outside of the glass tube.
1071  */
1072 static double
clipped_by_glass_p(double x,double y,double z,lavalite_configuration * bp)1073 clipped_by_glass_p (double x, double y, double z,
1074                     lavalite_configuration *bp)
1075 {
1076   double d2, or, or2, ir2;
1077 
1078   or = bp->max_bottle_radius;
1079 
1080   if (x > or || x < -or ||    /* quick check before multiplying */
1081       y > or || y < -or)
1082     return 0;
1083 
1084   d2 = (x*x + y*y);
1085   or = bottle_radius_at (bp, z);
1086 
1087   or2 = or*or;
1088 
1089   if (d2 > or2)   /* (sqrt(d) > or) */
1090     return 0;
1091 
1092   ir2 = or2 * 0.7;
1093 
1094   if (d2 > ir2)  /* (sqrt(d) > ir) */
1095     {
1096       double dr1 = or2;
1097       double dr2 = ir2;
1098       /* was: (1 - (d-ratio2) / (ratio1-ratio2)) */
1099       return (1 - (d2-dr2) / (dr1-dr2));
1100     }
1101 
1102   return 1;
1103 }
1104 
1105 
1106 
1107 /* callback for marching_cubes() */
1108 static double
obj_compute(double x,double y,double z,void * closure)1109 obj_compute (double x, double y, double z, void *closure)
1110 {
1111   lavalite_configuration *bp = (lavalite_configuration *) closure;
1112   double clip;
1113 
1114   x /= bp->grid_size;	/* convert from 0-N to 0-1. */
1115   y /= bp->grid_size;
1116   z /= bp->grid_size;
1117 
1118   x -= 0.5;	/* X and Y range from -.5 to +.5; z ranges from 0-1. */
1119   y -= 0.5;
1120 
1121   clip = clipped_by_glass_p (x, y, z, bp);
1122   if (clip == 0) return 0;
1123 
1124   return (clip *
1125           compute_metaball_influence (bp, x, y, z, bp->nballs, bp->balls));
1126 }
1127 
1128 
1129 /* callback for marching_cubes() */
1130 static void
obj_free(void * closure)1131 obj_free (void *closure)
1132 {
1133 }
1134 
1135 
1136 /* Send a new blob travelling upward.
1137    This blob will actually be composed of N metaballs that are near
1138    each other.
1139  */
1140 static void
launch_balls(ModeInfo * mi)1141 launch_balls (ModeInfo *mi)
1142 {
1143   lavalite_configuration *bp = &bps[MI_SCREEN(mi)];
1144   metaball *b0 = get_ball (mi);
1145   int i;
1146 
1147   if (!b0) return;
1148   reset_ball (mi, b0);
1149 
1150   for (i = 0; i < bp->blobs_per_group; i++)
1151     {
1152       metaball *b1 = get_ball (mi);
1153       if (!b1) break;
1154       *b1 = *b0;
1155 
1156       reset_ball (mi, b1);
1157       b1->leader = b0;
1158 
1159 # define FROB(FIELD,AMT) \
1160          b1->FIELD += (bellrand(AMT) * b0->FIELD)
1161 
1162    /* FROB (pos_r,  0.7); */
1163    /* FROB (pos_th, 0.7); */
1164       FROB (dr, 0.8);
1165       FROB (dz, 0.6);
1166 # undef FROB
1167     }
1168 
1169 }
1170 
1171 
1172 static void
animate_lava(ModeInfo * mi)1173 animate_lava (ModeInfo *mi)
1174 {
1175   lavalite_configuration *bp = &bps[MI_SCREEN(mi)];
1176   int wire = MI_IS_WIREFRAME(mi);
1177   Bool just_started_p = bp->just_started_p;
1178 
1179   double isolevel = 0.3;
1180 
1181   /* Maybe bubble a new blobby to the surface.
1182    */
1183   if (just_started_p ||
1184       frand(1.0) < bp->launch_chance)
1185     {
1186       bp->just_started_p = False;
1187       launch_balls (mi);
1188 
1189       if (do_impatient && just_started_p)
1190         while (1)
1191           {
1192             int i;
1193             move_balls (mi);
1194             for (i = 0; i < bp->nballs; i++)
1195               {
1196                 metaball *b = &bp->balls[i];
1197                 if (b->alive_p && !b->static_p && !b->leader &&
1198                     b->z > 0.5)
1199                   goto DONE;
1200               }
1201           }
1202       DONE: ;
1203     }
1204 
1205   move_balls (mi);
1206 
1207   glNewList (bp->ball_list, GL_COMPILE);
1208   glPushMatrix();
1209 
1210   glMaterialfv (GL_FRONT, GL_SPECULAR,            lava_spec);
1211   glMateriali  (GL_FRONT, GL_SHININESS,           lava_shininess);
1212   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, lava_color);
1213 
1214   /* For the blobbies, the origin is on the axis at the bottom of the
1215      glass bottle; and the top of the bottle is +1 on Z.
1216    */
1217   glTranslatef (0, 0, -0.5);
1218 
1219   mi->polygon_count = 0;
1220   {
1221     double s;
1222     if (bp->grid_size == 0) bp->grid_size = 1;  /* first time through */
1223     s = 1.0/bp->grid_size;
1224 
1225     glPushMatrix();
1226     glTranslatef (-0.5, -0.5, 0);
1227     glScalef (s, s, s);
1228     marching_cubes (resolution, isolevel, wire, do_smooth,
1229                     obj_init, obj_compute, obj_free, bp,
1230                     &mi->polygon_count);
1231     glPopMatrix();
1232   }
1233 
1234   mi->polygon_count += bp->bottle_poly_count;
1235 
1236   glPopMatrix();
1237   glEndList ();
1238 }
1239 
1240 
1241 
1242 /* Startup initialization
1243  */
1244 
1245 ENTRYPOINT Bool
lavalite_handle_event(ModeInfo * mi,XEvent * event)1246 lavalite_handle_event (ModeInfo *mi, XEvent *event)
1247 {
1248   lavalite_configuration *bp = &bps[MI_SCREEN(mi)];
1249 
1250   if (gltrackball_event_handler (event, bp->trackball,
1251                                  MI_WIDTH (mi), MI_HEIGHT (mi),
1252                                  &bp->button_down_p))
1253     return True;
1254 
1255   return False;
1256 }
1257 
1258 
1259 static void
parse_color(ModeInfo * mi,const char * name,const char * s,GLfloat * a)1260 parse_color (ModeInfo *mi, const char *name, const char *s, GLfloat *a)
1261 {
1262   XColor c;
1263   a[3] = 1.0;  /* alpha */
1264 
1265   if (! XParseColor (MI_DISPLAY(mi), MI_COLORMAP(mi), s, &c))
1266     {
1267       fprintf (stderr, "%s: can't parse %s color %s", progname, name, s);
1268       exit (1);
1269     }
1270   a[0] = c.red   / 65536.0;
1271   a[1] = c.green / 65536.0;
1272   a[2] = c.blue  / 65536.0;
1273 }
1274 
1275 
1276 ENTRYPOINT void
init_lavalite(ModeInfo * mi)1277 init_lavalite (ModeInfo *mi)
1278 {
1279   lavalite_configuration *bp;
1280   int wire = MI_IS_WIREFRAME(mi);
1281 
1282   MI_INIT (mi, bps);
1283 
1284   bp = &bps[MI_SCREEN(mi)];
1285 
1286   bp->glx_context = init_GL(mi);
1287 
1288   reshape_lavalite (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1289 
1290   {
1291     char *s = do_style;
1292     if (!s || !*s || !strcasecmp (s, "classic")) bp->style = CLASSIC;
1293     else if (!strcasecmp (s, "giant"))  bp->style = GIANT;
1294     else if (!strcasecmp (s, "cone"))   bp->style = CONE;
1295     else if (!strcasecmp (s, "rocket")) bp->style = ROCKET;
1296     else if (!strcasecmp (s, "random"))
1297       {
1298         if (random() & 1) bp->style = CLASSIC;  /* half the time */
1299         else bp->style = (random() % ((int) ROCKET+1));
1300       }
1301     else
1302       {
1303         fprintf (stderr,
1304          "%s: style must be Classic, Giant, Cone, or Rocket (not \"%s\")\n",
1305                  progname, s);
1306         exit (1);
1307       }
1308   }
1309 
1310   parse_color (mi, "lava",  lava_color_str,  lava_color);
1311   parse_color (mi, "fluid", fluid_color_str, fluid_color);
1312   parse_color (mi, "base",  base_color_str,  base_color);
1313   parse_color (mi, "table", table_color_str, table_color);
1314 
1315   if (!wire)
1316     {
1317       GLfloat amb[4]  = {0.0, 0.0, 0.0, 1.0};
1318       GLfloat dif[4]  = {1.0, 1.0, 1.0, 1.0};
1319       GLfloat spc0[4] = {0.0, 1.0, 1.0, 1.0};
1320       GLfloat spc1[4] = {1.0, 0.0, 1.0, 1.0};
1321 
1322       glEnable(GL_LIGHTING);
1323       glEnable(GL_LIGHT0);
1324       glEnable(GL_LIGHT1);
1325       glEnable(GL_DEPTH_TEST);
1326       glEnable(GL_CULL_FACE);
1327       glEnable(GL_NORMALIZE);
1328       glShadeModel(GL_SMOOTH);
1329 
1330       glLightfv(GL_LIGHT0, GL_AMBIENT,  amb);
1331       glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif);
1332       glLightfv(GL_LIGHT0, GL_SPECULAR, spc0);
1333 
1334       glLightfv(GL_LIGHT1, GL_AMBIENT,  amb);
1335       glLightfv(GL_LIGHT1, GL_DIFFUSE,  dif);
1336       glLightfv(GL_LIGHT1, GL_SPECULAR, spc1);
1337 
1338       glLightfv(GL_LIGHT2, GL_AMBIENT,  amb);
1339       glLightfv(GL_LIGHT2, GL_DIFFUSE,  dif);
1340       glLightfv(GL_LIGHT2, GL_SPECULAR, spc0);
1341     }
1342 
1343   {
1344     Bool spinx=False, spiny=False, spinz=False;
1345     double spin_speed   = 0.4;
1346     double wander_speed = 0.03;
1347 
1348     char *s = do_spin;
1349     while (*s)
1350       {
1351         if      (*s == 'x' || *s == 'X') spinx = True;
1352         else if (*s == 'y' || *s == 'Y') spiny = True;
1353         else if (*s == 'z' || *s == 'Z') spinz = True;
1354         else if (*s == '0') ;
1355         else
1356           {
1357             fprintf (stderr,
1358          "%s: spin must contain only the characters X, Y, or Z (not \"%s\")\n",
1359                      progname, do_spin);
1360             exit (1);
1361           }
1362         s++;
1363       }
1364 
1365     bp->rot = make_rotator (spinx ? spin_speed : 0,
1366                             spiny ? spin_speed : 0,
1367                             spinz ? spin_speed : 0,
1368                             1.0,
1369                             do_wander ? wander_speed : 0,
1370                             False);
1371     bp->rot2 = make_rotator (spin_speed, 0, 0,
1372                              1, 0.1,
1373                              False);
1374     bp->trackball = gltrackball_init (False);
1375 
1376     /* Tilt the scene a bit: lean the normal lamps toward the viewer,
1377        and the huge lamps away. */
1378     gltrackball_reset (bp->trackball,
1379                        -0.3 + frand(0.6),
1380                        (bp->style == ROCKET || bp->style == GIANT
1381                         ?  frand (0.2)
1382                         : -frand (0.6)));
1383   }
1384 
1385   switch (bp->style)
1386     {
1387     case CLASSIC: bp->model = classic_lamp; break;
1388     case GIANT:   bp->model = giant_lamp;   break;
1389     case CONE:    bp->model = cone_lamp;    break;
1390     case ROCKET:  bp->model = rocket_lamp;  break;
1391     default: abort(); break;
1392     }
1393 
1394   bp->max_bottle_radius = max_bottle_radius (bp);
1395 
1396   bp->launch_chance = speed;
1397   bp->blobs_per_group = BLOBS_PER_GROUP;
1398   bp->just_started_p = True;
1399 
1400   bp->nballs = (((MI_COUNT (mi) + 1) * bp->blobs_per_group)
1401                 + 2);
1402   bp->balls = (metaball *) calloc (sizeof(*bp->balls), bp->nballs+1);
1403 
1404   bp->bottle_list = glGenLists (1);
1405   bp->ball_list = glGenLists (1);
1406 
1407   generate_bottle (mi);
1408   generate_static_blobs (mi);
1409 }
1410 
1411 
1412 /* Render one frame
1413  */
1414 
1415 ENTRYPOINT void
draw_lavalite(ModeInfo * mi)1416 draw_lavalite (ModeInfo *mi)
1417 {
1418   lavalite_configuration *bp = &bps[MI_SCREEN(mi)];
1419   Display *dpy = MI_DISPLAY(mi);
1420   Window window = MI_WINDOW(mi);
1421 
1422   if (!bp->glx_context)
1423     return;
1424 
1425   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *bp->glx_context);
1426 
1427   glMatrixMode (GL_MODELVIEW);
1428   glPushMatrix ();
1429 
1430   {
1431     double cx, cy, cz; /* camera position, 0-1. */
1432     double px, py, pz; /* object position, 0-1. */
1433     double rx, ry, rz; /* object rotation, 0-1. */
1434 
1435     get_position (bp->rot2, 0,   &cy, &cz, !bp->button_down_p);
1436     get_rotation (bp->rot2, &cx, 0,   0,   !bp->button_down_p);
1437 
1438     get_position (bp->rot,  &px, &py, &pz, !bp->button_down_p);
1439     get_rotation (bp->rot,  &rx, &ry, &rz, !bp->button_down_p);
1440 
1441 #if 1
1442     cx = 0.5;
1443     cy = 0.5;
1444     cz = 1.0;
1445 
1446 #else  /* #### this crud doesn't really work yet */
1447 
1448 
1449     /* We have c[xyz] parameters describing a camera position, but we don't
1450        want to just map those to points in space: the lamp doesn't look very
1451        good from the inside, or from underneath...
1452 
1453        Good observation points form a ring around the lamp: basically, a
1454        torus ringing the lamp, parallel to the lamp's floor.
1455 
1456        We interpret cz as distance from the origin.
1457        cy as elevation.
1458        cx is then used as position in the torus (theta).
1459      */
1460 
1461     {
1462       double cx2, cy2, cz2;
1463       double d;
1464 
1465       cx2 = 0.5;
1466       cy2 = 0.5;
1467       cz2 = 1.0;
1468 
1469       cy2 = (cy * 0.4);		/* cam elevation: 0.0 (table) - 0.4 up. */
1470       d = 0.9 + cz;		/* cam distance: 0.9 - 1.9. */
1471 
1472       cz2 = 0.5 + (d * cos (cx * M_PI * 2));
1473       cx2 = 0.5 + (d * sin (cx * M_PI * 2));
1474 
1475 
1476       cx = cx2;
1477       cy = cy2;
1478       cz = cz2;
1479     }
1480 #endif  /* 0 */
1481 
1482     glLoadIdentity();
1483     glRotatef(current_device_rotation(), 0, 0, 1);
1484 
1485     gluLookAt ((cx - 0.5) * 8,		/* Position the camera */
1486                (cy - 0.5) * 8,
1487                (cz - 0.5) * 8,
1488                0, 0, 0,
1489                0, 1, 0);
1490 
1491     gltrackball_rotate (bp->trackball);	/* Apply mouse-based camera position */
1492 
1493     glRotatef (-90, 1, 0, 0);  /* Right side up */
1494 
1495 
1496     /* Place the lights relative to the object, before the object has
1497        been rotated or wandered within the scene. */
1498     glLightfv(GL_LIGHT0, GL_POSITION, light0_pos);
1499     glLightfv(GL_LIGHT1, GL_POSITION, light1_pos);
1500     glLightfv(GL_LIGHT2, GL_POSITION, light2_pos);
1501 
1502 
1503     /* Position the lamp in the scene according to the "wander" settings */
1504     glTranslatef ((px - 0.5), (py - 0.5), (pz - 0.5));
1505 
1506     /* Rotate the object according to the "spin" settings */
1507     glRotatef (rx * 360, 1.0, 0.0, 0.0);
1508     glRotatef (ry * 360, 0.0, 1.0, 0.0);
1509     glRotatef (rz * 360, 0.0, 0.0, 1.0);
1510 
1511     /* Move the lamp up slightly: make 0,0 be at its vertical center. */
1512     switch (bp->style)
1513       {
1514       case CLASSIC: glTranslatef (0, 0, 0.33); break;
1515       case GIANT:   glTranslatef (0, 0, 0.33); break;
1516       case CONE:    glTranslatef (0, 0, 0.16); break;
1517       case ROCKET:  glTranslatef (0, 0, 0.30);
1518                     glScalef (0.85,0.85,0.85); break;
1519       default: abort(); break;
1520       }
1521   }
1522 
1523   animate_lava (mi);
1524 
1525   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1526   glCallList (bp->bottle_list);
1527   glCallList (bp->ball_list);
1528   glPopMatrix ();
1529 
1530   if (mi->fps_p) do_fps (mi);
1531   glFinish();
1532 
1533   glXSwapBuffers(dpy, window);
1534 }
1535 
1536 
1537 ENTRYPOINT void
free_lavalite(ModeInfo * mi)1538 free_lavalite (ModeInfo *mi)
1539 {
1540   lavalite_configuration *bp = &bps[MI_SCREEN(mi)];
1541   if (!bp->glx_context) return;
1542   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *bp->glx_context);
1543   if (bp->balls) free (bp->balls);
1544   if (bp->trackball) gltrackball_free (bp->trackball);
1545   if (bp->rot) free_rotator (bp->rot);
1546   if (bp->rot2) free_rotator (bp->rot2);
1547   if (glIsList(bp->bottle_list)) glDeleteLists(bp->bottle_list, 1);
1548   if (glIsList(bp->ball_list)) glDeleteLists(bp->ball_list, 1);
1549 }
1550 
1551 XSCREENSAVER_MODULE ("Lavalite", lavalite)
1552 
1553 #endif /* USE_GL */
1554