~ubuntu-branches/ubuntu/dapper/xscreensaver/dapper-updates

« back to all changes in this revision

Viewing changes to hacks/glx/lavalite.c

  • Committer: Bazaar Package Importer
  • Author(s): Ralf Hildebrandt
  • Date: 2005-04-09 00:06:43 UTC
  • mfrom: (1.1.1 upstream)
  • mto: This revision was merged to the branch mainline in revision 3.
  • Revision ID: james.westby@ubuntu.com-20050409000643-z0abtifbt9s20pcc
Tags: 4.21-3
Patch by Joachim Breitner to check more frequently if DPMS kicked in (closes: #303374, #286664).

Show diffs side-by-side

added added

removed removed

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