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